{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "collapsible",
  "title": "Collapsible",
  "description": "A list that ends in the rest of itself: the words \"and 4 more\" are the control.",
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json",
    "https://thedirectors.agency/ui/r/button.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/collapsible.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Variant = \"tail\" | \"catchword\" | \"sotto\"\n\nconst Kind = React.createContext<Variant>(\"tail\")\n\ntype CollapsibleProps = React.ComponentProps<\"details\"> & {\n  /**\n   * tail: the words \"and 4 more\" are the control, as the list's last line. catchword: the first name of the\n   * rest waits at the foot, flush to the far edge, the way a printer set the next page's first word; opening,\n   * it steps back to the start as the first row. sotto: the rest is already there, said under the breath in\n   * one line of fine print; opening says it in full.\n   */\n  variant?: Variant\n}\n\n/**\n * A list that ends in the rest of itself: \"and 4 more\" is the control. Native\n * <details>. Show the first rows yourself (a CollapsibleList), then this holds the rest.\n */\nfunction Collapsible({ className, variant = \"tail\", ...props }: CollapsibleProps) {\n  return (\n    <Kind.Provider value={variant}>\n      <details data-slot=\"collapsible\" data-variant={variant === \"tail\" ? undefined : variant} className={cn(\"ot-collapse\", className)} {...props} />\n    </Kind.Provider>\n  )\n}\n\ntype CollapsibleTriggerProps = React.ComponentProps<\"summary\"> & {\n  /** What the control says once it is open (\"Hide these 4\"). Defaults to the closed words. */\n  openLabel?: React.ReactNode\n}\n\nfunction CollapsibleTrigger({ children, openLabel, className, ...props }: CollapsibleTriggerProps) {\n  const variant = React.useContext(Kind)\n  return (\n    <summary\n      data-slot=\"collapsible-trigger\"\n      data-variant={variant === \"tail\" ? \"quiet\" : undefined}\n      className={cn(variant === \"tail\" ? \"ot-btn\" : \"ot-collapse-cue\", className)}\n      {...props}\n    >\n      <span>{children}</span>\n      <span data-echo={openLabel === undefined ? \"\" : undefined}>{openLabel ?? children}</span>\n    </summary>\n  )\n}\n\nfunction CollapsibleContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"collapsible-content\" className={className} {...props} />\n}\n\n/** Rows ruled off with hairlines. Its rows arrive in turn when the rest opens. */\nfunction CollapsibleList({ className, ...props }: React.ComponentProps<\"ul\">) {\n  return <ul data-slot=\"collapsible-list\" className={cn(\"ot-collapse-list\", className)} {...props} />\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent, CollapsibleList, type CollapsibleProps, type CollapsibleTriggerProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/collapsible.css",
      "content": "@layer components {\n  /* ── ot-collapse ── a list that ends in the rest of itself */\n  .ot-collapse-list { list-style: none; margin: 0; padding: 0; }\n  .ot-collapse-list > li { padding-block: 0.45em; border-block-end: var(--ot-hairline) solid var(--ot-rule); }\n  .ot-collapse > summary { list-style: none; }\n  .ot-collapse > summary::-webkit-details-marker { display: none; }\n  .ot-collapse > summary > :last-child, .ot-collapse[open] > summary > :first-child { display: none; }\n  .ot-collapse[open] > summary > :last-child { display: inline; }\n  .ot-collapse::details-content { block-size: 0; overflow: clip; transition: block-size var(--ot-andante) var(--ot-exhale), content-visibility var(--ot-andante) allow-discrete; }\n  .ot-collapse[open]::details-content { block-size: auto; }\n  .ot-collapse[open] .ot-collapse-list > li { animation: ot-arrive var(--ot-moderato) var(--ot-exhale) backwards; animation-delay: calc(sibling-index() * 2 * var(--ot-arpeggio)); }\n\n  /* Tail (the default): the words are the list's last line, set as the poster's caption, straight under the last rule. */\n  .ot-collapse:not([data-variant]) > summary { margin-block-start: var(--ot-space-1); margin-inline-start: -0.1em; font-size: var(--ot-pp); letter-spacing: var(--ot-pp-tr); }\n  .ot-collapse:not([data-variant])[open] > summary { margin-block-end: var(--ot-space-2); } /* the rows that arrive stand clear of the words that opened them */\n\n  /* The cue: catchword and sotto set their own control, not a button. */\n  .ot-collapse-cue {\n    --dir: 1;\n    display: flex; align-items: baseline; justify-content: space-between; gap: var(--ot-space-5);\n    cursor: pointer; -webkit-tap-highlight-color: transparent;\n    font-size: var(--ot-pp); line-height: var(--ot-pp-lh); letter-spacing: var(--ot-pp-tr); color: var(--ot-pencil);\n  }\n  .ot-collapse-cue:is([dir=\"rtl\"], [dir=\"rtl\"] *) { --dir: -1; }\n  .ot-collapse[open] > .ot-collapse-cue > :last-child { display: inline; white-space: nowrap; }\n\n  /* Catchword (the printer's catchword): the first name of the rest waits at the foot, flush to the far edge,\n     as the next page's first word waits under the last line. Opening, it steps back to the start and grows\n     into the first row of the rest; the rest follow it. The summary is a container, so the word can travel\n     the measure: its start is 100cqi less its own width from the far edge. */\n  .ot-collapse[data-variant=\"catchword\"] > summary {\n    container-type: inline-size; padding-block: 0.45em;\n    border-block-end: var(--ot-hairline) solid transparent;\n    transition: font-size var(--ot-andante) var(--ot-exhale), color var(--ot-moderato), border-color var(--ot-andante);\n  }\n  .ot-collapse[data-variant=\"catchword\"] > summary > :first-child {\n    display: inline-block; translate: calc((100cqi - 100%) * var(--dir)) 0;\n    text-decoration: underline var(--ot-hairline) transparent; text-underline-offset: 0.3em;\n    transition: translate var(--ot-andante) var(--ot-exhale), text-decoration-color var(--ot-allegro);\n  }\n  .ot-collapse[data-variant=\"catchword\"]:not([open]) > summary:is(:hover, [data-force~=\"hover\"]) { color: var(--ot-ink); }\n  .ot-collapse[data-variant=\"catchword\"]:not([open]) > summary:is(:hover, [data-force~=\"hover\"]) > :first-child { text-decoration-color: var(--ot-rule-strong); }\n  .ot-collapse[data-variant=\"catchword\"][open] > summary {\n    font-size: var(--ot-p); line-height: 1.6; letter-spacing: 0; color: var(--ot-graphite);\n    border-block-end-color: var(--ot-rule);\n  }\n  .ot-collapse[data-variant=\"catchword\"][open] > summary > :first-child { display: inline-block; translate: 0 0; }\n  .ot-collapse[data-variant=\"catchword\"][open] > summary > :last-child:not([data-echo]) { font-size: var(--ot-pp); color: var(--ot-pencil); }\n  .ot-collapse[data-variant=\"catchword\"][open] > summary > [data-echo] { display: none; }\n  .ot-collapse[data-variant=\"catchword\"][open] > summary:hover > :last-child { color: var(--ot-ink); }\n  .ot-collapse[data-variant=\"catchword\"][open] .ot-collapse-list > li { animation-delay: calc(var(--ot-moderato) + sibling-index() * 2 * var(--ot-arpeggio)); }\n\n  /* Sotto voce: the rest is never hidden, only said under the breath: one run of fine print in pencil,\n     and the whole run is the control. Opening says it in full: each name grows into its row, in turn.\n     The content stays visible while closed, so the summary lies over it in the same cell. */\n  .ot-collapse[data-variant=\"sotto\"] { display: grid; }\n  .ot-collapse[data-variant=\"sotto\"]::details-content { grid-area: 1 / 1; content-visibility: visible; block-size: auto; transition: none; }\n  .ot-collapse[data-variant=\"sotto\"] > summary { grid-area: 1 / 1; position: relative; z-index: 1; }\n  .ot-collapse[data-variant=\"sotto\"]:not([open]) > summary > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n  .ot-collapse[data-variant=\"sotto\"]:not([open]) .ot-collapse-list { padding-block: 0.7em; font-size: var(--ot-pp); line-height: var(--ot-pp-lh); text-wrap: pretty; }\n  .ot-collapse[data-variant=\"sotto\"]:not([open]) .ot-collapse-list > li {\n    display: inline; padding: 0; border: 0;\n    font-size: var(--ot-pp); line-height: var(--ot-pp-lh); letter-spacing: var(--ot-pp-tr); color: var(--ot-pencil);\n    transition: color var(--ot-allegro);\n  }\n  .ot-collapse[data-variant=\"sotto\"]:not([open]) .ot-collapse-list > li:not(:last-child)::after { content: \", \"; }\n  .ot-collapse[data-variant=\"sotto\"]:not([open]):has(> summary:is(:hover, [data-force~=\"hover\"])) .ot-collapse-list > li { color: var(--ot-ink); }\n  .ot-collapse[data-variant=\"sotto\"][open] > summary { grid-area: 2 / 1; margin-block-start: var(--ot-space-1); padding-block: 0.7em; justify-content: flex-start; }\n  .ot-collapse[data-variant=\"sotto\"][open] > summary:hover { color: var(--ot-ink); }\n  .ot-collapse[data-variant=\"sotto\"][open] .ot-collapse-list > li { animation: ot-sotto var(--ot-andante) var(--ot-exhale) backwards; animation-delay: calc((sibling-index() - 1) * 2 * var(--ot-arpeggio)); }\n  @keyframes ot-sotto { from { font-size: var(--ot-pp); line-height: var(--ot-pp-lh); padding-block: 0; color: var(--ot-pencil); border-block-end-color: transparent; } }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/collapsible.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/collapsible.css\"": {}
  },
  "meta": {
    "movement": "IX",
    "underneath": "native",
    "contract": "ot-collapse"
  },
  "type": "registry:ui"
}
