{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "utils",
  "title": "cn",
  "description": "The `cn` class merger, configured for the M3 utility namespaces. Without it tailwind-merge treats `text-m3-label-lg` and `text-m3-on-primary` as one group and drops one of them.",
  "dependencies": [
    "clsx@^2.1.1",
    "tailwind-merge@^3.6.0"
  ],
  "files": [
    {
      "path": "src/lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { extendTailwindMerge } from \"tailwind-merge\"\n\n/**\n * tailwind-merge has to be taught this library's theme keys, because two M3\n * namespaces collide inside Tailwind's `text-*` utility:\n *\n *   text-m3-label-lg        font-size  (from --text-m3-*)\n *   text-m3-on-primary      color      (from --color-m3-*)\n *\n * Left unconfigured, merge treats them as the same group and silently drops\n * whichever came first — which is how a filled button ends up with unreadable\n * label text. The validators below split them apart by name.\n */\nconst TYPE_ROLES = [\"display\", \"headline\", \"title\", \"body\", \"label\"] as const\n\nconst isM3FontSize = (value: string) =>\n  TYPE_ROLES.some((role) => value.startsWith(`m3-${role}-`))\n\nconst isM3Color = (value: string) =>\n  value.startsWith(\"m3-\") && !isM3FontSize(value)\n\n/**\n * The spacing scale has the same problem in a quieter form: `p-m3-lg` is not a\n * value tailwind-merge recognises, so it does not see it as conflicting with\n * `p-4` and keeps both — leaving CSS source order to decide a component's\n * padding, which is exactly what `cn` exists to prevent.\n */\nconst SPACE_STEPS = /^m3-(none|xs|sm|md|lg|xl|2xl|3xl|4xl)$/\n\nconst isM3Space = (value: string) => SPACE_STEPS.test(value)\n\n/** Every Tailwind utility that draws from the spacing namespace. */\nconst SPACING_UTILITIES = [\n  \"p\", \"px\", \"py\", \"pt\", \"pr\", \"pb\", \"pl\", \"ps\", \"pe\",\n  \"m\", \"mx\", \"my\", \"mt\", \"mr\", \"mb\", \"ml\", \"ms\", \"me\",\n  \"gap\", \"gap-x\", \"gap-y\",\n  \"w\", \"h\", \"size\", \"min-w\", \"min-h\", \"max-w\", \"max-h\",\n  \"inset\", \"inset-x\", \"inset-y\", \"top\", \"right\", \"bottom\", \"left\",\n] as const\n\nconst spacingGroups = Object.fromEntries(\n  SPACING_UTILITIES.map((utility) => [utility, [{ [utility]: [isM3Space] }]]),\n)\n\nconst twMerge = extendTailwindMerge({\n  extend: {\n    classGroups: {\n      \"font-size\": [{ text: [isM3FontSize] }],\n      \"text-color\": [{ text: [isM3Color] }],\n      \"bg-color\": [{ bg: [isM3Color] }],\n      \"border-color\": [{ border: [isM3Color] }],\n      \"ring-color\": [{ ring: [isM3Color] }],\n      \"outline-color\": [{ outline: [isM3Color] }],\n      \"shadow-color\": [{ shadow: [isM3Color] }],\n      // M3 adds shape steps beyond Tailwind's built-in radius scale.\n      rounded: [{ rounded: [(v: string) => v.startsWith(\"m3-\")] }],\n      shadow: [{ shadow: [(v: string) => /^m3-[0-5]$/.test(v)] }],\n      ...spacingGroups,\n    },\n  },\n})\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n",
      "type": "registry:lib"
    }
  ],
  "categories": [
    "utilities"
  ],
  "type": "registry:lib"
}