{
  "version": "0.2.0-preview.1",
  "content_digest": "70ac710f4d22dce040e7ac593379ba8aca9dcb59a0b9c0f839819cae0e2bb721",
  "status": "preview",
  "authority": {
    "repository": "cloakedinc/design",
    "path": "packages/design-system",
    "catalog": "packages/design-system/catalog.json",
    "status": "preview"
  },
  "provenance": {
    "repo": "cloakedinc/automation",
    "commit": "f96f416e1c3bfbfaffcd155a88b87b18b8bb25f1",
    "path": "packages/design-system",
    "imported_spec_status": "approved"
  },
  "adaptations": [
    {
      "id": "button-content-gap",
      "status": "preview",
      "description": "Use the specification's 4px content gap instead of the inherited 8px implementation gap."
    },
    {
      "id": "keyboard-focus",
      "status": "preview",
      "description": "Add visible Button and Input keyboard focus and an overridable inset focus default for shared button resets, including FAQ cards and ListItem rows."
    },
    {
      "id": "supporting-text-contrast",
      "status": "preview",
      "description": "Map light secondary text to the existing grey-04-strong primitive for readable helper text on white and cream."
    },
    {
      "id": "danger-text-contrast",
      "status": "preview",
      "description": "Separate readable danger text/borders from destructive fills; preserve the primary danger-button fill and label pair."
    },
    {
      "id": "preview-status",
      "status": "preview",
      "description": "Local components, specs and token adaptations await human sign-off; upstream approved status is provenance only."
    },
    {
      "id": "motion-comparison-accessibility",
      "status": "preview",
      "description": "Preserve the original MotionShowcase comparisons while requiring explicit replay, responding to live reduced-motion changes, and keeping duration tracks usable on small screens."
    },
    {
      "id": "list-item-ownership",
      "status": "preview",
      "description": "List owns listitem wrappers for its content rows, while section labels stay outside the row count and standalone rows retain their existing semantics."
    },
    {
      "id": "card-delta-text-contrast",
      "status": "preview",
      "description": "Mix Card.Delta foregrounds from 60% existing tone solid and 40% primary text for readable contrast in both themes; preserve the four original tone fills."
    },
    {
      "id": "carousel-keyboard-access",
      "status": "preview",
      "description": "Keep all native-scrolling carousel slide controls exposed to assistive technology and keyboard navigation; pause autoplay while focus is inside the carousel."
    }
  ],
  "id": "input",
  "name": "Input",
  "category": "Primitives",
  "contract_status": "preview",
  "contract_kind": "curated",
  "review": {
    "local_status": "not-reviewed",
    "source_approval": null,
    "imported_spec_status": "approved",
    "spec_origin": "imported-adapted",
    "local_spec_source_status": "preview",
    "imported_source_revision": "f96f416e1c3bfbfaffcd155a88b87b18b8bb25f1",
    "note": "Inherited specification status is provenance, not approval of the current local implementation or extracted contract."
  },
  "approval": "required",
  "visual_reference": {
    "stories": [
      {
        "id": "cloaked-design-system-primitives-input--playground",
        "name": "Playground",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "Playground",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--playground&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--state-matrix",
        "name": "State Matrix",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "StateMatrix",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--state-matrix&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--focused",
        "name": "Focused",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "Focused",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--focused&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--types",
        "name": "Types",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "Types",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--types&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--with-slots",
        "name": "With Slots",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "WithSlots",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--with-slots&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--invalid-in-field",
        "name": "Invalid In Field",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "InvalidInField",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--invalid-in-field&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--raised-on-canvas",
        "name": "Raised On Canvas",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "RaisedOnCanvas",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--raised-on-canvas&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--mobile-form-flow",
        "name": "Mobile Form Flow",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "MobileFormFlow",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--mobile-form-flow&viewMode=story"
      },
      {
        "id": "cloaked-design-system-primitives-input--dark-theme",
        "name": "Dark Theme",
        "title": "Cloaked Design System/Primitives/Input",
        "exportName": "DarkTheme",
        "source_path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
        "source_digest": "fa5d0501c9c65ff82eb830653806b66532abdbac2ff3fe9da04c5258dff0db4f",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-primitives-input--dark-theme&viewMode=story"
      }
    ],
    "name": "Input",
    "id": "input",
    "category": "Primitives"
  },
  "stories_source": {
    "path": "packages/design-system/src/components/primitives/Input/Input.stories.tsx",
    "code": "import React, { useEffect, useRef, useState } from 'react';\nimport type { Meta, StoryObj } from '@storybook/react-vite';\nimport { VStack } from '../../layout/VStack/VStack';\nimport { Field } from '../../forms/Field/Field';\nimport { Button } from '../Button/Button';\nimport { Icon } from '../Icon/Icon';\nimport { disabledControl, selectArgType } from '../../../storybook/controlOptions';\nimport {\n    Input,\n    type InputEnterBehavior,\n    type InputEnterKeyHint,\n    type InputProps,\n    type InputSurface,\n    type InputType,\n} from './Input';\nimport { FullMobileContactForm } from './Input.stories.fixtures';\n\nconst INPUT_TYPES = [\n    'text',\n    'password',\n    'email',\n    'tel',\n    'url',\n    'search',\n    'number',\n] as const satisfies readonly InputType[];\nconst INPUT_SURFACES = ['default', 'raised'] as const satisfies readonly InputSurface[];\nconst INPUT_ENTER_BEHAVIORS = ['advance', 'callback'] as const satisfies readonly InputEnterBehavior[];\nconst INPUT_ENTER_KEY_HINTS = [\n    'enter',\n    'done',\n    'go',\n    'next',\n    'previous',\n    'search',\n    'send',\n] as const satisfies readonly InputEnterKeyHint[];\n\nconst meta: Meta<typeof Input> = {\n    title: 'Cloaked Design System/Primitives/Input',\n    component: Input,\n    parameters: {\n        a11y: { test: 'error' },\n        stage: 'form-control',\n        docs: {\n            description: {\n                component:\n                    'The single-line text field, and nothing else — no label, no helper, no error message. ' +\n                    'Drive its `type` for the right native keyboard and validation (`email`, `tel`, `url`, ' +\n                    '`number`, `search`, `password`), and set `enterKeyHint` + `enterBehavior` for iOS ' +\n                    'return-key flow between fields. Wrap it in `Field` for a label and validation copy; the ' +\n                    \"Field's `error` turns this red on its own. Use `Textarea` for multi-line and \" +\n                    '`SearchField` for the search chrome.',\n            },\n        },\n    },\n    decorators: [\n        Story => (\n            <div style={{ width: '100%', maxWidth: 354 }}>\n                <Story />\n            </div>\n        ),\n    ],\n    args: {\n        placeholder: 'xxxx',\n        ariaLabel: 'Example field',\n        onChange: () => {},\n    },\n    argTypes: {\n        type: {\n            ...selectArgType(INPUT_TYPES),\n            description: 'Native input type; picks the on-screen keyboard and native validation.',\n        },\n        surface: {\n            ...selectArgType(INPUT_SURFACES),\n            description:\n                'Container fill. `default` is cream, for the white surfaces forms normally sit on; ' +\n                '`raised` is white, for a field placed directly on the cream page canvas.',\n        },\n        enterBehavior: {\n            ...selectArgType(INPUT_ENTER_BEHAVIORS),\n            description: '`advance` moves focus to the next form control before falling back to `onEnter`.',\n        },\n        enterKeyHint: {\n            ...selectArgType(INPUT_ENTER_KEY_HINTS),\n            description: 'Return-key label hint shown on the native on-screen keyboard.',\n        },\n        invalid: {\n            control: 'boolean',\n            description: 'Paints the invalid ring. A parent Field sets this from its own `error`.',\n        },\n        onChange: { ...disabledControl, description: 'Called with the next value on each edit.' },\n        onEnter: { ...disabledControl, description: 'Called when Enter is pressed while focused.' },\n        leading: { ...disabledControl, description: 'Content inside the field shell before the input.' },\n        trailing: { ...disabledControl, description: 'Content inside the field shell after the input.' },\n        trailingAction: { ...disabledControl, description: 'Trailing interactive action (e.g. a clear button).' },\n    },\n};\nexport default meta;\ntype Story = StoryObj<typeof Input>;\n\n/** Focuses the rendered field on mount so the active states can be shown statically. */\nfunction FocusedInput(props: InputProps) {\n    const ref = useRef<HTMLDivElement>(null);\n\n    useEffect(() => {\n        ref.current?.querySelector('input')?.focus();\n    }, []);\n\n    return (\n        <div ref={ref}>\n            <Input {...props} />\n        </div>\n    );\n}\n\n/** Controlled wrapper so the Playground story accepts typed input. */\nfunction ControlledInput(args: InputProps) {\n    const [value, setValue] = useState('');\n    return <Input {...args} value={value} onChange={setValue} />;\n}\n\n/** Fully interactive, controlled field. */\nexport const Playground: Story = {\n    render: args => <ControlledInput {...args} />,\n    args: { placeholder: 'Type here…', testId: 'input-story-playground' },\n};\n\n/** Empty, filled, invalid, and disabled fields side by side. */\nexport const StateMatrix: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: 'Every resting state at once. `invalid` here is the standalone escape hatch — it paints the ring and sets `aria-invalid` but renders no text, so pair it with copy the user can read. Inside a `Field`, the error comes from the Field instead.',\n            },\n        },\n    },\n    render: () => (\n        <VStack gap={16}>\n            <Input value='' placeholder='Empty field' ariaLabel='Empty field' testId='input-story-matrix-empty' />\n            <Input value='User Input' ariaLabel='Filled field' testId='input-story-matrix-filled' />\n            <Input value='User Input' invalid ariaLabel='Invalid field' testId='input-story-matrix-invalid' />\n            <Input value='User Input' disabled ariaLabel='Disabled field' testId='input-story-matrix-disabled' />\n        </VStack>\n    ),\n};\n\n/** Focused, empty and filled. The native caret and active placeholder appear. */\nexport const Focused: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: 'Both fields focus on mount so the active treatment is visible without interaction; only the first keeps the real DOM focus.',\n            },\n        },\n    },\n    render: () => (\n        <VStack gap={16}>\n            <FocusedInput\n                value=''\n                placeholder='Empty field'\n                ariaLabel='Empty field'\n                testId='input-story-focused-empty'\n            />\n            <FocusedInput value='User Input' ariaLabel='Filled field' testId='input-story-focused-filled' />\n        </VStack>\n    ),\n};\n\n/** The `type` values that change more than the keyboard: masking, URL validation, numeric bounds. */\nexport const Types: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`password` masks the value, `url` validates it as a web address, and `number` honours `min`/`max`/`step` — shown read-only, which keeps the field focusable where `disabled` would not.',\n            },\n        },\n    },\n    render: () => (\n        <VStack gap={16}>\n            <Input value='hunter2' type='password' ariaLabel='Password' testId='input-story-type-password' />\n            <Input value='https://cloaked.com' type='url' ariaLabel='Website' testId='input-story-type-url' />\n            <Input\n                value='4'\n                type='number'\n                readOnly\n                min={1}\n                max={10}\n                step={1}\n                ariaLabel='Read-only number'\n                testId='input-story-type-number'\n            />\n        </VStack>\n    ),\n};\n\n/** Leading/trailing slots, and the compact `sm` tier below the default `md`. */\nexport const WithSlots: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`leading`, `trailing`, and `trailingAction` place icons, units, and compact actions inside the field shell. The second field is the `sm` tier (40px) against the default `md` (56px) above it.',\n            },\n        },\n    },\n    render: () => (\n        <VStack gap={16}>\n            <Input\n                value=''\n                placeholder='Search brokers'\n                ariaLabel='Search brokers'\n                testId='input-story-slots'\n                leading={<Icon name='search' size='small' />}\n                trailing={<span>12</span>}\n                trailingAction={\n                    <Button\n                        variant='link'\n                        size='sm'\n                        icon={<Icon name='close' size='small' />}\n                        ariaLabel='Clear'\n                        testId='input-story-slots-clear'\n                    />\n                }\n            />\n            <Input\n                value=''\n                type='search'\n                size='sm'\n                placeholder='Search Widget'\n                ariaLabel='Search Widget'\n                testId='input-story-search-small'\n                leading={<Icon name='search' size='small' />}\n            />\n        </VStack>\n    ),\n};\n\n/** The normal path: Field owns the message and the Input turns red on its own. */\nexport const InvalidInField: Story = {\n    argTypes: { invalid: disabledControl },\n    render: args => (\n        <Field label='Zip code' error='Enter a 5-digit zip code.'>\n            <Input {...args} />\n        </Field>\n    ),\n    args: { value: '021', testId: 'input-story-invalid-in-field' },\n};\n\n/** Raised surface: white container for sitting on a cream page canvas (e.g. inside a cream sheet). */\nexport const RaisedOnCanvas: Story = {\n    parameters: { stage: 'page' },\n    args: { value: 'User Input', surface: 'raised', testId: 'input-story-raised' },\n};\n\n/** Mobile return-key flow: `next` moves through fields and `done` submits on the terminal control. */\nexport const MobileFormFlow: Story = {\n    // The fixture owns its own fields, so the args panel would drive nothing. `value` stays\n    // only because InputProps requires it.\n    parameters: { controls: { disable: true } },\n    render: () => <FullMobileContactForm />,\n    args: { value: '' },\n};\n\n/** Dark theme rendering. */\nexport const DarkTheme: Story = {\n    args: { value: 'User Input', testId: 'input-story-dark' },\n    globals: { theme: 'dark' },\n};\n"
  },
  "specification": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "Input",
      "status": "preview",
      "version": "2.0.0",
      "summary": "A controlled single-line text field with native keyboard types, compact and standard sizes, slots, invalid state, and mobile return-key behavior. It renders no label or message of its own."
    },
    "parts": [
      {
        "id": "wrapper",
        "label": "Wrapper",
        "description": "Carries the caller's class and style around the control."
      },
      {
        "id": "control",
        "label": "Control",
        "description": "Owns the pill surface, selected size, invalid ring, and optional slots."
      },
      {
        "id": "field",
        "label": "Field",
        "description": "Provides native single-line input, keyboard, validation, and form semantics."
      },
      {
        "id": "leading",
        "label": "Leading slot",
        "description": "Holds a non-expanding icon or prefix before the field."
      },
      {
        "id": "trailing",
        "label": "Trailing slot",
        "description": "Holds a non-expanding suffix after the field."
      },
      {
        "id": "trailing-action",
        "label": "Trailing action",
        "description": "Holds a compact interactive action such as clear or show password."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "base": {
          "label": "Base",
          "description": "The field has one pill-shaped control treatment."
        }
      },
      "sizes": {
        "sm": {
          "label": "Small"
        },
        "md": {
          "label": "Medium"
        }
      },
      "types": {
        "text": {
          "label": "Text",
          "description": "General single-line content with no more specific native input type."
        },
        "password": {
          "label": "Password",
          "description": "Masks a secret value and requests password input behavior."
        },
        "email": {
          "label": "Email",
          "description": "Requests an email keyboard and native email validation."
        },
        "tel": {
          "label": "Telephone",
          "description": "Requests a telephone keyboard for phone values."
        },
        "url": {
          "label": "URL",
          "description": "Requests a URL keyboard and native URL validation."
        },
        "search": {
          "label": "Search",
          "description": "Provides native search semantics for a bare search field."
        },
        "number": {
          "label": "Number",
          "description": "Supports native numeric minimum, maximum, and step constraints."
        },
        "date": {
          "label": "Date",
          "description": "Requests the native date picker; the value is an ISO YYYY-MM-DD string and the placeholder is platform-owned."
        }
      },
      "surfaces": {
        "default": {
          "label": "Default",
          "description": "Uses the input-container role on a raised host surface."
        },
        "raised": {
          "label": "Raised",
          "description": "Uses the raised surface role when the field sits directly on the page canvas."
        }
      }
    },
    "variants": [
      {
        "id": "base",
        "label": "Base",
        "description": "One pill-shaped field treatment configured through the separate type, surface, and size axes.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [
      {
        "id": "sm",
        "label": "Small",
        "description": "40-unit compact field for dense contexts."
      },
      {
        "id": "md",
        "label": "Medium",
        "description": "56-unit standard form field."
      }
    ],
    "measurements": [
      {
        "id": "small-height",
        "label": "Small control height",
        "kind": "exact",
        "token": "--cds-size-40",
        "value": 40,
        "unit": "px",
        "appliesTo": [
          "control",
          "sm"
        ]
      },
      {
        "id": "medium-height",
        "label": "Medium control height",
        "kind": "exact",
        "token": "--cds-size-56",
        "value": 56,
        "unit": "px",
        "appliesTo": [
          "control",
          "md"
        ]
      },
      {
        "id": "slot-gap",
        "label": "Slot gap",
        "kind": "exact",
        "token": "--cds-space-8",
        "value": 8,
        "unit": "px",
        "appliesTo": [
          "leading",
          "field",
          "trailing",
          "trailing-action"
        ]
      }
    ],
    "layout": {
      "alignment": "Field text and slots align on the control center line.",
      "flow": "Leading slot, flexible field, trailing slot, and trailing action form one row.",
      "notes": [
        "Wrap Input in Field for a visible label, helper text, or any validation copy; Field is the only owner of error messages.",
        "The control fills its parent width."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Medium field text",
          "description": "Standard fields use the body ramp.",
          "token": "--cds-type-body-size"
        },
        {
          "label": "Small field text",
          "description": "Compact fields use the body-small ramp outside the web anti-zoom floor.",
          "token": "--cds-type-body-small-size"
        }
      ],
      "colors": [
        {
          "label": "Default container",
          "description": "Uses the theme-aware input surface.",
          "token": "--cds-input-container"
        },
        {
          "label": "Raised container",
          "description": "Uses the raised surface role on a page canvas.",
          "token": "--cds-bg-surface"
        },
        {
          "label": "Field text",
          "description": "Uses primary text emphasis.",
          "token": "--cds-text-primary"
        },
        {
          "label": "Placeholder and slots",
          "description": "Use secondary text emphasis.",
          "token": "--cds-text-secondary"
        },
        {
          "label": "Invalid",
          "description": "Use the readable semantic danger role for error indication in both themes.",
          "token": "--cds-text-danger"
        }
      ]
    },
    "states": [
      {
        "id": "empty",
        "label": "Empty",
        "description": "Shows the explicit or type-specific placeholder."
      },
      {
        "id": "focused",
        "label": "Focused",
        "description": "Accepts input and shows a caret; there is no focus ring, since the design system targets a touch surface and the keyboard/caret already signal focus."
      },
      {
        "id": "filled",
        "label": "Filled",
        "description": "Shows the controlled value in primary text."
      },
      {
        "id": "invalid",
        "label": "Invalid",
        "description": "Shows the danger ring and invalid semantics; the control renders no message of its own. An enclosing Field supplies the corrective copy, and a standalone caller renders it elsewhere, such as a form-level summary."
      },
      {
        "id": "disabled",
        "label": "Disabled",
        "description": "Blocks editing and dims the whole control."
      },
      {
        "id": "read-only",
        "label": "Read only",
        "description": "Preserves focus and form semantics while preventing edits."
      }
    ],
    "behavior": [
      {
        "label": "Controlled editing",
        "description": "Request the next string on every edit while the parent owns the rendered value."
      },
      {
        "label": "Return-key behavior",
        "description": "Support advance to the next compatible field or invoke a callback; search and explicit callbacks default to callback behavior."
      },
      {
        "label": "Return-key labels",
        "description": "Support enter, done, go, next, previous, search, and send keyboard hints."
      },
      {
        "label": "Native types",
        "description": "Preserve text, password, email, telephone, URL, search, number, and date input semantics."
      }
    ],
    "accessibility": [
      {
        "label": "Accessible name",
        "description": "Associate a visible Field label or provide an accessible label when the input stands alone."
      },
      {
        "label": "Error relationship",
        "description": "Report invalid state on the control itself and render no message. An enclosing Field associates its rendered message with the control; a standalone caller associates the copy it renders elsewhere."
      },
      {
        "label": "Trailing actions",
        "description": "Give every interactive trailing action its own accessible name and stable test identifier."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Placeholders",
        "description": "Use placeholders as examples or hints, never as the only persistent visible label for complex forms."
      },
      {
        "label": "Long values",
        "description": "Let the native single-line field scroll horizontally; use Textarea for multiline content."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Focus and validation",
          "description": "Change borders and rings without moving the control."
        }
      ],
      "haptics": [
        {
          "label": "No intrinsic feedback",
          "description": "Typing and focus do not request design-system haptics; a trailing action owns its own feedback."
        }
      ]
    },
    "platformDifferences": [
      {
        "platforms": [
          "web"
        ],
        "description": "Small field text is floored at the 16px body size in the webview instead of rendering the 14px body-small size.",
        "rationale": "WKWebView zooms focused inputs below 16px; the font floor prevents an unexpected viewport jump while retaining the compact 40px shell."
      }
    ]
  },
  "specification_source": "packages/design-system/src/components/primitives/Input/Input.spec.json",
  "guidance": {
    "summary": "A controlled text input, paired with Field for a persistent label and useful validation.",
    "when_to_use": [
      "Collect a single-line value.",
      "Choose the native input type for the value being collected.",
      "Place one Input inside each Field."
    ],
    "avoid": [
      "Using placeholder text as the only label.",
      "Rendering a controlled input without onChange unless readOnly or disabled.",
      "Showing a red ring without explanatory text."
    ],
    "accessibility": [
      "Field connects the visible label and helper or error text to the input.",
      "Use autoComplete and inputMode when applicable.",
      "Keyboard focus surrounds the complete control."
    ],
    "constraints": [
      "value is required; onChange receives the edited string.",
      "Use surface=raised directly on a page canvas.",
      "The return key advances within a form or data-cds-form-scope unless callback behavior is selected."
    ]
  },
  "links": {
    "human": "/component-input",
    "json": "/data/visual/components/input.json",
    "storybook": "/storybook/?path=/story/cloaked-design-system-primitives-input--playground"
  }
}
