{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rows",
  "title": "Rows",
  "description": "An index of hairline-separated rows. The one you point at reverses out of ink and steps forward with an arrow; or repeats are set as ditto marks; or a ring before each row fills once you have been there.",
  "dependencies": [
    "@radix-ui/react-slot"
  ],
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/rows.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\n\nimport { useComposedRefs } from \"@/lib/0nlytype/refs\"\nimport { cn } from \"@/lib/utils\"\n\ntype RowsProps = React.ComponentProps<\"ul\"> & {\n  /**\n   * reverse: the row you point at reverses out of ink. ditto: a kind or a\n   * year that repeats the row above is set as a ditto mark, and pointing\n   * spells it out. trail: a ring before each row that fills with ink once\n   * you've been there (the browser's own :visited); where you are is the accent.\n   */\n  variant?: \"reverse\" | \"ditto\" | \"trail\"\n}\n\n/** An index: hairline-separated rows. The one you point at reverses out of ink and steps forward. */\nfunction Rows({ className, variant = \"reverse\", ref, ...props }: RowsProps) {\n  const own = React.useRef<HTMLUListElement>(null)\n  const composedRef = useComposedRefs(own, ref)\n  // ditto: after every render, mark each kind or year that repeats the one in the row above.\n  React.useLayoutEffect(() => {\n    if (variant !== \"ditto\" || !own.current) return\n    let above = new Map<string, string>()\n    for (const li of own.current.children) {\n      const here = new Map<string, string>()\n      for (const cell of li.querySelectorAll<HTMLElement>(\"[data-slot=rows-kind], [data-slot=rows-meta]\")) {\n        const slot = cell.dataset.slot ?? \"\"\n        const text = cell.textContent?.trim() ?? \"\"\n        cell.toggleAttribute(\"data-ditto\", text !== \"\" && above.get(slot) === text)\n        here.set(slot, text)\n      }\n      above = here\n    }\n  })\n  return <ul ref={composedRef} data-slot=\"rows\" data-variant={variant === \"reverse\" ? undefined : variant} className={cn(\"ot-rows\", className)} {...props} />\n}\n\ntype RowProps = Omit<React.ComponentProps<\"li\">, \"onClick\"> & {\n  /** Makes the row a link. Without href (or asChild) it is a plain row that doesn't react. */\n  href?: string\n  /** Render your own link (Next's Link, say) inside the row with the row's look. */\n  asChild?: boolean\n  /** Pins a state for documentation (\"hover\", or \"visited\" for trail); set on the link. */\n  \"data-force\"?: string\n  /** Passed to the link. */\n  onClick?: React.MouseEventHandler<HTMLElement>\n}\n\n/**\n * One row. Give it the parts (RowTitle, RowKind, RowMeta). A row that opens\n * something draws a → on hover; the ink comes in from the side your hand came in\n * and leaves toward the side it goes.\n */\nfunction Row({ href, asChild = false, className, children, onClick, \"data-force\": force, \"aria-current\": current, ...props }: RowProps) {\n  const Comp = asChild ? Slot : href !== undefined ? \"a\" : \"div\"\n  const edge = (e: React.PointerEvent<HTMLElement>) => {\n    const r = e.currentTarget.getBoundingClientRect()\n    e.currentTarget.dataset.edge = e.clientY < r.top + r.height / 2 ? \"top\" : \"bottom\"\n  }\n  return (\n    <li data-slot=\"rows-item\" {...props}>\n      <Comp\n        data-slot=\"rows-row\"\n        data-force={force}\n        aria-current={current}\n        className={cn(\"ot-rows-link\", className)}\n        {...(href !== undefined ? { href } : {})}\n        onClick={onClick}\n        onPointerEnter={edge}\n        onPointerLeave={edge}\n      >\n        {children}\n      </Comp>\n    </li>\n  )\n}\n\nfunction RowTitle({ className, ...props }: React.ComponentProps<\"span\">) {\n  return <span data-slot=\"rows-title\" className={cn(\"ot-rows-title\", className)} {...props} />\n}\n\nfunction RowKind({ className, ...props }: React.ComponentProps<\"span\">) {\n  return <span data-slot=\"rows-kind\" className={cn(\"ot-rows-kind\", className)} {...props} />\n}\n\n/** The quiet fact at the end: a year, a count. */\nfunction RowMeta({ className, ...props }: React.ComponentProps<\"span\">) {\n  return <span data-slot=\"rows-meta\" className={cn(\"ot-rows-meta\", className)} {...props} />\n}\n\nexport { Rows, Row, RowTitle, RowKind, RowMeta, type RowsProps, type RowProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/rows.css",
      "content": "@layer components {\n  /* ── ot-rows ── an index; the row you point at steps forward.\n     Only a row that opens something (an a or a button) reverses and shows the arrow. */\n  .ot-rows { list-style: none; margin: 0; padding: 0; border-block-start: var(--ot-hairline) solid var(--ot-rule); }\n  .ot-rows > li { border-block-end: var(--ot-hairline) solid var(--ot-rule); }\n  .ot-rows-link {\n    position: relative; isolation: isolate;\n    display: grid; grid-template-columns: minmax(0, 1fr); grid-auto-flow: column; grid-auto-columns: auto;\n    align-items: baseline; column-gap: var(--ot-space-6);\n    padding-block: var(--ot-space-4); color: inherit; text-decoration: none; --step: 8px;\n  }\n  /* A row that is a button (a sheet's trigger) spans the row as a link does, and reads from the start. */\n  button.ot-rows-link { inline-size: 100%; margin: 0; border: 0; background: none; text-align: start; cursor: pointer; }\n  :is([dir=\"rtl\"], [dir=\"rtl\"] *) .ot-rows-link { --step: -8px; }\n  .ot-rows-title { font-size: var(--ot-mf); line-height: var(--ot-mf-lh); letter-spacing: var(--ot-mf-tr); font-weight: 300; color: var(--ot-ink); transition: translate var(--ot-moderato) var(--ot-exhale); }\n  .ot-rows-kind { color: var(--ot-graphite); }\n  .ot-rows-meta { min-inline-size: 4ch; color: var(--ot-pencil); font-variant-numeric: tabular-nums; text-align: end; }\n  /* Every row keeps the arrow's column, so a plain row lines up with the rows that open something. */\n  .ot-rows-link::after {\n    content: \"→\" / \"\"; inline-size: 1.2em; justify-self: end; color: var(--ot-ink); opacity: 0; translate: calc(-1 * var(--step)) 0;\n    transition: opacity var(--ot-allegro), translate var(--ot-moderato) var(--ot-exhale), color var(--ot-allegro);\n  }\n  :is([dir=\"rtl\"], [dir=\"rtl\"] *) .ot-rows-link::after { content: \"←\" / \"\"; }\n  /* The row you point at reverses: ink wipes in from the side your hand came in, the heaviest emphasis there is. */\n  .ot-rows:not([data-variant]) .ot-rows-link:is(a[href], button)::before {\n    content: \"\"; position: absolute; inset: 0 calc(-1 * var(--ot-space-3)); z-index: -1;\n    background: var(--ot-ink); scale: 1 0; transform-origin: 50% 0;\n    transition: scale var(--ot-moderato) var(--ot-exhale);\n  }\n  .ot-rows-link[data-edge=\"bottom\"]::before { transform-origin: 50% 100%; }\n  .ot-rows-link:is(a[href], button) > * { transition: color var(--ot-allegro), translate var(--ot-moderato) var(--ot-exhale), opacity var(--ot-allegro); }\n  .ot-rows:not([data-variant]) .ot-rows-link:is(a[href], button):is(:hover, :focus-visible, [data-force~=\"hover\"])::before { scale: 1 1; }\n  .ot-rows:not([data-variant]) .ot-rows-link:is(a[href], button):is(:hover, :focus-visible, [data-force~=\"hover\"]) > * { color: var(--ot-paper); }\n  .ot-rows-link:is(a[href], button):is(:hover, :focus-visible, [data-force~=\"hover\"])::after { opacity: 1; translate: 0 0; }\n  .ot-rows:not([data-variant]) .ot-rows-link:is(a[href], button):is(:hover, :focus-visible, [data-force~=\"hover\"])::after { color: var(--ot-paper); }\n  .ot-rows-link:is(a[href], button):is(:hover, :focus-visible, [data-force~=\"hover\"]) .ot-rows-title { translate: var(--step) 0; }\n  .ot-rows-link:focus-visible { outline-offset: 4px; }\n  /* Narrow, the title takes its own line and the kind and meta sit under it, near it: side by side they would run into each other. */\n  @media (max-width: 40rem) {\n    .ot-rows-link { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--ot-space-4); row-gap: var(--ot-space-1); }\n    .ot-rows-link > .ot-rows-title { grid-column: 1 / -1; }\n    .ot-rows-link > .ot-rows-kind { justify-self: start; } /* as wide as its word, so a ditto stands over the word */\n    .ot-rows-link::after { display: none; } /* the ink still says which row */\n  }\n\n  /* ditto: a kind or a year that repeats the row above is set as a ditto mark, as a ledger or a typed list does, so only what\n     changes is written out and the marks set a rhythm down the column. Pointing at a row (or focusing it) spells its dittos out. */\n  .ot-rows[data-variant=\"ditto\"] [data-ditto] { position: relative; transition: color var(--ot-allegro); }\n  .ot-rows[data-variant=\"ditto\"] :is(.ot-rows-kind, .ot-rows-meta)[data-ditto] { color: transparent; }\n  .ot-rows[data-variant=\"ditto\"] [data-ditto]::after {\n    content: \"\\2033\" / \"\"; position: absolute; inset: 0; text-align: center; font-size: 1.3em; line-height: 1.2; color: var(--ot-pencil); transition: opacity var(--ot-allegro);\n  }\n  .ot-rows[data-variant=\"ditto\"] .ot-rows-link:is(:hover, :focus-visible, [data-force~=\"hover\"]) .ot-rows-kind[data-ditto] { color: var(--ot-graphite); }\n  .ot-rows[data-variant=\"ditto\"] .ot-rows-link:is(:hover, :focus-visible, [data-force~=\"hover\"]) .ot-rows-meta[data-ditto] { color: var(--ot-pencil); }\n  .ot-rows[data-variant=\"ditto\"] .ot-rows-link:is(:hover, :focus-visible, [data-force~=\"hover\"]) [data-ditto]::after { opacity: 0; }\n\n  @media (forced-colors: active) {\n    .ot-rows[data-variant=\"ditto\"] [data-ditto] { forced-color-adjust: none; }\n    .ot-rows[data-variant=\"ditto\"] [data-ditto]::after,\n    .ot-rows[data-variant=\"ditto\"] .ot-rows-link:is(:hover, :focus-visible, [data-force~=\"hover\"]) :is(.ot-rows-kind, .ot-rows-meta)[data-ditto] { color: CanvasText; }\n  }\n\n  /* trail: after the \"28 December\" calendar, a ring hung in the margin before each row: to come, a hairline ring; been, filled\n     with ink (the browser's own :visited, so nothing is stored); here (aria-current), the accent. Pointing inks the ring's line. */\n  .ot-rows[data-variant=\"trail\"] .ot-rows-link::before {\n    content: \"\"; position: absolute; inset-inline-start: calc(-1 * var(--ot-space-5)); inset-block-start: calc(var(--ot-space-4) + var(--ot-mf) * 0.62);\n    inline-size: var(--ot-space-3); block-size: var(--ot-space-3); margin-block-start: calc(var(--ot-space-3) / -2); box-sizing: border-box; border-radius: 50%;\n    border: var(--ot-hairline) solid var(--ot-rule-strong); background-color: var(--ot-paper);\n    transition: border-color var(--ot-allegro), scale var(--ot-moderato) var(--ot-spiccato);\n  }\n  .ot-rows[data-variant=\"trail\"] .ot-rows-link:is(:hover, :focus-visible, [data-force~=\"hover\"])::before { border-color: var(--ot-ink); scale: 1.25; }\n  .ot-rows[data-variant=\"trail\"] .ot-rows-link:is(:visited, [data-force~=\"visited\"])::before { border-color: var(--ot-ink); background-color: var(--ot-ink); }\n  .ot-rows[data-variant=\"trail\"] .ot-rows-link[aria-current]::before { border-color: var(--ot-accent); background-color: var(--ot-accent); }\n  @media (max-width: 40rem) { .ot-rows[data-variant=\"trail\"] .ot-rows-link { padding-inline-start: var(--ot-space-5); } .ot-rows[data-variant=\"trail\"] .ot-rows-link::before { inset-inline-start: 0; } }\n  @media (forced-colors: active) {\n    .ot-rows[data-variant=\"trail\"] .ot-rows-link::before { forced-color-adjust: none; border-color: CanvasText; background-color: Canvas; }\n    .ot-rows[data-variant=\"trail\"] .ot-rows-link:is(:visited, [data-force~=\"visited\"])::before { border-color: VisitedText; background-color: VisitedText; }\n    .ot-rows[data-variant=\"trail\"] .ot-rows-link[aria-current]::before { border-color: Highlight; background-color: Highlight; }\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/rows.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/rows.css\"": {}
  },
  "meta": {
    "movement": "VIII",
    "underneath": "native",
    "contract": "ot-rows"
  },
  "type": "registry:ui"
}
