{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pixel-paragraph-words-inverse",
  "type": "registry:ui",
  "description": "Paragraph in pixel font where specific words escape into interactive sans/mono with hover swap or cycle",
  "dependencies": [
    "geist"
  ],
  "files": [
    {
      "path": "registry/default/ui/pixel-paragraph-words-inverse.tsx",
      "content": "/**\n * @module PixelParagraphInverse\n *\n * Renders a paragraph where the base text is in a pixel font and\n * specific words / phrases escape into sans or mono.\n *\n * This is the inverse of `PixelParagraph`: instead of highlighting\n * words *in* pixel, everything is pixel *except* the specified words.\n *\n * Setup — Geist Pixel Fonts with Tailwind CSS\n * =============================================\n *\n * All Geist fonts can be used through CSS variables:\n *\n *   GeistSans:          --font-geist-sans\n *   GeistMono:          --font-geist-mono\n *   GeistPixelSquare:   --font-geist-pixel-square\n *   GeistPixelGrid:     --font-geist-pixel-grid\n *   GeistPixelCircle:   --font-geist-pixel-circle\n *   GeistPixelTriangle: --font-geist-pixel-triangle\n *   GeistPixelLine:     --font-geist-pixel-line\n *\n * 1. Register the font variables in app/layout.js:\n *\n *   ```js\n *   import { GeistSans } from \"geist/font/sans\";\n *   import { GeistMono } from \"geist/font/mono\";\n *   import { GeistPixelSquare } from \"geist/font/pixel\";\n *\n *   export default function RootLayout({ children }) {\n *     return (\n *       <html\n *         lang=\"en\"\n *         className={`${GeistSans.variable} ${GeistMono.variable} ${GeistPixelSquare.variable}`}\n *       >\n *         <body>{children}</body>\n *       </html>\n *     );\n *   }\n *   ```\n *\n * 2. Map the CSS variables in your Tailwind CSS v4 theme (tailwind.css):\n *\n *   ```css\n *   @theme {\n *     --font-sans: var(--font-geist-sans);\n *     --font-mono: var(--font-geist-mono);\n *     --font-pixel-square: var(--font-geist-pixel-square);\n *     --font-pixel-grid: var(--font-geist-pixel-grid);\n *     --font-pixel-circle: var(--font-geist-pixel-circle);\n *     --font-pixel-triangle: var(--font-geist-pixel-triangle);\n *     --font-pixel-line: var(--font-geist-pixel-line);\n *   }\n *   ```\n *\n * Once configured, the `font-pixel-*` and `font-sans` / `font-mono`\n * utility classes used by this component will resolve correctly.\n *\n * @example\n * <PixelParagraphInverse\n *   text=\"54+ animated components and effects. Free, open source, and built to drop into any shadcn/ui project.\"\n *   plainWords={[\"animated\", \"shadcn/ui\"]}\n *   plainFont=\"sans\"\n *   pixelFont=\"square\"\n *   className=\"text-lg text-muted-foreground\"\n * />\n */\n\nimport { cn } from \"@/lib/utils\"\n\n/* ------------------------------------------------------------------ */\n/* Font constants                                                      */\n/* ------------------------------------------------------------------ */\n\ntype PlainFont = \"sans\" | \"mono\"\ntype PixelFont = \"square\" | \"grid\" | \"circle\" | \"triangle\" | \"line\"\n\nconst PLAIN_FONT_MAP: Record<PlainFont, string> = {\n  sans: \"font-sans\",\n  mono: \"font-mono\",\n}\n\nconst PIXEL_FONT_MAP: Record<PixelFont, string> = {\n  square: \"font-pixel-square\",\n  grid: \"font-pixel-grid\",\n  circle: \"font-pixel-circle\",\n  triangle: \"font-pixel-triangle\",\n  line: \"font-pixel-line\",\n}\n\n/* ------------------------------------------------------------------ */\n/* Text-splitting helper                                               */\n/* ------------------------------------------------------------------ */\n\ntype Segment = { type: \"pixel\"; text: string } | { type: \"plain\"; text: string }\n\n/**\n * Splits `text` into alternating pixel / plain segments based on the\n * provided `plainWords`.  Longer phrases are matched first so that\n * \"shadcn/ui\" wins over a hypothetical \"ui\" match.\n */\nfunction splitTextByPlainWords(text: string, plainWords: string[]): Segment[] {\n  if (plainWords.length === 0) return [{ type: \"pixel\", text }]\n\n  // Sort by length descending so longer matches take priority\n  const sorted = [...plainWords].sort((a, b) => b.length - a.length)\n\n  // Escape regex-special characters in each word\n  const escaped = sorted.map((w) => w.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n\n  const pattern = new RegExp(`(${escaped.join(\"|\")})`, \"g\")\n\n  const segments: Segment[] = []\n  let lastIndex = 0\n\n  for (const match of text.matchAll(pattern)) {\n    const matchStart = match.index ?? 0\n    if (matchStart > lastIndex) {\n      segments.push({ type: \"pixel\", text: text.slice(lastIndex, matchStart) })\n    }\n    segments.push({ type: \"plain\", text: match[0] })\n    lastIndex = matchStart + match[0].length\n  }\n\n  if (lastIndex < text.length) {\n    segments.push({ type: \"pixel\", text: text.slice(lastIndex) })\n  }\n\n  return segments\n}\n\n/* ------------------------------------------------------------------ */\n/* PixelParagraphInverse                                               */\n/* ------------------------------------------------------------------ */\n\nexport interface PixelParagraphInverseProps extends React.ComponentProps<\"p\"> {\n  /** The paragraph text to render. */\n  text: string\n  /**\n   * Words or phrases within `text` to render in a plain (sans/mono) font.\n   * Everything else renders in the pixel font.\n   * Matching is case-sensitive and longest-match-first.\n   */\n  plainWords?: string[]\n  /** The wrapper element to render. @default \"p\" */\n  as?: \"p\" | \"span\" | \"div\"\n  /** The pixel font used for the base text. @default \"square\" */\n  pixelFont?: PixelFont\n  /** The plain font for highlighted words. @default \"sans\" */\n  plainFont?: PlainFont\n  /** Extra className applied to each plain-word span. */\n  plainWordClassName?: string\n}\n\n/**\n * Paragraph where the base text is in a pixel font and specific words\n * or phrases escape into a sans or mono font.\n *\n * @example\n * <PixelParagraphInverse\n *   text=\"54+ animated components and effects. Free, open source, and built to drop into any shadcn/ui project.\"\n *   plainWords={[\"animated\", \"shadcn/ui\"]}\n *   plainFont=\"sans\"\n *   pixelFont=\"square\"\n *   className=\"text-lg text-muted-foreground\"\n * />\n */\nexport function PixelParagraphInverse({\n  text,\n  plainWords = [],\n  as: Tag = \"p\",\n  className,\n  pixelFont = \"square\",\n  plainFont = \"sans\",\n  plainWordClassName,\n  ...props\n}: PixelParagraphInverseProps) {\n  const segments = splitTextByPlainWords(text, plainWords)\n  const pixelFontClass = PIXEL_FONT_MAP[pixelFont]\n  const plainFontClass = PLAIN_FONT_MAP[plainFont]\n\n  return (\n    <Tag\n      data-slot=\"pixel-paragraph-inverse\"\n      className={cn(pixelFontClass, className)}\n      {...props}\n    >\n      {segments.map((segment, index) => {\n        const key = `${segment.type}-${segment.text}-${index}`\n        return segment.type === \"plain\" ? (\n          <span\n            key={key}\n            data-slot=\"plain-word\"\n            className={cn(plainFontClass, plainWordClassName)}\n          >\n            {segment.text}\n          </span>\n        ) : (\n          <span key={key}>{segment.text}</span>\n        )\n      })}\n    </Tag>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}