{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "number-field",
  "type": "registry:ui",
  "title": "Number Field",
  "description": "A numeric input with increment/decrement controls and built-in scrubbing. Wraps @base-ui/react/number-field. Re-exports the compositional parts (Root, Group, Increment, Input, Decrement, ScrubArea) under DS-styled wrappers without owning state.",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "form",
    "primitive"
  ],
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/cn.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/number-field.tsx",
      "target": "components/ui/number-field.tsx",
      "type": "registry:ui",
      "content": "'use client';\n\nimport * as React from 'react';\n\n// @interlace/number-field v1.1.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/number-field\n// What changed since: https://ds.interlace.tools/c/number-field#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — NumberField\n *\n * A numeric input with increment/decrement controls and built-in scrubbing.\n * Wraps @base-ui/react/number-field. Re-exports the compositional parts\n * (Root, Group, Increment, Input, Decrement, ScrubArea) under DS-styled\n * wrappers without owning state.\n *\n * ## Anatomy\n *\n *   NumberField (Root)               (div — data-min-viewport=320)\n *     ├─ NumberFieldScrubArea       (optional drag-to-scrub region)\n *     └─ NumberFieldGroup            (the visible button group)\n *         ├─ NumberFieldDecrement   (minus button)\n *         ├─ NumberFieldInput        (the actual number input)\n *         └─ NumberFieldIncrement    (plus button)\n *\n * ## MIN_VIEWPORT — 320\n *\n * ## Target size (SC 2.5.8, AA)\n *\n * The increment / decrement buttons are `w-9` (36px) against the input's\n * `h-9` (36px) — 36×36, comfortably past the 24×24 floor with no\n * pseudo-element hit area needed. Native keyboard (Up/Down arrows)\n * inherited from Base UI.\n *\n * ## Contrast (verified by token math)\n *\n * | Composite                                       | Light   | Dark    | Floor           |\n * | ----------------------------------------------- | ------- | ------- | --------------- |\n * | group `border-input` on `--background`          | 3.62:1  | 3.35:1  | 3:1 (SC 1.4.11) |\n * | value text `--foreground` on `bg-background`    | 19.65:1 | 16.97:1 | 4.5:1 (SC 1.4.3)|\n * | `focus-within:ring-ring` on `--background`      | 8.80:1  | 11.79:1 | 3:1 (SC 2.4.13) |\n *\n * The ring here is full-opacity (not the `/60` alpha the flat-surface\n * controls use), so it clears the focus floor with room to spare.\n * `disabled:opacity-50` is exempt (inactive component).\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends Base UI part props       | each wrapper extends `React.ComponentProps<typeof BaseNF.X>` |\n * | R6   | data-slot per part               | number-field / -group / -input / -increment / -decrement    |\n * | R7   | cn + ...rest                     | `cn(BASE, className)` + `{...props}`                        |\n * | R12  | Reuse over wrap                  | Base UI owns scrub + keyboard + clamping                    |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const |\n * | R19  | Tokens only                      | border-input, bg-background, ring-ring — semantic tokens    |\n * | R25  | Client component                 | Required — Base UI NumberField ships client hooks           |\n * | R26  | A11y from upstream               | aria-valuenow, aria-valuemin, aria-valuemax handled         |\n */\n\nimport { NumberField as BaseNumberField } from '@base-ui/react/number-field';\n\nimport { cn } from '@/lib/utils';\n\nexport const MIN_VIEWPORT = 320 as const;\n\nconst NumberField = React.forwardRef<\n  React.ElementRef<typeof BaseNumberField.Root>,\n  React.ComponentProps<typeof BaseNumberField.Root>\n>(({ className, ...props }, ref) => (\n  <BaseNumberField.Root\n    ref={ref}\n    data-slot=\"number-field\"\n    data-min-viewport={String(MIN_VIEWPORT)}\n    className={cn('inline-flex items-stretch', className)}\n    {...props}\n  />\n));\nNumberField.displayName = 'NumberField';\n\nconst NumberFieldGroup = React.forwardRef<\n  React.ElementRef<typeof BaseNumberField.Group>,\n  React.ComponentProps<typeof BaseNumberField.Group>\n>(({ className, ...props }, ref) => (\n  <BaseNumberField.Group\n    ref={ref}\n    data-slot=\"number-field-group\"\n    className={cn(\n      'border-input bg-background flex items-stretch overflow-hidden rounded-md border shadow-sm',\n      'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2',\n      className,\n    )}\n    {...props}\n  />\n));\nNumberFieldGroup.displayName = 'NumberFieldGroup';\n\nconst NumberFieldInput = React.forwardRef<\n  React.ElementRef<typeof BaseNumberField.Input>,\n  React.ComponentProps<typeof BaseNumberField.Input>\n>(({ className, ...props }, ref) => (\n  <BaseNumberField.Input\n    ref={ref}\n    data-slot=\"number-field-input\"\n    className={cn(\n      'h-9 w-20 bg-transparent px-3 text-center text-sm tabular-nums',\n      'focus:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n      className,\n    )}\n    {...props}\n  />\n));\nNumberFieldInput.displayName = 'NumberFieldInput';\n\nconst NumberFieldIncrement = React.forwardRef<\n  React.ElementRef<typeof BaseNumberField.Increment>,\n  React.ComponentProps<typeof BaseNumberField.Increment>\n>(({ className, ...props }, ref) => (\n  <BaseNumberField.Increment\n    ref={ref}\n    data-slot=\"number-field-increment\"\n    className={cn(\n      'border-input hover:bg-muted active:bg-accent flex w-9 cursor-pointer items-center justify-center border-l text-sm transition-colors',\n      'disabled:cursor-not-allowed disabled:opacity-50',\n      className,\n    )}\n    {...props}\n  />\n));\nNumberFieldIncrement.displayName = 'NumberFieldIncrement';\n\nconst NumberFieldDecrement = React.forwardRef<\n  React.ElementRef<typeof BaseNumberField.Decrement>,\n  React.ComponentProps<typeof BaseNumberField.Decrement>\n>(({ className, ...props }, ref) => (\n  <BaseNumberField.Decrement\n    ref={ref}\n    data-slot=\"number-field-decrement\"\n    className={cn(\n      'border-input hover:bg-muted active:bg-accent flex w-9 cursor-pointer items-center justify-center border-r text-sm transition-colors',\n      'disabled:cursor-not-allowed disabled:opacity-50',\n      className,\n    )}\n    {...props}\n  />\n));\nNumberFieldDecrement.displayName = 'NumberFieldDecrement';\n\nconst NumberFieldScrubArea = React.forwardRef<\n  React.ElementRef<typeof BaseNumberField.ScrubArea>,\n  React.ComponentProps<typeof BaseNumberField.ScrubArea>\n>(({ className, ...props }, ref) => (\n  <BaseNumberField.ScrubArea\n    ref={ref}\n    data-slot=\"number-field-scrub-area\"\n    className={cn('cursor-ew-resize', className)}\n    {...props}\n  />\n));\nNumberFieldScrubArea.displayName = 'NumberFieldScrubArea';\n\nexport {\n  NumberField,\n  NumberFieldGroup,\n  NumberFieldInput,\n  NumberFieldIncrement,\n  NumberFieldDecrement,\n  NumberFieldScrubArea,\n};\n"
    }
  ],
  "meta": {
    "tier": "primitive",
    "client": true,
    "minViewport": 320,
    "loading": false,
    "version": "1.1.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/number-field\n\nInstalled to `components/ui/number-field.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/number-field';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/number-field\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
