{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "legend",
  "type": "registry:ui",
  "title": "Legend",
  "description": "Toggles which series a chart shows: a wrapping row of buttons, each a 12 by 4px hued rail swatch, the name and an optional mono reading, pressed while its series shows. Hidden turns the swatch to the grey track and the name to the subtle ink. The last shown series cannot be hidden.",
  "registryDependencies": [
    "https://littmd.vercel.app/r/litt.json",
    "https://littmd.vercel.app/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/litt/ui/legend.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/legend 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Legend: toggles which series a chart shows. The Legend filters, the chart draws (growth step, decision 4). It\n * holds which series are hidden and draws nothing else; the page passes the shown series to any chart, a table, or\n * both, so one Legend can drive several views.\n *\n * - Anatomy: a wrapping row of toggle buttons in a `role=\"group\"` named by `label`. Each is a swatch (a 12 by 4px\n *   rail at the control radius, the Breakdown legend's swatch) in the hue's mark tone, the name (small, `--litt-fg`), and\n *   an optional reading after it (caption, `--litt-fg-subtle`, tabular), such as a total. With `lines`, for a LineChart,\n *   the rail is cut as the chart dashes the line (teal dashed, violet dotted, rose dash-dotted), so a series is never\n *   told by color alone (WCAG 1.4.1); blue and amber, solid lines, keep the plain rail.\n * - Hues: assigned exactly as the Chart's `paints(series, true)` does with `hues` on, so a Legend and a BarChart or\n *   LineChart given the same list always agree: an item with `hue` keeps it, and every item without one takes the\n *   next hue in the fixed order (blue, amber, teal, violet, rose), counting only items without one. The order comes\n *   from the full list, never the shown one, so hiding a series never repaints the others. A chart counts only the\n *   series it is given, so pass it the shown series with their hues pinned: `legendHues(items)` gives each name its\n *   hue. Five items at most, as in the Chart: past five the order would start again.\n * - States (R8, state without shift): shown is the hue's swatch and the name in `--litt-fg`, and `aria-pressed=\"true\"`.\n *   Hidden is the swatch as the Meter's track (fg at 12%) and the name in `--litt-fg-subtle`. Only color changes, on the\n *   instant tier: nothing moves, the box never changes size, and the weight stays 400.\n * - Size: `h-litt-control-sm` (32) with 8px of side padding at the control radius, and 44px tall on a coarse pointer.\n *   Hover fills fg 6%. On a coarse pointer a press fills the same fg 6%, as rows do. The OS tap flash is off.\n * - The last shown item cannot be hidden: pressing it does nothing, and while it is the only one shown it carries\n *   `aria-disabled=\"true\"` and a visually hidden note, \"At least one series stays shown.\" It is never natively\n *   disabled, so it keeps its tab stop and its name.\n * - Solo: Alt (Option) with a click, or Alt with Enter, shows only that item. On the item that is already the only\n *   one shown it does nothing, like a plain press.\n * - Keyboard: each item is its own tab stop (they are independent toggles, not a radio group). Space or Enter\n *   toggles, because each is a native button. Focus shows the house `:focus-visible` ring, 2px out.\n * - Motion: none beyond the instant tier's color change.\n * - Bodies (proposals/worlds.md): this file resolves the state once into LegendBodyProps and renders the body the\n *   material picks (byWorld). Press, here, is blocks in a tray: every series is a block in one tray (--litt-tray,\n *   --litt-tray-shade) with its hue chip (the 12 by 4px rail, cut as the chart dashes the line with `lines`) set in the\n *   block's face. A shown series stands: --litt-block on --litt-highlight and --litt-block-lift, its name in --litt-fg. A hidden one\n *   sinks flush with the tray (no block, the bay's own --litt-cut-shade), its chip the Meter's empty track, and says\n *   \"Hidden\" in words after its name, in the slot the reading uses, so a press never changes the box's size. Under\n *   the hand a block sinks the --litt-press-depth. The tray's floor is darker than the page, so the words on it keep\n *   --litt-fg and --litt-fg-muted, never --litt-fg-subtle. Flat is words with a hue dot; Device is a bank of latching keys, each\n *   lamp in its series' hue (bodies/).\n */\nimport { useId, useState, type CSSProperties, type KeyboardEvent, type MouseEvent } from \"react\";\nimport { byWorld, useMaterial } from \"../../lib/litt/motion\";\nimport { LegendFlatBody } from \"./bodies/legend.flat\";\nimport { LegendDeviceBody } from \"./bodies/legend.device\";\n\n/** The five data hues, in their fixed order (R7). The same names as the Chart's and the Breakdown's. */\nexport type LegendHue = \"blue\" | \"amber\" | \"teal\" | \"violet\" | \"rose\";\n\n/** The order an item without a hue takes them in: the Chart's `HUE_ORDER`. */\nconst HUE_ORDER: LegendHue[] = [\"blue\", \"amber\", \"teal\", \"violet\", \"rose\"];\n\n/** A hue's mark tone fills the swatch, as literal classes so Tailwind sees them and the registry can rename them. */\nconst MARK: Record<LegendHue, string> = {\n  blue: \"bg-litt-data-blue\",\n  amber: \"bg-litt-data-amber\",\n  teal: \"bg-litt-data-teal\",\n  violet: \"bg-litt-data-violet\",\n  rose: \"bg-litt-data-rose\",\n};\n/** A hidden series' swatch: the Meter's track. */\nconst TRACK = \"bg-[color-mix(in_oklab,var(--litt-fg)_12%,transparent)]\";\nconst LINE: Record<LegendHue, string> = {\n  blue: \"stroke-litt-data-blue\",\n  amber: \"stroke-litt-data-amber\",\n  teal: \"stroke-litt-data-teal\",\n  violet: \"stroke-litt-data-violet\",\n  rose: \"stroke-litt-data-rose\",\n};\nconst LINE_TRACK = \"stroke-[color-mix(in_oklab,var(--litt-fg)_12%,transparent)]\";\n/** The LineChart's dashes (its HUE_DASH: dashed, dotted, dash-dotted) cut into this swatch's 12 by 4px rail. */\nconst DASH: Partial<Record<LegendHue, string>> = { teal: \"5 2\", violet: \"2 2\", rose: \"6 2 2 2\" };\n\nexport type LegendItem = {\n  /** The series' name, exactly as the chart's series is named: \"Builds\". It is the button's text and the key in `hidden`, so keep names unique. */\n  name: string;\n  /** Pins the series to one of the five data hues. Give the chart's series the same hue, or use `legendHues` to read them. */\n  hue?: LegendHue;\n  /** A reading shown after the name in tabular figures, such as the series' total: \"1,240\". Leave it out when the name is enough. */\n  value?: string;\n};\n\nexport type LegendProps = {\n  /** Names the group for assistive tech, such as \"Series\" or \"Kinds of build\". It is not shown. */\n  label: string;\n  /** The series, in the chart's order. Five at most. */\n  items: LegendItem[];\n  /** The names of the hidden series, when you control it. Pair it with `onHiddenChange`. */\n  hidden?: string[];\n  /** The names hidden at first, when you do not control it. */\n  defaultHidden?: string[];\n  /** Called with the new list of hidden names each time an item is pressed. Pass the shown series to the chart from it. */\n  onHiddenChange?: (hidden: string[]) => void;\n  /** Cuts each swatch the way the LineChart dashes that hue's line. Set it when the Legend keys a LineChart. */\n  lines?: boolean;\n  /** Extra classes for the row, such as a bottom margin. */\n  className?: string;\n};\n\n/**\n * Each item's hue by name, assigned the way the Chart assigns them with `hues`: a set hue is kept, and every item\n * without one takes the next hue in the fixed order. Pin these on the series you pass to a chart, so a series keeps\n * its color when others are hidden.\n */\nexport function legendHues(items: LegendItem[]): Record<string, LegendHue> {\n  let next = 0;\n  const out: Record<string, LegendHue> = {};\n  for (const item of items) out[item.name] = item.hue ?? HUE_ORDER[next++ % HUE_ORDER.length]!;\n  return out;\n}\n\n/** One series as a body draws it: its hue resolved from the whole list, and whether it shows or is the last one standing. */\nexport type LegendBodyItem = LegendItem & { hue: LegendHue; shown: boolean; last: boolean };\n\n/** What a body gets: the state resolved once. Every body renders the same group of toggle buttons. */\nexport type LegendBodyProps = {\n  label: string;\n  items: LegendBodyItem[];\n  lines: boolean;\n  className: string;\n  /** The id of the hidden note the last shown item points at; the body renders the note itself. */\n  noteId: string;\n  /** The hidden note's words. */\n  note: string;\n  /** A click: toggles the series, or with Alt solos it. */\n  press: (e: MouseEvent<HTMLButtonElement>, name: string) => void;\n  /** Alt with Enter solos the series. */\n  keyDown: (e: KeyboardEvent<HTMLButtonElement>, name: string) => void;\n};\n\n\nconst LEGEND_NOTE = \"At least one series stays shown.\";\n\n/* A block changes in the instant tier's colors, and sinks by transform: the cast, the face and the travel. */\nconst flip: CSSProperties = {\n  transitionProperty: \"transform, color, background-color, box-shadow\",\n  transitionDuration: \"var(--litt-dur-instant)\",\n  transitionTimingFunction: \"var(--litt-ease-out)\",\n};\n/* A block standing in the tray, and a bay with no block in it (hidden): the cut's own shade, flush with the floor. */\nconst standing = \"bg-litt-block text-litt-fg shadow-[var(--litt-highlight),var(--litt-block-lift)]\";\nconst flush = \"bg-transparent text-litt-fg shadow-(--litt-cut-shade)\";\nconst handle =\n  \"hover:bg-[color-mix(in_oklab,var(--litt-block),transparent_55%)] \" +\n  \"active:shadow-[var(--litt-highlight),var(--litt-block-press)] active:[transform:translateY(var(--litt-press-depth))]\";\n\n/** Press, blocks in a tray: a shown series is a block with its hue chip, a hidden one sinks flush and says Hidden. */\nfunction PressBody({ label, items, lines, className, noteId, note, press, keyDown }: LegendBodyProps) {\n  return (\n    <div\n      data-body=\"press\"\n      role=\"group\"\n      aria-label={label}\n      className={\"flex w-fit max-w-full flex-wrap gap-0.5 overflow-hidden rounded-litt-control bg-litt-tray p-0.5 shadow-(--litt-tray-shade) forced-colors:border \" + className}\n    >\n      {items.map((item) => {\n        const { shown, hue, last } = item;\n        return (\n          <button\n            key={item.name}\n            type=\"button\"\n            aria-pressed={shown}\n            aria-disabled={last || undefined}\n            aria-describedby={last ? noteId : undefined}\n            aria-keyshortcuts=\"Alt+Enter\"\n            style={flip}\n            className={\n              \"litt-target relative inline-flex h-litt-control-sm grow items-center gap-2 rounded-[calc(var(--litt-radius-control)-2px)] px-2.5 \" +\n              \"whitespace-nowrap select-none text-litt-small [-webkit-tap-highlight-color:transparent] pointer-coarse:h-litt-control-md forced-colors:border \" +\n              \"focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-litt-focus \" +\n              (shown ? standing : flush + \" forced-colors:border-dashed\") +\n              (last ? \" cursor-default\" : \" cursor-pointer \" + handle)\n            }\n            onClick={(e) => press(e, item.name)}\n            onKeyDown={(e) => keyDown(e, item.name)}\n          >\n            {lines && DASH[hue] ? (\n              <svg width={12} height={4} aria-hidden=\"true\" focusable=\"false\" className=\"shrink-0\">\n                <line\n                  x1={0}\n                  x2={12}\n                  y1={2}\n                  y2={2}\n                  strokeWidth={4}\n                  strokeDasharray={DASH[hue]}\n                  className={\"transition-colors duration-(--litt-dur-instant) ease-litt \" + (shown ? LINE[hue] : LINE_TRACK)}\n                />\n              </svg>\n            ) : (\n              <span\n                aria-hidden=\"true\"\n                className={\"h-1 w-3 shrink-0 rounded-litt-control transition-colors duration-(--litt-dur-instant) ease-litt \" + (shown ? MARK[hue] : TRACK)}\n              />\n            )}\n            <span className=\"litt-trim font-litt-title\">{item.name}</span>\n            {/* The reading and the word Hidden share one slot, sized to the wider, so a press never moves the block. */}\n            <span className=\"grid text-litt-caption font-normal\">\n              <span aria-hidden=\"true\" className={\"col-start-1 row-start-1 text-litt-fg-muted\" + (shown ? \" invisible\" : \"\")}>\n                <span className=\"litt-trim\">Hidden</span>\n              </span>\n              {item.value !== undefined && (\n                <span className={\"col-start-1 row-start-1 justify-self-end tabular-nums text-litt-fg-muted\" + (shown ? \"\" : \" invisible\")}>\n                  <span className=\"litt-trim\">{item.value}</span>\n                </span>\n              )}\n            </span>\n          </button>\n        );\n      })}\n      <span id={noteId} className=\"sr-only\">\n        {note}\n      </span>\n    </div>\n  );\n}\n\n/** A row of toggles, one per series: a hued swatch and the name, pressed while the series shows. */\nexport function Legend({ label, items, hidden: hiddenProp, defaultHidden = [], onHiddenChange, lines = false, className = \"\" }: LegendProps) {\n  const noteId = useId();\n  const [inner, setInner] = useState<string[]>(defaultHidden);\n  const hidden = hiddenProp ?? inner;\n  const hues = legendHues(items);\n  const isShown = (name: string) => !hidden.includes(name);\n  const shownCount = items.filter((item) => isShown(item.name)).length;\n\n  const commit = (next: string[]) => {\n    if (hiddenProp === undefined) setInner(next);\n    onHiddenChange?.(next);\n  };\n  const toggle = (name: string) => {\n    if (isShown(name)) {\n      if (shownCount <= 1) return; // the last one stays\n      commit([...hidden, name]);\n    } else {\n      commit(hidden.filter((n) => n !== name));\n    }\n  };\n  const solo = (name: string) => {\n    if (isShown(name) && shownCount <= 1) return;\n    /* Keep names the caller hid that are not items, so a shared hidden list is not wiped. */\n    const others = hidden.filter((n) => !items.some((item) => item.name === n));\n    commit([...others, ...items.map((item) => item.name).filter((n) => n !== name)]);\n  };\n\n  const Body = byWorld(useMaterial(), { press: PressBody, flat: LegendFlatBody, device: LegendDeviceBody });\n\n  return (\n    <Body\n      label={label}\n      lines={lines}\n      className={className}\n      noteId={noteId}\n      note={LEGEND_NOTE}\n      items={items.map((item) => {\n        const shown = isShown(item.name);\n        return { ...item, hue: hues[item.name]!, shown, last: shown && shownCount <= 1 };\n      })}\n      press={(e, name) => (e.altKey ? solo(name) : toggle(name))}\n      keyDown={(e, name) => {\n        if (e.altKey && e.key === \"Enter\") {\n          e.preventDefault();\n          solo(name);\n        }\n      }}\n    />\n  );\n}\n",
      "target": "components/litt/legend.tsx"
    },
    {
      "path": "registry/litt/ui/bodies/legend.device.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/legend 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Legend, the Device body (proposals/worlds.md, Bodies; material.md, 8): a bank of latching keys, one per series.\n *\n * - One well (--litt-tray, --litt-tray-shade) holds the keys, wrapping as a row of keys does. A key is --litt-block on --litt-key-lift,\n *   its corners the well's less its 2px inset, with 2px of the well left under it for its skirt. Its legend is the\n *   series' name, engraved (litt-legend, --litt-engrave) in the mono (font-litt-field) at the title weight, then the optional\n *   reading in the glass's ink.\n * - Latching: a shown series is a key down: sunk the whole --litt-press-depth (2px) onto its skirt, its face a step darker\n *   (--litt-block mixed 55% toward --litt-tray) and inset-shaded with --litt-tray-shade, its lamp lit. A hidden series is a key\n *   standing up, its lamp a dim ring and its word a step down. The word never changes, so the state is said by\n *   aria-pressed and by the key's height, not by the lamp alone.\n * - Lamps carry the data (R7): a lit lamp is in its series' data hue, the same hue the chart draws it in, and a dim one\n *   is the ring in --litt-fg-disabled. With `lines` the lamp is the 12 by 4px rail cut as the chart dashes the line, so a\n *   series is never told by color alone (WCAG 1.4.1).\n * - The last shown key cannot be let up: it stays down, with aria-disabled and the hidden note.\n * - Motion: nothing glides. A key switches in place in three steps on the instant tier (100ms); reduced motion zeroes\n *   the tier, so it switches at once.\n * - Forced colors gives the well and each key a real border, a lit lamp is a system highlight and a dim one an outline.\n */\nimport type { CSSProperties } from \"react\";\nimport type { LegendBodyProps, LegendHue } from \"../legend\";\n\n/* The detent: a key changes in three steps on the instant tier, never a glide. */\nconst STEPS = \"steps(3, end)\";\nconst detent: CSSProperties = {\n  transitionProperty: \"transform, color, box-shadow, background-color\",\n  transitionDuration: \"var(--litt-dur-instant)\",\n  transitionTimingFunction: `${STEPS}, ${STEPS}, ${STEPS}, ${STEPS}`,\n};\n\n/* A lit lamp in its series' hue (literal classes, so Tailwind sees them and the registry renames them). */\nconst LAMP: Record<LegendHue, string> = {\n  blue: \"bg-litt-data-blue shadow-[0_0_6px_var(--litt-data-blue)]\",\n  amber: \"bg-litt-data-amber shadow-[0_0_6px_var(--litt-data-amber)]\",\n  teal: \"bg-litt-data-teal shadow-[0_0_6px_var(--litt-data-teal)]\",\n  violet: \"bg-litt-data-violet shadow-[0_0_6px_var(--litt-data-violet)]\",\n  rose: \"bg-litt-data-rose shadow-[0_0_6px_var(--litt-data-rose)]\",\n};\nconst LINE: Record<LegendHue, string> = {\n  blue: \"stroke-litt-data-blue\",\n  amber: \"stroke-litt-data-amber\",\n  teal: \"stroke-litt-data-teal\",\n  violet: \"stroke-litt-data-violet\",\n  rose: \"stroke-litt-data-rose\",\n};\nconst LINE_OFF = \"stroke-litt-fg-disabled\";\nconst DASH: Partial<Record<LegendHue, string>> = { teal: \"5 2\", violet: \"2 2\", rose: \"6 2 2 2\" };\n\nconst up = \"bg-litt-block text-litt-fg-subtle shadow-(--litt-key-lift)\";\nconst down = \"bg-[color-mix(in_oklab,var(--litt-block),var(--litt-tray)_55%)] text-litt-fg shadow-(--litt-tray-shade) [transform:translateY(var(--litt-press-depth))]\";\nconst held =\n  \"hover:text-litt-fg active:text-litt-fg active:shadow-(--litt-block-press) active:[transform:translateY(var(--litt-press-depth))]\";\n\n/** Device: a bank of latching keys, a shown series down with its lamp lit in its hue. */\nexport function LegendDeviceBody({ label, items, lines, className, noteId, note, press, keyDown }: LegendBodyProps) {\n  return (\n    <div\n      data-body=\"device\"\n      role=\"group\"\n      aria-label={label}\n      className={\n        \"flex w-fit max-w-full flex-wrap items-start gap-0.5 overflow-hidden rounded-litt-control bg-litt-tray p-0.5 shadow-(--litt-tray-shade) forced-colors:border \" + className\n      }\n    >\n      {items.map((item) => {\n        const { shown, hue, last } = item;\n        return (\n          <button\n            key={item.name}\n            type=\"button\"\n            aria-pressed={shown}\n            aria-disabled={last || undefined}\n            aria-describedby={last ? noteId : undefined}\n            aria-keyshortcuts=\"Alt+Enter\"\n            style={detent}\n            className={\n              \"litt-target relative inline-flex h-9.5 grow items-center gap-1.5 rounded-[calc(var(--litt-radius-control)-2px)] px-2.5 font-litt-field font-litt-title text-litt-caption litt-legend \" +\n              \"whitespace-nowrap select-none [-webkit-tap-highlight-color:transparent] forced-colors:border \" +\n              \"focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-litt-focus \" +\n              (shown ? down + (last ? \" cursor-default forced-colors:border-2\" : \" cursor-pointer forced-colors:border-2\") : up + \" cursor-pointer \" + held)\n            }\n            onClick={(e) => press(e, item.name)}\n            onKeyDown={(e) => keyDown(e, item.name)}\n          >\n            {lines && DASH[hue] ? (\n              <svg width={12} height={4} aria-hidden=\"true\" focusable=\"false\" className=\"shrink-0 forced-colors:stroke-[CanvasText]\">\n                <line x1={0} x2={12} y1={2} y2={2} strokeWidth={4} strokeDasharray={DASH[hue]} className={shown ? LINE[hue] : LINE_OFF} />\n              </svg>\n            ) : (\n              <span\n                aria-hidden=\"true\"\n                className={\n                  \"size-1.5 shrink-0 rounded-litt-pill forced-colors:border \" +\n                  (shown ? LAMP[hue] + \" forced-colors:bg-[Highlight]\" : \"shadow-[inset_0_0_0_1px_var(--litt-fg-disabled)] forced-colors:bg-transparent\")\n                }\n              />\n            )}\n            <span className=\"litt-trim\">{item.name}</span>\n            {item.value !== undefined && <span className=\"litt-trim font-normal tabular-nums text-litt-fg-subtle\">{item.value}</span>}\n          </button>\n        );\n      })}\n      <span id={noteId} className=\"sr-only\">\n        {note}\n      </span>\n    </div>\n  );\n}\n",
      "target": "components/litt/bodies/legend.device.tsx"
    },
    {
      "path": "registry/litt/ui/bodies/legend.flat.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/legend 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Legend, the Flat body (proposals/worlds.md, Bodies): series are words with a hue dot, ink on paper.\n *\n * - No tray, no block, no fill at rest. Each series is its name in ink at the title weight, a 10px dot before it in the\n *   series' hue (R7, where the color carries the data), set in a pill whose hit area touches its neighbours', so space\n *   does the dividing and a pointer never lands between two. Under the hand the pill takes a grey bubble (fg 6%).\n * - Ink means on, an outline means off: a shown series is a filled dot and its word in --litt-fg; a hidden one is the dot as\n *   an outline in --litt-fg-subtle (the same 10px, a 2px border either way) and its word in --litt-fg-subtle, so only color\n *   changes, on the instant tier, and the weight never does. The reading after the name is --litt-fg-subtle caption.\n * - With `lines` the dot is the 12px rail cut as the chart dashes the line (never color alone, WCAG 1.4.1); hidden, it\n *   is the same dashes drawn thin.\n * - The last shown series cannot be hidden: aria-disabled and the hidden note, and it keeps its tab stop. Focus is the\n *   house ring, 2px out. Forced colors gives the hidden dot a real border.\n */\nimport type { LegendBodyProps, LegendHue } from \"../legend\";\n\nconst MARK: Record<LegendHue, string> = {\n  blue: \"bg-litt-data-blue\",\n  amber: \"bg-litt-data-amber\",\n  teal: \"bg-litt-data-teal\",\n  violet: \"bg-litt-data-violet\",\n  rose: \"bg-litt-data-rose\",\n};\nconst LINE: Record<LegendHue, string> = {\n  blue: \"stroke-litt-data-blue\",\n  amber: \"stroke-litt-data-amber\",\n  teal: \"stroke-litt-data-teal\",\n  violet: \"stroke-litt-data-violet\",\n  rose: \"stroke-litt-data-rose\",\n};\nconst DASH: Partial<Record<LegendHue, string>> = { teal: \"5 2\", violet: \"2 2\", rose: \"6 2 2 2\" };\n\n/** Flat: words with a hue dot, a hidden one grey with an outline dot. */\nexport function LegendFlatBody({ label, items, lines, className, noteId, note, press, keyDown }: LegendBodyProps) {\n  return (\n    <div data-body=\"flat\" role=\"group\" aria-label={label} className={\"flex flex-wrap \" + className}>\n      {items.map((item) => {\n        const { shown, hue, last } = item;\n        return (\n          <button\n            key={item.name}\n            type=\"button\"\n            aria-pressed={shown}\n            aria-disabled={last || undefined}\n            aria-describedby={last ? noteId : undefined}\n            aria-keyshortcuts=\"Alt+Enter\"\n            className={\n              \"litt-target inline-flex h-litt-control-sm shrink-0 items-center gap-2 rounded-litt-pill px-3 whitespace-nowrap select-none font-litt-title \" +\n              \"transition-colors duration-(--litt-dur-instant) ease-litt [-webkit-tap-highlight-color:transparent] pointer-coarse:h-litt-control-md \" +\n              \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-litt-focus \" +\n              (last\n                ? \"cursor-default\"\n                : \"cursor-pointer hover:bg-[color-mix(in_oklab,var(--litt-fg)_6%,transparent)] pointer-coarse:active:bg-[color-mix(in_oklab,var(--litt-fg)_6%,transparent)]\")\n            }\n            onClick={(e) => press(e, item.name)}\n            onKeyDown={(e) => keyDown(e, item.name)}\n          >\n            {lines && DASH[hue] ? (\n              <svg width={12} height={10} aria-hidden=\"true\" focusable=\"false\" className=\"shrink-0\">\n                <line\n                  x1={0}\n                  x2={12}\n                  y1={5}\n                  y2={5}\n                  strokeWidth={shown ? 4 : 1}\n                  strokeDasharray={DASH[hue]}\n                  className={\"transition-colors duration-(--litt-dur-instant) ease-litt \" + (shown ? LINE[hue] : \"stroke-litt-fg-subtle\")}\n                />\n              </svg>\n            ) : (\n              <span\n                aria-hidden=\"true\"\n                className={\n                  \"size-2.5 shrink-0 rounded-litt-pill border-2 transition-colors duration-(--litt-dur-instant) ease-litt forced-colors:border-[CanvasText] \" +\n                  (shown ? \"border-transparent \" + MARK[hue] : \"border-litt-fg-subtle bg-transparent\")\n                }\n              />\n            )}\n            <span className={\"litt-trim text-litt-small transition-colors duration-(--litt-dur-instant) ease-litt \" + (shown ? \"text-litt-fg\" : \"text-litt-fg-subtle\")}>\n              {item.name}\n            </span>\n            {item.value !== undefined && <span className=\"litt-trim text-litt-caption font-normal tabular-nums text-litt-fg-subtle\">{item.value}</span>}\n          </button>\n        );\n      })}\n      <span id={noteId} className=\"sr-only\">\n        {note}\n      </span>\n    </div>\n  );\n}\n",
      "target": "components/litt/bodies/legend.flat.tsx"
    }
  ],
  "docs": "Import Legend and legendHues from components/litt/legend. Pass a label and the items as { name, hue?, value? } in the chart's order, five at most. Control it with hidden and onHiddenChange, or start it with defaultHidden. Pass the chart only the shown series, each with hue: legendHues(items)[name], so a series keeps its color when another is hidden, and turn the chart's own legend off with legend={false}. Alt with a click or Enter shows only that series.",
  "meta": {
    "version": "2026.11.09",
    "released": "2026-11-09",
    "changelog": "https://littmd.vercel.app/changelog.md"
  }
}
