{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "prose",
  "title": "Prose",
  "description": "Prose typography for rendered markdown in React and shadcn, with a chat density for AI replies and a document density for long reads.",
  "registryDependencies": [
    "https://eased.dev/r/eased-tokens.json"
  ],
  "files": [
    {
      "path": "src/registry/eased/prose/prose.tsx",
      "content": "import type { ComponentPropsWithoutRef, ElementType, ReactNode } from \"react\";\n\nexport type ProseDensity = \"chat\" | \"document\";\n\nexport type ProseProps<T extends ElementType = \"div\"> = {\n  /**\n   * chat (default): 14px, headings stay at UI sizes and the rhythm is tight, for assistant\n   * replies and comments. document: 16px body, h1–h4 on the display scale and a wider rhythm,\n   * for docs, changelogs, policies and articles.\n   */\n  density?: ProseDensity;\n  /** The element to render. Default \"div\"; \"article\" for a standalone document. */\n  as?: T;\n  className?: string;\n  children?: ReactNode;\n} & Omit<ComponentPropsWithoutRef<T>, \"as\" | \"className\" | \"children\">;\n\n/** Inline code: mono at 0.9em on a sunken tint. Prose styles `<code>` the same way. */\nexport const codeClass = \"rounded-control-2xs bg-surface-sunken px-1 py-px font-mono text-[0.9em] text-text [box-decoration-break:clone]\";\n\n/** Inline code, for UI copy outside Prose: \"Run `pnpm build`\". */\nexport function Code({ className = \"\", ...props }: ComponentPropsWithoutRef<\"code\">) {\n  return <code {...props} className={`${codeClass} ${className}`} />;\n}\n\n/*\n * Every element is styled from the container, so Prose wraps any rendered markdown or HTML\n * (MDX, react-markdown, Streamdown, a CMS string) without a component map or a global\n * stylesheet. Nothing outside the container is touched.\n */\nconst BASE = [\n  \"min-w-0 text-text [overflow-wrap:break-word]\",\n  // Text\n  \"[&_p]:text-pretty [&_strong]:font-medium [&_strong]:text-text [&_em]:italic [&_del]:text-text-muted [&_del]:line-through\",\n  \"[&_mark]:rounded-control-2xs [&_mark]:bg-warning-subtle [&_mark]:px-0.5 [&_mark]:text-text\",\n  \"[&_small]:text-ui-sm [&_small]:text-text-muted\",\n  // Links: brand text, a quiet underline that firms up on hover\n  \"[&_a]:rounded-control-2xs [&_a]:text-brand-text [&_a]:underline [&_a]:decoration-brand-text/30 [&_a]:underline-offset-2 [&_a]:transition-[text-decoration-color] [&_a]:duration-150 [&_a]:ease-standard\",\n  \"[@media(hover:hover)_and_(pointer:fine)]:[&_a:hover]:decoration-brand-text [&_a:focus-visible]:outline-2 [&_a:focus-visible]:outline-offset-2 [&_a:focus-visible]:outline-focus\",\n  // Lists: markers muted, numbers tabular, nested lists tight\n  \"[&_ul]:list-disc [&_ul]:ps-5 [&_ol]:list-decimal [&_ol]:ps-5 [&_li]:ps-0.5 [&_li::marker]:text-text-muted [&_ol>li::marker]:tabular-nums\",\n  \"[&_li+li]:mt-1 [&_li>:is(ul,ol)]:mt-1 [&_li>p+p]:mt-2\",\n  // Quotes and rules\n  \"[&_blockquote]:border-s-2 [&_blockquote]:border-line [&_blockquote]:ps-3 [&_blockquote]:text-text-secondary\",\n  \"[&_hr]:border-0 [&_hr]:border-t [&_hr]:border-line-subtle\",\n  // Code: inline tint; blocks sunken, scrolling sideways rather than wrapping\n  \"[&_:not(pre)>code]:rounded-control-2xs [&_:not(pre)>code]:bg-surface-sunken [&_:not(pre)>code]:px-1 [&_:not(pre)>code]:py-px [&_:not(pre)>code]:font-mono [&_:not(pre)>code]:text-[0.9em] [&_:not(pre)>code]:[box-decoration-break:clone]\",\n  \"[&_pre]:overflow-x-auto [&_pre]:rounded-control [&_pre]:border [&_pre]:border-line-subtle [&_pre]:bg-surface-sunken [&_pre]:px-3 [&_pre]:py-2.5 [&_pre]:font-mono [&_pre]:text-ui-sm [&_pre]:text-text\",\n  \"[&_kbd]:rounded-control-2xs [&_kbd]:border [&_kbd]:border-line [&_kbd]:bg-surface [&_kbd]:px-1 [&_kbd]:font-sans [&_kbd]:text-ui-xs [&_kbd]:text-text-secondary\",\n  // Tables: full width, hairline rows, quiet header; cells wrap so narrow screens never scroll the page\n  \"[&_table]:w-full [&_table]:border-collapse [&_table]:text-ui-sm [&_table]:tabular-nums\",\n  \"[&_th]:border-b [&_th]:border-line [&_th]:py-1.5 [&_th]:pe-4 [&_th]:text-start [&_th]:font-medium [&_th]:text-text-secondary [&_th]:whitespace-nowrap\",\n  \"[&_td]:border-b [&_td]:border-line-subtle [&_td]:py-1.5 [&_td]:pe-4 [&_td]:align-top [&_tr:last-child>td]:border-0 [&_:is(th,td):last-child]:pe-0\",\n  // Media\n  \"[&_img]:max-w-full [&_img]:rounded-control [&_figure>img]:w-full [&_figcaption]:mt-2 [&_figcaption]:text-ui-sm [&_figcaption]:text-text-muted\",\n  // Footnotes (GFM): small, under a rule\n  \"[&_sup]:text-[0.75em] [&_sup>a]:no-underline [&_sup>a]:tabular-nums [&_.footnotes]:border-t [&_.footnotes]:border-line-subtle [&_.footnotes]:pt-4 [&_.footnotes]:text-ui-sm [&_.footnotes]:text-text-secondary\",\n  // Headings: balanced, one weight per size\n  \"[&_:is(h1,h2,h3,h4,h5,h6)]:text-text [&_:is(h1,h2,h3,h4,h5,h6)]:text-balance [&_:is(h1,h2,h3,h4,h5,h6)]:break-words\",\n].join(\" \");\n\n// Gaps inside a group are smaller than between groups (rule 106): a heading sits close to\n// what it introduces and far from what came before.\nconst DENSITY: Record<ProseDensity, string> = {\n  chat: [\n    \"text-ui\",\n    \"[&>*+*]:mt-3 [&>*+:is(h1,h2,h3,h4,h5,h6)]:mt-5 [&>:is(h1,h2,h3,h4,h5,h6)+:not(h1,h2,h3,h4,h5,h6)]:mt-1.5\",\n    \"[&_:is(h1,h2)]:text-ui-md [&_:is(h1,h2)]:font-medium [&_:is(h3,h4,h5,h6)]:text-ui [&_:is(h3,h4,h5,h6)]:font-medium\",\n    \"[&_.footnotes]:mt-5\",\n  ].join(\" \"),\n  document: [\n    \"text-ui-md\",\n    \"[&>*+*]:mt-4 [&>*+h2]:mt-10 [&>*+h3]:mt-8 [&>*+:is(h4,h5,h6)]:mt-6 [&>:is(h1,h2,h3,h4,h5,h6)+:not(h1,h2,h3,h4,h5,h6)]:mt-2\",\n    \"[&_h1]:text-display-3xl [&_h1]:font-semibold [&_h2]:text-display-2xl [&_h2]:font-semibold [&_h3]:text-display-xl [&_h3]:font-medium [&_:is(h4,h5,h6)]:text-ui-md [&_:is(h4,h5,h6)]:font-medium\",\n    \"[&>h1+p]:text-ui-lg [&>h1+p]:text-text-secondary\",\n    \"[&_li+li]:mt-1.5 [&_blockquote]:ps-4 [&_figure]:my-6 [&_.footnotes]:mt-12\",\n  ].join(\" \"),\n};\n\n/**\n * Typography for rendered markdown and rich text: paragraphs, links, lists, quotes, code,\n * tables, images, footnotes and headings in Eased's tokens. Two densities share one element\n * map: chat for replies, document for long reads.\n */\nexport function Prose<T extends ElementType = \"div\">({ density = \"chat\", as, className = \"\", ...props }: ProseProps<T>) {\n  const Tag: ElementType = as ?? \"div\";\n  return <Tag {...props} data-density={density} className={`${BASE} ${DENSITY[density]} ${className}`} />;\n}\n",
      "type": "registry:ui",
      "target": "@components/eased/prose/prose.tsx"
    }
  ],
  "meta": {
    "details": "Typography for rendered markdown and rich text, from one element map in two densities: chat for replies and document for long reads. Display headings, hairline tables and footnotes.",
    "industries": [
      "ai",
      "devtools",
      "saas",
      "legal",
      "education",
      "media",
      "health",
      "finance"
    ],
    "releaseAt": "2026-10-23"
  },
  "docs": "Import Prose (and Code) from components/eased/prose/prose. Wrap any rendered markdown or HTML: <Prose density=\"document\">{content}</Prose>. Styles stay inside the container. The Eased tokens were merged into your global CSS; nothing to import.",
  "type": "registry:ui"
}