Skip to content

[Design System] Modularize discoverable unavailable toolbar actions #899

Description

@seonghobae

Buyer and maintainer problem

PR #858 establishes a correct bounded behavior for discoverable unavailable canvas actions, but the implementation repeats state guards, aria-disabled, description IDs, rendered reasons, tooltip markup, and click suppression across many buttons in App.tsx. Repetition makes it easy for a future action to drift: the displayed reason, activation guard, busy state, and accessible description can disagree.

Product boundary

Create one reusable toolbar-action component in the frontend design system. This is a refactor and Storybook/visual-contract slice, not a change to availability policy.

Suggested public contract:

interface AccessibleToolbarActionProps {
  accessibleName: string
  available: boolean
  busy?: boolean
  unavailableReason?: string
  busyReason?: string
  title: string
  onActivate: () => void
  children: React.ReactNode
}

The final API may differ, but it must own:

  • native button semantics;
  • stable unique reason IDs through useId or an equivalent deterministic seam;
  • aria-disabled, optional aria-busy, and aria-describedby synchronization;
  • a single guarded pointer/Enter/Space activation path;
  • rendered description/tooltip markup whose explanatory copy tells the customer what prerequisite or next action is required;
  • focus-visible and hover presentation using existing design tokens;
  • optional analytics action code without customer schema content;
  • disabled/busy reason consistency assertions.

Current design authority — 2026-08-19

Fresh live Figma inspection of csnpEEJfmqFWB0vNUoTkWA shows the reusable design authority is now ahead of this issue's original sequencing assumption:

  • ToolbarButton (35:30) has Neutral/Primary Default, Hover, Pressed, Disabled, and token-backed Focus variants;
  • CanvasToolbar (35:125) has Density=Compact|Labeled, State=Ready|SomeDisabled, and Minimap=Shown|Hidden variants;
  • the toolbar uses the existing --pg-* semantic token set rather than one-off colors/spacing;
  • the live Figma contract therefore must be preserved and mapped to code/Storybook, not recreated after implementation.

Do not make Figma an independent behavioral source: production availability policy, accessible semantics, and tests remain executable authority; Figma owns the reviewed visual/state contract and token mapping.

Storybook inventory

The repository still has no discoverable @storybook configuration or stories. Add Storybook to the owning frontend package if that remains true at implementation time, without introducing a second package manager or duplicating central CI. Include stories for:

  • available;
  • unavailable and focused;
  • unavailable and hovered;
  • busy;
  • long localized reason;
  • high zoom / narrow viewport;
  • reduced motion;
  • forced colors;
  • keyboard activation and inert unavailable state.

Map every story state to the corresponding Figma component/state and shared semantic tokens. Where Figma does not yet define a required accessibility or responsive state, update Figma and Storybook together before production adoption.

Writer / sequencing boundary

PR #858 currently owns the overlapping App.tsx canvas-toolbar surface. Do not race that branch or copy its stale/intermediate tree into this design-system slice. While #858 is actively moving, this issue is design/contract preparation only. After #858 reaches a stable integrated state (or is explicitly superseded), reconstruct the reusable component from then-current protected main, refetch the exact Figma component state, and migrate the toolbar in a separate bounded PR.

Acceptance evidence

  • migrate every canvas-toolbar action covered by a11y(frontend): keep unavailable toolbar actions discoverable #858 from the then-current protected-main behavior;
  • no duplicate description ID in one rendered toolbar;
  • pointer, Enter, and Space are inert while unavailable/busy;
  • enabled activation fires exactly once;
  • reason and guard cannot disagree by construction;
  • complete focused/component/App regression coverage;
  • Storybook accessibility checks and visual snapshots;
  • explicit Figma ↔ Storybook ↔ code state/token mapping;
  • forced-colors and 200% zoom review;
  • docs/doctoring/discoverable-unavailable-toolbar-actions.md updated with the final component inventory and claim boundary;
  • CHANGELOG.md updated only if buyer-visible behavior changes.

Standards

Use the current W3C WAI-ARIA Authoring Practices button, keyboard-interface, toolbar, and tooltip guidance and WCAG 2.2 as normative/reference inputs. Do not claim whole-product conformance from component tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: accessibilityAccessibility and assistive-technology supportarea: apiAPI, protocol, event, or external contractarea: authAuthentication, authorization, identity, or tenant isolationarea: ci-cdCI, GitHub Actions, checks, release, or supply chainenhancementNew feature or requestpriority: mediumNormal-priority or P2 workstatus: triagedOpen issue has an organization taxonomy assignmenttype: featureNew or expanded product capability

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions