Tangible Design System · @tangible/ui 0.2.36 · Interactive version · Markdown

Sidebar

import { Sidebar } from '@tangible/ui';

Interactive docs and examples: Sidebar in Storybook.

Sidebar props

Prop Type Default Description
children ReactNode Sidebar content (Header, Nav, etc.).
aria-label? string Accessible label for the sidebar landmark (static) or dialog (drawer). When a page has multiple sidebars, each must have a unique label so screen reader users can distinguish between landmarks (e.g. "Course navigation", "Lesson settings").
className? string Additional CSS class names.
collapsed? boolean Collapsed state (controlled). Pair with onCollapsedChange. Leave undefined to let the sidebar own the state.
collapsible? boolean false Opt into the collapsed rail: the sidebar can shrink to --tui-sidebar-collapsed-width, hiding Header, Nav and Footer while leaving any other child — a Sidebar.Toggle, an icon rail — visible. Deliberately absent from drawer mode: a drawer is already a show/hide overlay, and a rail inside one has nothing to sit beside.
defaultCollapsed? boolean false Initial collapsed state when uncontrolled.
drawer? boolean false Enable drawer mode (mobile overlay).
initialFocusSelector? string CSS selector, matched inside the drawer when it opens, for the element that takes focus: say, the current lesson in a long outline, so keyboard and screen reader users start where they are. Escape still closes and the close button is a Shift+Tab away. Don't point it at a destructive action. The target must be able to take focus (a control, a link, or anything with tabindex, -1 included) and be rendered at open. Otherwise a dev warning fires and focus goes to the default: an element marked data-autofocus, else the first control. It's read once, when the drawer opens; changing it while open doesn't move focus. Same prop as Modal's, but the default differs: Modal skips its own close button, while a drawer has none, so its first control is often your Header's close button. That's what this prop is for.
onClose? () => void Callback to close the drawer. Required when drawer={true}. Called when ESC is pressed or backdrop is clicked. Omitting this in drawer mode means the user cannot dismiss the overlay, which is a WCAG 2.1.1 (Keyboard) failure.
onCollapsedChange? (collapsed: boolean) => void Called with the next collapsed state whenever Sidebar.Toggle is pressed — in both controlled and uncontrolled mode.
open? boolean Drawer open state.
position? "left" | "right" 'left' Sidebar position. Affects border placement and drawer slide direction.

Sidebar.Header

Prop Type Default Description
children ReactNode Header content (back button, title, progress, CTAs).
className? string Additional CSS class names.

Sidebar.Footer

Prop Type Default Description
children ReactNode Footer content (CTAs, user info, etc.). Sticks to the bottom of the sidebar.
className? string Additional CSS class names.

Sidebar.Nav

Prop Type Default Description
children ReactNode Nav content (typically Accordion with StepList).
aria-label? string Accessible label for the nav landmark. Use this for the primary label since nav is the actionable region.
aria-labelledby? string ID of a visible heading that labels the navigation.
className? string Additional CSS class names.

Sidebar.Toggle

Prop Type Default Description
className? string Additional CSS class names.
disabled? boolean Disabled state.
href? never
labels? SidebarToggleLabels Accessible names for the two states. Defaults are English and are exported as defaultSidebarToggleLabels; a partial object overrides only the keys it names.
loading? boolean Loading state: shows a spinner and blocks activation while keeping the button focusable (aria-disabled, not native disabled), so a keyboard user who triggered it keeps their place. Only onClick is guarded: put the action there, not in onKeyDown/onPointerDown. The start and end of loading aren't announced; confirm completion through your own status message (a Toast, say).
loadingLabel? string `${label}, loading` (English) Accessible label override during loading state. For i18n support.
shape? "circle" | "square" 'square' Button shape. Use 'circle' for avatar-adjacent actions, FABs, or anywhere a round affordance is expected.
showTooltip? boolean false Show the label as a tooltip on hover. Since icon-only buttons have no visible text, enabling this helps sighted users discover what the button does. Recommended for most standalone icon buttons. Can be omitted in toolbars where a shared Tooltip provider handles it, or where adjacent visible text already communicates the action.
size? "lg" | "md" | "sm" | "xs" 'sm' Size of the button. 'xs': 24px — below WCAG 2.5.8 minimum target size (24×24 CSS px). Use only in dense, mouse-first UIs (toolbars, table rows) where touch is not expected. Consider pairing with adequate spacing or a larger touch target via padding/margin. 'sm': 32px (default); 'md': 40px; 'lg': 48px
theme? "danger" | "info" | "primary" | "secondary" | "success" | "warning" 'secondary' Semantic color theme. Note: Defaults to 'secondary' (unlike Button which defaults to 'primary') because IconButtons are typically used for toolbar/utility actions.
tooltipSide? "bottom" | "left" | "right" | "top" 'top' Tooltip placement side.
variant? "ghost" | "outline" | "solid" 'ghost' Visual style variant. 'solid': Filled background; 'outline': Border only, transparent background; 'ghost': No border or background, icon only — has no at-rest visual affordance. Best suited for toolbar groups, icon bars, or other contexts where surrounding UI makes the interactive nature obvious.

Also accepts the standard attributes of ButtonHTMLAttributes (data-*, aria-*, event handlers), passed to the element it renders.

Other exports

Examples

Other stories: Static, With Header and Nav Slots, With Footer, Right Position, Drawer Mode, Drawer (Right Position), Drawer Keyboard Interaction, Course Viewer (Full Integration) (interactive page).

Component API tokens

Read via fallback, never declared. Set one on an ancestor to restyle every instance under it.