{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "company-mark",
  "title": "Company Mark",
  "description": "The mark of a company with no logo to show: a shape and a colour drawn from its name, in the square a real logo would fill.",
  "registryDependencies": [
    "https://ui.ndekocode.dev/r/theme.json",
    "https://ui.ndekocode.dev/r/utils.json",
    "https://ui.ndekocode.dev/r/seeded.json"
  ],
  "files": [
    {
      "path": "registry/ndeko/ui/company-mark.tsx",
      "content": "import type { CSSProperties, ReactNode } from 'react';\n\nimport { hashOf, pickedFrom } from '@/registry/ndeko/lib/seeded';\nimport { cn } from '@/registry/ndeko/lib/utils';\n\nexport type CompanyMarkSize = 'sm' | 'md' | 'lg';\n\nconst TILE_CLASS: Record<CompanyMarkSize, string> = {\n  sm: 'size-4 rounded-[4px]',\n  md: 'size-5 rounded-[5px]',\n  lg: 'size-7 rounded-md',\n};\n\nconst HEXAGON = (\n  <path\n    fillRule='evenodd'\n    d='M12 2.5 20.5 7.25v9.5L12 21.5l-8.5-4.75v-9.5ZM12 8l-3.5 2v4l3.5 2 3.5-2v-4Z'\n  />\n);\n\nconst SUNRISE = (\n  <>\n    <path d='M4.5 14a7.5 7.5 0 0 1 15 0Z' />\n    <rect x='3' y='16.5' width='18' height='3' rx='1.5' opacity='0.55' />\n  </>\n);\n\nconst LEAF_OUTLINE = 'M12 21C6.5 17.5 5 11 12 3c7 8 5.5 14.5 0 18Z';\n\nconst LEAF = (\n  <>\n    <path d={LEAF_OUTLINE} opacity='0.55' transform='rotate(-28 12 21)' />\n    <path d={LEAF_OUTLINE} transform='rotate(24 12 21)' />\n  </>\n);\n\nconst ARCH = (\n  <path d='M4 20V12a8 8 0 0 1 16 0v8h-4.5v-8a3.5 3.5 0 0 0-7 0v8Z' />\n);\n\nconst PINWHEEL = (\n  <>\n    <path d='M12 3.5a4.25 4.25 0 0 1 0 8.5Z' />\n    <path d='M20.5 12a4.25 4.25 0 0 1-8.5 0Z' opacity='0.6' />\n    <path d='M12 20.5a4.25 4.25 0 0 1 0-8.5Z' />\n    <path d='M3.5 12a4.25 4.25 0 0 1 8.5 0Z' opacity='0.6' />\n  </>\n);\n\nconst TILES = (\n  <>\n    <rect x='3' y='3' width='8' height='8' rx='2' />\n    <rect x='13' y='3' width='8' height='8' rx='4' opacity='0.6' />\n    <rect x='3' y='13' width='8' height='8' rx='4' opacity='0.6' />\n    <rect x='13' y='13' width='8' height='8' rx='2' />\n  </>\n);\n\nconst ORBIT = (\n  <>\n    <circle\n      cx='12'\n      cy='12'\n      r='7.5'\n      fill='none'\n      stroke='currentColor'\n      strokeWidth='2.5'\n    />\n    <circle cx='18.5' cy='6' r='2.5' />\n  </>\n);\n\nconst BARS = (\n  <>\n    <rect x='4' y='12' width='4' height='8' rx='2' opacity='0.5' />\n    <rect x='10' y='8' width='4' height='12' rx='2' opacity='0.75' />\n    <rect x='16' y='4' width='4' height='16' rx='2' />\n  </>\n);\n\nconst STACK = (\n  <>\n    <rect x='3' y='4' width='18' height='4' rx='2' />\n    <rect x='5' y='10' width='14' height='4' rx='2' opacity='0.7' />\n    <rect x='7' y='16' width='10' height='4' rx='2' opacity='0.45' />\n  </>\n);\n\nconst DROP = (\n  <path\n    fillRule='evenodd'\n    d='M12 3c4 4.8 6.5 8 6.5 11.5a6.5 6.5 0 0 1-13 0C5.5 11 8 7.8 12 3Zm0 9a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5Z'\n  />\n);\n\nconst PETALS = (\n  <g opacity='0.8'>\n    <circle cx='12' cy='7' r='4.5' />\n    <circle cx='12' cy='17' r='4.5' />\n    <circle cx='7' cy='12' r='4.5' />\n    <circle cx='17' cy='12' r='4.5' />\n  </g>\n);\n\nconst CHEVRONS = (\n  <g\n    fill='none'\n    stroke='currentColor'\n    strokeWidth='2.8'\n    strokeLinecap='round'\n    strokeLinejoin='round'\n  >\n    <path d='m5 6 6 6-6 6' opacity='0.55' />\n    <path d='m12 6 6 6-6 6' />\n  </g>\n);\n\nconst QUARTERS = (\n  <>\n    <path d='M11 11V2a9 9 0 0 0-9 9Z' />\n    <path d='M13 2h9a9 9 0 0 1-9 9Z' opacity='0.6' />\n    <path d='M2 13h9a9 9 0 0 1-9 9Z' opacity='0.6' />\n    <path d='M22 22v-9a9 9 0 0 0-9 9Z' />\n  </>\n);\n\nconst PEAKS = (\n  <>\n    <path d='M2 19 9 6l7 13Z' />\n    <path d='m11 19 5.5-9 5.5 9Z' opacity='0.55' />\n  </>\n);\n\nconst SPLIT = (\n  <>\n    <circle\n      cx='12'\n      cy='12'\n      r='8.5'\n      fill='none'\n      stroke='currentColor'\n      strokeWidth='2'\n    />\n    <path d='M12 3.5a8.5 8.5 0 0 1 0 17Z' />\n  </>\n);\n\nconst WAVES = (\n  <g fill='none' stroke='currentColor' strokeWidth='2.6' strokeLinecap='round'>\n    <path d='M3 9c3-3.2 6-3.2 9 0s6 3.2 9 0' />\n    <path d='M3 15.5c3-3.2 6-3.2 9 0s6 3.2 9 0' opacity='0.55' />\n  </g>\n);\n\n/** A name lands on a position of this list, so the order is part of the design: moving a shape changes whose mark it is. */\nconst SHAPES: ReactNode[] = [\n  HEXAGON,\n  SUNRISE,\n  LEAF,\n  ARCH,\n  PINWHEEL,\n  TILES,\n  ORBIT,\n  BARS,\n  STACK,\n  DROP,\n  PETALS,\n  CHEVRONS,\n  QUARTERS,\n  PEAKS,\n  SPLIT,\n  WAVES,\n];\n\nconst INK = '#18181b';\n\n/** Colours a company would pick for itself. They belong to the mark, so they stay the same in both themes. */\nconst COLORS = [\n  '#8b5cf6',\n  '#e5484d',\n  '#0f9b8e',\n  INK,\n  '#2f9e5b',\n  '#52607a',\n  '#c28a04',\n  '#c2379a',\n  '#5b5bd6',\n  '#2f4fc4',\n  '#0b84f3',\n  '#f26b21',\n];\n\ntype Plate = 'solid' | 'paper';\n\n/** Most companies fill their square with their colour; one in three draws in colour on a plain square. */\nconst PLATES: Plate[] = ['solid', 'solid', 'paper'];\n\nconst PLATE_CLASS: Record<Plate, string> = {\n  solid: 'bg-(--mark) text-white ring-black/10 dark:ring-white/15',\n  paper:\n    'bg-card ring-border text-(--mark) dark:text-[color-mix(in_oklab,var(--mark)_72%,white)]',\n};\n\nconst SHAPE_SHIFT = 14;\nconst COLOR_SHIFT = 1;\nconst PLATE_SHIFT = 20;\n\n/** Ink on a plain square disappears in the dark theme, so the ink colour always fills its square. */\nfunction plateOf(hash: number, color: string) {\n  if (color === INK) {\n    return 'solid';\n  }\n  return pickedFrom(PLATES, hash, PLATE_SHIFT);\n}\n\nfunction roleOf(isNamed: boolean) {\n  if (isNamed) {\n    return 'img';\n  }\n  return undefined;\n}\n\nfunction hiddenOf(isNamed: boolean) {\n  if (isNamed) {\n    return undefined;\n  }\n  return true;\n}\n\ntype MarkStyle = CSSProperties & { '--mark': string };\n\ntype CompanyMarkProps = {\n  /** The name of the company, or its id when the name can change. The same text gives the same mark everywhere. */\n  seed: string;\n  size?: CompanyMarkSize;\n  /** The accessible name. Without it the mark is decorative, for a row that already names the company. */\n  title?: string;\n  className?: string;\n};\n\n/**\n * The mark of a company that has no logo to show: a shape and a colour drawn from its name, in the\n * square a real logo would fill. A known product wears its own mark through `BrandTile` instead.\n */\nexport function CompanyMark({\n  seed,\n  size = 'md',\n  title,\n  className,\n}: CompanyMarkProps) {\n  const hash = hashOf(seed);\n  const shape = pickedFrom(SHAPES, hash, SHAPE_SHIFT);\n  const color = pickedFrom(COLORS, hash, COLOR_SHIFT);\n  const plate = plateOf(hash, color);\n  const markStyle: MarkStyle = { '--mark': color };\n  const isNamed = Boolean(title);\n\n  return (\n    <span\n      role={roleOf(isNamed)}\n      aria-label={title}\n      aria-hidden={hiddenOf(isNamed)}\n      style={markStyle}\n      className={cn(\n        'hairline-ring inline-flex shrink-0 items-center justify-center ring-inset',\n        PLATE_CLASS[plate],\n        TILE_CLASS[size],\n        className\n      )}\n    >\n      <svg\n        viewBox='0 0 24 24'\n        fill='currentColor'\n        focusable='false'\n        aria-hidden='true'\n        className='size-[62%]'\n      >\n        {shape}\n      </svg>\n    </span>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "primitives"
  ],
  "type": "registry:ui"
}