{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "radio",
  "type": "registry:component",
  "title": "Radio",
  "description": "Selects one option from a group. The selected dot fills with ink.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/radio.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { hidden } from \"./hidden.stylex\";\n\ninterface RadioGroupContextValue {\n  name: string;\n  value: string | undefined;\n  setValue: (value: string) => void;\n}\n\nconst RadioGroupContext = React.createContext<RadioGroupContextValue | null>(null);\n\nexport interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n  name?: string;\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n}\n\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(function RadioGroup({\n  name,\n  value,\n  defaultValue,\n  onValueChange,\n  children,\n  ...props\n}, ref) {\n  const autoName = React.useId();\n  const [inner, setInner] = React.useState(defaultValue);\n  const isControlled = value !== undefined;\n  const current = isControlled ? value : inner;\n  const setValue = (next: string) => {\n    if (!isControlled) setInner(next);\n    onValueChange?.(next);\n  };\n  return (\n    <div ref={ref} role=\"radiogroup\" {...props}>\n      <RadioGroupContext.Provider value={{ name: name ?? autoName, value: current, setValue }}>\n        {children}\n      </RadioGroupContext.Provider>\n    </div>\n  );\n});\n\nexport interface RadioProps\n  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"type\" | \"value\"> {\n  value: string;\n  label?: React.ReactNode;\n}\n\nconst styles = stylex.create({\n  radio: {\n    display: \"flex\",\n    alignItems: \"center\",\n    gap: {\n      default: \"0.5625rem\",\n      [mq.phone]: \"0.75rem\",\n    },\n    fontSize: {\n      default: \"0.875rem\",\n      [mq.phone]: \"1.0625rem\",\n    },\n    color: vlak.ink,\n    letterSpacing: \"-0.01em\",\n    minHeight: {\n      default: vlak.hit,\n      [mq.phone]: vlak.hit,\n    },\n    minWidth: vlak.hit,\n  },\n  dot: {\n    width: {\n      default: \"1rem\",\n      [mq.phone]: \"1.375rem\",\n    },\n    height: {\n      default: \"1rem\",\n      [mq.phone]: \"1.375rem\",\n    },\n    borderRadius: \"50%\",\n    borderWidth: 1.5,\n    borderStyle: \"solid\",\n    borderColor: {\n      default: vlak.controlBorder,\n      [mq.forcedColors]: \"CanvasText\",\n    },\n    position: \"relative\",\n    flexShrink: 0,\n    boxSizing: \"border-box\",\n  },\n  on: {\n    borderColor: {\n      default: vlak.ink,\n      [mq.forcedColors]: \"Highlight\",\n    },\n  },\n  fill: {\n    position: \"absolute\",\n    inset: {\n      default: \"0.1875rem\",\n      [mq.phone]: \"0.3125rem\",\n    },\n    borderRadius: \"50%\",\n    backgroundColor: {\n      default: vlak.ink,\n      [mq.forcedColors]: \"Highlight\",\n    },\n    forcedColorAdjust: \"none\",\n  },\n});\n\n/** A real native radio inside a RadioGroup; the ink dot mirrors its state. */\nexport const Radio = React.forwardRef<HTMLInputElement, RadioProps>(function Radio(\n  { value, label, className, style, onChange, ...props },\n  ref,\n) {\n  const group = React.useContext(RadioGroupContext);\n  const on = group ? group.value === value : undefined;\n  const row = rs([\"rs-radio\", className], styles.radio);\n  const sr = rs([\"rs-sr\"], hidden.sr);\n  const mark = rs([\"rs-radio-dot\", on && \"rs-radio-on\"], styles.dot, on && styles.on);\n  const fill = rs([\"rs-radio-fill\"], styles.fill);\n  return (\n    <label className={row.className} style={{ ...row.style, ...style }}>\n      <input\n        ref={ref}\n        type=\"radio\"\n        className={sr.className}\n        style={sr.style}\n        name={group?.name}\n        value={value}\n        checked={on}\n        onChange={(e) => {\n          group?.setValue(value);\n          onChange?.(e);\n        }}\n        {...props}\n      />\n      <span className={mark.className} style={mark.style} aria-hidden=\"true\">\n        {on ? <span className={fill.className} style={fill.style} /> : null}\n      </span>\n      {label}\n    </label>\n  );\n});\n",
      "type": "registry:component",
      "target": "components/vlak/radio.tsx"
    },
    {
      "path": "vlak/styles/radio.css",
      "content": "/* ── radio: generated from packages/react/src/components/radio.tsx ── */\n.rs-radio{display:flex;align-items:center;gap:0.5625rem;font-size:0.875rem;color:var(--text);letter-spacing:-0.01em;min-height:var(--hit);min-width:var(--hit)}\n@media (max-width: 640px){.rs-radio{gap:0.75rem;font-size:1.0625rem;min-height:var(--hit)}}\n.rs-radio-dot{width:1rem;height:1rem;border-radius:50%;border-width:1.5px;border-style:solid;border-color:var(--control-border);position:relative;flex-shrink:0;box-sizing:border-box}\n@media (max-width: 640px){.rs-radio-dot{width:1.375rem;height:1.375rem}}\n@media (forced-colors: active){.rs-radio-dot{border-color:CanvasText}}\n.rs-radio-on{border-color:var(--text)}\n@media (forced-colors: active){.rs-radio-on{border-color:Highlight}}\n.rs-radio-fill{position:absolute;inset:0.1875rem;border-radius:50%;background-color:var(--text);forced-color-adjust:none}\n@media (max-width: 640px){.rs-radio-fill{inset:0.3125rem}}\n@media (forced-colors: active){.rs-radio-fill{background-color:Highlight}}\n",
      "type": "registry:file",
      "target": "styles/vlak/radio.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "forms",
      "classes": [
        "rs-radio",
        "rs-radio-dot",
        "rs-radio-on",
        "rs-radio-fill"
      ],
      "snippet": "<label class=\"rs-radio\"><span class=\"rs-radio-dot rs-radio-on\"></span>Monthly</label>",
      "cssOnly": false,
      "registryDependencies": [
        "vlak-lib"
      ],
      "aliases": [
        "Radio",
        "Radio group",
        "RadioGroup",
        "Radio button"
      ],
      "example": "import { useState } from \"react\";\nimport { Radio, RadioGroup } from \"@noorddev/vlak-react\";\n\nconst [plan, setPlan] = useState(\"monthly\");\n\n<RadioGroup aria-label=\"Billing\" value={plan} onValueChange={setPlan}>\n  <Radio value=\"monthly\" label=\"Monthly\" />\n  <Radio value=\"yearly\" label=\"Yearly\" />\n</RadioGroup>",
      "usage": {
        "use": [
          "One choice from two to six visible options.",
          "Choices that should all be readable at once."
        ],
        "avoid": [
          "Many options; use Select.",
          "Independent on and off choices; use Checkbox."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Moves focus into the group, onto the checked radio"
        },
        {
          "keys": "Arrow up, Arrow down, Arrow left, Arrow right",
          "does": "Moves the selection"
        },
        {
          "keys": "Space",
          "does": "Selects the focused radio"
        }
      ],
      "a11y": [
        "RadioGroup renders role=\"radiogroup\"; pass aria-label or aria-labelledby to name it.",
        "Radio is a native <input type=\"radio\"> hidden from view inside a <label>; the ink dot mirrors its state.",
        "The group shares a generated name unless you pass one."
      ]
    }
  }
}
