{
  "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": "bottomsheet",
  "name": "BottomSheet",
  "category": "Overlays",
  "contract_status": "preview",
  "contract_kind": "source-derived",
  "review": {
    "local_status": "not-reviewed",
    "source_approval": null,
    "imported_spec_status": "approved",
    "spec_origin": "imported-adapted",
    "local_spec_source_status": "approved",
    "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-overlays-bottomsheet--playground",
        "name": "Playground",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "Playground",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--playground&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--size-matrix",
        "name": "Size Matrix",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "SizeMatrix",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--size-matrix&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--small",
        "name": "Small",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "Small",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--small&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--medium",
        "name": "Medium",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "Medium",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--medium&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--large",
        "name": "Large",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "Large",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--large&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--full",
        "name": "Full",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "Full",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--full&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--full-screen",
        "name": "Full Screen",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "FullScreen",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--full-screen&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--raised-surface",
        "name": "Raised Surface",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "RaisedSurface",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--raised-surface&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--serif-header",
        "name": "Serif Header",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "SerifHeader",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--serif-header&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--with-slots",
        "name": "With Slots",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "WithSlots",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--with-slots&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--swipe-dismiss-disabled",
        "name": "Swipe Dismiss Disabled",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "SwipeDismissDisabled",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--swipe-dismiss-disabled&viewMode=story"
      },
      {
        "id": "cloaked-design-system-overlays-bottomsheet--dark-theme",
        "name": "Dark Theme",
        "title": "Cloaked Design System/Overlays/BottomSheet",
        "exportName": "DarkTheme",
        "source_path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
        "source_digest": "a7215f706c24b90befbd609b513f7a8889d5588c2ad0b7344d7ad641d024b6f6",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-overlays-bottomsheet--dark-theme&viewMode=story"
      }
    ],
    "name": "BottomSheet",
    "id": "bottomsheet",
    "category": "Overlays"
  },
  "stories_source": {
    "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.stories.tsx",
    "code": "import React, { useState } from 'react';\nimport type { Meta, StoryObj } from '@storybook/react-vite';\nimport { BottomSheet } from './BottomSheet';\nimport type { BottomSheetPresentation, BottomSheetSize, BottomSheetTitleVariant } from './BottomSheet';\nimport { HStack } from '../../layout/HStack/HStack';\nimport { VStack } from '../../layout/VStack/VStack';\nimport { Button } from '../../primitives/Button/Button';\nimport { Text } from '../../primitives/Text/Text';\nimport { SURFACE_OPTIONS, disabledControl, hapticArgType, selectArgType } from '../../../storybook/controlOptions';\n\nconst BOTTOM_SHEET_SIZES = ['small', 'medium', 'large', 'full', 'auto'] as const satisfies readonly BottomSheetSize[];\nconst BOTTOM_SHEET_PRESENTATIONS = ['sheet', 'fullScreen'] as const satisfies readonly BottomSheetPresentation[];\n\nconst meta: Meta<typeof BottomSheet> = {\n    title: 'Cloaked Design System/Overlays/BottomSheet',\n    component: BottomSheet,\n    parameters: {\n        a11y: { test: 'error' },\n        docs: {\n            description: {\n                component:\n                    'The mobile bottom-sheet dialog — the standard way to present focused, dismissible content over a ' +\n                    'screen. `presentation` picks the chrome: `sheet` is a rounded bottom sheet with an optional drag ' +\n                    'handle and swipe-to-dismiss; `fullScreen` fills the viewport for immersive flows. `size` sets the ' +\n                    'sheet height (`auto` hugs content, `full` reaches the sheet max-height). Compose it from slots: ' +\n                    '`BottomSheet.Header` for the top chrome, a `BottomSheet.Title` inside it, and `BottomSheet.Body` ' +\n                    'for the content. It renders through a portal, so keep `isOpen` in parent state and close it from ' +\n                    '`onDismiss`.',\n            },\n        },\n    },\n    args: {\n        isOpen: false,\n        onDismiss: () => undefined,\n        size: 'medium',\n        presentation: 'sheet',\n        surface: 'surface',\n        showDragHandle: true,\n        children: (\n            <>\n                <BottomSheet.Header>\n                    <BottomSheet.Title>Details</BottomSheet.Title>\n                </BottomSheet.Header>\n                <BottomSheet.Body>\n                    <Text variant='body' color='secondary' as='p'>\n                        Tap the backdrop or swipe down to dismiss.\n                    </Text>\n                </BottomSheet.Body>\n            </>\n        ),\n    },\n    argTypes: {\n        isOpen: disabledControl,\n        children: disabledControl,\n        size: {\n            ...selectArgType(BOTTOM_SHEET_SIZES),\n            description: 'Sheet height preset. `auto` hugs content; `full` reaches the sheet max-height.',\n        },\n        presentation: {\n            ...selectArgType(BOTTOM_SHEET_PRESENTATIONS),\n            description:\n                'Chrome shape: `sheet` is a rounded bottom sheet; `fullScreen` fills the viewport with no radius.',\n        },\n        surface: {\n            ...selectArgType(SURFACE_OPTIONS),\n            description: 'Sheet background surface token. Defaults to `surface`.',\n        },\n        ariaLabel: {\n            control: 'text',\n            description: 'Names a sheet that carries no `BottomSheet.Title`. Ignored once a Title is slotted in.',\n        },\n        showDragHandle: {\n            control: 'boolean',\n            description: 'Shows the grab-handle affordance at the top of the sheet.',\n        },\n        disableSwipeToDismiss: {\n            control: 'boolean',\n            description: 'Turns off swipe-down-to-dismiss — use for required decisions.',\n        },\n        portalTarget: { ...disabledControl, description: 'Mount node for the portal. Defaults to `document.body`.' },\n        onDismiss: disabledControl,\n        onBeforeDismiss: disabledControl,\n        haptic: hapticArgType,\n    },\n};\nexport default meta;\ntype Story = StoryObj<typeof BottomSheet>;\n\nfunction PlaygroundDemo(args: React.ComponentProps<typeof BottomSheet>) {\n    const [open, setOpen] = useState(false);\n    return (\n        <VStack padding={24}>\n            <Button variant='primary' size='md' onPress={() => setOpen(true)} testId='bottom-sheet-playground-open'>\n                Open bottom sheet\n            </Button>\n            <BottomSheet {...args} isOpen={open} onDismiss={() => setOpen(false)} />\n        </VStack>\n    );\n}\n\ninterface SheetStageProps {\n    title: string;\n    size: BottomSheetSize;\n    presentation?: BottomSheetPresentation;\n    titleVariant?: BottomSheetTitleVariant;\n    /** Extra header child, to show what the header stacks beneath the title row. */\n    subtitle?: string;\n    withClose?: boolean;\n    showDragHandle?: boolean;\n    disableSwipeToDismiss?: boolean;\n}\n\nfunction SheetStage({\n    title,\n    size,\n    presentation = 'sheet',\n    titleVariant,\n    subtitle,\n    withClose = false,\n    showDragHandle,\n    disableSwipeToDismiss,\n}: SheetStageProps) {\n    const [isOpen, setIsOpen] = useState(false);\n    const close = () => setIsOpen(false);\n\n    return (\n        <VStack padding={24}>\n            <Button variant='primary' size='md' onPress={() => setIsOpen(true)} testId={`bottom-sheet-${size}-open`}>\n                {`Open ${size}`}\n            </Button>\n            <BottomSheet\n                isOpen={isOpen}\n                onDismiss={close}\n                size={size}\n                presentation={presentation}\n                showDragHandle={showDragHandle}\n                disableSwipeToDismiss={disableSwipeToDismiss}\n                testId='bottom-sheet-story'\n            >\n                <BottomSheet.Header onClose={withClose ? close : undefined} testId='bottom-sheet-story-header'>\n                    <BottomSheet.Title variant={titleVariant}>{title}</BottomSheet.Title>\n                    {subtitle && (\n                        <Text variant='body-small' color='secondary'>\n                            {subtitle}\n                        </Text>\n                    )}\n                </BottomSheet.Header>\n                <BottomSheet.Body>\n                    <Text variant='body' color='secondary' as='p'>\n                        {`A ${size} ${presentation} bottom sheet. Tap the backdrop or swipe down to dismiss.`}\n                    </Text>\n                </BottomSheet.Body>\n            </BottomSheet>\n        </VStack>\n    );\n}\n\nfunction LaunchersDemo() {\n    return (\n        <HStack gap={12} wrap padding={24}>\n            {BOTTOM_SHEET_SIZES.map(size => (\n                <SheetStage key={size} size={size} title={`${size} sheet`} />\n            ))}\n        </HStack>\n    );\n}\n\nexport const Playground: Story = { render: args => <PlaygroundDemo {...args} /> };\nexport const SizeMatrix: Story = {\n    parameters: { docs: { description: { story: 'Every `size` preset as a bottom sheet.' } } },\n    render: () => <LaunchersDemo />,\n};\nexport const Small: Story = { render: () => <SheetStage size='small' title='Sort by' /> };\nexport const Medium: Story = { render: () => <SheetStage size='medium' title='Details' /> };\nexport const Large: Story = { render: () => <SheetStage size='large' title='Details' /> };\nexport const Full: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`size=\"full\"` with the default `sheet` chrome — a tall rounded bottom sheet that stops below the status bar.',\n            },\n        },\n    },\n    render: () => <SheetStage size='full' title='Full sheet' />,\n};\nexport const FullScreen: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`presentation=\"fullScreen\"` fills the viewport edge-to-edge with no radius or drag handle — for immersive flows. `BottomSheet.Header` is the element that reserves the top safe-area inset here.',\n            },\n        },\n    },\n    render: () => <SheetStage size='full' title='Full screen' presentation='fullScreen' showDragHandle={false} />,\n};\nexport const RaisedSurface: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: 'Overriding `surface` swaps the sheet background token — here `page` instead of the default `surface`.',\n            },\n        },\n    },\n    render: args => <PlaygroundDemo {...args} surface='page' size='medium' />,\n};\nexport const SerifHeader: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`BottomSheet.Title variant=\"serif\"` renders the title in the display serif; `onClose` on the header adds the close button.',\n            },\n        },\n    },\n    render: () => (\n        <SheetStage size='large' title='Privacy detail' titleVariant='serif' subtitle='Updated today' withClose />\n    ),\n};\nexport const WithSlots: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`BottomSheet.Title` inside `BottomSheet.Header`, plus `BottomSheet.Body`. The title shares its row with the close button; other header children stack below it.',\n            },\n        },\n    },\n    render: () => <SheetStage size='large' title='Privacy detail' subtitle='Updated today' withClose />,\n};\nexport const SwipeDismissDisabled: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story: '`disableSwipeToDismiss` forces a deliberate choice — the sheet only closes via the backdrop or an in-content action.',\n            },\n        },\n    },\n    render: () => <SheetStage size='medium' title='Required decision' showDragHandle={false} disableSwipeToDismiss />,\n};\nexport const DarkTheme: Story = {\n    render: () => <SheetStage size='medium' title='Details' />,\n    globals: { theme: 'dark' },\n};\n"
  },
  "specification": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "BottomSheet",
      "status": "preview",
      "version": "1.3.0",
      "summary": "A mobile bottom-sheet dialog with sheet or full-screen chrome, height presets, swipe-to-dismiss, and optional header and body slots."
    },
    "parts": [
      {
        "id": "root",
        "label": "Root",
        "description": "The sheet surface presenting the dialog.",
        "required": true
      },
      {
        "id": "overlay",
        "label": "Overlay",
        "description": "Backdrop that dims and dismisses on tap."
      },
      {
        "id": "drag-handle",
        "label": "Drag handle",
        "description": "Optional grab affordance at the top of the sheet."
      },
      {
        "id": "content",
        "label": "Content",
        "description": "Scrollable content region."
      },
      {
        "id": "header",
        "label": "BottomSheet.Header",
        "description": "Top chrome slot: an optional BottomSheet.Title, an optional close button, and any content beneath them."
      },
      {
        "id": "title",
        "label": "BottomSheet.Title",
        "description": "Heading slot that names the sheet. Belongs inside BottomSheet.Header."
      },
      {
        "id": "body",
        "label": "BottomSheet.Body",
        "description": "Composed body slot with consistent spacing."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "sheet": {
          "label": "Sheet",
          "description": "Rounded bottom-sheet chrome with an optional drag handle."
        },
        "full-screen": {
          "label": "Full screen",
          "description": "The fullScreen presentation fills the viewport with no radius."
        }
      },
      "sizes": {
        "small": {
          "label": "Small",
          "description": "40vh height preset."
        },
        "medium": {
          "label": "Medium",
          "description": "60vh height preset."
        },
        "large": {
          "label": "Large",
          "description": "85vh height preset."
        },
        "full": {
          "label": "Full",
          "description": "Fills the viewport height."
        },
        "auto": {
          "label": "Auto",
          "description": "Hugs content height, capped at the sheet max-height."
        }
      }
    },
    "variants": [
      {
        "id": "sheet",
        "label": "Sheet",
        "description": "Rounded bottom-sheet presentation.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      },
      {
        "id": "full-screen",
        "label": "Full screen",
        "description": "Edge-to-edge fullScreen presentation.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [
      {
        "id": "small",
        "label": "Small",
        "description": "40vh sheet."
      },
      {
        "id": "medium",
        "label": "Medium",
        "description": "60vh sheet."
      },
      {
        "id": "large",
        "label": "Large",
        "description": "85vh sheet."
      },
      {
        "id": "full",
        "label": "Full",
        "description": "Full-viewport sheet."
      },
      {
        "id": "auto",
        "label": "Auto",
        "description": "Content-height sheet."
      }
    ],
    "measurements": [
      {
        "id": "sheet-padding-top",
        "label": "Sheet top inset",
        "kind": "exact",
        "token": "--cds-space-24",
        "value": 24,
        "unit": "px",
        "appliesTo": [
          "root"
        ]
      },
      {
        "id": "sheet-inline-inset",
        "label": "Content horizontal gutter",
        "kind": "exact",
        "token": "--cds-space-20",
        "value": 20,
        "unit": "px",
        "appliesTo": [
          "content"
        ],
        "notes": "Default gutter on the content region; padding props on the sheet override it, so paddingX={0} hosts edge-to-edge content. The built-in title keeps this inset either way."
      },
      {
        "id": "content-gap",
        "label": "Content gap",
        "kind": "exact",
        "token": "--cds-space-12",
        "value": 12,
        "unit": "px",
        "appliesTo": [
          "content"
        ]
      },
      {
        "id": "corner-radius",
        "label": "Sheet corner radius",
        "kind": "exact",
        "token": "--cds-radius-xl",
        "value": 24,
        "unit": "px",
        "appliesTo": [
          "root"
        ],
        "notes": "Top corners only; the sheet presentation rounds its top edge."
      }
    ],
    "layout": {
      "alignment": "The sheet anchors to the bottom edge and centers horizontally; content scrolls within it.",
      "flow": "A backdrop hosts the sheet, which stacks an optional drag handle, an optional header slot, and scrollable content.",
      "notes": [
        "Compose BottomSheet.Header with a BottomSheet.Title inside it; there is no title prop on the root.",
        "BottomSheet.Title must sit inside BottomSheet.Header. The header is the one element reserving the top safe-area inset, so a title that could also be topmost would need the inset in two places.",
        "The top safe-area inset is reserved only by the fullScreen presentation, which reads --cds-safe-area-top-chrome; a bottom-anchored sheet never reaches the notch.",
        "Padding props on the sheet set the content gutter and default to the 20px page inset; paddingX={0} lets a child run edge to edge.",
        "BottomSheet.Header keeps the 20px gutter whatever the padding props say, so paddingX={0} opens up the body without dragging the title to the screen edge."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Title",
          "description": "BottomSheet.Title uses the h4 ramp, or the h3-serif ramp with variant=\"serif\".",
          "token": "--cds-type-h4-size"
        }
      ],
      "colors": [
        {
          "label": "Surface",
          "description": "The sheet uses the raised surface by default.",
          "token": "--cds-bg-surface"
        },
        {
          "label": "Content text",
          "description": "Content uses primary text.",
          "token": "--cds-text-primary"
        }
      ]
    },
    "states": [
      {
        "id": "open",
        "label": "Open",
        "description": "Mounted and presented over the backdrop."
      },
      {
        "id": "dismissing",
        "label": "Dismissing",
        "description": "Sliding out after a dismiss gesture or backdrop tap."
      },
      {
        "id": "lowered",
        "label": "Lowered",
        "description": "Slid partway down with the scrim faded, revealing the screen behind without dismissing."
      },
      {
        "id": "swipe-locked",
        "label": "Swipe locked",
        "description": "Swipe-to-dismiss disabled for required decisions."
      }
    ],
    "behavior": [
      {
        "label": "Controlled open",
        "description": "Keep isOpen in parent state and close from onDismiss."
      },
      {
        "label": "Dismissal",
        "description": "Backdrop tap, swipe down, or Escape dismiss the sheet unless swipe is disabled."
      },
      {
        "label": "Lowering",
        "description": "A host can set lowered (0-1) to duck the sheet by that fraction of its height and bring it back; the scrim fades in step and the sheet stays mounted and controlled."
      },
      {
        "label": "Scrim opacity",
        "description": "scrimOpacity (0-1) caps how dark the backdrop may go, for a host that needs the screen behind the sheet readable while the sheet holds still. It composes with lowered by relief, not by multiplication: the scrim renders at whichever of the two asks for more, so a sheet that only ducks behaves exactly as it did before."
      }
    ],
    "accessibility": [
      {
        "label": "Dialog semantics",
        "description": "The sheet is role=dialog with aria-modal, named by its BottomSheet.Title when one is present and by ariaLabel otherwise. A slot is invisible to the compiler, so a sheet with neither is caught by a dev-time check rather than a type error."
      },
      {
        "label": "Focus management",
        "description": "Focus moves into the sheet once the entrance settles, and is restored to the launcher the moment a close becomes irreversible — a dismiss during the entrance cancels the pending move rather than pulling VoiceOver into a sheet that is already leaving. The sheet suppresses the focus outline: the runtime is touch-only, so a ring on its edge signals nothing."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Height presets",
        "description": "size sets the sheet height; auto hugs content up to the max-height."
      },
      {
        "label": "Keyboard avoidance",
        "description": "The sheet insets its bottom padding when the keyboard is shown."
      },
      {
        "label": "Content gutter",
        "description": "The content region carries the page gutter by default. Pass paddingX={0} for edge-to-edge content such as a full-bleed list, and inset the sheet's own chrome instead."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Slide and spring",
          "description": "The sheet slides up on open and spring-tracks the drag on dismiss."
        },
        {
          "label": "Lower and return",
          "description": "Lowering animates over 400ms with --cds-ease-in-out, on a separate property from the dismiss spring so the two never fight."
        }
      ],
      "haptics": [
        {
          "label": "Dismiss haptic",
          "description": "Dismissal requests a light haptic before it completes."
        }
      ]
    },
    "platformDifferences": [
      {
        "platforms": [
          "web"
        ],
        "description": "Web renders the sheet through a portal to document.body and supports Escape-to-dismiss for hardware keyboards.",
        "rationale": "The bottom-sheet interaction is the cross-platform product pattern; the portal and Escape support keep it usable in the web runtime."
      }
    ]
  },
  "specification_source": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.spec.json",
  "guidance": {
    "summary": "A mobile bottom-sheet dialog with sheet or full-screen chrome, height presets, swipe-to-dismiss, and optional header and body slots.",
    "guidance": [
      {
        "heading": "Overview",
        "body": "The mobile bottom-sheet dialog — the standard way to present focused, dismissible\ncontent over a screen. It renders through a portal, so keep `isOpen` in parent\nstate and close it from `onDismiss`."
      },
      {
        "heading": "Use when",
        "body": "`size` sets the sheet height. Pick it by how much content the sheet carries.\n\n`small`, `large`, `auto`, and `full` are all in use today; `medium` is available\nbut currently unused in the app."
      },
      {
        "heading": "Presentation",
        "body": "`presentation` defaults to `sheet` — rounded bottom-sheet chrome with an optional\ndrag handle and swipe-to-dismiss. Switch to `fullScreen` to fill the viewport\nedge-to-edge with no radius or handle, for immersive flows. A `fullScreen` sheet\nreaches the notch, and `BottomSheet.Header` is what holds its content clear of\nit — so give one a header even when the chrome is only a title."
      },
      {
        "heading": "Composition",
        "body": "The sheet is composed entirely from slots. `BottomSheet.Header` is the top\nchrome, `BottomSheet.Title` names the sheet from inside it, and\n`BottomSheet.Body` holds the content and owns its own scrolling.\n\n```tsx\n<BottomSheet isOpen={isOpen} onDismiss={close} size='large' testId='privacy-detail-sheet'>\n    <BottomSheet.Header onClose={close} testId='privacy-detail-sheet-header'>\n        <BottomSheet.Title>Privacy detail</BottomSheet.Title>\n        <Text variant='body-small' color='secondary'>\n            Updated today\n        </Text>\n    </BottomSheet.Header>\n    <BottomSheet.Body>\n        <Text variant='body' color='secondary' as='p'>\n            Composed slots keep titles, close actions, and body spacing consistent.\n        </Text>\n    </BottomSheet.Body>\n</BottomSheet>\n```\n\n`BottomSheet.Title` must be a direct child of `BottomSheet.Header`. A fragment\nbetween the two is fine; a wrapper element is not, and\n`@cloakedinc/bottom-sheet-slots` fails the lint if you reach for one. The reason\nis the `fullScreen` presentation: `.header` is the single element that carries\nthe sheet's top safe-area inset, and a title that could also sit topmost gives\nthat inset two possible owners — the padding bug this containment closes.\n\nThe title shares a row with the close button, and every other header child\nstacks below that row. It draws no divider of its own; if a rule between header\nand body is what the design calls for, place a `Divider` yourself.\n\n`variant='serif'` swaps the title to the display serif. The default is `sans`."
      },
      {
        "heading": "Accessible name",
        "body": "The title doubles as the sheet's accessible name, so most sheets need nothing\nfurther. A sheet with no title — a picker whose options speak for themselves —\ntakes `ariaLabel` instead. A slot is invisible to the compiler, so this is the\none requirement the types cannot enforce. `@cloakedinc/bottom-sheet-slots` fails\nthe lint on a sheet with neither, and a sheet whose title only appears at runtime\nlogs a dev-time error — pass `ariaLabel` there, since a Title that does mount\ntakes the name back."
      },
      {
        "heading": "Required decisions",
        "body": "`disableSwipeToDismiss` forces a deliberate choice — the sheet closes only via the\nbackdrop or an in-content action, not a swipe."
      }
    ],
    "usage": [
      {
        "variant": "small",
        "use": "Brief content — a short option list, a confirm, an info blurb. The most common size in the app.",
        "avoid": "Long or scrolling content that will feel cramped.",
        "axis": "Size"
      },
      {
        "variant": "medium",
        "use": "A moderate amount of content that needs more room than a small sheet.",
        "axis": "Size"
      },
      {
        "variant": "large",
        "use": "Taller content — a catalog, results, or a detail with several rows.",
        "axis": "Size"
      },
      {
        "variant": "full",
        "use": "Long or scrolling content that should still read as a bottom sheet; reaches the sheet max-height, stopping below the status bar.",
        "axis": "Size"
      },
      {
        "variant": "auto",
        "use": "Content whose height you want the sheet to hug exactly, capped at the sheet max-height.",
        "avoid": "Content whose height varies enough that a fixed preset reads more predictably.",
        "axis": "Size"
      }
    ],
    "examples": [],
    "source": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.mdx",
    "status": "local-preview",
    "note": "Guidance is read from local documentation, originally imported from the frozen source. Platform assertions in guidance are not parity verification."
  },
  "links": {
    "human": "/component-bottomsheet",
    "json": "/data/visual/components/bottomsheet.json",
    "storybook": "/storybook/?path=/story/cloaked-design-system-overlays-bottomsheet--playground"
  }
}
