{
  "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",
  "summary": "A mobile bottom-sheet dialog with sheet or full-screen chrome, height presets, swipe-to-dismiss, and optional header and body slots.",
  "documentation": {
    "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."
  },
  "source": {
    "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
    "code": "import React, { useCallback, useEffect, useId, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport { cn } from '../../../utilities';\nimport { useCdsTheme } from '../../../hooks/useCdsTheme';\nimport { useHaptic } from '../../../hooks/useHaptic';\nimport { useScrollLock } from '../../../hooks/useScrollLock';\nimport type { HapticFeedback } from '../../../tokens/CdsHaptic';\nimport type { CdsSurface } from '../../../tokens/CdsSurface';\nimport { type PaddingProps } from '../../layout/resolvePadding';\nimport { Body } from './BottomSheetBody';\nimport { BottomSheetContext } from './BottomSheetContext';\nimport { Header } from './BottomSheetHeader';\nimport { Title } from './BottomSheetTitle';\nimport { useKeyboardInset } from './hooks/useKeyboardInset';\nimport { useSheetFocus } from './hooks/useSheetFocus';\nimport { useSheetTitleRegistry } from './hooks/useSheetTitleRegistry';\nimport { useSwipeToDismiss } from './hooks/useSwipeToDismiss';\nimport type { SwipeToDismissArea } from './hooks/useSwipeToDismiss';\nimport { BOTTOM_SHEET_LOWER_DURATION_MS } from './bottomSheetLowerDurationMs';\nimport { resolveContentStyle } from './resolveContentStyle';\nimport { resolveScrimOpacity } from './resolveScrimOpacity';\nimport styles from './BottomSheet.module.scss';\n\n/**\n * Bottom-sheet height preset. `auto` hugs the content height, capped at the sheet max-height.\n */\nexport type BottomSheetSize = 'small' | 'medium' | 'large' | 'full' | 'auto';\n\nexport type BottomSheetPresentation = 'sheet' | 'fullScreen';\nexport type { BottomSheetBodyProps } from './BottomSheetBody';\nexport type { BottomSheetHeaderProps } from './BottomSheetHeader';\nexport type { BottomSheetTitleProps, BottomSheetTitleVariant } from './BottomSheetTitle';\n\n/**\n * Base props shared by every bottom sheet.\n *\n * Padding props land on the content region and fall back to the page gutter, so `paddingX={0}`\n * is how a sheet hosts edge-to-edge content.\n */\ninterface BottomSheetBaseProps extends PaddingProps {\n    /**\n     * Drives the sheet. Flipping to `false` starts the exit spring rather than unmounting — the\n     * sheet stays in the DOM until that motion settles.\n     */\n    isOpen: boolean;\n    /** Called when the user dismisses (backdrop tap, swipe, or escape). */\n    onDismiss: () => void;\n    /** Height preset — `small`/`medium`/`large` are fixed at 40/60/85vh, `full` at 100vh. */\n    size: BottomSheetSize;\n    /**\n     * Slides the sheet down by this fraction of its own height (0–1) and fades the backdrop in step,\n     * without dismissing it.\n     */\n    lowered?: number;\n    /** Backdrop opacity while the sheet is at full height, clamped to 0-1. */\n    scrimOpacity?: number;\n    /** Chrome treatment. `sheet` is rounded bottom-sheet chrome; `fullScreen` fills the viewport. */\n    presentation?: BottomSheetPresentation;\n    /** Sheet surface token. Defaults to `surface`. */\n    surface?: CdsSurface;\n    /** Shows the drag handle. Defaults to `true`; it is also the only grab target for `swipeToDismissArea='handle'`. */\n    showDragHandle?: boolean;\n    /** Turns off swipe-to-dismiss. Defaults to `false`; backdrop tap and Escape still dismiss. */\n    disableSwipeToDismiss?: boolean;\n    /** Synchronous disable check (same tick) for nested swipe surfaces. */\n    disableSwipeToDismissRef?: React.RefObject<boolean>;\n    /** Region where a swipe-to-dismiss gesture can begin. Defaults to the whole sheet. */\n    swipeToDismissArea?: SwipeToDismissArea;\n    /**\n     * Names the sheet when it has no `BottomSheet.Title`. A slot is invisible to the compiler, so\n     * unlike the old `title` prop this cannot be type-enforced — `@cloakedinc/bottom-sheet-slots`\n     * rejects a sheet with neither, and a dev-time check catches what lint cannot read statically.\n     */\n    ariaLabel?: string;\n    /** Mount node for the portal. Defaults to `document.body`. */\n    portalTarget?: HTMLElement | null;\n    /** Fired right before dismissal completes — host hook for native effects (haptics). */\n    onBeforeDismiss?: () => void;\n    /**\n     * Fired once the close animation fully settles and the sheet unmounts. Use this to sequence\n     * post-dismiss work (e.g. navigation) so it starts only after the sheet has visually left.\n     */\n    onExited?: () => void;\n    /** Haptic feedback for dismissal. Defaults to `true` (`light`). */\n    haptic?: HapticFeedback;\n    className?: string;\n    style?: React.CSSProperties;\n    children: React.ReactNode;\n    /** `data-testid` for the sheet; the backdrop gets `${testId}-overlay`. */\n    testId?: string;\n}\n\n/**\n * Public prop type for `BottomSheet` — import this when wrapping the sheet in a host component.\n */\nexport type BottomSheetProps = BottomSheetBaseProps;\n\n/**\n * Mobile bottom-sheet dialog. Owns presentation, slide/drag animation,\n * swipe-to-dismiss, scroll lock, keyboard avoidance, and VoiceOver semantics.\n * Platform concerns stay out of the package: pass `portalTarget` for a custom\n * mount node and `onBeforeDismiss` to run native side effects such as haptics.\n */\nfunction BottomSheetRoot({\n    isOpen,\n    onDismiss,\n    size,\n    lowered = 0,\n    scrimOpacity = 1,\n    presentation = 'sheet',\n    surface = 'surface',\n    ariaLabel,\n    showDragHandle = true,\n    disableSwipeToDismiss = false,\n    disableSwipeToDismissRef,\n    swipeToDismissArea = 'sheet',\n    portalTarget,\n    onBeforeDismiss,\n    onExited,\n    haptic = true,\n    className,\n    style,\n    children,\n    testId,\n    ...paddingRest\n}: BottomSheetProps) {\n    const keyboardInset = useKeyboardInset(isOpen);\n    const theme = useCdsTheme();\n\n    const fireHaptic = useHaptic('light');\n\n    const handleBeforeDismiss = useCallback(() => {\n        fireHaptic(haptic);\n        onBeforeDismiss?.();\n    }, [fireHaptic, haptic, onBeforeDismiss]);\n\n    const handleDismiss = useCallback(() => {\n        handleBeforeDismiss();\n        onDismiss();\n    }, [handleBeforeDismiss, onDismiss]);\n\n    const { sheetRef, dragHandleRef, y, mounted, committed, onTouchStart, onTouchEnd } = useSwipeToDismiss({\n        isOpen,\n        disabled: disableSwipeToDismiss,\n        disabledRef: disableSwipeToDismissRef,\n        swipeToDismissArea,\n        onBeforeDismiss: handleBeforeDismiss,\n        onDismiss,\n        onExited,\n    });\n    const titleId = useId();\n    const { hasTitle, registerTitle } = useSheetTitleRegistry(ariaLabel);\n    const bottomSheetContext = useMemo(() => ({ titleId, registerTitle }), [titleId, registerTitle]);\n\n    useScrollLock(isOpen);\n\n    // Escape to dismiss — harmless on touch, supports hardware keyboards.\n    useEffect(() => {\n        if (!isOpen) return;\n        const onKeyDown = (e: KeyboardEvent) => {\n            if (e.key === 'Escape') handleDismiss();\n        };\n        document.addEventListener('keydown', onKeyDown);\n        return () => document.removeEventListener('keydown', onKeyDown);\n    }, [handleDismiss, isOpen]);\n\n    useSheetFocus(mounted, committed, sheetRef);\n\n    if (!mounted) return null;\n\n    // Position is one number: 0 = open, sheet height = fully dismissed. The spring\n    // (or the finger, mid-drag) owns `y`; everything visual derives from it.\n    const sheetHeight = sheetRef.current?.offsetHeight || window.innerHeight;\n    const progress = Math.min(1, y / sheetHeight);\n\n    const contentStyle = resolveContentStyle(paddingRest);\n\n    const lowerFraction = Math.min(1, Math.max(0, lowered));\n    const sheetStyle: React.CSSProperties = {\n        ...style,\n        transform: `translateY(${y}px)`,\n        translate: `0 ${lowerFraction * 100}%`,\n        // Padding lifts content clear of the keyboard; the negative margin keeps the sheet bottom-anchored.\n        ...(keyboardInset > 0 && { paddingBottom: keyboardInset, marginBottom: -keyboardInset }),\n    };\n\n    // Backdrop fades in step with the sheet, and goes inert the instant a close is committed so the\n    // content behind is interactive immediately.\n    const overlayStyle = {\n        opacity: 1 - progress,\n        pointerEvents: committed ? 'none' : 'auto',\n        '--sheet-scrim-opacity': resolveScrimOpacity(lowerFraction, scrimOpacity),\n        '--sheet-lower-duration': `${BOTTOM_SHEET_LOWER_DURATION_MS}ms`,\n        ...(keyboardInset > 0 && { paddingBottom: keyboardInset }),\n    } as React.CSSProperties;\n\n    // A lowered sheet is parked, not dismissible: the host that ducked it owns bringing it back, and\n    // the revealed screen behind is exactly where a stray tap lands.\n    const handleOverlayClick = lowerFraction > 0 ? undefined : handleDismiss;\n\n    return createPortal(\n        <div\n            className={styles.overlay}\n            onClick={handleOverlayClick}\n            style={overlayStyle}\n            data-theme={theme}\n            data-testid={testId ? `${testId}-overlay` : undefined}\n        >\n            <div\n                ref={sheetRef}\n                className={cn(\n                    styles.sheet,\n                    styles[`presentation-${presentation}`],\n                    styles[`surface-${surface}`],\n                    styles[`size-${size}`],\n                    className\n                )}\n                style={sheetStyle}\n                onClick={e => e.stopPropagation()}\n                onTouchStart={onTouchStart}\n                onTouchEnd={onTouchEnd}\n                role='dialog'\n                aria-modal='true'\n                aria-labelledby={hasTitle ? titleId : undefined}\n                aria-label={hasTitle ? undefined : ariaLabel}\n                tabIndex={-1}\n                data-testid={testId}\n            >\n                {showDragHandle && <div ref={dragHandleRef} className={styles.dragHandle} aria-hidden='true' />}\n                <BottomSheetContext.Provider value={bottomSheetContext}>\n                    <div className={styles.content} style={contentStyle}>\n                        {children}\n                    </div>\n                </BottomSheetContext.Provider>\n            </div>\n        </div>,\n        portalTarget ?? document.body\n    );\n}\n\n/**\n * Mobile bottom-sheet dialog, with its slots attached: compose the chrome as `BottomSheet.Header`\n * and the content as `BottomSheet.Body`.\n */\nexport const BottomSheet = Object.assign(BottomSheetRoot, {\n    Header,\n    Title,\n    Body,\n});\n"
  },
  "spec": {
    "$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."
      }
    ]
  },
  "spec_source": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.spec.json",
  "guidance_source": {
    "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.mdx",
    "code": "import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';\nimport { ComponentSpec, UsageTable, Do, Dont, Compare } from '../../../../.storybook/blocks';\nimport * as Stories from './BottomSheet.stories';\nimport bottomSheetSpec from './BottomSheet.spec.json';\n\n<Meta of={Stories} />\n\n# BottomSheet\n\nThe 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`.\n\n<Canvas of={Stories.Playground} />\n\n## Use when\n\n`size` sets the sheet height. Pick it by how much content the sheet carries.\n\n<UsageTable\n    axis='Size'\n    rows={[\n        {\n            variant: 'small',\n            use: 'Brief content — a short option list, a confirm, an info blurb. The most common size in the app.',\n            avoid: 'Long or scrolling content that will feel cramped.',\n        },\n        {\n            variant: 'medium',\n            use: 'A moderate amount of content that needs more room than a small sheet.',\n        },\n        {\n            variant: 'large',\n            use: 'Taller content — a catalog, results, or a detail with several rows.',\n        },\n        {\n            variant: 'full',\n            use: 'Long or scrolling content that should still read as a bottom sheet; reaches the sheet max-height, stopping below the status bar.',\n        },\n        {\n            variant: 'auto',\n            use: 'Content whose height you want the sheet to hug exactly, capped at the sheet max-height.',\n            avoid: 'Content whose height varies enough that a fixed preset reads more predictably.',\n        },\n    ]}\n/>\n\n`small`, `large`, `auto`, and `full` are all in use today; `medium` is available\nbut currently unused in the app.\n\n## Presentation\n\n`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.\n\n<Canvas of={Stories.FullScreen} />\n\n## Composition\n\nThe 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`.\n\n<Canvas of={Stories.WithSlots} />\n<Canvas of={Stories.SerifHeader} />\n\n## Accessible name\n\nThe 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.\n\n## Required decisions\n\n`disableSwipeToDismiss` forces a deliberate choice — the sheet closes only via the\nbackdrop or an in-content action, not a swipe.\n\n<Canvas of={Stories.SwipeDismissDisabled} />\n\n## Dark theme\n\n<Canvas of={Stories.DarkTheme} />\n\n## Design Guidelines\n\n<ComponentSpec spec={bottomSheetSpec} />\n\n## Props\n\n<Controls of={Stories.Playground} />\n"
  },
  "props": [
    {
      "name": "isOpen",
      "type": "boolean",
      "declared_type": "boolean",
      "required": true,
      "description": "Drives the sheet. Flipping to `false` starts the exit spring rather than unmounting — the\nsheet stays in the DOM until that motion settles.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 45
      },
      "values": [
        false,
        true
      ],
      "values_complete": true
    },
    {
      "name": "onDismiss",
      "type": "() => void",
      "declared_type": "() => void",
      "required": true,
      "description": "Called when the user dismisses (backdrop tap, swipe, or escape).",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 47
      }
    },
    {
      "name": "size",
      "type": "BottomSheetSize",
      "declared_type": "BottomSheetSize",
      "required": true,
      "description": "Height preset — `small`/`medium`/`large` are fixed at 40/60/85vh, `full` at 100vh.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 49
      },
      "values": [
        "auto",
        "full",
        "large",
        "medium",
        "small"
      ],
      "values_complete": true
    },
    {
      "name": "lowered",
      "type": "number | undefined",
      "declared_type": "number",
      "required": false,
      "description": "Slides the sheet down by this fraction of its own height (0–1) and fades the backdrop in step,\nwithout dismissing it.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 54
      },
      "default": "0",
      "default_kind": "source-expression"
    },
    {
      "name": "scrimOpacity",
      "type": "number | undefined",
      "declared_type": "number",
      "required": false,
      "description": "Backdrop opacity while the sheet is at full height, clamped to 0-1.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 56
      },
      "default": "1",
      "default_kind": "source-expression"
    },
    {
      "name": "presentation",
      "type": "BottomSheetPresentation | undefined",
      "declared_type": "BottomSheetPresentation",
      "required": false,
      "description": "Chrome treatment. `sheet` is rounded bottom-sheet chrome; `fullScreen` fills the viewport.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 58
      },
      "values": [
        "fullScreen",
        "sheet"
      ],
      "values_complete": true,
      "default": "'sheet'",
      "default_kind": "source-expression"
    },
    {
      "name": "surface",
      "type": "\"page\" | \"surface\" | undefined",
      "declared_type": "CdsSurface",
      "required": false,
      "description": "Sheet surface token. Defaults to `surface`.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 60
      },
      "values": [
        "page",
        "surface"
      ],
      "values_complete": true,
      "default": "'surface'",
      "default_kind": "source-expression"
    },
    {
      "name": "showDragHandle",
      "type": "boolean | undefined",
      "declared_type": "boolean",
      "required": false,
      "description": "Shows the drag handle. Defaults to `true`; it is also the only grab target for `swipeToDismissArea='handle'`.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 62
      },
      "values": [
        false,
        true
      ],
      "values_complete": true,
      "default": "true",
      "default_kind": "source-expression"
    },
    {
      "name": "disableSwipeToDismiss",
      "type": "boolean | undefined",
      "declared_type": "boolean",
      "required": false,
      "description": "Turns off swipe-to-dismiss. Defaults to `false`; backdrop tap and Escape still dismiss.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 64
      },
      "values": [
        false,
        true
      ],
      "values_complete": true,
      "default": "false",
      "default_kind": "source-expression"
    },
    {
      "name": "disableSwipeToDismissRef",
      "type": "React.RefObject<boolean> | undefined",
      "declared_type": "React.RefObject<boolean>",
      "required": false,
      "description": "Synchronous disable check (same tick) for nested swipe surfaces.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 66
      }
    },
    {
      "name": "swipeToDismissArea",
      "type": "SwipeToDismissArea | undefined",
      "declared_type": "SwipeToDismissArea",
      "required": false,
      "description": "Region where a swipe-to-dismiss gesture can begin. Defaults to the whole sheet.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 68
      },
      "values": [
        "chrome",
        "handle",
        "sheet"
      ],
      "values_complete": true,
      "default": "'sheet'",
      "default_kind": "source-expression"
    },
    {
      "name": "ariaLabel",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Names the sheet when it has no `BottomSheet.Title`. A slot is invisible to the compiler, so\nunlike the old `title` prop this cannot be type-enforced — `@cloakedinc/bottom-sheet-slots`\nrejects a sheet with neither, and a dev-time check catches what lint cannot read statically.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 74
      }
    },
    {
      "name": "portalTarget",
      "type": "HTMLElement | null | undefined",
      "declared_type": "HTMLElement | null",
      "required": false,
      "description": "Mount node for the portal. Defaults to `document.body`.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 76
      }
    },
    {
      "name": "onBeforeDismiss",
      "type": "(() => void) | undefined",
      "declared_type": "() => void",
      "required": false,
      "description": "Fired right before dismissal completes — host hook for native effects (haptics).",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 78
      }
    },
    {
      "name": "onExited",
      "type": "(() => void) | undefined",
      "declared_type": "() => void",
      "required": false,
      "description": "Fired once the close animation fully settles and the sheet unmounts. Use this to sequence\npost-dismiss work (e.g. navigation) so it starts only after the sheet has visually left.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 83
      }
    },
    {
      "name": "haptic",
      "type": "HapticFeedback | undefined",
      "declared_type": "HapticFeedback",
      "required": false,
      "description": "Haptic feedback for dismissal. Defaults to `true` (`light`).",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 85
      },
      "values": [
        "error",
        "heavy",
        "light",
        "medium",
        "selection",
        "success",
        "warning",
        false,
        true
      ],
      "values_complete": true,
      "default": "true",
      "default_kind": "source-expression"
    },
    {
      "name": "className",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 86
      }
    },
    {
      "name": "style",
      "type": "React.CSSProperties | undefined",
      "declared_type": "React.CSSProperties",
      "required": false,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 87
      }
    },
    {
      "name": "children",
      "type": "React.ReactNode",
      "declared_type": "React.ReactNode",
      "required": true,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 88
      }
    },
    {
      "name": "testId",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "`data-testid` for the sheet; the backdrop gets `${testId}-overlay`.",
      "source": {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 90
      }
    },
    {
      "name": "padding",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Padding on all four sides.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 10
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    },
    {
      "name": "paddingX",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Horizontal padding (left + right). Overrides `padding` on the X axis.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 12
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    },
    {
      "name": "paddingY",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Vertical padding (top + bottom). Overrides `padding` on the Y axis.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 14
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    },
    {
      "name": "paddingTop",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Top padding. Overrides `paddingY` and `padding`.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 16
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    },
    {
      "name": "paddingRight",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Right padding. Overrides `paddingX` and `padding`.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 18
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    },
    {
      "name": "paddingBottom",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Bottom padding. Overrides `paddingY` and `padding`.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 20
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    },
    {
      "name": "paddingLeft",
      "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
      "declared_type": "CdsSpace",
      "required": false,
      "description": "Left padding. Overrides `paddingX` and `padding`.",
      "source": {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 22
      },
      "values": [
        0,
        12,
        120,
        16,
        20,
        24,
        240,
        32,
        4,
        40,
        48,
        56,
        64,
        72,
        8,
        80,
        96
      ],
      "values_complete": true
    }
  ],
  "prop_shapes": {},
  "examples": [],
  "tokens": [
    {
      "name": "--cds-space-24",
      "light": "24px",
      "dark": "24px"
    },
    {
      "name": "--cds-space-20",
      "light": "20px",
      "dark": "20px"
    },
    {
      "name": "--cds-space-12",
      "light": "12px",
      "dark": "12px"
    },
    {
      "name": "--cds-radius-xl",
      "light": "24px",
      "dark": "24px"
    },
    {
      "name": "--cds-safe-area-top-chrome",
      "light": "0px",
      "dark": "0px"
    },
    {
      "name": "--cds-type-h4-size",
      "light": "1.25rem",
      "dark": "1.25rem"
    },
    {
      "name": "--cds-bg-surface",
      "light": "#ffffff",
      "dark": "#1b1b18"
    },
    {
      "name": "--cds-text-primary",
      "light": "#141410",
      "dark": "#f3f1ed"
    },
    {
      "name": "--cds-ease-in-out",
      "light": "cubic-bezier(0.77, 0, 0.175, 1)",
      "dark": "cubic-bezier(0.77, 0, 0.175, 1)"
    }
  ],
  "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": [
    {
      "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"
    }
  ],
  "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"
  },
  "links": {
    "human": "/component-bottomsheet",
    "json": "/data/components/bottomsheet.json",
    "markdown": "/data/components/bottomsheet.md",
    "storybook": "/storybook/?path=/story/cloaked-design-system-overlays-bottomsheet--playground"
  },
  "contract_status": "preview",
  "contract_kind": "source-derived",
  "approval": "required",
  "api": {
    "method": "typescript-type-checker",
    "typescript_version": "5.9.3",
    "public_export": "BottomSheet",
    "public_path": "BottomSheet",
    "import_path": "@cloakedinc/design-system",
    "root_kind": "component",
    "entrypoints": [
      {
        "name": "BottomSheet",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
          "line": 104
        },
        "props_type": "BottomSheetBaseProps",
        "props": [
          {
            "name": "isOpen",
            "type": "boolean",
            "declared_type": "boolean",
            "required": true,
            "description": "Drives the sheet. Flipping to `false` starts the exit spring rather than unmounting — the\nsheet stays in the DOM until that motion settles.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 45
            },
            "values": [
              false,
              true
            ],
            "values_complete": true
          },
          {
            "name": "onDismiss",
            "type": "() => void",
            "declared_type": "() => void",
            "required": true,
            "description": "Called when the user dismisses (backdrop tap, swipe, or escape).",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 47
            }
          },
          {
            "name": "size",
            "type": "BottomSheetSize",
            "declared_type": "BottomSheetSize",
            "required": true,
            "description": "Height preset — `small`/`medium`/`large` are fixed at 40/60/85vh, `full` at 100vh.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 49
            },
            "values": [
              "auto",
              "full",
              "large",
              "medium",
              "small"
            ],
            "values_complete": true
          },
          {
            "name": "lowered",
            "type": "number | undefined",
            "declared_type": "number",
            "required": false,
            "description": "Slides the sheet down by this fraction of its own height (0–1) and fades the backdrop in step,\nwithout dismissing it.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 54
            },
            "default": "0",
            "default_kind": "source-expression"
          },
          {
            "name": "scrimOpacity",
            "type": "number | undefined",
            "declared_type": "number",
            "required": false,
            "description": "Backdrop opacity while the sheet is at full height, clamped to 0-1.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 56
            },
            "default": "1",
            "default_kind": "source-expression"
          },
          {
            "name": "presentation",
            "type": "BottomSheetPresentation | undefined",
            "declared_type": "BottomSheetPresentation",
            "required": false,
            "description": "Chrome treatment. `sheet` is rounded bottom-sheet chrome; `fullScreen` fills the viewport.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 58
            },
            "values": [
              "fullScreen",
              "sheet"
            ],
            "values_complete": true,
            "default": "'sheet'",
            "default_kind": "source-expression"
          },
          {
            "name": "surface",
            "type": "\"page\" | \"surface\" | undefined",
            "declared_type": "CdsSurface",
            "required": false,
            "description": "Sheet surface token. Defaults to `surface`.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 60
            },
            "values": [
              "page",
              "surface"
            ],
            "values_complete": true,
            "default": "'surface'",
            "default_kind": "source-expression"
          },
          {
            "name": "showDragHandle",
            "type": "boolean | undefined",
            "declared_type": "boolean",
            "required": false,
            "description": "Shows the drag handle. Defaults to `true`; it is also the only grab target for `swipeToDismissArea='handle'`.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 62
            },
            "values": [
              false,
              true
            ],
            "values_complete": true,
            "default": "true",
            "default_kind": "source-expression"
          },
          {
            "name": "disableSwipeToDismiss",
            "type": "boolean | undefined",
            "declared_type": "boolean",
            "required": false,
            "description": "Turns off swipe-to-dismiss. Defaults to `false`; backdrop tap and Escape still dismiss.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 64
            },
            "values": [
              false,
              true
            ],
            "values_complete": true,
            "default": "false",
            "default_kind": "source-expression"
          },
          {
            "name": "disableSwipeToDismissRef",
            "type": "React.RefObject<boolean> | undefined",
            "declared_type": "React.RefObject<boolean>",
            "required": false,
            "description": "Synchronous disable check (same tick) for nested swipe surfaces.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 66
            }
          },
          {
            "name": "swipeToDismissArea",
            "type": "SwipeToDismissArea | undefined",
            "declared_type": "SwipeToDismissArea",
            "required": false,
            "description": "Region where a swipe-to-dismiss gesture can begin. Defaults to the whole sheet.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 68
            },
            "values": [
              "chrome",
              "handle",
              "sheet"
            ],
            "values_complete": true,
            "default": "'sheet'",
            "default_kind": "source-expression"
          },
          {
            "name": "ariaLabel",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Names the sheet when it has no `BottomSheet.Title`. A slot is invisible to the compiler, so\nunlike the old `title` prop this cannot be type-enforced — `@cloakedinc/bottom-sheet-slots`\nrejects a sheet with neither, and a dev-time check catches what lint cannot read statically.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 74
            }
          },
          {
            "name": "portalTarget",
            "type": "HTMLElement | null | undefined",
            "declared_type": "HTMLElement | null",
            "required": false,
            "description": "Mount node for the portal. Defaults to `document.body`.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 76
            }
          },
          {
            "name": "onBeforeDismiss",
            "type": "(() => void) | undefined",
            "declared_type": "() => void",
            "required": false,
            "description": "Fired right before dismissal completes — host hook for native effects (haptics).",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 78
            }
          },
          {
            "name": "onExited",
            "type": "(() => void) | undefined",
            "declared_type": "() => void",
            "required": false,
            "description": "Fired once the close animation fully settles and the sheet unmounts. Use this to sequence\npost-dismiss work (e.g. navigation) so it starts only after the sheet has visually left.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 83
            }
          },
          {
            "name": "haptic",
            "type": "HapticFeedback | undefined",
            "declared_type": "HapticFeedback",
            "required": false,
            "description": "Haptic feedback for dismissal. Defaults to `true` (`light`).",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 85
            },
            "values": [
              "error",
              "heavy",
              "light",
              "medium",
              "selection",
              "success",
              "warning",
              false,
              true
            ],
            "values_complete": true,
            "default": "true",
            "default_kind": "source-expression"
          },
          {
            "name": "className",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 86
            }
          },
          {
            "name": "style",
            "type": "React.CSSProperties | undefined",
            "declared_type": "React.CSSProperties",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 87
            }
          },
          {
            "name": "children",
            "type": "React.ReactNode",
            "declared_type": "React.ReactNode",
            "required": true,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 88
            }
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "`data-testid` for the sheet; the backdrop gets `${testId}-overlay`.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
              "line": 90
            }
          },
          {
            "name": "padding",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Padding on all four sides.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 10
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingX",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Horizontal padding (left + right). Overrides `padding` on the X axis.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 12
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingY",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Vertical padding (top + bottom). Overrides `padding` on the Y axis.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 14
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingTop",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Top padding. Overrides `paddingY` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 16
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingRight",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Right padding. Overrides `paddingX` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 18
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingBottom",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Bottom padding. Overrides `paddingY` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 20
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingLeft",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Left padding. Overrides `paddingX` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 22
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      },
      {
        "name": "BottomSheet.Header",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
          "line": 30
        },
        "props_type": "BottomSheetHeaderProps",
        "props": [
          {
            "name": "onClose",
            "type": "(() => void) | undefined",
            "declared_type": "() => void",
            "required": false,
            "description": "Shows a close button when provided. Pressing it only fires this callback; actually closing\nthe sheet is the host's job.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
              "line": 17
            }
          },
          {
            "name": "children",
            "type": "React.ReactNode",
            "declared_type": "React.ReactNode",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
              "line": 18
            }
          },
          {
            "name": "className",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
              "line": 19
            }
          },
          {
            "name": "style",
            "type": "React.CSSProperties | undefined",
            "declared_type": "React.CSSProperties",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
              "line": 20
            }
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "`data-testid` for the header; the close button gets `${testId}-close`.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
              "line": 22
            }
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      },
      {
        "name": "BottomSheet.Title",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetTitle.tsx",
          "line": 24
        },
        "props_type": "BottomSheetTitleProps",
        "props": [
          {
            "name": "children",
            "type": "string",
            "declared_type": "string",
            "required": true,
            "description": "Doubles as the sheet's accessible name.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetTitle.tsx",
              "line": 13
            }
          },
          {
            "name": "variant",
            "type": "BottomSheetTitleVariant | undefined",
            "declared_type": "BottomSheetTitleVariant",
            "required": false,
            "description": "Type treatment. Defaults to `sans`.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetTitle.tsx",
              "line": 15
            },
            "values": [
              "sans",
              "serif"
            ],
            "values_complete": true,
            "default": "'sans'",
            "default_kind": "source-expression"
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "`data-testid` for the heading. Rarely needed — the title is not interactable.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetTitle.tsx",
              "line": 17
            }
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      },
      {
        "name": "BottomSheet.Body",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
          "line": 28
        },
        "props_type": "BottomSheetBodyProps",
        "props": [
          {
            "name": "children",
            "type": "React.ReactNode",
            "declared_type": "React.ReactNode",
            "required": true,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
              "line": 12
            }
          },
          {
            "name": "scroll",
            "type": "boolean | undefined",
            "declared_type": "boolean",
            "required": false,
            "description": "Scrolls within the slot, with overscroll contained so scrolling doesn't chain to ancestors.\nDefaults to `true`; `false` defers scrolling to the sheet's own content wrapper.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
              "line": 17
            },
            "values": [
              false,
              true
            ],
            "values_complete": true,
            "default": "true",
            "default_kind": "source-expression"
          },
          {
            "name": "className",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
              "line": 18
            }
          },
          {
            "name": "style",
            "type": "React.CSSProperties | undefined",
            "declared_type": "React.CSSProperties",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
              "line": 19
            }
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "`data-testid` for the slot element.",
            "source": {
              "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
              "line": 21
            }
          },
          {
            "name": "padding",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Padding on all four sides.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 10
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingX",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Horizontal padding (left + right). Overrides `padding` on the X axis.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 12
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingY",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Vertical padding (top + bottom). Overrides `padding` on the Y axis.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 14
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingTop",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Top padding. Overrides `paddingY` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 16
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingRight",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Right padding. Overrides `paddingX` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 18
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingBottom",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Bottom padding. Overrides `paddingY` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 20
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          },
          {
            "name": "paddingLeft",
            "type": "0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined",
            "declared_type": "CdsSpace",
            "required": false,
            "description": "Left padding. Overrides `paddingX` and `padding`.",
            "source": {
              "path": "packages/design-system/src/components/layout/resolvePadding.ts",
              "line": 22
            },
            "values": [
              0,
              12,
              120,
              16,
              20,
              24,
              240,
              32,
              4,
              40,
              48,
              56,
              64,
              72,
              8,
              80,
              96
            ],
            "values_complete": true
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      }
    ],
    "type_sources": [
      {
        "path": "packages/design-system/src/components/layout/resolvePadding.ts",
        "line": 8,
        "name": "PaddingProps",
        "code": "export interface PaddingProps {\n    /** Padding on all four sides. */\n    padding?: CdsSpace;\n    /** Horizontal padding (left + right). Overrides `padding` on the X axis. */\n    paddingX?: CdsSpace;\n    /** Vertical padding (top + bottom). Overrides `padding` on the Y axis. */\n    paddingY?: CdsSpace;\n    /** Top padding. Overrides `paddingY` and `padding`. */\n    paddingTop?: CdsSpace;\n    /** Right padding. Overrides `paddingX` and `padding`. */\n    paddingRight?: CdsSpace;\n    /** Bottom padding. Overrides `paddingY` and `padding`. */\n    paddingBottom?: CdsSpace;\n    /** Left padding. Overrides `paddingX` and `padding`. */\n    paddingLeft?: CdsSpace;\n}"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 27,
        "name": "BottomSheetSize",
        "code": "export type BottomSheetSize = 'small' | 'medium' | 'large' | 'full' | 'auto';"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 29,
        "name": "BottomSheetPresentation",
        "code": "export type BottomSheetPresentation = 'sheet' | 'fullScreen';"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 40,
        "name": "BottomSheetBaseProps",
        "code": "interface BottomSheetBaseProps extends PaddingProps {\n    /**\n     * Drives the sheet. Flipping to `false` starts the exit spring rather than unmounting — the\n     * sheet stays in the DOM until that motion settles.\n     */\n    isOpen: boolean;\n    /** Called when the user dismisses (backdrop tap, swipe, or escape). */\n    onDismiss: () => void;\n    /** Height preset — `small`/`medium`/`large` are fixed at 40/60/85vh, `full` at 100vh. */\n    size: BottomSheetSize;\n    /**\n     * Slides the sheet down by this fraction of its own height (0–1) and fades the backdrop in step,\n     * without dismissing it.\n     */\n    lowered?: number;\n    /** Backdrop opacity while the sheet is at full height, clamped to 0-1. */\n    scrimOpacity?: number;\n    /** Chrome treatment. `sheet` is rounded bottom-sheet chrome; `fullScreen` fills the viewport. */\n    presentation?: BottomSheetPresentation;\n    /** Sheet surface token. Defaults to `surface`. */\n    surface?: CdsSurface;\n    /** Shows the drag handle. Defaults to `true`; it is also the only grab target for `swipeToDismissArea='handle'`. */\n    showDragHandle?: boolean;\n    /** Turns off swipe-to-dismiss. Defaults to `false`; backdrop tap and Escape still dismiss. */\n    disableSwipeToDismiss?: boolean;\n    /** Synchronous disable check (same tick) for nested swipe surfaces. */\n    disableSwipeToDismissRef?: React.RefObject<boolean>;\n    /** Region where a swipe-to-dismiss gesture can begin. Defaults to the whole sheet. */\n    swipeToDismissArea?: SwipeToDismissArea;\n    /**\n     * Names the sheet when it has no `BottomSheet.Title`. A slot is invisible to the compiler, so\n     * unlike the old `title` prop this cannot be type-enforced — `@cloakedinc/bottom-sheet-slots`\n     * rejects a sheet with neither, and a dev-time check catches what lint cannot read statically.\n     */\n    ariaLabel?: string;\n    /** Mount node for the portal. Defaults to `document.body`. */\n    portalTarget?: HTMLElement | null;\n    /** Fired right before dismissal completes — host hook for native effects (haptics). */\n    onBeforeDismiss?: () => void;\n    /**\n     * Fired once the close animation fully settles and the sheet unmounts. Use this to sequence\n     * post-dismiss work (e.g. navigation) so it starts only after the sheet has visually left.\n     */\n    onExited?: () => void;\n    /** Haptic feedback for dismissal. Defaults to `true` (`light`). */\n    haptic?: HapticFeedback;\n    className?: string;\n    style?: React.CSSProperties;\n    children: React.ReactNode;\n    /** `data-testid` for the sheet; the backdrop gets `${testId}-overlay`. */\n    testId?: string;\n}"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheet.tsx",
        "line": 96,
        "name": "BottomSheetProps",
        "code": "export type BottomSheetProps = BottomSheetBaseProps;"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetBody.tsx",
        "line": 11,
        "name": "BottomSheetBodyProps",
        "code": "export interface BottomSheetBodyProps extends PaddingProps {\n    children: React.ReactNode;\n    /**\n     * Scrolls within the slot, with overscroll contained so scrolling doesn't chain to ancestors.\n     * Defaults to `true`; `false` defers scrolling to the sheet's own content wrapper.\n     */\n    scroll?: boolean;\n    className?: string;\n    style?: React.CSSProperties;\n    /** `data-testid` for the slot element. */\n    testId?: string;\n}"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetHeader.tsx",
        "line": 12,
        "name": "BottomSheetHeaderProps",
        "code": "export interface BottomSheetHeaderProps {\n    /**\n     * Shows a close button when provided. Pressing it only fires this callback; actually closing\n     * the sheet is the host's job.\n     */\n    onClose?: () => void;\n    children?: React.ReactNode;\n    className?: string;\n    style?: React.CSSProperties;\n    /** `data-testid` for the header; the close button gets `${testId}-close`. */\n    testId?: string;\n}"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetTitle.tsx",
        "line": 6,
        "name": "BottomSheetTitleVariant",
        "code": "export type BottomSheetTitleVariant = 'sans' | 'serif';"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/BottomSheetTitle.tsx",
        "line": 11,
        "name": "BottomSheetTitleProps",
        "code": "export interface BottomSheetTitleProps {\n    /** Doubles as the sheet's accessible name. */\n    children: string;\n    /** Type treatment. Defaults to `sans`. */\n    variant?: BottomSheetTitleVariant;\n    /** `data-testid` for the heading. Rarely needed — the title is not interactable. */\n    testId?: string;\n}"
      },
      {
        "path": "packages/design-system/src/components/overlays/BottomSheet/hooks/useSwipeToDismiss.ts",
        "line": 19,
        "name": "SwipeToDismissArea",
        "code": "export type SwipeToDismissArea = 'sheet' | 'handle' | 'chrome';"
      },
      {
        "path": "packages/design-system/src/tokens/CdsHaptic.ts",
        "line": 6,
        "name": "HapticFeedback",
        "code": "export type HapticFeedback = boolean | HapticType;"
      },
      {
        "path": "packages/design-system/src/tokens/CdsSpace.ts",
        "line": 8,
        "name": "PUI_SPACES",
        "code": "PUI_SPACES = [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 96, 120, 240] as const"
      },
      {
        "path": "packages/design-system/src/tokens/CdsSpace.ts",
        "line": 13,
        "name": "CdsSpace",
        "code": "export type CdsSpace = (typeof PUI_SPACES)[number];"
      },
      {
        "path": "packages/design-system/src/tokens/CdsSurface.ts",
        "line": 5,
        "name": "PUI_SURFACES",
        "code": "PUI_SURFACES = ['page', 'surface'] as const"
      },
      {
        "path": "packages/design-system/src/tokens/CdsSurface.ts",
        "line": 7,
        "name": "CdsSurface",
        "code": "export type CdsSurface = (typeof PUI_SURFACES)[number];"
      },
      {
        "path": "packages/design-system/src/tokens/HapticType.ts",
        "line": 2,
        "name": "HapticType",
        "code": "export type HapticType = 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error' | 'selection';"
      }
    ],
    "defaults_policy": "Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source."
  },
  "platforms": {
    "implemented": [
      "web"
    ],
    "native": "Imported design guidance only; no native implementation or parity verification in this preview.",
    "react": {
      "implementation": "local",
      "status": "preview",
      "parity": "web-reference"
    },
    "vue": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "swift": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "kotlin": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    }
  },
  "missing_facts": [
    {
      "code": "human-review-pending",
      "detail": "This local preview has no recorded human design or engineering sign-off."
    },
    {
      "code": "platform-parity-unverified",
      "detail": "Vue, Swift and Kotlin implementations are absent from this package; platform parity has not been verified."
    },
    {
      "code": "figma-mapping-unverified",
      "detail": "No current component-level Figma node and immutable revision have been verified for this contract."
    }
  ],
  "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."
  }
}
