{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "drawer",
  "type": "registry:component",
  "title": "Drawer",
  "description": "Opens a focused task from the bottom edge. Native dialog with platform focus handling and backdrop.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/dialog.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/drawer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { DialogCloseButton, DialogContext, dialogStyles, useDialogPart, useNativeDialog, type NativeDialogOptions } from \"./dialog\";\n\nexport interface DrawerProps\n  extends Omit<React.DialogHTMLAttributes<HTMLDialogElement>, \"open\" | \"onClose\">,\n    NativeDialogOptions {}\n\nconst styles = stylex.create({\n  frame: {\n    boxSizing: \"border-box\",\n    position: \"fixed\",\n    top: \"auto\",\n    insetInlineEnd: 0,\n    bottom: 0,\n    insetInlineStart: 0,\n    margin: 0,\n    width: \"100%\",\n    maxWidth: \"none\",\n    height: \"auto\",\n    maxHeight: \"85dvh\",\n    borderWidth: 0,\n    borderStyle: \"solid\",\n    borderTopWidth: vlak.hairline,\n    borderColor: vlak.divider,\n    borderStartStartRadius: vlak.radiusSm,\n    borderStartEndRadius: vlak.radiusSm,\n    borderEndEndRadius: 0,\n    borderEndStartRadius: 0,\n    paddingBlock: \"1.5rem\",\n    paddingInline: \"1.25rem\",\n    backgroundColor: vlak.paper,\n    color: vlak.ink,\n    boxShadow: \"none\",\n    \"::backdrop\": {\n      backgroundColor: [\"rgba(0,0,0,0.25)\", `color-mix(in srgb, ${vlak.paper} 55%, transparent)`],\n    },\n  },\n  title: {\n    display: \"block\",\n    fontSize: {\n      default: \"0.9375rem\",\n      [mq.phone]: \"1.125rem\",\n    },\n    fontWeight: 600,\n    letterSpacing: \"-0.01em\",\n    color: vlak.ink,\n    marginTop: 0,\n    marginInlineEnd: 0,\n    marginBottom: \"0.375rem\",\n    marginInlineStart: 0,\n  },\n  body: {\n    fontSize: {\n      default: \"0.84375rem\",\n      [mq.phone]: \"1rem\",\n    },\n    lineHeight: 1.45,\n    letterSpacing: \"-0.01em\",\n    color: vlak.gray,\n    margin: 0,\n  },\n});\n\n/**\n * A native <dialog> from the bottom edge. The platform provides the\n * focus trap, Escape, and the backdrop; the title names it.\n */\nexport const Drawer = React.forwardRef<HTMLDialogElement, DrawerProps>(function Drawer(\n  { open, onClose, dismissable, lightDismiss, closeLabel, className, style, children, ...props },\n  forwardedRef,\n) {\n  const { ref, context, dialogProps } = useNativeDialog({ open, onClose, dismissable, lightDismiss }, props, forwardedRef);\n  const sx = rs([\"rs-drawer\", className], styles.frame);\n  const close = rs([\"rs-drawer-close\"], dialogStyles.close);\n  return (\n    <DialogContext.Provider value={context}>\n      <dialog ref={ref} {...props} {...dialogProps} className={sx.className} style={{ ...sx.style, ...style }}>\n        {closeLabel != null && (\n          <DialogCloseButton label={closeLabel} className={close.className} style={close.style} onClick={() => onClose?.()} />\n        )}\n        {children}\n      </dialog>\n    </DialogContext.Provider>\n  );\n});\n\nexport interface DrawerTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {\n  as?: \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"span\" | \"div\";\n}\n\nexport const DrawerTitle = React.forwardRef<HTMLHeadingElement, DrawerTitleProps>(function DrawerTitle(\n  { as: Tag = \"h2\", className, style, id, ...props },\n  ref,\n) {\n  const titleId = useDialogPart(\"title\", id);\n  const sx = rs([\"rs-drawer-title\", className], styles.title);\n  return <Tag ref={ref as React.Ref<never>} {...props} id={titleId} className={sx.className} style={{ ...sx.style, ...style }} />;\n});\n\nexport const DrawerBody = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(function DrawerBody(\n  { className, style, id, ...props },\n  ref,\n) {\n  const bodyId = useDialogPart(\"body\", id);\n  const sx = rs([\"rs-drawer-body\", className], styles.body);\n  return <p ref={ref} {...props} id={bodyId} className={sx.className} style={{ ...sx.style, ...style }} />;\n});\n",
      "type": "registry:component",
      "target": "components/vlak/drawer.tsx"
    },
    {
      "path": "vlak/styles/drawer.css",
      "content": "/* ── drawer: generated from packages/react/src/components/drawer.tsx ── */\n.rs-drawer{box-sizing:border-box;position:fixed;top:auto;inset-inline-end:0;bottom:0;inset-inline-start:0;margin:0;width:100%;max-width:none;height:auto;max-height:85dvh;border-width:0;border-style:solid;border-top-width:1px;border-color:var(--divider);border-start-start-radius:var(--radius-sm);border-start-end-radius:var(--radius-sm);border-end-end-radius:0;border-end-start-radius:0;padding-block:1.5rem;padding-inline:1.25rem;background-color:var(--bg);color:var(--text);box-shadow:none}\n.rs-drawer::backdrop{background-color:rgba(0,0,0,0.25);background-color:color-mix(in srgb, var(--bg) 55%, transparent)}\n.rs-drawer-title{display:block;font-size:0.9375rem;font-weight:600;letter-spacing:-0.01em;color:var(--text);margin-top:0;margin-inline-end:0;margin-bottom:0.375rem;margin-inline-start:0}\n@media (max-width: 640px){.rs-drawer-title{font-size:1.125rem}}\n.rs-drawer-body{font-size:0.84375rem;line-height:1.45;letter-spacing:-0.01em;color:var(--text-secondary);margin:0}\n@media (max-width: 640px){.rs-drawer-body{font-size:1rem}}\n",
      "type": "registry:file",
      "target": "styles/vlak/drawer.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "surfaces",
      "classes": [
        "rs-drawer",
        "rs-drawer-title",
        "rs-drawer-body",
        "rs-drawer-close"
      ],
      "snippet": "<dialog class=\"rs-drawer\" aria-labelledby=\"notes-title\" open><button class=\"rs-drawer-close\" type=\"button\" aria-label=\"Close\">&times;</button><h2 class=\"rs-drawer-title\" id=\"notes-title\">Notes</h2><p class=\"rs-drawer-body\">A bottom panel. Escape closes it.</p></dialog>",
      "cssOnly": false,
      "registryDependencies": [
        "dialog",
        "vlak-lib"
      ],
      "aliases": [
        "Drawer",
        "Bottom sheet",
        "Vaul",
        "Bottom panel"
      ],
      "example": "import { useState } from \"react\";\nimport { Button, Drawer, DrawerBody, DrawerTitle } from \"@noorddev/vlak-react\";\n\nconst [open, setOpen] = useState(false);\n\n<Button variant=\"ghost\" onClick={() => setOpen(true)}>Notes</Button>\n<Drawer open={open} onClose={() => setOpen(false)} closeLabel=\"Close\">\n  <DrawerTitle>Notes</DrawerTitle>\n  <DrawerBody>A bottom panel. Escape closes it.</DrawerBody>\n</Drawer>",
      "usage": {
        "use": [
          "Phone-first panels that rise from the bottom edge: options, a share sheet, a short form.",
          "Content that reads in one screen."
        ],
        "avoid": [
          "Desktop side panels; use Sheet.",
          "Long content; the drawer is not a page."
        ]
      },
      "keyboard": [
        {
          "keys": "Escape",
          "does": "Calls onClose (when dismissable, the default)"
        },
        {
          "keys": "Tab, Shift + Tab",
          "does": "Cycles focus inside the drawer"
        }
      ],
      "a11y": [
        "A native modal <dialog> from the bottom edge: the platform traps focus and provides the backdrop.",
        "DrawerTitle names it through aria-labelledby; DrawerBody describes it. Focus moves in on open and returns on close.",
        "closeLabel renders a labelled close button. The ref is forwarded to the <dialog>."
      ]
    }
  }
}
