Skip to content

feat(drawer): emit "trigger" reason in onOpenChange on open#1655

Open
te6-in wants to merge 15 commits into
alphafrom
feat/drawer-open-reason
Open

feat(drawer): emit "trigger" reason in onOpenChange on open#1655
te6-in wants to merge 15 commits into
alphafrom
feat/drawer-open-reason

Conversation

@te6-in

@te6-in te6-in commented Jun 9, 2026

Copy link
Copy Markdown
Member

No description provided.

te6-in and others added 15 commits June 9, 2026 16:20
…bleLayer hook (#1410, #1455, #1642)

* feat(menu): add Menu compound component primitives

Scaffold Menu styled components using Primitive.* elements as
placeholders for future headless and recipe integration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(menu): wrap Base UI Menu primitives as headless layer

Replace Primitive-based placeholders with @base-ui-components/react
Menu primitives. MenuPositioner internally wraps Portal + Positioner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(menu): add recipe, submenu support, and stackflow-spa example

- Add qvism-preset menu slot recipe with size variant and tokens
- Wire recipe to React components via createSlotRecipeContext
- Add MenuSubmenuRoot and MenuSubmenuTrigger exports
- Add ActivityMenu with basic, grouped, nested, disabled demos

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(menu): add headless useMenu hook with TDD test suite

Implement useMenu hook on top of @floating-ui/react@0.27 (public API),
composing useClick, useRole, useDismiss, useListNavigation, useTypeahead,
and useInteractions for a fully headless menu with:
- Open/close state (controlled + uncontrolled)
- Arrow key navigation with loopFocus
- Typeahead character search
- Escape and outside click dismiss
- Modal scroll lock
- data-* attributes (data-open, data-highlighted, data-disabled)

42/47 core tests passing, 2/16 submenu tests passing.
Remaining core gaps: group aria-labelledby wiring, Enter/Space item
activation on non-button elements, drag-from-trigger interaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(menu): add Enter/Space activation for non-button items, cleanup unused ref

- Add onKeyDown handler to getItemProps for Enter/Space click simulation
  on div-based menu items (floating-ui only handles buttons natively)
- Remove unused groupLabelIdMap ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(menu): align useMenu API with SEED headless pattern, update test harnesses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(menu): add headless React components, context, and exports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(menu): use headless components in test harnesses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor

* feat: menu implementation

* refactor: remove @radix-ui/react-dialog; add @seed-design/dismissable-layer

* docs: menu

* feat: allow customizing the portalled root of menu

* docs

* feat: implement matching reference width

* refactor

* refactor

* fix: fix dismissing menu on bottom sheet by clicking outside dismissing bottom sheet as well

* fix(menu): use dynamic available height from Floating UI size middleware

Use Floating UI's size middleware to dynamically constrain menu max-height
to the actual available viewport space, preventing overflow when the
trigger is near viewport edges.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(menu): add rootage specs for menu and menu-item

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: remove modal option

* feat: add pressBehavior option; refactor callbacks

* test(dismissible-layer): fix failing tests and migrate to userEvent

- Fix 3 failing tests after pressBehavior default changed to "confirm"
- Add NOOP defaults for required callbacks in test helpers
- Migrate 12 tests from fireEvent to userEvent where applicable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: fix types

* test(menu): fix flaky typeahead tests

- Add missing waitForFocus() for rAF-deferred focus to settle
- Remove invalid Space typeahead test (useTypeahead doesn't provide
  item-level handlers; Space activates items by design)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(menu): replace MenuDivider with CSS auto-divider between groups

Use `group + group::before` pseudo-element to automatically render
dividers between consecutive MenuGroup elements, eliminating the need
for a separate MenuDivider component. Removes the divider slot, component,
and all consumer usage across headless, react, docs, and examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dismissible-layer): only cascade-dismiss child layers, not siblings

removeLayer previously cascade-dismissed ALL layers added after the
removed one, treating them as nested children. This incorrectly
dismissed independent sibling layers (e.g., two separate menus).

Add parentNode tracking to the Layer interface so removeLayer only
cascade-dismisses layers that explicitly declare the removed node as
their parent. Add DismissibleParentContext (following base-ui's
FloatingNodeContext pattern) so nested layers automatically know their
parent via React context propagation through portals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(menu): provide DismissibleParentContext to fix sibling menu dismiss

Wrap children of MenuContent, DialogContent, and DrawerContent with
DismissibleParentContext.Provider so nested dismissible layers (e.g.,
menu inside dialog) correctly cascade-dismiss while sibling menus
remain independent.

Add sibling menu tests covering both mouse and touch interactions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(dismissible-layer): extract DismissibleLayer headless component

Replace manual useDismissibleLayer + ref composition + props merging +
DismissibleParentContext.Provider in each consumer with a single
<DismissibleLayer> wrapper component. Follows the Presence component
pattern using Radix Slot to merge ref and props onto the child element.

Consumers (Menu, Dialog, Drawer) no longer need to know about
DismissibleParentContext or layerNode — the component handles it
internally. Remove DismissibleParentContext and useDismissibleParentNode
from public exports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs

* docs

* fix: add missing forwardRef

* fix: fix focusscope not working

* fix: remove content focus style from dialog recipe

* docs

* fix: fix flip not happening before opening; apply x axis transform origin

* feat

* docs

* fix: fix styles

* fix(dialog): add missing displayName to DialogPositioner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(drawer): remove duplicate defaultPrevented check in onPressOutside

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(menu): add rationale comment to commented-out highlight code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dismissible-layer): reset isFocusInsideReactTreeRef on effect cleanup

When `enabled` toggles off→on without focus leaving the layer (e.g.,
programmatic state change), onBlurCapture never fires and the ref stays
stale. The first focus-outside event after re-enable is silently ignored.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(presence): remove as any casts from usePresence and Presence

- stylesRef: {} as any → proper CSSStyleDeclaration | null with guarded access
- ref callback: accept HTMLElement | null to match React's ref contract
- state machine reducer: narrowed from as any to Record<string, MachineState<M>>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(menu): narrow MenuRootProps to not extend HTMLAttributes

MenuRoot renders no DOM element (it's a context provider only). Extending
HTMLAttributes and PrimitiveProps exposed className, style, onClick etc.
as valid props that were silently ignored. Now matches Dialog/Drawer/Popover
Root pattern: UseMenuProps + children.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dismissible-layer): re-compute index after cascade-dismiss in removeLayer

Cascade-dismiss calls child.dismiss() which can trigger recursive
removeLayer, shifting indices in the layers array. The pre-computed
index could become stale, causing splice to remove the wrong layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dismissible-layer): widen onPressOutside type to PointerEvent | TouchEvent

Touch-drag dismiss paths in use-pointer-down-outside produce real
TouchEvents. Remove the as unknown as PointerEvent cast (violates
TECH.md) and widen the callback type through the chain:
- use-pointer-down-outside onPressOutside
- useDismissibleLayer onPressOutside
- Menu/Dialog/Drawer interactOutside detail event types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dismissible-layer): add duplicate-registration guard in addLayer

Unlike Radix which uses Set (implicitly idempotent), our layer stack uses
arrays for ordering. Add explicit findIndex + splice guard to prevent
double-registration from React strict mode re-running effects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(menu): comment out unused getSubmenuTriggerProps

Not consumed by any component yet. Commented out with "implement when
submenu is needed" to match the highlight code convention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(dismissible-layer): verify Escape does not propagate when inner layer ignores it

Confirms that the isTopMost guard in useEscapeKeydown prevents Escape
from reaching outer layers, even when the inner layer's handler does not
call preventDefault. This is not a behavioral regression from Radix —
SEED's architecture gates Escape to the topmost layer only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dismissible-layer): fix body.style.pointerEvents leak in nested blocking layers

The per-layer save/restore pattern failed when React cleaned up effects
bottom-up: the child that saved the original value couldn't restore
(parent still blocking), and the parent that survived longest had null
(never saved). The original value was orphaned permanently.

Follow Radix's proven pattern: save the original value to a module-level
variable when the first blocking layer mounts, restore when the last
unmounts. This decouples save/restore from mount/unmount order.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: move comment

* docs(drawer): clarify onFocusOutside empty handler intent

Replace ambiguous TODO comment with explanation of why the handler is
intentionally empty. The protection from PR #1187 (e.preventDefault on
onFocusOutside) is no longer needed — the new DismissibleLayer handles
cascade dismiss via onCascadeDismiss instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add cases for onOpenChange reason

* chore: comment

* refactor: rename label to typeaheadLabel

* fix: allow focusing outer elements when drawer has modal=false

* fix: fix bottom sheet modal=true styles

* fix: fix registry component types; ref passing

* docs: fix label

* fix: fix drawer modal behavior

* change: update menu registry and stories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(dialog,drawer): apply aria-hidden to siblings when modal is open

Add `hideOthers` from `aria-hidden` package to Dialog and Drawer so
screen-reader virtual cursors can no longer reach background content
while a modal overlay is active.

- Dialog: add `modal` prop (default true) controlling FocusScope trap,
  aria-modal attribute, and aria-hidden on siblings
- Drawer: add aria-hidden on siblings when `modal && isOpen`
- Docs: add `modal={isActive}` to Keeping Mounted patterns for
  Alert Dialog, Bottom Sheet, and Menu Sheet

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(examples): add missing modal={isActive} to Keeping Mounted patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(dialog,drawer): use useState callback ref for hideOthers timing

Presence defers child mounting by one render cycle when re-entering
(useLayoutEffect dispatches MOUNT, which triggers a re-render).
With useRef, the useEffect fires before children mount, so
contentRef.current is null and hideOthers is never called on reopen.

Switch to useState callback ref (same pattern as useDismissibleLayer)
so that setContentNode triggers a re-render, causing the useEffect to
re-evaluate with the actual DOM node.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(react): add SideNavigation compound component skeleton

Scaffold the SideNavigation compound component with 13 slots:
Root (nav), Header, Content, Footer, Group, GroupLabel,
MenuGroup (Collapsible.Root), MenuGroupContent (Collapsible.Content),
MenuItem, MenuItemTrigger (Collapsible.Trigger), MenuSubItem,
Trigger, and Inset.

Also adds Primitive.nav to react-primitive for semantic navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(react): rename SideNavigation menu item components and add usage example

Rename for clarity:
- MenuItem → MenuItemButton (standalone navigation)
- MenuGroup → MenuItemCollapsibleRoot
- MenuGroupContent → MenuItemCollapsibleContent
- MenuItemTrigger → MenuItemCollapsibleTrigger
- MenuSubItem → MenuItemCollapsibleItem

Add ActivitySideNavigation in stackflow-spa as a bare usage example
demonstrating all compound component slots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(qvism-preset): add SideNavigation recipes and wire to React components

Add three recipes in side-navigation.ts:
- sideNavigation (slot recipe): layout slots for root, header, content, footer, group, groupLabel, trigger
- sideNavigationInset (single recipe): no-op placeholder for Inset (lives outside Root context)
- sideNavigationMenuItem (slot recipe): item, collapsibleContent, chevron slots

Update React components to use createSlotRecipeContext pattern:
- Root uses withProvider, child slots use withContext
- MenuItemCollapsibleRoot uses withRootProvider for menuItem context
- MenuItemButton and Inset use standalone recipe calls (Badge pattern)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(react-headless): add SideNavigation headless package with collapse coordination

Introduce @seed-design/react-side-navigation with:
- useSideNavigation: collapsed state management (controllable pattern)
- useSideNavigationCollapsible: sidebar-aware collapsible (useCollapsible superset)
- Compound components: Provider, Root, Trigger, MenuItemCollapsible*
- Derived state pattern (effectiveOpen = collapsed ? false : userOpen)
- Provider separated from Root to support sibling Inset access

Update styled layer to consume headless components and add collapse recipes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(react-headless): remove sidebar uncollapse on collapsible trigger click

Remove the behavior where clicking a collapsible trigger in collapsed
state would uncollapse the sidebar and open the collapsible. In collapsed
state, collapsible open requests are now ignored. This interaction will
be replaced with hover-based Menu behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(side-navigation): improve scrollbar handling and collapse state preservation

- Use CSS-only panel collapse (scrollbar-gutter + overflow) instead of JS state reset
  to preserve collapsible open/close state across sidebar collapse/expand cycles
- Enforce single-line labels with ellipsis truncation for consistent item height
- Add scrollbar-gutter: stable in expanded state, auto in collapsed state
- Add docs registry and stackflow example for SideNavigation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(side-navigation): add scroll divider, focus rings, flatten snippet exports, and expand example

- Add CSS background-attachment scroll divider on content slot (local/scroll trick)
- Add inside focus ring on menu item ::before, outside focus ring on trigger
- Flatten snippet exports to SideNavigation-prefixed named exports (no object export)
- Add more menu groups to stackflow example for scroll testing
- Remove duplicate SideNavigationMenuItemButton (merged into SideNavigationMenuItem)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(side-navigation): add top scroll divider and bottom scroll fog on content slot

- Top: 1px divider via background-attachment local/scroll trick
- Bottom: 16px mask-image fog that fades into paddingBottom: 16px
- No JS needed — fog is always on, padding ensures no content is obscured at scroll end

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: collapse all collapsibles when sidebar isn't expanded

* chore: bump icons

* feat: implement selected & disabled

* docs: block example

* fix: fix styles

* feat

* feat: integrate menu with side navigation

* docs

* docs: add missing list item

* refactor

* refactor

* refactor(side-navigation): add current prop to collapsible trigger

ItemCollapsibleTrigger now accepts `current` prop via
useSideNavigationItem, enabling proper current state propagation
to child icons during collapse transitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(side-navigation): consolidate snippet into data-driven Group

Replace ItemCollapsible + GroupLabel with a single SideNavigationGroup
component that accepts `items` data with discriminated union
(leaf items with onClick vs collapsible items with sub-items).

- Group renders leaf items, collapsible sections, and menu popups
- Collapsible/menu mode switching handled internally via collapsed state
- hasCurrentChild derived from sub-items for trigger highlight
- ItemButton retained for standalone use (e.g. Footer)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add examples

* docs

* fix: fix styles

* docs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs

* docs
# Conflicts:
#	docs/public/rootage/index.json
)

* feat: basic implementation

* feat: file with status

* feat: implementation

* refactor: refactor recipe

* chore: scaffold @seed-design/react-middle-truncate package

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(middle-truncate): add splitFileName utility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(middle-truncate): implement MiddleTruncate component with Canvas-based multi-line truncation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move splitFileName to file-upload package

splitFileName is a file-upload domain concern, not a generic truncation utility.
MiddleTruncate only needs `end` (number of chars to preserve).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(middle-truncate): use Primitive.span with asChild support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add @seed-design/react-middle-truncate dependency to react package

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(file-upload): remove CSS line-clamp from name slot

JS (MiddleTruncate) handles truncation now. CSS keeps overflow:hidden
as a safety net.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(file-upload): integrate MiddleTruncate for extension-preserving filename display

FileUploadItemName now uses MiddleTruncate with splitFileName to preserve
file extensions (e.g. ".pdf") when truncating long filenames. maxLines=2
replaces the previous CSS -webkit-line-clamp approach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(middle-truncate): replace unit tests with Storybook stories

DOM-mocked unit tests were tautological (mock encoded expected results).
Remove them in favor of Storybook visual tests for truncation accuracy.
Switch measurement from Canvas to DOM-based scrollHeight for correct
multi-line truncation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(middle-truncate): convert to compound component pattern

Split single MiddleTruncate into Root + Content compound components
with hook-first architecture (useMiddleTruncate) and context sharing.
Root now self-measures via rootRef instead of relying on parentElement.
Update FileUpload and Storybook consumers to use new API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor

* refactor

* refactor: simplify snippet api

* feat: focus styles

* refactor: refactor & implement basic dropzone & action button

* refactor: refactor exposed api; add tests

* fix: make form related props follow the text input pattern; add props merging tests

* fix: fix snippet interface

* refactor: refactor snippet API

* refactor

* refactor

* fix: fix styles

* feat: support custom inset

* refactor: provide id to file entry; simplify onFileReject parameter

* refactor

* fix: fix consumers

* feat: add reorderable snippet

* fix: styles

* test: stories

* fix: sync accepted entries with the input

* fix: fix onAcceptedFileEntriesChange being called on file rejection

* fix: fix styles

* fix: add missing snippet item

* docs

* docs

* fix

* test: add dropzone cases

* refactor: rename

* refactor: rename snippet components

* refactor: rename css variable

* refactor: reexport middletruncate from react package

* feat: implement preventDocumentDrop

* feat: filter AutoScroller plugin to prevent default behavior

* feat: add dragging-over style

* feat(attachment-display): add headless package with hook, primitives, and tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(attachment-display): add styled components reusing AttachmentInput CSS recipes

- Root, Container, ItemGroup, Context (attachmentInput recipe)
- Trigger, TriggerIcon, TriggerItemCount (attachmentInputTrigger recipe)
- Item, ItemImage, ItemThumbnail, ItemPreview, ItemActionButton, ItemRemoveButton (attachmentInputItem recipe, type:"image")
- Namespace exports and barrel registration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add AttachmentDisplay documentation, registry snippet, and examples

- Registry snippet with AttachmentDisplayField, AttachmentDisplayInput, AttachmentDisplayItem
- Examples: preview, status, controlled, disabled, bridge integration
- MDX documentation with usage guide and comparison table
- Add headless re-export to @seed-design/react/primitive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: fix trigger position

* chore: generate

* fix: render retry button only when retry is defined

* fix

* feat: add callback onFileAccept

* fix: fix side effects in render

* fix

* fix: spread stateProps to root

* fix: fix styles

* fix: remove disabled attribute if max files reached

* fix: fix id

* fix: fix className being attached to fragment

* docs

* refactor: rename example components

* refactor

* refactor: remove AttachmentDisplay to separate branch

AttachmentDisplay headless, styled components, docs, registry entries,
and all references in shared files are removed. These will be moved to
a dedicated feat/attachment-display branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: generate

* fix: fix dropzone style when using extend css variable

* fix: fix x axis scroll being disabled; add activity example

* refactor: refactor rootage declaration; add item badge slot

* docs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

feat(attachment-input): add readonly support (#1584)

* fix(attachment-input): mask remove button gap for layer independence

Replace the remove button's outset shadow ring (which only matched
layer-default backgrounds) with a composite SVG mask that punches a
transparent hole out of the item's ::before, image, and backdrop slots.
The gap around the button is now layer-agnostic and works on
layer-floating and basement surfaces.

- Add removeButtonMask slot to attachment-input-item spec (disabled
  while dragging, so the button and mask are hidden together)
- Drop the foobarWidth/foobarColor placeholders from the remove button
  spec since the outset ring is no longer used
- Render the remove button in the AttachmentInputItemBadge story so
  the mask behavior is visible there too

Resolves DES-1713.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: add readonly styles

* feat: implement readonly behavior

* fix(file-upload): hide hidden input from a11y tree

Add aria-hidden=true to the hidden <input type="file"> so screen reader
browse-mode cursors do not announce it as a second control alongside the
trigger button. tabIndex=-1 alone does not stop virtual cursor navigation.

ref: chakra-ui/zag@e847201

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(attachment-input): strip aria-readonly from hidden input

Field spreads aria-readonly to every child input via inputAriaAttributes,
but <input type="file"> has no implicit role that accepts aria-readonly
per ARIA 1.2 (valid only on textbox, spinbutton, checkbox, switch, slider,
etc). Destructure it out of the merged props on AttachmentInputHiddenInput
so Field's other aria attributes still flow through.

Reverses the test that previously asserted the invalid attribute was set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: fix focus styles since it conflicts with item action button ring

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(box): add margin props with per-direction bleed exclusion

Adds margin/m, marginX/mx, marginY/my, marginTop/mt, marginRight/mr,
marginBottom/mb, marginLeft/ml to StyleProps. The CSS uses a
var(--margin, calc(--bleed * -1)) pattern with `initial` defaults so
margin, bleed, and `auto` values all work without JS-side handling.

StyleProps becomes a union where margin and bleed props are mutually
exclusive per CSS direction — e.g. bleedTop + marginBottom compile, but
marginX + bleedX do not. A DistributiveOmit helper preserves this union
through wrapper chains (Flex, Grid, Stack, etc.), so the constraint
propagates transparently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(box): surface "auto" literal and unblock list snippet extension

- Add "auto" as an explicit literal to margin prop types so it appears in
  IDE autocomplete (previously accepted via `(string & {})` but hidden).
- Convert `ListProps` in the list snippet from `interface extends` to a
  `type` alias. `SeedList.RootProps` is now a constrained union and TS
  rejects `interface X extends <union>` (TS2312).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(box): add margin auto playground story

Covers static centering, responsive auto across breakpoints, asymmetric
per-direction auto, mixed dimension values, and cross-direction
margin-auto + bleed combinations to verify the var() fallback pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Revert "docs(box): add margin auto playground story"

This reverts commit 816ed8f.

* test(box): add margin playground activity to stackflow-spa

Covers shorthand cascade (m → mx/my → mt/mr/mb/ml override), value
types (dimension tokens / raw px / zero / negative), `auto` centering
and push variants, responsive values across all breakpoints including
`auto` at select breakpoints, cross-direction margin + bleed
combinations, and baseline regression checks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(stackflow-spa): document activity registration checklist

Adds AGENTS.md covering the four registration points (activity file,
Stack.tsx components, stackflow.config.ts routes, ActivityHome link)
and the snippet sync rule vs docs/registry/ui.

Also registers the missing route entry for ActivityMarginPlayground
that was added in the previous commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(box): simplify margin/bleed constraint to all-or-nothing

Collapses the per-direction exclusion (four DirectionExclusion
intersections over eight direction key groups) into a single
all-or-nothing constraint over two key groups. Derives MarginKeys
and BleedKeys via Extract<keyof StylePropsBase, ...> so the lists
stay validated against the source interface.

Type-level consequence: cross-direction combinations such as
bleedTop + marginBottom or marginX + bleedY are no longer allowed.
Margin-only and bleed-only usage, including all shorthand cascade
and responsive auto cases, are unchanged. StyleProps remains a
union, so wrapper type aliases and DistributiveOmit stay in place.

Playground section demonstrating cross-direction combinations is
dropped since it would now fail to compile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: add `bleed` to StyleProps

* fix: types

* chore: jsdoc

* fix(docs): narrow createStoryWithParameters generic to avoid variance trap

The helper was constrained on the full `StoryObj<T>` / `StoryObj<unknown>`,
which re-instantiates `DecoratorFunction<R, T>` internally. `T` is in a
contravariant position there, so a concrete `StoryObj<ComponentProps>`
is not a subtype of `StoryObj<unknown>` — the constraint silently failed
once Box-family props became a constrained union.

The helper only touches `parameters`; the rest is passed through. Swap
the generic to `S extends { parameters?: Parameters }` so the function
never re-derives `StoryObj` internals. Callers' `: Story` annotation on
the surrounding declaration still performs the full story shape check.

Also drop the now-redundant explicit `<typeof meta>` generic argument at
the three ContentPlaceholder call sites — with the new signature, S is
inferred from the story value, not the component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(text-input, input-button): apply disabled color to prefix and suffix slots

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: add/update size=medium variants

* refactor(field-button): hoist input-button props provider to root

Apply the Checkbox/RadioGroup pattern: split props with
splitMultipleVariantsProps and propagate inputButton variants via
InputButtonPropsProvider so size flows from FieldButtonRoot to its
descendant Control without explicit prop drilling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(text-field): add size example

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(field-button): add size example

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(field-button): add clear button case to storybook variant table

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(text-input): align multiline size variants with Figma spec

- type=multiline,size=large: minHeight 95px → 94px
- type=multiline,size=medium: minHeight 90px → 82px, paddingY 11px → $dimension.x3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#1537)

* chore(hooks): use bun test for qvism post-edit checks

@seed-design/qvism-core has no `test` script; the previous filter command
errored out on every ecosystem/qvism edit. Replace with `bun test ecosystem/qvism`
so the existing test files (e.g. `ecosystem/qvism/core/src/css.test.ts`) run
directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(qvism): add description and metadata to recipe definition

Surface recipe-level and per-variant descriptions through the qvism dts
generator so they reach React component prop hovers.

- types: add `description?` (top-level) and `metadata?` containers to
  RecipeDefinition / SlotRecipeDefinition. Value-level entries use
  `{ description }` objects to leave room for future flags
  (deprecated, since, etc.) without breaking changes.
- dts: emit recipe-level description on every public declaration
  (Variant interface, VariantMap, VariantProps, SlotName, const exports)
  prefixed with the recipe name in backticks for visual anchoring.
  Variant axis description and per-value descriptions render as a
  Markdown list inside the axis property's JSDoc body — the format that
  TypeScript actually surfaces in IDE hover, given that union member
  JSDoc is not honored (see microsoft/TypeScript#54510).

Pilot recipes (action-button, page-banner) live in a follow-up commit so
this one stays focused on the core API + dts generator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(rootage-artifacts): publish generated json and serve via docs route handler

Promote rootage codegen output from a docs-internal asset into a first-class
artifact of the @seed-design/rootage-artifacts package, so recipe ts and other
consumers can import it as typed data.

Package:
- packages/rootage/package.json: add `dist/**/*.json` to files, expose `.`,
  `./index.json`, `./components/*.json`, `./*.json` via exports, and run
  `rootage json ./dist` from rootage:generate.
- packages/rootage/.gitignore: ignore generated dist/.

Docs:
- docs/app/rootage/[...path]/route.ts: new force-static route handler that
  reads packages/rootage/dist via fs and emits one static asset per path
  (96 files: index.json + tokens + components). External fetch surface
  (seed-design.io/rootage/...) and existing iOS/Android handoff URLs are
  preserved, with byte-identical output.
- docs/lib/rootage.ts: server-only loader split out of components/rootage.ts;
  uses fs to read packages/rootage/dist (process.cwd-relative) so node:*
  builtins do not leak into client bundles.
- docs/components/rootage.ts: trimmed to client-safe stringifier helpers;
  callers updated to import server-only fns from @/lib/rootage.
- docs/app/_llms/rules/{token-reference,component-spec-block}-rule.ts:
  switch from process.cwd()/public/rootage to require.resolve of
  @seed-design/rootage-artifacts.
- docs/package.json: drop the in-docs `rootage:generate` script and let
  prebuild run the rootage-artifacts generator instead. Add server-only
  + workspace dependency on rootage-artifacts.

Webpack monorepo + scoped package + dynamic import context is broken in our
setup (webpack/webpack#16149); webpackMode/webpackContext do not help. Static
import + server-only fs is the only path that survives Next.js webpack
without leaking node:* into client bundles.

Verified against alpha baseline build:
- Same 96 files emitted under out/rootage/
- Identical paths, identical content for all token / component json
- index.json now matches alpha pretty-printed formatting
- yaml change → bun rootage:generate → docs build → out/rootage updated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

# Conflicts:
#	docs/public/rootage/components/input-button.json
#	docs/public/rootage/components/text-input.json

* feat(qvism): enforce yaml metadata as a strict subset of recipe variants

Spreading yaml-sourced ComponentSpec directly into a recipe (e.g.
`metadata: { variants: spec.data.schema.variants }`) used to silently
swallow any axis or value key that the recipe did not declare, because
RecipeMetadata is a Partial<T>-shaped record. That meant a yaml change
adding a new variant value would never surface as a build failure even
though the description never reached the generated dts.

Add ExactRecipeMetadata<T, M> in qvism-core: a conditional that admits
M only when its `variants` axis keys are a subset of `keyof T`, and each
axis's `values` keys are a subset of `keyof T[axis]`. Any extra axis or
value collapses the relevant slot to `never`, which the corresponding
property in defineRecipe / defineSlotRecipe rejects at compile time.

Recipe authors keep the one-line spread; mismatches now turn into a
build error pointing at the offending metadata slot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(qvism-preset): wire remaining recipes into rootage-artifacts metadata

Migrate every recipe whose yaml carries variant value descriptions to pull
those descriptions from rootage-artifacts instead of repeating them. The
intersection rule is enforced by ExactRecipeMetadata, so each metadata
block lifts only the axes and values that exist in both yaml and recipe.

Most recipes use the shortest form
`metadata: { variants: { axis: spec.data.schema.variants.axis } }`; a few
need explicit per-value picks (avatar `size`, skeleton `radius`,
progress-circle `size`/`tone`) because the recipe carries values yaml
does not describe — but those still derive every string from spec, never
copy-pasted. text recipe maps to typography yaml (27 textStyle entries).

Also normalize the import name to `spec` everywhere so the line is
identical across recipes, and widen ExactRecipeMetadata's key comparison
to stringify both sides — recipes that key variants by number (avatar,
progress-circle) compile against JSON-imported string keys without
manual quoting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: align build flow with rootage-artifacts dist consumption

qvism-preset recipes now import @seed-design/rootage-artifacts dist JSON
at compile time, so rootage:generate must produce packages/rootage/dist
before packages:build runs.

- actions/setup: cache ecosystem/**/bin, recover a missing rootage CLI
  bin on cache hit, and run rootage:build → install → rootage:generate
  before packages:build on cache miss. Drop the build-rootage branch
  that ordered packages:build before rootage:build.
- release-packages: drop the changeset-release-PR step that committed
  docs/public/rootage. The route handler added in 2cb365f replaces
  that static copy, so the step has nothing to commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(qvism-core): simplify dts variant interface generation

Extract `buildJsdocBlock` helper, accept the full definition in
`generateVariantInterface` to consolidate three positional args, and
replace the `hasJsdoc` flag with an early return on empty sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(docs): cache rootage loader and dedupe index.json reads

- Wrap `getRootage` with `react.cache` so multiple Server Components
  in the same render tree share one `buildContext` result.
- Flatten `getComponentStatus` with early returns.
- In LLM rules, import `index.json` directly instead of reading and
  parsing it twice; drop the redundant existence-probe in
  `loadTokenData`.
- In the rootage route handler, drop the duplicated `index.json`
  branch and read it from disk like every other resource.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* revert: align build flow with rootage-artifacts dist consumption

This reverts commit 23aca9b.

Both changes assumed dist json would be regenerated on every CI run:
the setup-action build sequence rewrite that strict-ordered rootage cli
build before packages:build, and the removal of the release-packages
step that used to commit the version-synced dist artifacts.

The next commit shifts to tracking dist json in git (mirroring alpha's
docs/public/rootage pattern, just at the new package-published location)
which makes the build-flow rewrite unnecessary and re-enables the
release-time version sync step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(rootage-artifacts): track dist json in git

Mirror alpha's docs/public/rootage tracking pattern at the new
package-published location. With dist json checked in, the build graph
loses its sole non-package-graph invariant: packages:build no longer
needs rootage cli bin or rootage:generate to have run beforehand,
because qvism-preset's recipe imports resolve directly against the
files already on disk.

Workflow consequences:
- packages:build callers (setup action, docs-test, continuous-releases,
  package.json release) need no rootage-specific sequencing.
- The release-packages step that bumps dist json on version change
  becomes meaningful again at the new path (handled in the next commit).
- Authors run bun rootage:generate after editing yaml, then commit
  packages/rootage/dist alongside the source change. Drift can be
  guarded with a stale check in CI if needed later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(release): retarget rootage version sync step to packages/rootage/dist

The version-sync step now writes the new package version into
packages/rootage/dist instead of docs/public/rootage. This restores the
alpha-era invariant under the new artifact location: every release PR
contains a dist json bump matching the package version it ships.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(rootage-artifacts): rename dist to __generated__

The dist convention signals "build artifact, gitignored"; tracking it in
git was a signal mismatch. __generated__ matches the existing seed-design
convention (packages/figma/.../data/__generated__) and the dunder prefix
visually flags "auto-generated, do not edit by hand".

The published export paths are unchanged (./components/*.json, etc.),
so consumers see no surface difference; the rename is internal-only.

- packages/rootage/package.json: rename internal paths in scripts,
  files, and exports.
- packages/rootage/__generated__: rename via git mv (96 files,
  byte-identical to before).
- docs route handler / server loader / llm rules / release workflow:
  update the path references that point at this directory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): drop redundant rootage:generate prebuild

The prebuild was added in 2cb365f to ensure dist json existed before
docs build under the gitignored model. With dist json now tracked in git
(via 73e18d8), the file is always on disk at build time, so the
prebuild has nothing to do — and worse, it ran rootage cli at a point
where ecosystem bins were not guaranteed to be linked, causing
"Script not found rootage" failures in docs deploy jobs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(rootage-artifacts): drop misleading clean script

Every other workspace's `clean` removes a gitignored build artifact
directory (lib, dist, bin). With __generated__ tracked in git, this
clean would silently delete 96 committed files from the working tree
and stage them as deletions — the same name with the opposite semantics.

The script was added in 2cb365f under the gitignored dist model and
became a trap once dist json moved into git (73e18d8). Alpha never
had this script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

# Conflicts:
#	docs/app/source.tsx
#	docs/components/rootage.ts
#	packages/rootage/__generated__/components/top-navigation-icon-button.json
#	packages/rootage/__generated__/components/top-navigation-text-button.json
…1532)

* feat(typography): add t11-t14 sizes and fill missing weight variants

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(typography): describe sm-breakpoint usage for t11-t14 tokens

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(accordion): add rootage component spec and generate vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(accordion): add qvism-preset recipe and generate CSS

* feat(accordion): add React styled components with hook and context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(accordion): fix collapsible prop type narrowing in AccordionRoot

* feat(accordion): add registry snippet with built-in chevron

* docs(accordion): add React implementation docs with code examples

* docs(accordion): add design docs, storybook stories, and stackflow example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(accordion): fix storybook story type by using AccordionRoot as meta component

* fix(accordion): fix storybook story types and remove unused import

* chore(accordion): add generated registry/CSS files and update rootage AGENTS.md

* fix(accordion): add missing generated public JSON files for docs build

* fix(accordion): remove PlatformStatusTable pending Sanity registration

* fix(accordion): restore PlatformStatusTable with correct componentId

* fix(accordion): resolve PR review issues and improve animation

- Fix collapsible prop leaking to DOM via rest spread
- Add aria-labelledby to AccordionContent for WCAG 4.1.2 compliance
- Add missing keyboard interaction docs (ArrowDown/Up, Home, End)
- Fix choppy animation by moving padding to new contentInner slot
- Add opacity transition for smoother open/close animation
- Remove redundant composeRefs in AccordionContent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(accordion): add engaged (pressed) background color for trigger

* fix(accordion): propagate disabled state to child slots and fix registry patterns

- Add withStateProps to AccordionTitle/AccordionDescription for data-disabled propagation
- Add data-disabled to AccordionSuffixIcon for disabled color styling
- Add ContentInner wrapper to stackflow-spa AccordionContent for proper padding
- Change type aliases to interface extends in registry snippets for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(accordion): add inline divider and move engaged background to item level

* docs(accordion): add PrefixAvatar/inline examples and improve React docs

* fix(accordion): add body slot, fix typography, add open stories, add color transition

- Add body wrapper slot for vertical Title+Description layout
- Change description fontWeight to medium
- Add contentInner typography (fontSize, lineHeight, fontWeight, color)
- Add item color transition for engaged state (colorDuration, colorTimingFunction)
- Add OpenLightTheme and OpenDarkTheme storybook stories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(accordion): add body slot, fix typography, add open stories, add color transition

* fix(accordion): add chromatic delay for open state story snapshots

* fix(accordion): fix prefix layout, disable engaged on disabled, add storybook prefix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: trigger CI rebuild for chromatic baseline

* refactor(accordion): merge PrefixIcon/PrefixAvatar into single Prefix slot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(accordion): add Body wrapper to ActivityAccordion examples

* fix(accordion): move engaged to trigger::before with list-item margin/radius animation

* docs(accordion): remove customizing-trigger example and docs section

* docs(accordion): add custom content example with styled React nodes

* refactor(accordion): prop-based snippet API, prefix disabled style, code cleanup

* chore: merge dev and regenerate all

* fix(accordion): use Icon svg wrapper in prefix examples and update docs

* refactor(accordion): switch to named exports in snippet and all examples

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(accordion): switch to named exports in snippet and all examples

* docs(qvism-preset): add engaged ::before pattern to AGENTS.md

* fix(accordion): add 12px padding to inline divider using ::after pseudo element

* fix(accordion): change prefix icon color from neutral-subtle to neutral

* docs(changeset): add accordion minor release note

* docs(spec): add accordion headless extraction design spec

* docs(plan): add accordion headless extraction implementation plan

* feat(react-accordion): scaffold package metadata

* feat(react-accordion): add useAccordion hook with type guard predicate

* fix(react-accordion): restore useCallback/useMemo memoization in useAccordion

* feat(react-accordion): add context providers

* feat(react-accordion): add Root (Impl split) + Item/Header/Trigger/Content

* fix(react-accordion): add Primitive.h3 and fix AccordionItem spread order

- Register h3 in Primitive registry to unblock AccordionHeader compile.
- Spread {...rest} before controlled props (open/onOpenChange/disabled/ref)
  so user-provided overrides cannot break accordion state.
- Memoize handleOpenChange via useCallback for consistency with precedent.

* feat(react-accordion): add barrel exports and namespace

* test(react-accordion): add useAccordion unit tests

* test(react-accordion): add Accordion integration tests (keyboard, ARIA)

* feat(accordion): add header slot for semantic heading wrapper

* refactor(accordion): shrink react layer to styled wrapper over headless

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(accordion-snippet): auto-wrap Trigger in AccordionHeader

* docs(changeset): add react-accordion to changeset and note header auto-wrap

* refactor(react-accordion): extract data-accordion-trigger constant and remove last as cast

* chore: ignore docs/superpowers/ directory

* fix(accordion): re-add AccordionVariantProps to AccordionRootProps for variant/size type inference

* fix(react-accordion): pass native disabled attribute to trigger button

* chore(css): regenerate CSS after rebuilding postcss-engaged

* refactor(accordion): add data-open to state props and drop unused compose-refs dep

* chore(stories): use createStoryWithParameters helper for Accordion stories

* fix(stories): pass explicit meta type to createStoryWithParameters for Accordion

* docs(accordion): add value array example, align headless state and focus scope, register triggers directly, add docs (#1514)

* docs(accordion): add value array example

* fix(accordion): align headless state and focus scope

* fix(accordion): register triggers directly

* chore(accordion): downgrade changeset to patch and pin snippet dep to ~1.2.10

* docs(changeset): tighten accordion changelog to impact-focused summary

* docs(changeset): add accordion usage example

* refactor(accordion): adopt reviewed api and recipe changes

* chore(accordion): clarify docs examples and display names

* fix(accordion): align snippet and header types

* refactor(accordion): move headless interaction logic into hooks

* refactor(accordion): use interfaces in registry snippets

* fix(accordion): align prefix API with list pattern

* docs(accordion): specify default expanded type

* docs(accordion): update changeset snippet usage

* docs(accordion): remove prefix avatar example

* docs(accordion): align value changes example layout

* docs(accordion): improve heading level example layout

* docs(accordion): clean up stories and stackflow example

* docs(accordion): expand heading level example width

* docs(accordion): stabilize value changes example layout

* docs(accordion): improve prefix example content

* docs(accordion): clarify prefix icon guidance

* fix(accordion): address review feedback

* fix(accordion): narrow stackflow example props

* feedback

* interface

* refactor(accordion): drop collapsible prop and switch RootProps to interfaces

- Remove collapsible from useAccordion (controlled pattern via useState +
  onValuesChange guard covers the same use case).
- Collapse UseAccordionBaseProps + UseAccordionProps union into a single
  interface.
- Convert RootProps (react-headless, react, docs registry, stackflow-spa
  snippet) from type aliases to interfaces for consistency with other slots.
- Replace examples/collapsible.tsx with controlled-required-open.tsx and
  rename the docs section "Collapsible" -> "Always one open".
- Drop collapsible plumbing from examples/stackflow-spa AccordionSection.
- Update useAccordion tests for the new API.

* docs(accordion): consolidate changesets into a single minor entry

Accordion has not been released yet, so the collapsible removal note is
unnecessary churn. Bump the existing Accordion changeset to minor and drop
the obsolete collapsible code example.

* docs(accordion): replace single example with multiple and fix value-changes copy

- Rename "Single Expand" section to "Multiple" with a new multiple-mode
  example (multiple defaultValues, independent toggling).
- Generalize the "Value Array Changes" copy: values is always an array in
  controlled mode regardless of single/multiple.

* fix(stories): drop invalid type argument from createStoryWithParameters in Accordion stories

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

docs(accordion): migrate guideline from Figma source (#1576)

* docs(accordion): migrate guideline from Figma source

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(accordion): translate variant descriptions to Korean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(accordion): expose variant descriptions via recipe metadata

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(accordion): move to display group and refine guideline

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(accordion): add cover image

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: fix id

* docs: remove figma specific info

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ty (#1596)

`BottomSheetContent` no longer exposes `onPointerDownOutside` after the recent Menu refactor (#1410, #1455) removed those callbacks from `Drawer.ContentProps`. Move the "ignore outside-press while AlertDialog is open" intent to `BottomSheetRoot.closeOnInteractOutside={!alertDialog.open}` instead, which is the public API the drawer headless layer still exposes. This restores the `deploy-seed-design-stackflow-spa-alpha-pages` build that has been red since 2026-05-12.
* feat: add component AttachmentDisplay

Add a URL-based attachment display component for media sourced from native
bridges or external systems. Shares visual styling with AttachmentInput
recipes (attachmentInput, attachmentInputItem, attachmentInputTrigger) but
ships its own headless layer (@seed-design/react-attachment-display) since
it has no file-upload semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(stories): remove unused VIEWPORT_MODES from AttachmentDisplay LightTheme

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(stackflow-spa): add AttachmentDisplayField example activity

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(attachment-display): pass action helpers to onTriggerClick/onRetry

Expose the headless action API through the snippet callbacks so consumers
drive entries via injected helpers instead of re-implementing setEntries:

- onTriggerClick(({ addEntries, updateEntryStatus }))
- onRetry((entry, { updateEntryStatus }))

addEntries enforces the maxEntries cap and single-mode replacement internally,
which the previous setEntries((prev) => [...prev, x]) pattern bypassed. Mirrors
AttachmentField's onFileAccept/onRetry helper convention.

Migrate docs examples, the usage mdx, the stackflow-spa demo activity, and the
synced snippet copies to the new API. Add a controlled functional-updater test
covering addEntries reading the current controlled value across calls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: group attachment field pages under "Attachment Input"

Move attachment-field and attachment-display-field MDX into an
(attachment-input) route group with explicit page ordering, mirroring
the existing Tabs group. URLs are unchanged (route group strips no segment).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(changeset): add react-attachment-display to attachment field release

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: add ComponentExample  `isolate` option

* feat(react-headless): add floating and tooltip packages

Extract the shared floating-positioning logic into @seed-design/react-floating
and refactor popover to consume it. Add @seed-design/react-tooltip: a
hover/focus tooltip headless with fixed open/close delays, delay grouping
(NextFloatingDelayGroup), and an optional interactive content mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(react): add HelpBubbleTooltip

Add the styled HelpBubbleTooltip (Root/Trigger/Positioner/Content/Arrow/Body/
Title/Description) and HelpBubbleTooltipDelayGroup. The help-bubble recipe
skips its enter/exit animation while a delay group switches between tooltips
(data-instant) so the swap reads as instant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(react): add HelpBubbleTooltip documentation, examples, and stories

Add the HelpBubbleTooltip docs page, examples, and Storybook story, and move
the HelpBubble page into the shared (help-bubble) route group.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(stackflow-spa): add HelpBubbleTooltip example

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(changeset): include @seed-design/css patch in HelpBubbleTooltip changeset

The HelpBubble recipe gained a `[data-instant]` rule that compiles into the
published @seed-design/css package, so add a css patch bump. Also reword the
summary to describe HelpBubbleTooltip as a separate component instead of a
mode added to HelpBubble.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs

* docs

* docs: make variant=ghost action buttons use consistent icon styles across examples

* chore: generate

* feat: add snippet component `HelpBubbleTooltipTriggerPortal`

* docs

* fix(help-bubble-tooltip): add displayName to snippet trigger components

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(tooltip): cover content pointer-events and state data-attributes

Lock the glue useTooltip owns on top of floating-ui: the default
pointer-events block on content (and its keepOpenOnContentHover escape
hatch), and the placement/transition-status to data-* mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(help-bubble): add displayName to snippet trigger and anchor components

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drawer onOpenChange only carried close reasons; opening via the trigger passed no details. Add "trigger" to the reason map and pass it with the native event from the trigger onClick, matching Dialog. Restores the follow-up deferred in #1198 that was orphaned when #1202 closed unmerged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ba45d70

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8a7bd5c-840a-4e5a-becc-8b5528407a06

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/drawer-open-reason

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Alpha Preview (Stackflow SPA)

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Alpha Preview (Storybook)

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Alpha Preview (Docs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants