{
  "generatedFrom": "input-number.contract.json",
  "component": "InputNumber",
  "slug": "input-number",
  "group": "Data Entry",
  "tier": "composite-antd",
  "summary": "Enter a number with steppers, min/max and precision — quantities, limits, durations — rendered through the shared InputNumber.",
  "install": {
    "package": "@ahaslides-product/design",
    "registry": "https://npm.pkg.github.com",
    "scope": "@ahaslides-product",
    "auth": "Published to GitHub Packages — needs a GitHub token with read:packages. Configure the @ahaslides-product scope in .npmrc before installing.",
    "npmrc": "@ahaslides-product:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}   # a GitHub token with read:packages",
    "command": "npm i @ahaslides-product/design",
    "tokenLayer": "import '@ahaslides-product/design/tokens.css';",
    "import": "import '@ahaslides-product/design/input-number-theme';",
    "element": null,
    "registers": null
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/input-number/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/input-number/input-number.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/input-number.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/input-number.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — a CDN-React runnable page (React + antd loaded from a CDN, no build step) — React/Vue use the same shared-themed DataTable via your bundler. Paste into an .html file and it renders with no build step.",
  "frameworks": {
    "react": {
      "package": "antd",
      "major": 6
    },
    "vue": {
      "package": "ant-design-vue",
      "major": 4
    },
    "html": {
      "via": "cdn-react",
      "runnable": "paste-and-run — React + antd loaded from a CDN, no build step"
    }
  },
  "props": [
    {
      "name": "value",
      "type": "number",
      "default": "—",
      "desc": "Controlled numeric value"
    },
    {
      "name": "size",
      "type": "'small' | 'middle' | 'large'",
      "default": "'middle'",
      "desc": "Field height: small 24 · default 32 · large 40"
    },
    {
      "name": "status",
      "type": "'error' | 'warning'",
      "default": "—",
      "desc": "Validation state — coloured border/glow"
    },
    {
      "name": "min",
      "type": "number",
      "default": "-Infinity",
      "desc": "Lower bound"
    },
    {
      "name": "max",
      "type": "number",
      "default": "Infinity",
      "desc": "Upper bound"
    },
    {
      "name": "step",
      "type": "number",
      "default": "1",
      "desc": "Increment for steppers / arrow keys"
    },
    {
      "name": "precision",
      "type": "number",
      "default": "—",
      "desc": "Fixed decimal places"
    },
    {
      "name": "controls",
      "type": "boolean",
      "default": "true",
      "desc": "Show the up/down steppers"
    },
    {
      "name": "disabled",
      "type": "boolean",
      "default": "false",
      "desc": "Non-interactive, greyed field"
    },
    {
      "name": "readOnly",
      "type": "boolean",
      "default": "false",
      "desc": "Value visible but not editable"
    },
    {
      "name": "prefix",
      "type": "ReactNode",
      "default": "—",
      "desc": "Inline leading affix (e.g. $)"
    },
    {
      "name": "addonBefore",
      "type": "ReactNode",
      "default": "—",
      "desc": "Attached leading label"
    },
    {
      "name": "addonAfter",
      "type": "ReactNode",
      "default": "—",
      "desc": "Attached trailing label (e.g. unit)"
    },
    {
      "name": "onChange",
      "type": "(value) => void",
      "default": "—",
      "desc": "Fires with the new number"
    }
  ],
  "tokens": [
    "colorPrimary",
    "borderRadius",
    "controlHeight",
    "colorBorder",
    "InputNumber.activeBorderColor",
    "InputNumber.hoverBorderColor"
  ],
  "spec": [
    {
      "label": "Field",
      "value": "height 32 (md) · radius 8 · 1px #E3E3E3 border"
    },
    {
      "label": "Sizes",
      "value": "small 24 · default 32 · large 40 · font 14"
    },
    {
      "label": "Hover",
      "value": "#D3B4FF (purple-30) border"
    },
    {
      "label": "Focus",
      "value": "brand #6A1EBB border"
    },
    {
      "label": "Status",
      "value": "error #F5222D · warning border/glow"
    },
    {
      "label": "Steppers",
      "value": "up/down on hover; keyboard ↑ ↓; toggle via controls"
    },
    {
      "label": "Placeholder",
      "value": "#8A8A8A"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "InputNumber",
        "when": "a bounded numeric quantity — a limit, a count, a duration where steppers help"
      },
      {
        "what": "Slider",
        "when": "an approximate value across a known range where the exact number matters less"
      },
      {
        "what": "Input type=number",
        "when": "a free number with no bounds or steppers — but prefer InputNumber for validity"
      }
    ],
    "note": "Set min/max to the real limits so the field constrains instead of validating after the fact. Pre-fill a sensible default rather than empty; use precision for currency/percentages.",
    "dsv3": "COVERED — antd v6 InputNumber themed by the shared inputNumberTheme; matrix: size (small 24 · default 32 · large 40) × status (none · error · warning) × state (steppers on/off · disabled · read-only), plus min/max/step/precision, prefix and addonBefore/addonAfter. Verified via Figma search_design_system against the DS V3 InputNumber component_set (fileKey MwjvUjVI0HnfwD9EwPXRAy); size + status axes match the measured input contract (inp|sm/md/lg · rest/focus/error, 24/32/40 · font 14)."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "settings"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step, no bundler.\n     InputNumber is a COMPOSITE (antd, no framework-free element), so its HTML form is a\n     CDN-React page — React + antd load from a CDN (esm.sh) and mount on open. It consumes the\n     SAME shared `inputNumberTheme` the React/Vue wrappers use, so it renders the DS V3 look\n     (32px field, radius 8, #E3E3E3 border, brand focus/hover).\n     No JSX here (JSX needs a compiler) — we use React.createElement via the `h` alias. -->\n<div id=\"root\"></div>\n<script type=\"module\">\n  import React from 'https://esm.sh/react@18';\n  import { createRoot } from 'https://esm.sh/react-dom@18/client';\n  import { ConfigProvider, InputNumber } from 'https://esm.sh/antd@6?deps=react@18,react-dom@18';\n  import { inputNumberTheme } from 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/input-number-theme.js';\n\n  const h = React.createElement;\n  const w = { width: 200 };\n\n  createRoot(document.getElementById('root')).render(\n    h(ConfigProvider, { theme: inputNumberTheme },\n      h('div', { style: { display: 'flex', flexDirection: 'column', gap: 16 } },\n        // Default (32) · small (24) · large (40)\n        h(InputNumber, { min: 0, max: 100, defaultValue: 3, style: w }),\n        h(InputNumber, { size: 'small', min: 0, max: 100, defaultValue: 3, style: w }),\n        h(InputNumber, { size: 'large', min: 0, max: 100, defaultValue: 3, style: w }),\n        // Status: error · warning\n        h(InputNumber, { status: 'error', defaultValue: 120, max: 100, style: w }),\n        h(InputNumber, { status: 'warning', defaultValue: 95, max: 100, style: w }),\n        // Precision + affixes; steppers off; disabled / read-only\n        h(InputNumber, { prefix: '$', min: 0, step: 0.01, precision: 2, defaultValue: 19.99, style: w }),\n        h(InputNumber, { addonBefore: 'Every', addonAfter: 'min', min: 0, defaultValue: 30, style: { width: 240 } }),\n        h(InputNumber, { controls: false, defaultValue: 42, style: w }),\n        h(InputNumber, { disabled: true, defaultValue: 3, style: w }),\n        h(InputNumber, { readOnly: true, defaultValue: 3, style: w })\n      )\n    )\n  );\n</script>\n",
    "react": "import { ConfigProvider, InputNumber } from 'antd';   // antd v6\nimport { inputNumberTheme } from '@ahaslides-product/design/input-number-theme';\n\n<ConfigProvider theme={inputNumberTheme}>\n  {/* Default field — size default (32), steppers on */}\n  <InputNumber min={0} max={100} defaultValue={3} onChange={setCount} style={{ width: 200 }} />\n\n  {/* Sizes: small (24) · large (40) */}\n  <InputNumber size=\"small\" min={0} max={100} defaultValue={3} style={{ width: 200 }} />\n  <InputNumber size=\"large\" min={0} max={100} defaultValue={3} style={{ width: 200 }} />\n\n  {/* Status: error · warning */}\n  <InputNumber status=\"error\" defaultValue={120} max={100} style={{ width: 200 }} />\n  <InputNumber status=\"warning\" defaultValue={95} max={100} style={{ width: 200 }} />\n\n  {/* Precision + affixes; steppers off; disabled / read-only */}\n  <InputNumber prefix=\"$\" min={0} step={0.01} precision={2} defaultValue={19.99} style={{ width: 200 }} />\n  <InputNumber addonBefore=\"Every\" addonAfter=\"min\" min={0} defaultValue={30} style={{ width: 240 }} />\n  <InputNumber controls={false} defaultValue={42} style={{ width: 200 }} />\n  <InputNumber disabled defaultValue={3} style={{ width: 200 }} />\n  <InputNumber readOnly defaultValue={3} style={{ width: 200 }} />\n</ConfigProvider>\n// One shared inputNumberTheme → the DS V3 look; set min/max to constrain, not post-validation.\n",
    "vue": "<script setup>\nimport { ref } from 'vue';\nimport { ConfigProvider, InputNumber } from 'ant-design-vue';   // ant-design-vue v4\nimport { inputNumberTheme } from '@ahaslides-product/design/input-number-theme';\n\nconst count = ref(3);\n</script>\n\n<template>\n  <a-config-provider :theme=\"inputNumberTheme\">\n    <!-- Default field — size default (32), steppers on -->\n    <a-input-number v-model:value=\"count\" :min=\"0\" :max=\"100\" style=\"width: 200px\" />\n\n    <!-- Sizes: small (24) · large (40) -->\n    <a-input-number size=\"small\" :min=\"0\" :max=\"100\" :value=\"3\" style=\"width: 200px\" />\n    <a-input-number size=\"large\" :min=\"0\" :max=\"100\" :value=\"3\" style=\"width: 200px\" />\n\n    <!-- Status: error · warning -->\n    <a-input-number status=\"error\" :value=\"120\" :max=\"100\" style=\"width: 200px\" />\n    <a-input-number status=\"warning\" :value=\"95\" :max=\"100\" style=\"width: 200px\" />\n\n    <!-- Precision + affixes; steppers off; disabled / read-only -->\n    <a-input-number prefix=\"$\" :min=\"0\" :step=\"0.01\" :precision=\"2\" :value=\"19.99\" style=\"width: 200px\" />\n    <a-input-number addon-before=\"Every\" addon-after=\"min\" :min=\"0\" :value=\"30\" style=\"width: 240px\" />\n    <a-input-number :controls=\"false\" :value=\"42\" style=\"width: 200px\" />\n    <a-input-number disabled :value=\"3\" style=\"width: 200px\" />\n    <a-input-number :read-only=\"true\" :value=\"3\" style=\"width: 200px\" />\n  </a-config-provider>\n</template>\n<!-- The SAME inputNumberTheme as React → one DS V3 look across both vendor libraries. -->\n",
    "theme": "// @ahaslides-product/design/input-number-theme — declared ONCE, consumed by both tiers.\nimport { inputNumberTheme } from '@ahaslides-product/design/input-number-theme';\n\nexport const inputNumberTheme = {\n  token: {\n    colorPrimary: '#6A1EBB',\n    borderRadius: 8,\n    controlHeight: 32,\n    colorBorder: '#E3E3E3',\n    colorTextPlaceholder: '#8A8A8A',\n    fontFamily: 'var(--aha-font-product, \"Plus Jakarta Sans\", sans-serif)',\n  },\n  components: {\n    InputNumber: {\n      activeBorderColor: '#6A1EBB',   // brand focus border\n      hoverBorderColor: '#D3B4FF',    // purple-30 hover border\n      borderRadius: 8,\n      controlHeight: 32,\n    },\n  },\n};\n"
  }
}
