{
  "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": "field",
  "name": "Field",
  "category": "Forms",
  "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-forms-field--playground",
        "name": "Playground",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "Playground",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--playground&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--with-helper-text",
        "name": "With Helper Text",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "WithHelperText",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--with-helper-text&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--with-action",
        "name": "With Action",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "WithAction",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--with-action&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--with-error",
        "name": "With Error",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "WithError",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--with-error&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--without-label",
        "name": "Without Label",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "WithoutLabel",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--without-label&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--do-field-owns-error",
        "name": "Do Field Owns Error",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "DoFieldOwnsError",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--do-field-owns-error&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--dont-hand-roll-the-message",
        "name": "Dont Hand Roll The Message",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "DontHandRollTheMessage",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--dont-hand-roll-the-message&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--required",
        "name": "Required",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "Required",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--required&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--disabled",
        "name": "Disabled",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "Disabled",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--disabled&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--state-matrix",
        "name": "State Matrix",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "StateMatrix",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--state-matrix&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--long-label-and-helper",
        "name": "Long Label And Helper",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "LongLabelAndHelper",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--long-label-and-helper&viewMode=story"
      },
      {
        "id": "cloaked-design-system-forms-field--dark-theme",
        "name": "Dark Theme",
        "title": "Cloaked Design System/Forms/Field",
        "exportName": "DarkTheme",
        "source_path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
        "source_digest": "daffd21dfde07251f850f0a6b8a09bb7d45935278540f188cc4a70b06e0564f2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-forms-field--dark-theme&viewMode=story"
      }
    ],
    "name": "Field",
    "id": "field",
    "category": "Forms"
  },
  "stories_source": {
    "path": "packages/design-system/src/components/forms/Field/Field.stories.tsx",
    "code": "import React from 'react';\nimport type { Meta, StoryObj } from '@storybook/react-vite';\nimport { VStack } from '../../layout/VStack/VStack';\nimport { Input } from '../../primitives/Input/Input';\nimport { Button } from '../../primitives/Button/Button';\nimport { Text } from '../../primitives/Text/Text';\nimport { disabledControl } from '../../../storybook/controlOptions';\nimport { Field } from './Field';\n\nconst meta: Meta<typeof Field> = {\n    title: 'Cloaked Design System/Forms/Field',\n    component: Field,\n    parameters: {\n        a11y: { test: 'error' },\n        stage: 'form-control',\n        docs: {\n            description: {\n                component:\n                    'The label + validation wrapper for a single form control, and the only owner of ' +\n                    'validation copy. Wrap `Input`, `Textarea`, `Select`, `DatePicker`, or `RadioGroup` and ' +\n                    'Field owns the visible `label`, the `*` required marker, `helperText`, and `error` — ' +\n                    'wiring `id`/`aria-describedby`/`aria-invalid` onto the child so screen readers announce ' +\n                    'them together. Setting `error` also marks the child invalid through context, so `Input` ' +\n                    'paints its red ring without the caller setting the error twice. `helperText` and `error` ' +\n                    'are mutually exclusive: an `error` replaces the helper text. `label` is optional — a ' +\n                    'label-less Field is the way to attach an error to a control that names itself.',\n            },\n        },\n    },\n    decorators: [\n        Story => (\n            <div style={{ width: '100%', maxWidth: 354 }}>\n                <Story />\n            </div>\n        ),\n    ],\n    args: {\n        label: 'Email',\n        children: <Input value='' type='email' testId='field-email' />,\n    },\n    argTypes: {\n        children: {\n            ...disabledControl,\n            description: 'The single form control to wrap; Field clones it to inject id and aria wiring.',\n        },\n        action: {\n            ...disabledControl,\n            description: 'Optional control aligned to the right of the label, e.g. an \"Add\" or \"Use current\" Button.',\n        },\n        label: { control: 'text', description: 'Visible label text rendered above the control.' },\n        helperText: {\n            control: 'text',\n            description: 'Supporting copy shown below the control. Hidden while an `error` is present.',\n        },\n        error: {\n            control: 'text',\n            description: 'Validation message shown below the control; replaces `helperText` and sets `aria-invalid`.',\n        },\n        required: { control: 'boolean', description: 'Adds the `*` marker and forwards `required` to the control.' },\n        disabled: { control: 'boolean', description: 'Forwards the disabled state to the wrapped control.' },\n    },\n};\nexport default meta;\ntype Story = StoryObj<typeof Field>;\n\nexport const Playground: Story = {};\n\nexport const WithHelperText: Story = {\n    parameters: {\n        docs: { description: { story: 'Helper text sits below the control and clears once an error appears.' } },\n    },\n    args: {\n        label: 'Phone number',\n        helperText: 'Used only for account recovery.',\n        children: <Input value='' type='tel' testId='field-phone' />,\n    },\n};\n\nexport const WithAction: Story = {\n    parameters: {\n        docs: { description: { story: 'A label-row action (here an \"Add\" link) for repeatable or shortcut fields.' } },\n    },\n    args: {\n        label: 'Names',\n        action: (\n            <Button variant='link' size='sm' onPress={() => {}} testId='field-add-name'>\n                Add\n            </Button>\n        ),\n        children: <Input value='' placeholder='Legal name' testId='field-name' />,\n    },\n};\n\nexport const WithError: Story = {\n    parameters: {\n        docs: {\n            description: { story: 'An `error` replaces helper text and flags the control invalid to assistive tech.' },\n        },\n    },\n    args: {\n        label: 'Password',\n        error: 'Password is required.',\n        children: <Input value='' type='password' testId='field-password' />,\n    },\n};\n\nexport const WithoutLabel: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: 'Omit `label` to attach an error to a control that already names itself elsewhere on the screen.',\n            },\n        },\n    },\n    args: {\n        label: undefined,\n        error: 'Enter a 9-digit number.',\n        children: <Input value='123' ariaLabel='Social security number' testId='field-unlabelled' />,\n    },\n};\n\nexport const DoFieldOwnsError: Story = {\n    parameters: {\n        docs: { description: { story: 'One `error`, on the Field. The ring and the message stay in step.' } },\n    },\n    args: {\n        label: 'Zip code',\n        error: 'Enter a 5-digit zip code.',\n        children: <Input value='021' testId='field-do-error' />,\n    },\n};\n\nexport const DontHandRollTheMessage: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: 'A loose Text below a bare Input looks the same and is not wired to it: no red ring, no aria-describedby.',\n            },\n        },\n    },\n    render: () => (\n        <VStack gap={8}>\n            <Input value='021' ariaLabel='Zip code' testId='field-dont-error' />\n            <Text variant='body-small' color='danger'>\n                Enter a 5-digit zip code.\n            </Text>\n        </VStack>\n    ),\n};\n\nexport const Required: Story = {\n    parameters: {\n        docs: { description: { story: 'The `*` marker plus forwarded `required` for native form semantics.' } },\n    },\n    args: {\n        label: 'Recovery email',\n        required: true,\n        children: <Input value='' type='email' testId='field-recovery-email' />,\n    },\n};\n\nexport const Disabled: Story = {\n    parameters: { docs: { description: { story: 'Disabled state forwarded from the wrapper onto the control.' } } },\n    args: {\n        label: 'Disabled field',\n        disabled: true,\n        helperText: 'This field cannot be edited right now.',\n        children: <Input value='Locked value' testId='field-disabled' />,\n    },\n};\n\nexport const StateMatrix: Story = {\n    parameters: {\n        docs: {\n            description: { story: 'Default, required, helper, error, and disabled fields stacked for comparison.' },\n        },\n    },\n    render: () => (\n        <VStack gap={16}>\n            <Field label='Default'>\n                <Input value='' testId='field-matrix-default' />\n            </Field>\n            <Field label='Required' required>\n                <Input value='' testId='field-matrix-required' />\n            </Field>\n            <Field label='With helper text' helperText='Used only for account recovery.'>\n                <Input value='' testId='field-matrix-helper' />\n            </Field>\n            <Field label='With error' error='Enter a valid value.'>\n                <Input value='Invalid value' testId='field-matrix-error' />\n            </Field>\n            <Field label='Disabled' disabled helperText='This field cannot be edited right now.'>\n                <Input value='Locked value' testId='field-matrix-disabled' />\n            </Field>\n        </VStack>\n    ),\n};\n\nexport const LongLabelAndHelper: Story = {\n    parameters: {\n        docs: { description: { story: 'Long label and helper copy stay attached to the control on narrow screens.' } },\n    },\n    args: {\n        label: 'A longer field label that wraps on small mobile screens',\n        helperText: 'Long helper text should remain readable and stay attached to its control.',\n        children: <Input value='' testId='field-long' />,\n    },\n};\n\nexport const DarkTheme: Story = {\n    globals: { theme: 'dark' },\n    parameters: { docs: { description: { story: 'Label, helper, and error tokens rendered on the dark theme.' } } },\n    args: {\n        label: 'Recovery email',\n        error: 'Enter a valid email.',\n        children: <Input value='invalid' type='email' testId='field-dark' />,\n    },\n};\n"
  },
  "specification": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "Field",
      "status": "preview",
      "version": "2.0.0",
      "summary": "A form-control wrapper that owns visible labelling, optional label-side action, required state, helper copy, and validation — including the invalid state the child control renders."
    },
    "parts": [
      {
        "id": "root",
        "label": "Root",
        "description": "Stacks header, one compatible control, and one supporting message."
      },
      {
        "id": "header",
        "label": "Header",
        "description": "Aligns the visible label and optional label-side action."
      },
      {
        "id": "label",
        "label": "Label",
        "description": "Provides the persistent visible and programmatic name for the child control."
      },
      {
        "id": "required-mark",
        "label": "Required mark",
        "description": "Visually indicates required input without becoming part of the label text."
      },
      {
        "id": "action",
        "label": "Label-side action",
        "description": "Optional compact action related to the field, such as Add or Use current."
      },
      {
        "id": "control",
        "label": "Child control",
        "description": "Exactly one compatible form control receiving label, message, required, disabled, and invalid wiring."
      },
      {
        "id": "helper-message",
        "label": "Helper message",
        "description": "Supporting copy shown only while there is no error."
      },
      {
        "id": "error-message",
        "label": "Error message",
        "description": "Corrective copy that replaces helper text and marks the control invalid."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "labelled": {
          "label": "Labelled",
          "description": "One field wrapper whose optional parts are configured independently."
        }
      },
      "sizes": {}
    },
    "variants": [
      {
        "id": "labelled",
        "label": "Labelled",
        "description": "Persistent label and message scaffolding around one compatible form control.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [],
    "measurements": [
      {
        "id": "stack-gap",
        "label": "Field stack gap",
        "kind": "exact",
        "token": "--cds-space-8",
        "value": 8,
        "unit": "px",
        "appliesTo": [
          "root",
          "header",
          "control",
          "helper-message",
          "error-message"
        ]
      },
      {
        "id": "header-gap",
        "label": "Label-to-action minimum gap",
        "kind": "exact",
        "token": "--cds-space-12",
        "value": 12,
        "unit": "px",
        "appliesTo": [
          "header",
          "label",
          "action"
        ]
      },
      {
        "id": "required-mark-gap",
        "label": "Label-to-required-mark gap",
        "kind": "exact",
        "token": "--cds-space-4",
        "value": 4,
        "unit": "px",
        "appliesTo": [
          "label",
          "required-mark"
        ]
      }
    ],
    "layout": {
      "alignment": "Label begins at the control edge while a label-side action remains trailing; messages align with the control.",
      "flow": "Optional header, exactly one child control, then either helper or error copy.",
      "notes": [
        "Use Field whenever a control needs a persistent visible label, supporting message, or validation; the label itself is optional.",
        "Do not place multiple controls under one Field instance."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Label and messages",
          "description": "Use body-small text for labels, helpers, and validation copy.",
          "token": "--cds-type-body-small-size"
        }
      ],
      "colors": [
        {
          "label": "Label",
          "description": "Use primary text emphasis for the persistent label.",
          "token": "--cds-text-primary"
        },
        {
          "label": "Helper",
          "description": "Use secondary emphasis for non-critical supporting copy.",
          "token": "--cds-text-secondary"
        },
        {
          "label": "Required and error",
          "description": "Use the readable semantic danger role for error indication in both themes.",
          "token": "--cds-text-danger"
        }
      ]
    },
    "states": [
      {
        "id": "default",
        "label": "Default",
        "description": "Shows the label and child control without supporting copy."
      },
      {
        "id": "helper",
        "label": "Helper",
        "description": "Associates non-critical supporting copy with the control."
      },
      {
        "id": "required",
        "label": "Required",
        "description": "Shows the visual mark and forwards required semantics."
      },
      {
        "id": "error",
        "label": "Error",
        "description": "Replaces helper copy, associates the error, and puts the child control into its invalid treatment."
      },
      {
        "id": "disabled",
        "label": "Disabled",
        "description": "Forwards disabled state while retaining label and supporting context."
      }
    ],
    "behavior": [
      {
        "label": "Child wiring",
        "description": "Publish id, disabled, required, labelled-by, described-by, and invalid to the child control, which fills in only the values it does not set itself."
      },
      {
        "label": "Invalid propagation",
        "description": "Publish invalid state to the child control so the control renders its own invalid treatment without the caller setting the error twice. A control that sets invalid itself keeps its own value. The wiring travels by context, so it still reaches a control nested inside a wrapper component."
      },
      {
        "label": "Message precedence",
        "description": "Show and associate error copy instead of helper copy whenever an error is present."
      },
      {
        "label": "Label-side action",
        "description": "Keep the optional action separate from the child control and give the action its own name and test identifier."
      }
    ],
    "accessibility": [
      {
        "label": "Persistent name",
        "description": "Bind the visible label to the child control through id and labelled-by relationships."
      },
      {
        "label": "Message relationship",
        "description": "Associate the one visible helper or error message through described-by."
      },
      {
        "label": "Required mark",
        "description": "Hide the decorative asterisk from assistive technology and forward required state to the control."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Long labels",
        "description": "Allow the label to wrap while preserving space for a compact trailing action."
      },
      {
        "label": "Corrective errors",
        "description": "Write errors as specific correction guidance rather than repeating the label."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Message replacement",
          "description": "Helper and error copy may replace one another without animating or moving the child control independently."
        }
      ],
      "haptics": [
        {
          "label": "No intrinsic feedback",
          "description": "Field owns no interaction; the child control and label-side action own their haptics."
        }
      ]
    },
    "platformDifferences": [
      {
        "platforms": [
          "web"
        ],
        "description": "Web associates the label and messages with DOM id and ARIA references; native implementations use platform accessibility labels, values, hints, and errors.",
        "rationale": "The visible hierarchy is shared while each platform uses its native form relationship APIs."
      }
    ]
  },
  "specification_source": "packages/design-system/src/components/forms/Field/Field.spec.json",
  "guidance": {
    "summary": "Own the label, helper text, and error for exactly one form control.",
    "when_to_use": [
      "A control needs a visible label or explanatory message.",
      "A validation error should also mark its control invalid.",
      "A field is required or disabled as a unit."
    ],
    "avoid": [
      "Placing two Field-aware controls inside one Field.",
      "Duplicating error text below the Input.",
      "Overriding control IDs or accessible labels without preserving the visible label association."
    ],
    "accessibility": [
      "Field owns stable IDs for its label, description, and control.",
      "An error replaces helper text and is referenced by aria-describedby.",
      "Required markers are decorative; the child receives native required semantics."
    ],
    "constraints": [
      "Compatible controls consume FieldContext at any nesting depth.",
      "Explicit child props take precedence over context values."
    ]
  },
  "links": {
    "human": "/component-field",
    "json": "/data/visual/components/field.json",
    "storybook": "/storybook/?path=/story/cloaked-design-system-forms-field--playground"
  }
}
