Drawer
A panel that slides in from the edge of the screen.
import { defineConfig } from '@pandacss/dev'
import { createPreset } from '@park-ui/panda-preset'
export default defineConfig({
preflight: true,
presets: [
'@pandacss/preset-base',
createPreset({
accentColor: '__ACCENT_COLOR__',
grayColor: '__GRAY_COLOR__',
borderRadius: '__BORDER_RADIUS__',
}),
],
include: ['./src/**/*.{js,jsx,ts,tsx}'],
jsxFramework: '__JS_FRAMEWORK__',
outdir: 'styled-system',
})
import { parkwindPlugin } from '@park-ui/tailwind-plugin'
import type { Config } from 'tailwindcss'
const config: Config = {
content: ['./src/**/*.{astro,html,js,jsx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [parkwindPlugin],
parkUI: {
accentColor: '__ACCENT_COLOR__',
grayColor: '__GRAY_COLOR__',
borderRadius: '__BORDER_RADIUS__',
},
darkMode: ['class'],
}
export default config
A panel that slides in from the edge of the screen.
import { XIcon } from 'lucide-react'
import { Button } from '~/components/ui/button'
import * as Drawer from '~/components/ui/drawer'
import { IconButton } from '~/components/ui/icon-button'
export const Demo = (props: Drawer.RootProps) => {
return (
<Drawer.Root {...props}>
<Drawer.Trigger asChild>
<Button>Open Drawer</Button>
</Drawer.Trigger>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Title</Drawer.Title>
<Drawer.Description>Description</Drawer.Description>
<Drawer.CloseTrigger asChild position="absolute" top="3" right="4">
<IconButton variant="ghost">
<XIcon />
</IconButton>
</Drawer.CloseTrigger>
</Drawer.Header>
<Drawer.Body>{/* Content */}</Drawer.Body>
<Drawer.Footer gap="3">
<Drawer.CloseTrigger asChild>
<Button variant="outline">Cancel</Button>
</Drawer.CloseTrigger>
<Button>Primary</Button>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Positioner>
</Drawer.Root>
)
}
Insert code snippet into your project. Update import paths as needed.
import type { Assign, HTMLArkProps } from '@ark-ui/react'
import { Dialog as Drawer } from '@ark-ui/react/dialog'
import { ark } from '@ark-ui/react/factory'
import { type DrawerVariantProps, drawer } from 'styled-system/recipes'
import type { JsxStyleProps } from 'styled-system/types'
import { createStyleContext } from '~/lib/create-style-context'
const { withRootProvider, withContext } = createStyleContext(drawer)
export interface RootProps extends Drawer.RootProps, DrawerVariantProps {}
export const Root = withRootProvider<RootProps>(Drawer.Root)
export const Backdrop = withContext<HTMLDivElement, Assign<JsxStyleProps, Drawer.BackdropProps>>(
Drawer.Backdrop,
'backdrop',
)
export const Body = withContext<HTMLDivElement, Assign<JsxStyleProps, HTMLArkProps<'div'>>>(
ark.div,
'body',
)
export const CloseTrigger = withContext<
HTMLButtonElement,
Assign<JsxStyleProps, Drawer.CloseTriggerProps>
>(Drawer.CloseTrigger, 'closeTrigger')
export const Content = withContext<HTMLDivElement, Assign<JsxStyleProps, Drawer.ContentProps>>(
Drawer.Content,
'content',
)
export const Description = withContext<
HTMLParagraphElement,
Assign<JsxStyleProps, Drawer.DescriptionProps>
>(Drawer.Description, 'description')
export const Footer = withContext<HTMLDivElement, Assign<JsxStyleProps, HTMLArkProps<'div'>>>(
ark.div,
'footer',
)
export const Header = withContext<HTMLDivElement, Assign<JsxStyleProps, HTMLArkProps<'div'>>>(
ark.div,
'header',
)
export const Positioner = withContext<
HTMLDivElement,
Assign<JsxStyleProps, Drawer.PositionerProps>
>(Drawer.Positioner, 'positioner')
export const Title = withContext<HTMLHeadingElement, Assign<JsxStyleProps, Drawer.TitleProps>>(
Drawer.Title,
'title',
)
export const Trigger = withContext<HTMLButtonElement, Assign<JsxStyleProps, Drawer.TriggerProps>>(
Drawer.Trigger,
'trigger',
)
export {
DialogContext as Context,
type DialogContextProps as ContextProps,
} from '@ark-ui/react/dialog'
import { type Assign, Dialog as Drawer, type HTMLArkProps, ark } from '@ark-ui/solid'
import { type DrawerVariantProps, drawer } from 'styled-system/recipes'
import type { JsxStyleProps } from 'styled-system/types'
import { createStyleContext } from '~/lib/create-style-context'
const { withRootProvider, withContext } = createStyleContext(drawer)
export interface RootProps extends Drawer.RootProps, DrawerVariantProps {}
export const Root = withRootProvider<RootProps>(Drawer.Root)
export const Backdrop = withContext<Assign<JsxStyleProps, Drawer.BackdropProps>>(
Drawer.Backdrop,
'backdrop',
)
export const Body = withContext<Assign<JsxStyleProps, HTMLArkProps<'div'>>>(ark.div, 'body')
export const CloseTrigger = withContext<Assign<JsxStyleProps, Drawer.CloseTriggerProps>>(
Drawer.CloseTrigger,
'closeTrigger',
)
export const Content = withContext<Assign<JsxStyleProps, Drawer.ContentProps>>(
Drawer.Content,
'content',
)
export const Description = withContext<Assign<JsxStyleProps, Drawer.DescriptionProps>>(
Drawer.Description,
'description',
)
export const Footer = withContext<Assign<JsxStyleProps, HTMLArkProps<'div'>>>(ark.div, 'footer')
export const Header = withContext<Assign<JsxStyleProps, HTMLArkProps<'div'>>>(ark.div, 'header')
export const Positioner = withContext<Assign<JsxStyleProps, Drawer.PositionerProps>>(
Drawer.Positioner,
'positioner',
)
export const Title = withContext<Assign<JsxStyleProps, Drawer.TitleProps>>(Drawer.Title, 'title')
export const Trigger = withContext<Assign<JsxStyleProps, Drawer.TriggerProps>>(
Drawer.Trigger,
'trigger',
)
export {
DialogContext as Context,
type DialogContextProps as ContextProps,
} from '@ark-ui/solid'
Not yet available
This step is necessary only if you do not use any of the Park UI plugins.
import { dialogAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
const anatomy = dialogAnatomy.extendWith('header', 'body', 'footer')
export const drawer = defineSlotRecipe({
className: 'drawer',
slots: [...anatomy.keys()],
base: {
backdrop: {
backdropFilter: 'blur(4px)',
background: {
base: 'white.a10',
_dark: 'black.a10',
},
height: '100vh',
left: '0',
position: 'fixed',
top: '0',
width: '100vw',
zIndex: 'overlay',
_open: {
animation: 'backdrop-in',
},
_closed: {
animation: 'backdrop-out',
},
},
positioner: {
alignItems: 'center',
display: 'flex',
height: '100dvh',
justifyContent: 'center',
position: 'fixed',
top: 0,
width: { base: '100vw', sm: 'sm' },
zIndex: 'modal',
},
content: {
background: 'bg.default',
boxShadow: 'lg',
display: 'grid',
divideY: '1px',
gridTemplateColumns: '1fr',
gridTemplateRows: 'auto 1fr auto',
gridTemplateAreas: `
'header'
'body'
'footer'
`,
height: 'full',
width: 'full',
_hidden: {
display: 'none',
},
},
header: {
display: 'flex',
flexDirection: 'column',
gap: '1',
gridArea: 'header',
pt: { base: '4', md: '6' },
pb: '4',
px: { base: '4', md: '6' },
},
body: {
display: 'flex',
flexDirection: 'column',
gridArea: 'body',
overflow: 'auto',
p: { base: '4', md: '6' },
},
footer: {
display: 'flex',
gridArea: 'footer',
justifyContent: 'flex-end',
py: '4',
px: { base: '4', md: '6' },
},
title: {
color: 'fg.default',
fontWeight: 'semibold',
textStyle: 'xl',
},
description: {
color: 'fg.muted',
textStyle: 'sm',
},
},
defaultVariants: {
variant: 'right',
},
variants: {
variant: {
left: {
positioner: {
left: 0,
},
content: {
_open: {
animation: 'drawer-in-left',
},
_closed: {
animation: 'drawer-out-left',
},
},
},
right: {
positioner: {
right: 0,
},
content: {
_open: {
animation: 'drawer-in-right',
},
_closed: {
animation: 'drawer-out-right',
},
},
},
},
},
})