{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented",
  "type": "registry:ui",
  "title": "Segmented control",
  "description": "A short choice of two to five options in one track. The selected surface glides to your pick, and the control is built from native radios, so the arrow keys work.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://littmd.vercel.app/r/litt.json",
    "https://littmd.vercel.app/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/litt/ui/segmented.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/segmented 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Segmented control (components.md, \"Segmented control\"): a short choice of two to five\n * options, in one track. Built from a fieldset and native radio inputs, so the role, the\n * arrow keys and the form value come from the browser.\n *\n * - Track: a sunken tray (R3: --litt-tray, --litt-tray-shade) at the control radius, 2px padding, as the Switch's. The segments\n *   inside sit at the control radius less that inset, so the corners stay concentric.\n * - Flat (proposals/flat-pill.md): no tray at all; the selected word is a grey pill bubble (--litt-block is --litt-surface-3), and the others are --litt-fg-muted words. Its forced-colors border stays.\n * - Segment: --litt-fg-subtle, and --litt-fg on hover. The selected one is the raised block (--litt-block, --litt-block-lift), in --litt-fg:\n *   selected is raised, the same block as a switch's. Labels at the title weight (R8), and no segment changes size\n *   when selected.\n * - Sizes are on the control scale: sm is a 32px track with caption text (the spec), md is a\n *   44px track with small text, for forms beside 44px inputs. On touch screens sm grows to 44px.\n * - Segments share one width (the widest label), so the selected surface keeps its shape as\n *   it moves.\n * - Motion: the selected surface glides to the picked segment on the responsive tier (it\n *   moves through transform), as in Tabs (Press: a CSS translate, as the Switch's block; Flat: a layout glide). Color changes are instant. In fun mode (motion/fun.tsx, a trial, off unless\n *   a <FunMode> is on) it glides on fun mode's glide spring and settles a hair past the segment; reduced motion turns\n *   it off.\n * - Touch: there is no hover, so on a coarse pointer an unselected segment that is held down fills\n *   (PRESS: fg 6%, color only, instant tier) and takes the --litt-fg text. The selected one needs no fill,\n *   its surface is the answer. The box never moves. The OS tap flash is off, so the two do not double.\n * - Bodies (proposals/worlds.md): this file resolves the state once into SegmentedBodyProps and renders the body the\n *   material picks (byWorld). Press, here, is blocks in a tray: one raised block over empty bays (the bays are a\n *   --litt-cut-shade, the block --litt-block with --litt-block-lift and --litt-highlight). It crosses by translate on the responsive tier,\n *   and a hand can take it and drag it one to one, as the Switch's block does: a press on it sinks it --litt-press-depth on\n *   --litt-block-press, a drag past 4px follows the finger, and it lands on the nearer enabled option when let go (a drag\n *   that ends where it started changes nothing). A click and the arrow keys stay as they were. Flat\n *   (bodies/segmented.flat.tsx) is words alone with a grey pill bubble sized to the chosen word, and no tray. Device\n *   (bodies/segmented.device.tsx) is a row of keys in one well, the chosen one latched down with its lamp lit, and\n *   nothing glides. The props, the radios and the arrow keys are the same in all three.\n * - The legend is visually hidden by default, as the spec says. Pass showLegend in a form.\n */\nimport { useId, useRef, useState, type CSSProperties, type MouseEvent, type PointerEvent, type ReactNode } from \"react\";\nimport type { Transition } from \"motion/react\";\nimport { byWorld, useFun, useMaterial, useTransition } from \"../../lib/litt/motion\";\nimport { SegmentedFlatBody } from \"./bodies/segmented.flat\";\nimport { SegmentedDeviceBody } from \"./bodies/segmented.device\";\n\nexport type SegmentedSize = \"sm\" | \"md\";\nexport type SegmentedOption<T extends string> = {\n  /** What onValueChange gets and what a form sends. */\n  value: T;\n  /** The text in the segment. Keep it to a word or two. */\n  label: ReactNode;\n  /** Makes this one option unavailable. The others keep working. */\n  disabled?: boolean;\n};\n\nexport type SegmentedProps<T extends string> = {\n  /** The group's name, read out with each option. Required. Hidden from sight unless you pass showLegend. */\n  legend: ReactNode;\n  /** Shows the legend as a label above the track, for a form. Off by default, because a switch in a toolbar is named by its options. */\n  showLegend?: boolean;\n  /** The radios' shared name, which is what makes the arrow keys move between them and what a form sends. Made up for you when left out. */\n  name?: string;\n  /** The value of the selected option, when you control it. Pair it with onValueChange. */\n  value?: T;\n  /** The option that starts selected when you do not control it. Without one, the first option starts selected. */\n  defaultValue?: T;\n  /** Called with the picked option's value each time the selection changes, by click, tap or arrow key. */\n  onValueChange?: (value: T) => void;\n  /** The choices, in order. Give it two to five. */\n  options: SegmentedOption<T>[];\n  /** Track height on the control scale: sm is 32 with 12px text, md is 44 with 13px text. Use md in forms. */\n  size?: SegmentedSize;\n  /** Fills the width of its container, with equal segments. */\n  block?: boolean;\n  /** Disables every option, through the fieldset. */\n  disabled?: boolean;\n  /** Extra classes for the fieldset, such as a width. */\n  className?: string;\n};\n\n/** What a finger holding an unselected segment shows: the fg 6% fill that rows and tabs share, and the hover text color. */\nconst PRESS = \"pointer-coarse:active:bg-[color-mix(in_oklab,var(--litt-fg)_6%,transparent)] pointer-coarse:active:text-litt-fg\";\n\nconst sizes: Record<SegmentedSize, { track: string; segment: string }> = {\n  sm: { track: \"h-litt-control-sm pointer-coarse:h-litt-control-md\", segment: \"px-litt-control-pad-sm text-litt-caption pointer-coarse:text-litt-small\" },\n  md: { track: \"h-litt-control-md\", segment: \"px-litt-control-pad-md text-litt-small\" },\n};\n\n/** What a body gets: the state resolved once, and the radios' wiring. Every body renders the same fieldset of native radios. */\nexport type SegmentedBodyProps<T extends string = string> = {\n  legend: ReactNode;\n  showLegend: boolean;\n  name: string;\n  /** The selected option's value. */\n  value: T | undefined;\n  options: SegmentedOption<T>[];\n  size: SegmentedSize;\n  block: boolean;\n  /** The whole control is disabled (the fieldset). */\n  disabled: boolean;\n  className: string;\n  /** Selects an option: sets the inner state and calls onValueChange. */\n  select: (value: T) => void;\n  /** The LayoutGroup id, so two controls never share a gliding surface (Flat's bubble). */\n  groupId: string;\n  /** How the selected surface glides, for the bodies that glide with the layout engine (Flat). Press glides by CSS. */\n  enter: Transition;\n};\n\n/** How far a finger moves before a press on the chosen block becomes a drag, in px (the Switch's). */\nconst SLOP = 4;\n\n/* The block's crossing on the responsive tier, and its cast on the instant tier. While a hand drags it, it follows\n   one to one, so only the cast keeps its fade. */\nconst crossing: CSSProperties = {\n  transitionProperty: \"translate, box-shadow\",\n  transitionDuration: \"var(--litt-dur-responsive), var(--litt-dur-instant)\",\n  transitionTimingFunction: \"var(--litt-ease-responsive), var(--litt-ease-out)\",\n};\nconst following: CSSProperties = {\n  transitionProperty: \"box-shadow\",\n  transitionDuration: \"var(--litt-dur-instant)\",\n  transitionTimingFunction: \"var(--litt-ease-out)\",\n};\n\n/** Press, blocks in a tray: one raised block rides the tray over empty bays, and a hand can drag it to the nearer option. */\nfunction PressBody({ legend, showLegend, name, value, options, size, block, disabled, className, select }: SegmentedBodyProps) {\n  const look = sizes[size];\n  const fun = useFun();\n  const n = options.length;\n  const at = options.findIndex((o) => o.value === value);\n  /* A hand on the chosen block: where it took hold, and where the block is now (px from the first bay). A press becomes a drag past SLOP px. */\n  const hand = useRef<{ id: number; x0: number; from: number; at: number; step: number; moved: boolean } | null>(null);\n  const dropped = useRef(-1);\n  const [held, setHeld] = useState(false);\n  const [dragX, setDragX] = useState<number | null>(null);\n  const dragging = dragX !== null;\n\n  const onPointerDown = (e: PointerEvent<HTMLDivElement>) => {\n    if (disabled || at < 0 || (e.pointerType === \"mouse\" && e.button !== 0)) return;\n    const seg = (e.target as HTMLElement).closest<HTMLElement>(\"label[data-chosen]\");\n    if (!seg) return;\n    const tray = e.currentTarget;\n    const scale = tray.getBoundingClientRect().width / (tray.offsetWidth || 1) || 1;\n    const step = seg.offsetWidth;\n    hand.current = { id: e.pointerId, x0: e.clientX / scale, from: at * step, at: at * step, step, moved: false };\n    setHeld(true);\n  };\n  const onPointerMove = (e: PointerEvent<HTMLDivElement>) => {\n    const h = hand.current;\n    if (!h || h.id !== e.pointerId) return;\n    const scale = e.currentTarget.getBoundingClientRect().width / (e.currentTarget.offsetWidth || 1) || 1;\n    const dx = e.clientX / scale - h.x0;\n    if (!h.moved) {\n      if (Math.abs(dx) < SLOP) return;\n      h.moved = true;\n      e.currentTarget.setPointerCapture(e.pointerId);\n    }\n    h.at = Math.min((n - 1) * h.step, Math.max(0, h.from + dx));\n    setDragX(h.at);\n  };\n  const onPointerEnd = (e: PointerEvent<HTMLDivElement>) => {\n    const h = hand.current;\n    if (!h || h.id !== e.pointerId) return;\n    hand.current = null;\n    setHeld(false);\n    if (!h.moved) return;\n    setDragX(null);\n    if (e.type === \"pointercancel\") return;\n    /* The click that follows a drag must not pick again: the drop already chose the nearer enabled option. */\n    dropped.current = e.timeStamp;\n    const best = nearest(options, h.at, h.step, at);\n    if (best !== at) select(options[best].value);\n  };\n  const onClickCapture = (e: MouseEvent<HTMLDivElement>) => {\n    if (dropped.current >= 0 && e.timeStamp - dropped.current < 500) e.preventDefault();\n    dropped.current = -1;\n  };\n\n  /* The option the block is over: the chosen one, or while dragging the nearer enabled one. */\n  const over = dragging && hand.current ? nearest(options, dragX, hand.current.step, at) : at;\n\n  const motion: CSSProperties = dragging\n    ? following\n    : fun\n      ? {\n          ...crossing,\n          transitionDuration: `${fun.glideCss.duration}ms, var(--litt-dur-instant)`,\n          transitionTimingFunction: `${fun.glideCss.easing}, var(--litt-ease-out)`,\n        }\n      : crossing;\n  const sunk = (held || dragging) && !disabled;\n\n  return (\n    <fieldset data-body=\"press\" disabled={disabled} className={\"m-0 min-w-0 border-0 p-0 \" + className}>\n      <legend className={showLegend ? \"mb-2 p-0 text-litt-small font-litt-title text-litt-fg\" : \"sr-only\"}>{legend}</legend>\n      <div\n        onPointerDown={onPointerDown}\n        onPointerMove={onPointerMove}\n        onPointerUp={onPointerEnd}\n        onPointerCancel={onPointerEnd}\n        onClickCapture={onClickCapture}\n        className={\n          \"relative touch-pan-y grid-flow-col auto-cols-fr rounded-litt-control bg-litt-tray p-0.5 shadow-(--litt-tray-shade) forced-colors:border \" +\n          (block ? \"grid w-full \" : \"inline-grid \") +\n          look.track\n        }\n      >\n        {/* The block: one bay wide, less the tray's 2px walls. It crosses by translate (a percent of its own width), or follows the hand in px. */}\n        {at >= 0 && (\n          <span\n            aria-hidden=\"true\"\n            className={\n              \"pointer-events-none absolute inset-y-0.5 left-0.5 rounded-[calc(var(--litt-radius-control)-2px)] forced-colors:border-2 \" +\n              (disabled ? \"bg-litt-surface-2 shadow-[inset_0_0_0_1px_var(--litt-line)]\" : sunk ? \"bg-litt-block shadow-[var(--litt-highlight),var(--litt-block-press)]\" : \"bg-litt-block shadow-[var(--litt-highlight),var(--litt-block-lift)]\")\n            }\n            style={{\n              ...motion,\n              width: `calc((100% - 4px) / ${n})`,\n              translate: `${dragging ? `${dragX}px` : `${at * 100}%`} ${sunk ? \"var(--litt-press-depth)\" : \"0px\"}`,\n            }}\n          />\n        )}\n        {options.map((o) => {\n          const selected = value === o.value;\n          const off = disabled || !!o.disabled;\n          const here = o.value === options[over]?.value;\n          return (\n            <label\n              key={o.value}\n              data-chosen={selected ? \"\" : undefined}\n              className={\n                \"litt-target relative z-1 grid h-full select-none place-items-center rounded-[calc(var(--litt-radius-control)-2px)] font-litt-title \" +\n                \"transition-colors duration-(--litt-dur-instant) ease-litt [-webkit-tap-highlight-color:transparent] \" +\n                look.segment +\n                (off\n                  ? \" cursor-not-allowed text-litt-fg-disabled\"\n                  : \" cursor-pointer \" + (here ? \"text-litt-fg\" : \"text-litt-fg-subtle hover:text-litt-fg \" + PRESS))\n              }\n            >\n              <input\n                type=\"radio\"\n                className=\"peer sr-only\"\n                name={name}\n                value={o.value}\n                checked={selected}\n                disabled={o.disabled}\n                onChange={() => {\n                  select(o.value);\n                }}\n              />\n              {/* An empty bay: the cut the block can sit in, drawn as a shade and never a line. */}\n              <span\n                aria-hidden=\"true\"\n                className=\"pointer-events-none absolute inset-x-px inset-y-0 -z-1 rounded-[calc(var(--litt-radius-control)-2px)] shadow-(--litt-cut-shade)\"\n              />\n              <span className=\"litt-trim relative\">{o.label}</span>\n              <span\n                aria-hidden=\"true\"\n                className=\"pointer-events-none absolute inset-0 rounded-[calc(var(--litt-radius-control)-2px)] peer-focus-visible:outline-2 peer-focus-visible:-outline-offset-2 peer-focus-visible:outline-litt-focus\"\n              />\n            </label>\n          );\n        })}\n      </div>\n    </fieldset>\n  );\n}\n\n/** The index of the enabled option whose bay is nearest to the block at x px, or the fallback when none is. */\nfunction nearest(options: SegmentedOption<string>[], x: number, step: number, fallback: number) {\n  let best = fallback;\n  let gap = Infinity;\n  options.forEach((o, i) => {\n    const d = Math.abs(i * step - x);\n    if (!o.disabled && d < gap) {\n      best = i;\n      gap = d;\n    }\n  });\n  return best;\n}\n\n/** A group of native radios in one track, for a short choice such as Viewer or Editor, or System, Light and Dark. */\nexport function Segmented<T extends string>({\n  legend,\n  showLegend = false,\n  name: nameProp,\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  options,\n  size = \"md\",\n  block = false,\n  disabled = false,\n  className = \"\",\n}: SegmentedProps<T>) {\n  const auto = useId();\n  const name = nameProp ?? auto;\n  const responsive = useTransition(\"responsive\");\n  /* Fun mode (motion/fun.tsx, a trial): the selected surface glides on a springier spring and settles past its mark. */\n  const fun = useFun();\n  const enter = fun ? fun.glide : responsive;\n  const [inner, setInner] = useState<T | undefined>(defaultValue ?? options.find((o) => !o.disabled)?.value);\n  const value = valueProp ?? inner;\n  const Body = byWorld(useMaterial(), { press: PressBody, flat: SegmentedFlatBody, device: SegmentedDeviceBody });\n\n  return (\n    <Body\n      legend={legend}\n      showLegend={showLegend}\n      name={name}\n      value={value}\n      options={options as SegmentedOption<string>[]}\n      size={size}\n      block={block}\n      disabled={disabled}\n      className={className}\n      groupId={auto}\n      enter={enter}\n      select={(v) => {\n        setInner(v as T);\n        onValueChange?.(v as T);\n      }}\n    />\n  );\n}\n",
      "target": "components/litt/segmented.tsx"
    },
    {
      "path": "registry/litt/ui/bodies/segmented.device.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/segmented 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Segmented control, the Device body (proposals/worlds.md, Bodies; material.md, 8): a row of keys on one skirt.\n *\n * - One well (--litt-tray, --litt-tray-shade) holds the keys, side by side, each the width of the widest word. A key is\n *   --litt-block on --litt-key-lift, its corners the well's less its 2px inset, with 2px of the well left under it for its skirt.\n * - The chosen key stays latched down: sunk the whole --litt-press-depth (2px) onto the skirt, so its skirt is gone, its\n *   face a step darker (--litt-block mixed 55% toward --litt-tray) and inset-shaded with --litt-tray-shade, the well's own cut, its lamp lit in --litt-fg.\n *   The others stand up on --litt-key-lift, whose skirt is the 2px of well left under each key, their lamp a dim ring. A press on a standing key sinks it too, and choosing it\n *   lets the last one up. The word stays, so a lamp never says the state alone.\n * - Nothing glides. The change switches in place in three steps on the instant tier (100ms), as the Switch's detent\n *   does; reduced motion zeroes the tier, so it happens at once.\n * - Words are in the mono at the title weight (font-litt-field). Forced colors gives the well and every key real borders,\n *   the lit lamp is a system highlight and the dim one an outline.\n */\nimport type { CSSProperties } from \"react\";\nimport type { SegmentedBodyProps, SegmentedSize } from \"../segmented\";\n\nconst sizes: Record<SegmentedSize, { track: string; key: string }> = {\n  sm: { track: \"h-litt-control-sm pointer-coarse:h-litt-control-md\", key: \"px-2.5 text-litt-caption pointer-coarse:text-litt-small\" },\n  md: { track: \"h-litt-control-md\", key: \"px-3 text-litt-small\" },\n};\n\n/* The detent: the sink, the colors and the cast change in three steps on the instant tier. */\nconst STEPS = \"steps(3, end)\";\nconst flip: CSSProperties = {\n  transitionProperty: \"transform, color, box-shadow, background-color\",\n  transitionDuration: \"var(--litt-dur-instant)\",\n  transitionTimingFunction: `${STEPS}, ${STEPS}, ${STEPS}, ${STEPS}`,\n};\n\n/** Device, the keys: a row of keys in one well, the chosen one latched down with its lamp lit. */\nexport function SegmentedDeviceBody({ legend, showLegend, name, value, options, size, block, disabled, className, select }: SegmentedBodyProps) {\n  const look = sizes[size];\n  return (\n    <fieldset data-body=\"device\" disabled={disabled} className={\"m-0 min-w-0 border-0 p-0 \" + className}>\n      <legend className={showLegend ? \"mb-2 p-0 text-litt-small font-litt-title text-litt-fg\" : \"sr-only\"}>{legend}</legend>\n      <div\n        className={\n          \"relative grid-flow-col auto-cols-fr items-stretch overflow-hidden rounded-litt-control bg-litt-tray p-0.5 shadow-(--litt-tray-shade) forced-colors:border forced-colors:p-px \" +\n          (block ? \"grid w-full \" : \"inline-grid \") +\n          look.track\n        }\n      >\n        {options.map((o) => {\n          const selected = value === o.value;\n          const off = disabled || !!o.disabled;\n          return (\n            <label\n              key={o.value}\n              className={\n                \"group/key litt-target relative grid select-none [-webkit-tap-highlight-color:transparent] \" +\n                (off ? \"cursor-not-allowed\" : \"cursor-pointer\")\n              }\n            >\n              <input\n                type=\"radio\"\n                className=\"peer sr-only\"\n                name={name}\n                value={o.value}\n                checked={selected}\n                disabled={o.disabled}\n                onChange={() => select(o.value)}\n              />\n              <span\n                style={flip}\n                className={\n                  \"relative mb-0.5 flex items-center justify-center gap-1.5 rounded-[calc(var(--litt-radius-control)-2px)] font-litt-field font-litt-title forced-colors:border \" +\n                  look.key +\n                  (off\n                    ? \" bg-litt-surface-2 text-litt-fg-disabled shadow-[inset_0_0_0_1px_var(--litt-line)]\"\n                    : selected\n                      ? \" 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))]\"\n                      : \" bg-litt-block text-litt-fg-subtle shadow-(--litt-key-lift) hover:text-litt-fg group-active/key:text-litt-fg group-active/key:shadow-(--litt-block-press) group-active/key:[transform:translateY(var(--litt-press-depth))]\")\n                }\n              >\n                <span\n                  aria-hidden=\"true\"\n                  className={\n                    \"size-1.5 shrink-0 rounded-litt-pill \" +\n                    (selected && !off\n                      ? \"bg-litt-fg shadow-[0_0_6px_var(--litt-fg)] forced-colors:bg-[Highlight]\"\n                      : \"shadow-[inset_0_0_0_1px_var(--litt-fg-disabled)] forced-colors:border\")\n                  }\n                />\n                <span className=\"litt-trim\">{o.label}</span>\n              </span>\n              <span\n                aria-hidden=\"true\"\n                className=\"pointer-events-none absolute inset-0 rounded-[calc(var(--litt-radius-control)-2px)] peer-focus-visible:outline-2 peer-focus-visible:-outline-offset-2 peer-focus-visible:outline-litt-focus\"\n              />\n            </label>\n          );\n        })}\n      </div>\n    </fieldset>\n  );\n}\n",
      "target": "components/litt/bodies/segmented.device.tsx"
    },
    {
      "path": "registry/litt/ui/bodies/segmented.flat.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/segmented 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Segmented control, the Flat body (proposals/worlds.md, Bodies; proposals/flat-pill.md): words alone, set in a line.\n * There is no tray, no fill and no frame. The chosen word sits in a grey bubble (--litt-block is --litt-surface-3) at the pill\n * radius, sized to its own word, and the others are --litt-fg-muted words with nothing around them. The bubble glides to\n * the pick, and it changes its width on the way because the words do not share one. Space does the dividing: the\n * words are set apart by their own padding, and their hit areas touch, so a pointer never lands between two.\n * Forced colors gives the bubble a real border, which is the only edge, since there is no tray to keep.\n */\nimport { LayoutGroup, motion } from \"motion/react\";\nimport type { SegmentedBodyProps, SegmentedSize } from \"../segmented\";\n\n/* A held unselected word on a coarse pointer fills (fg 6%, color only) and takes the --litt-fg text. */\nconst PRESS = \"pointer-coarse:active:bg-[color-mix(in_oklab,var(--litt-fg)_6%,transparent)] pointer-coarse:active:text-litt-fg\";\n\nconst sizes: Record<SegmentedSize, { track: string; segment: string }> = {\n  sm: { track: \"h-litt-control-sm pointer-coarse:h-litt-control-md\", segment: \"px-3.5 text-litt-caption pointer-coarse:text-litt-small\" },\n  md: { track: \"h-litt-control-md\", segment: \"px-4 text-litt-small\" },\n};\n\n/** Flat, the grey bubble that glides behind the chosen word. */\nexport function SegmentedFlatBody({ legend, showLegend, name, value, options, size, block, disabled, className, select, groupId, enter }: SegmentedBodyProps) {\n  const look = sizes[size];\n  return (\n    <fieldset data-body=\"flat\" disabled={disabled} className={\"m-0 min-w-0 border-0 p-0 \" + className}>\n      <legend className={showLegend ? \"mb-2 p-0 text-litt-small font-litt-title text-litt-fg\" : \"sr-only\"}>{legend}</legend>\n      <LayoutGroup id={groupId}>\n        <div className={\"relative flex \" + (block ? \"w-full \" : \"w-fit \") + look.track}>\n          {options.map((o) => {\n            const selected = value === o.value;\n            const off = disabled || !!o.disabled;\n            return (\n              <label\n                key={o.value}\n                className={\n                  \"litt-target relative grid h-full select-none place-items-center rounded-litt-pill font-litt-title \" +\n                  \"transition-colors duration-(--litt-dur-instant) ease-litt [-webkit-tap-highlight-color:transparent] \" +\n                  (block ? \"flex-1 \" : \"\") +\n                  look.segment +\n                  (off\n                    ? \" cursor-not-allowed text-litt-fg-disabled\"\n                    : \" cursor-pointer \" + (selected ? \"text-litt-fg\" : \"text-litt-fg-muted hover:text-litt-fg \" + PRESS))\n                }\n              >\n                <input\n                  type=\"radio\"\n                  className=\"peer sr-only\"\n                  name={name}\n                  value={o.value}\n                  checked={selected}\n                  disabled={o.disabled}\n                  onChange={() => {\n                    select(o.value);\n                  }}\n                />\n                {selected && (\n                  <motion.span\n                    layoutId=\"segmented-bubble\"\n                    transition={enter}\n                    aria-hidden=\"true\"\n                    className={\"absolute inset-0 rounded-litt-pill forced-colors:border-2 \" + (off ? \"bg-litt-surface-2\" : \"bg-litt-block\")}\n                  />\n                )}\n                <span className=\"litt-trim relative z-1\">{o.label}</span>\n                {/* The word and the ring sit above the bubble (z-1), so the bubble never covers them while it glides. */}\n                <span\n                  aria-hidden=\"true\"\n                  className=\"pointer-events-none absolute inset-0 z-1 rounded-litt-pill peer-focus-visible:outline-2 peer-focus-visible:-outline-offset-2 peer-focus-visible:outline-litt-focus\"\n                />\n              </label>\n            );\n          })}\n        </div>\n      </LayoutGroup>\n    </fieldset>\n  );\n}\n",
      "target": "components/litt/bodies/segmented.flat.tsx"
    }
  ],
  "docs": "Import Segmented from components/litt/segmented. Give it a legend, a value, onValueChange and options. In a form, pass showLegend and block.",
  "meta": {
    "version": "2026.11.09",
    "released": "2026-11-09",
    "changelog": "https://littmd.vercel.app/changelog.md"
  }
}
