{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"native-select","title":"Native Select","description":"Choose one option with the browser's own select element, styled like the Select trigger. Search terms: html select, dropdown.","dependencies":["@base-ui/react@1.7.0"],"registryDependencies":["@siteplane/base"],"files":[{"path":"packages/ui/src/components/native-select/native-select.tsx","content":"\"use client\";\n\nimport { Field as FieldPrimitive } from \"@base-ui/react/field\";\nimport type * as React from \"react\";\n\nimport {\n  type SiteplaneControlSize,\n  useResolvedControlSize,\n} from \"../../system/provider/siteplane-ui-provider\";\nimport { SelectCaretIcon } from \"../../system/styles/select-caret\";\nimport type { FormControlTriggerVariant } from \"../../system/styles/surface\";\nimport { cn } from \"../../system/utils/cn\";\nimport { slotClass } from \"../../system/utils/slot-class\";\n\nimport \"./native-select.css\";\n\nexport type NativeSelectProps = Omit<\n  React.ComponentPropsWithoutRef<\"select\">,\n  \"size\"\n> &\n  React.RefAttributes<HTMLSelectElement> & {\n    /**\n     * The control size, 36, 40, 44 and 48 px from sm to xl; unset, the\n     * provider's.\n     */\n    size?: SiteplaneControlSize | undefined;\n    /**\n     * The surface. `default` rests raised, as the Select trigger does.\n     * `outline` rests transparent inside the hairline rim of an outline\n     * Button, for a filter bar or a toolbar beside an inset search field, as\n     * the Select trigger's `outline` does. Default: `default`.\n     */\n    variant?: FormControlTriggerVariant | undefined;\n    /**\n     * `full` fills the container, like Input. `fit` is as wide as the value\n     * and the caret, at the width the browser gives the select, that of its\n     * widest option. Default: `full`.\n     */\n    width?: \"fit\" | \"full\" | undefined;\n  };\n\n/**\n * The browser's own select on the field scale: the options open in the\n * platform's list, a phone's picker on a phone, and the value is a form\n * value. It looks like the Select trigger, with the same caret, sizes,\n * focus, invalid and disabled states (design-rework.css), so the two stand\n * side by side in a form. Its options are NativeSelectOption and\n * NativeSelectOptGroup children.\n *\n * In a Field it is the Field's control: FieldLabel names it,\n * FieldDescription and FieldError describe it, and the Field reads its value\n * and its validity.\n *\n * It stands on its own, the kit's one native select, which people choose on\n * purpose. It never sits inside another kit part: a select in an InputGroup\n * segment is the kit Select, a SelectTrigger with `variant=\"segment\"` in a\n * bare InputGroupAddon (rulebook K3, Lukas, 2 November 2026).\n */\nexport function NativeSelect({\n  children,\n  className,\n  ref,\n  size,\n  style,\n  variant = \"default\",\n  width = \"full\",\n  ...props\n}: NativeSelectProps): React.ReactElement {\n  const resolvedSize = useResolvedControlSize(size);\n\n  return (\n    <span\n      className={cn(\n        \"sp-native-select-wrapper\",\n        // The select is a field of its size: its height, the value's padding,\n        // the caret's place and the text follow it (the field scale), and a\n        // Field reads it for its hint.\n        \"sp-field-scale\",\n        className,\n      )}\n      data-size={resolvedSize}\n      data-slot=\"native-select-wrapper\"\n      // The surface, from the Select trigger's mode rules (design-rework.css).\n      data-variant={variant}\n      // The width of the field (native-select.css).\n      data-width={width}\n      // A hidden select hides its whole control.\n      hidden={props.hidden}\n      // `className` and the consumer's `style` style the field, the wrapper\n      // that draws it (Lukas, 4 November 2026); the other props reach the\n      // select. The kit sets no style of its own here.\n      {...(style === undefined ? {} : { style })}\n    >\n      {/* The select's props go to the Field's control, as an Input's do, so\n          the Field's rule decides: its name and its disabled state come first,\n          an id the select leaves unset keeps the label's target, and its\n          description joins the select's own aria-describedby. */}\n      <FieldPrimitive.Control\n        className={slotClass(\"native-select\", props)}\n        data-slot=\"native-select\"\n        {...(props as FieldPrimitive.Control.Props)}\n        ref={ref}\n        render={(controlProps: React.ComponentProps<\"select\">) => (\n          <select {...controlProps}>{children}</select>\n        )}\n      />\n      <span\n        aria-hidden=\"true\"\n        className=\"sp-select-icon\"\n        data-slot=\"select-icon\"\n      >\n        <SelectCaretIcon />\n      </span>\n    </span>\n  );\n}\n\n/** An option of a NativeSelect: the native `option`. */\nexport function NativeSelectOption({\n  className,\n  ...props\n}: React.ComponentProps<\"option\">): React.ReactElement {\n  return (\n    <option\n      className={cn(slotClass(\"native-select-option\", props), className)}\n      data-slot=\"native-select-option\"\n      {...props}\n    />\n  );\n}\n\n/** A labelled group of options of a NativeSelect: the native `optgroup`. */\nexport function NativeSelectOptGroup({\n  className,\n  ...props\n}: React.ComponentProps<\"optgroup\">): React.ReactElement {\n  return (\n    <optgroup\n      className={cn(slotClass(\"native-select-optgroup\", props), className)}\n      data-slot=\"native-select-optgroup\"\n      {...props}\n    />\n  );\n}\n","type":"registry:ui","target":"~/src/siteplane/components/native-select/native-select.tsx"},{"path":"packages/ui/src/components/native-select/native-select.css","content":"@layer theme, base, components, siteplane, utilities;\n\n@layer siteplane {\n  /* The wrapper is the control the eye sees: the Select trigger's field, its\n   * sizes, rim, dome, hover, press, focus, invalid and disabled states come\n   * from the select-trigger rules (design-rework.css), which name it beside\n   * the trigger. These defaults lay it out: as wide as its container, as a\n   * Select trigger is. */\n  :where(.sp-native-select-wrapper) {\n    position: relative;\n    display: inline-flex;\n    width: 100%;\n    min-width: 0;\n    vertical-align: middle;\n    /* The value's weight, which the select-trigger rules name for the\n     * trigger's Button: the body weight. */\n    font-weight: var(--sp-rw-button-font-weight);\n  }\n\n  /* width=\"fit\": as wide as the value and the caret, at the width the browser\n   * gives the select, that of its widest option, as a fit Select trigger is\n   * as wide as its value. */\n  :where(.sp-native-select-wrapper[data-width=\"fit\"]) {\n    width: fit-content;\n  }\n\n  /* The select fills the wrapper and draws nothing of its own: the browser's\n   * arrow is off, and the value starts and ends where a Select trigger's does,\n   * at the field's inline padding and 8 px before the caret (the\n   * select-trigger rules name both). It acts as a button, so it shows the\n   * pointer, and not-allowed while it is disabled (rulebook T8). */\n  :where(.sp-native-select) {\n    flex: 1 1 auto;\n    width: 100%;\n    min-width: 0;\n    height: auto;\n    margin: 0;\n    border: 0;\n    border-radius: inherit;\n    padding-block: 0;\n    padding-inline: var(--sp-rw-select-value-start)\n      var(--sp-rw-select-value-end);\n    appearance: none;\n    background: transparent;\n    color: inherit;\n    font: inherit;\n    letter-spacing: inherit;\n    text-overflow: ellipsis;\n    outline: none;\n    cursor: pointer;\n  }\n\n  :where(.sp-native-select:disabled) {\n    cursor: not-allowed;\n  }\n\n  /* The list is the platform's. Where the browser draws the options with the\n   * select's colours, they take the system's canvas and text, so they stay\n   * legible on a dark page whose select is transparent. */\n  :where(.sp-native-select-option),\n  :where(.sp-native-select-optgroup) {\n    background-color: Canvas;\n    color: CanvasText;\n  }\n}\n","type":"registry:ui","target":"~/src/siteplane/components/native-select/native-select.css"},{"path":"packages/ui/src/components/native-select/index.ts","content":"export * from \"./native-select\";\n","type":"registry:ui","target":"~/src/siteplane/components/native-select/index.ts"}],"type":"registry:ui","docs":"https://ui.siteplane.io/docs-markdown/components/native-select.md","categories":["Forms & selection"],"meta":{"avoidWhen":["Avoid when options need icons, avatars or descriptions, or people choose several; use Select.","Avoid for a long list that people search in; use Combobox.","Avoid inside an Input Group or another component; a select in an Input Group segment is a Select with SelectTrigger variant=\"segment\"."],"description":"Choose one option with the browser's own select element, styled like the Select trigger.","knownLimitations":["The list is the browser's: its look and keyboard behavior differ between browsers and systems, and its options are plain text.","It holds a single choice; a multiple select is not laid out as a field.","A floating label is not available; the label stays above the field."],"providerNotes":[],"releaseStatus":"preview","searchTerms":["native select","select element","html select","dropdown"],"useWhen":["Use where the platform's own list serves people best, such as the system picker on a phone or a long form of plain choices."]}}