{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitive-avatar",
  "title": "Avatar",
  "description": "Avatar is a production-ready ui component from BJORK UI, packaged for shadcn registries.",
  "dependencies": [
    "clsx",
    "framer-motion",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "components/bjork-ui/primitives/avatar.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useReducedMotion } from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface BjorkShaderAvatarProps extends React.ComponentProps<\"div\"> {\n  initials?: string;\n  label?: string;\n  size?: number;\n}\n\nfunction useBjorkDocumentTheme() {\n  const [theme, setTheme] = React.useState<\"light\" | \"dark\">(\"dark\");\n\n  React.useEffect(() => {\n    const syncTheme = () => {\n      setTheme(document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\");\n    };\n\n    syncTheme();\n\n    const observer = new MutationObserver(syncTheme);\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\"],\n    });\n\n    return () => observer.disconnect();\n  }, []);\n\n  return theme;\n}\n\nexport function BjorkShaderAvatar({\n  initials = \"IB\",\n  label = \"Shader avatar\",\n  size = 76,\n  className,\n  style,\n  ...props\n}: BjorkShaderAvatarProps) {\n  const canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n  const shouldReduceMotion = useReducedMotion();\n  const theme = useBjorkDocumentTheme();\n  const isLightTheme = theme === \"light\";\n\n  React.useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const gl = canvas.getContext(\"webgl\", {\n      antialias: true,\n      alpha: true,\n      preserveDrawingBuffer: false,\n    });\n    if (!gl) return;\n    const canvasElement = canvas;\n    const context = gl;\n\n    const vertexSource = `\n      attribute vec2 position;\n      varying vec2 vUv;\n      void main() {\n        vUv = position * 0.5 + 0.5;\n        gl_Position = vec4(position, 0.0, 1.0);\n      }\n    `;\n    const fragmentSource = `\n      precision highp float;\n      varying vec2 vUv;\n      uniform float time;\n      uniform float lightMode;\n      uniform vec2 resolution;\n      void main() {\n        vec2 uv = vUv * 2.0 - 1.0;\n        uv.x *= resolution.x / resolution.y;\n        float wave = sin((uv.x * 3.4) + time * 0.45) + cos((uv.y * 4.2) - time * 0.38);\n        float ring = sin(length(uv) * 9.0 - time * 0.72);\n        float vignette = smoothstep(1.22, 0.08, length(uv));\n\n        if (lightMode > 0.5) {\n          vec3 linen = vec3(0.955, 0.918, 0.842);\n          vec3 clay = vec3(0.84, 0.42, 0.16);\n          vec3 ivory = vec3(1.0, 0.985, 0.945);\n          vec3 umber = vec3(0.54, 0.45, 0.34);\n          vec3 color = mix(linen, clay, smoothstep(-0.82, 1.02, wave + ring * 0.34) * 0.74);\n          color = mix(color, ivory, smoothstep(0.70, 1.0, wave * 0.32 + ring * 0.52) * 0.86);\n          color = mix(umber, color, 0.86 + vignette * 0.14);\n          gl_FragColor = vec4(color, 1.0);\n          return;\n        }\n\n        vec3 ink = vec3(0.07, 0.07, 0.07);\n        vec3 ember = vec3(0.925, 0.36, 0.075);\n        vec3 pearl = vec3(0.93, 0.93, 0.90);\n        vec3 color = mix(ink, ember, smoothstep(-0.85, 1.0, wave + ring * 0.35));\n        color = mix(color, pearl, smoothstep(0.72, 1.0, wave * 0.32 + ring * 0.55));\n        gl_FragColor = vec4(color * (0.38 + vignette * 0.82), 1.0);\n      }\n    `;\n\n    function compileShader(type: number, source: string) {\n      const shader = context.createShader(type);\n      if (!shader) return null;\n      context.shaderSource(shader, source);\n      context.compileShader(shader);\n      return context.getShaderParameter(shader, context.COMPILE_STATUS) ? shader : null;\n    }\n\n    const vertex = compileShader(context.VERTEX_SHADER, vertexSource);\n    const fragment = compileShader(context.FRAGMENT_SHADER, fragmentSource);\n    const program = context.createProgram();\n    if (!vertex || !fragment || !program) return;\n\n    context.attachShader(program, vertex);\n    context.attachShader(program, fragment);\n    context.linkProgram(program);\n    if (!context.getProgramParameter(program, context.LINK_STATUS)) return;\n    context.useProgram(program);\n\n    const buffer = context.createBuffer();\n    context.bindBuffer(context.ARRAY_BUFFER, buffer);\n    context.bufferData(\n      context.ARRAY_BUFFER,\n      new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n      context.STATIC_DRAW,\n    );\n\n    const positionLocation = context.getAttribLocation(program, \"position\");\n    const timeLocation = context.getUniformLocation(program, \"time\");\n    const lightModeLocation = context.getUniformLocation(program, \"lightMode\");\n    const resolutionLocation = context.getUniformLocation(program, \"resolution\");\n    context.enableVertexAttribArray(positionLocation);\n    context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0);\n\n    let frame = 0;\n    const startedAt = performance.now();\n\n    function resize() {\n      const rect = canvasElement.getBoundingClientRect();\n      const ratio = Math.min(window.devicePixelRatio || 1, 2);\n      canvasElement.width = Math.max(1, Math.floor(rect.width * ratio));\n      canvasElement.height = Math.max(1, Math.floor(rect.height * ratio));\n      context.viewport(0, 0, canvasElement.width, canvasElement.height);\n    }\n\n    function render(now: number) {\n      resize();\n      const elapsed = shouldReduceMotion ? 0.9 : (now - startedAt) / 1000;\n      context.uniform1f(timeLocation, elapsed);\n      context.uniform1f(lightModeLocation, isLightTheme ? 1 : 0);\n      context.uniform2f(resolutionLocation, canvasElement.width, canvasElement.height);\n      context.drawArrays(context.TRIANGLES, 0, 6);\n      if (!shouldReduceMotion) frame = window.requestAnimationFrame(render);\n    }\n\n    render(performance.now());\n\n    return () => {\n      if (frame) window.cancelAnimationFrame(frame);\n      context.deleteProgram(program);\n      context.deleteShader(vertex);\n      context.deleteShader(fragment);\n      context.deleteBuffer(buffer);\n    };\n  }, [isLightTheme, shouldReduceMotion]);\n\n  return (\n    <div\n      aria-label={label}\n      className={cn(\n        \"relative grid shrink-0 place-items-center overflow-hidden rounded-full border text-sm font-medium\",\n        isLightTheme\n          ? \"border-[#e1d7c8] bg-[#f7f3ea] text-[#5b4a37] shadow-[inset_0_1px_0_rgba(255,255,255,0.82),0_18px_34px_-26px_rgba(119,93,57,0.28)]\"\n          : \"border-[#2b2b2b] bg-[#111] text-[#fff5ef] shadow-[inset_0_1px_0_rgba(255,255,255,0.08),0_18px_34px_-24px_rgba(236,92,19,0.62)]\",\n        className,\n      )}\n      style={{ width: size, height: size, ...style }}\n      {...props}\n    >\n      <canvas ref={canvasRef} className=\"absolute inset-0 h-full w-full\" />\n      <span\n        className={cn(\n          \"relative z-10\",\n          isLightTheme\n            ? \"mix-blend-multiply drop-shadow-[0_1px_5px_rgba(255,255,255,0.5)]\"\n            : \"mix-blend-screen drop-shadow-[0_1px_8px_rgba(255,255,255,0.2)]\",\n        )}\n      >\n        {initials}\n      </span>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/primitives/avatar.tsx"
    },
    {
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n",
      "type": "registry:lib",
      "target": "@lib/utils.ts"
    }
  ],
  "cssVars": {
    "light": {
      "bjork-bg": "#f7f3ea",
      "bjork-surface": "#fffcf6",
      "bjork-surface-muted": "rgba(250, 246, 237, 0.94)",
      "bjork-surface-hover": "#f5efe3",
      "bjork-surface-active": "#efe7d8",
      "bjork-panel": "#f8f2e7",
      "bjork-menu": "rgba(255, 252, 246, 0.98)",
      "bjork-field": "rgba(255, 252, 246, 0.96)",
      "bjork-field-muted": "rgba(248, 242, 231, 0.82)",
      "bjork-field-inset": "rgba(239, 231, 216, 0.72)",
      "bjork-error-bg": "#fff0e8",
      "bjork-text": "#171717",
      "bjork-text-strong": "rgba(23, 23, 23, 0.9)",
      "bjork-text-medium": "rgba(23, 23, 23, 0.72)",
      "bjork-text-muted": "rgba(23, 23, 23, 0.52)",
      "bjork-text-soft": "rgba(23, 23, 23, 0.36)",
      "bjork-text-faint": "rgba(23, 23, 23, 0.22)",
      "bjork-inverted-text": "#fff8f0",
      "bjork-border": "#eee6db",
      "bjork-border-muted": "#f5ede2",
      "bjork-border-strong": "#e1d7c8",
      "bjork-thumb": "#a49b8e",
      "bjork-track": "#ded6ca",
      "bjork-accent": "#ec7d43",
      "bjork-accent-hover": "#f0935f",
      "bjork-accent-soft": "rgba(236, 125, 67, 0.1)",
      "bjork-accent-muted": "rgba(236, 125, 67, 0.2)",
      "bjork-accent-badge": "rgba(236, 125, 67, 0.16)",
      "bjork-accent-badge-foreground": "#8d421d",
      "bjork-accent-foreground": "#3f2112",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.92), inset 1px 0 0 rgba(88, 72, 49, 0.026), inset -1px 0 0 rgba(255, 255, 255, 0.68), 0 14px 22px -9px rgba(66, 52, 33, 0.11)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.86)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.64), 0 18px 38px -30px rgba(66, 52, 33, 0.2)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 0 12px 24px rgba(88, 72, 49, 0.022), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.62), 0 22px 44px -26px rgba(66, 52, 33, 0.22)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(88, 72, 49, 0.12), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.72)",
      "bjork-ring-offset": "#f7f3ea"
    },
    "dark": {
      "bjork-bg": "#111111",
      "bjork-surface": "#121212",
      "bjork-surface-muted": "rgba(22, 22, 22, 0.92)",
      "bjork-surface-hover": "#161616",
      "bjork-surface-active": "#202020",
      "bjork-panel": "#0d0d0d",
      "bjork-menu": "rgba(18, 18, 18, 0.98)",
      "bjork-field": "rgba(18, 18, 18, 0.95)",
      "bjork-field-muted": "rgba(18, 18, 18, 0.55)",
      "bjork-field-inset": "rgba(9, 9, 9, 0.75)",
      "bjork-error-bg": "#130d0a",
      "bjork-text": "#ededed",
      "bjork-text-strong": "rgba(237, 237, 237, 0.9)",
      "bjork-text-medium": "rgba(237, 237, 237, 0.72)",
      "bjork-text-muted": "rgba(237, 237, 237, 0.52)",
      "bjork-text-soft": "rgba(237, 237, 237, 0.36)",
      "bjork-text-faint": "rgba(237, 237, 237, 0.22)",
      "bjork-inverted-text": "#080808",
      "bjork-border": "#232323",
      "bjork-border-muted": "#1c1c1c",
      "bjork-border-strong": "#343434",
      "bjork-thumb": "#626262",
      "bjork-track": "#090909",
      "bjork-accent": "#ec5c13",
      "bjork-accent-hover": "#f07832",
      "bjork-accent-soft": "rgba(236, 92, 19, 0.12)",
      "bjork-accent-muted": "rgba(236, 92, 19, 0.24)",
      "bjork-accent-badge": "#ec5c13",
      "bjork-accent-badge-foreground": "#fff2ea",
      "bjork-accent-foreground": "#fff2ea",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(255, 255, 255, 0.03), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.06), 0 14px 20px -6px rgba(0, 0, 0, 0.45)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(255, 255, 255, 0.045)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 18px 36px -28px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(255, 255, 255, 0.055), inset 0 12px 24px rgba(255, 255, 255, 0.018), inset 0 -18px 26px rgba(0, 0, 0, 0.24), 0 22px 42px -22px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(0, 0, 0, 0.45)",
      "bjork-ring-offset": "#080808"
    }
  },
  "css": {
    ".bjork-range": {
      "box-shadow": "inset 0 1px 1px rgba(255, 255, 255, 0.04), 0 0 14px rgba(189, 69, 20, 0.06)"
    },
    ".bjork-range::-webkit-slider-thumb": {
      "appearance": "none",
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-range::-moz-range-thumb": {
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-layered-button-accent, .bjork-layered-button-raised": {
      "position": "relative",
      "isolation": "isolate",
      "appearance": "none",
      "overflow": "hidden",
      "border": "0",
      "border-radius": "13px",
      "box-shadow": "inset 0 1px 0 #ffffff38, 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a",
      "transition": "filter 150ms ease-out, box-shadow 150ms ease-out"
    },
    ".bjork-layered-button-accent::after, .bjork-layered-button-raised::after": {
      "position": "absolute",
      "inset": "0",
      "pointer-events": "none",
      "content": "\"\"",
      "border": "1.5px solid transparent",
      "border-radius": "inherit",
      "background": "linear-gradient(#ffffffb8, #0000003d 41% 75%, #ffffff47) border-box",
      "mix-blend-mode": "overlay",
      "-webkit-mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "-webkit-mask-clip": "padding-box, border-box",
      "mask-clip": "padding-box, border-box",
      "-webkit-mask-origin": "padding-box, border-box",
      "mask-origin": "padding-box, border-box",
      "-webkit-mask-composite": "xor",
      "mask-composite": "exclude"
    },
    ".bjork-layered-button-accent": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000040",
      "background": "radial-gradient(ellipse at -20px top, rgba(255, 255, 255, 0.14), transparent), linear-gradient(180deg, #ec5c13 0%, #ec5c13 62%, color-mix(in oklab, #ec5c13 84%, black 16%) 100%)"
    },
    ".bjork-layered-button-raised": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, oklch(0.1881 0.006 265), oklch(0.5493 0.0081 265))"
    },
    ".dark .bjork-layered-button-raised": {
      "color": "oklch(0.2574 0.0056 265)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, color-mix(in oklab, oklch(0.9674 0.0013 265) 80%, oklch(0.2574 0.0056 265) 20%), color-mix(in oklab, oklch(0.669 0.0107 265) 80%, oklch(0.2574 0.0056 265) 20%))"
    },
    "@media (hover: hover)": {
      ".bjork-layered-button-accent:hover": {
        "filter": "brightness(1.08) saturate(1.04)"
      },
      ".bjork-layered-button-raised:hover": {
        "filter": "brightness(1.08) contrast(0.9)"
      }
    }
  },
  "meta": {
    "collection": "UI",
    "registryUrl": "https://ui.isaiahbjork.com/primitive-avatar.json",
    "installCommand": "npx shadcn@latest add https://ui.isaiahbjork.com/primitive-avatar.json"
  },
  "type": "registry:component"
}