Skip to content

Drawer: support arbitrary widths + collapse closed-panel grid column #992

@downsDrew

Description

@downsDrew

Package: @accelint/design-toolkit@9.10.0 (Drawer / DrawerLayout)

Two related, sibling issues with the same component — filing together so they can be triaged in one place.

1. size prop is locked to three pixel values

Drawer.size is typed as 'small' | 'medium' | 'large', and styles.module.css hardcodes the three widths:

--drawer-size-small: 100px;
--drawer-size-medium: 200px;
--drawer-size-large: 400px;

Tablet-class consumers (~280–320px target) have no path. Today the only options are:

  • CSS-module shadow override that targets [data-component=drawer][data-size=...] selectors — couples to internal data-attribute naming, brittle.
  • patches/ entry per consumer — duplicates fixes across forks.

Asks (either is sufficient):

  • Widen the size prop type to accept number or a token reference (e.g. --spacing-…) alongside the three named variants.
  • OR add 'tablet-medium' / 'tablet-large' to the variant set with corresponding CSS custom properties so consumers can theme them.

2. Closed-state panels still allocate their grid column

<DrawerLayout> uses grid-template-columns: auto 1fr auto to host left / main / right panels. Closed panels translate off-screen via transform: translateX(100%) (or equivalent for top/bottom/left). transform is paint-only — the grid column still allocates the panel's intrinsic content width.

Concrete impact: consumers using push='left right' see the main column visually shifted inward by ~400px (the closed large drawer's content width) even when no drawer is open. Visually this looks like "the layout treats the drawer as if it's always open."

Reproducer:

  1. <DrawerLayout push='left right'> with a single right-side <Drawer size='large'> whose open is false.
  2. Place a footer or any content at the bottom of the main column.
  3. Observe the footer is offset inboard by ~400px when the drawer is closed.

Suggested fix (CSS sketch — not yet validated in storybook): add width: 0; overflow: hidden to the closed state, transition width with a delay equal to --animation-duration-slow so the column collapses after the slide-out animation finishes (otherwise close looks like a jarring shrink before slide):

.drawer {
  @apply bg-surface-default text-body-m pointer-events-none relative flex flex-col;

  @variant motion-safe {
    transition:
      transform var(--animation-duration-slow) var(--animation-easing-standard),
      width 0ms linear var(--animation-duration-slow);
  }

  @variant open {
    transition:
      transform var(--animation-duration-slow) var(--animation-easing-standard),
      width 0ms linear;
  }

  @variant placement-right {
    @apply col-start-3 col-end-4 row-span-full;
    transform: translateX(100%);
    width: 0;
    overflow: hidden;

    @variant open {
      transform: translateX(0);
      width: auto;
      overflow: visible;
    }
  }
}

Mirror the same pattern for placement-left (width: 0), placement-top / placement-bottom (height: 0).

Validation suggestions:

  • Existing Drawer.stories.tsx with an open/closed toggle: verify open → closed → open cycle has no flicker, no content overlap during transition frames, and that sibling content reclaims the column on close.
  • Test all four placements.
  • Test with push='left right' consumer wiring — that's the configuration that surfaces the issue most clearly.

Happy to validate / PR either piece if useful — let me know which direction makes sense for the API.

Versions

  • Next.js 16.1.6 (App Router)
  • React 19.1.0 with reactStrictMode: true
  • Turbopack dev mode
  • pnpm 10.9 hoisted node_modules

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions