···55 return getComputedStyle(document.body).getPropertyValue(variable).trim();
66}
7788+99+export function getHexCSSVar(variable: string) {
1010+ return convertCSSToHex(getCSSVar(variable));
1111+}
1212+1313+814/**
915 * Converts a CSS color string to a hue value in the 0-1 range
1016 */
+84
src/lib/components/qr/QRCodeDisplay.svelte
···11+<script lang="ts">
22+ import { getHexCSSVar } from '$lib/cards/helper';
33+ import { onMount } from 'svelte';
44+55+ let {
66+ url,
77+ icon,
88+ iconColor,
99+ class: className = ''
1010+ }: {
1111+ url: string;
1212+ icon?: string;
1313+ iconColor?: string;
1414+ class?: string;
1515+ } = $props();
1616+1717+ let container: HTMLDivElement | undefined = $state();
1818+1919+ // Convert SVG string to data URI for use as QR center image
2020+ function svgToDataUri(svg: string, color: string): string {
2121+ // Add fill color to SVG - insert fill attribute on the svg tag
2222+ let coloredSvg = svg;
2323+ if (!svg.includes('fill=')) {
2424+ // No fill attribute, add it to the svg tag
2525+ coloredSvg = svg.replace('<svg', `<svg fill="${color}"`);
2626+ } else {
2727+ // Replace existing fill attributes
2828+ coloredSvg = svg.replace(/fill="[^"]*"/g, `fill="${color}"`);
2929+ }
3030+ const encoded = encodeURIComponent(coloredSvg);
3131+ return `data:image/svg+xml,${encoded}`;
3232+ }
3333+3434+ onMount(async () => {
3535+ if (!container) return;
3636+3737+ // Use iconColor or accent color, ensure # prefix
3838+ const rawColor = iconColor || getHexCSSVar('--color-accent-600');
3939+ const dotColor = rawColor.startsWith('#') ? rawColor : `#${rawColor}`;
4040+4141+ const module = await import('qr-code-styling');
4242+ const QRCodeStyling = module.default;
4343+4444+ // Get container size for responsive QR
4545+ const rect = container.getBoundingClientRect();
4646+ const size = Math.min(rect.width, rect.height) || 280;
4747+4848+ const options: ConstructorParameters<typeof QRCodeStyling>[0] = {
4949+ width: size,
5050+ height: size,
5151+ data: url,
5252+ dotsOptions: {
5353+ color: dotColor,
5454+ type: 'rounded'
5555+ },
5656+ backgroundOptions: {
5757+ color: '#FFF'
5858+ },
5959+ cornersSquareOptions: {
6060+ type: 'extra-rounded',
6161+ color: dotColor
6262+ },
6363+ cornersDotOptions: {
6464+ type: 'dot',
6565+ color: dotColor
6666+ },
6767+ margin: 10
6868+ };
6969+7070+ // Add icon as center image if provided (as SVG string)
7171+ if (icon) {
7272+ options.image = svgToDataUri(icon, dotColor);
7373+ options.imageOptions = {
7474+ margin: 10,
7575+ imageSize: 0.5
7676+ };
7777+ }
7878+7979+ const qrCode = new QRCodeStyling(options);
8080+ qrCode.append(container);
8181+ });
8282+</script>
8383+8484+<div bind:this={container} class="flex items-center justify-center {className}"></div>