Skip to content

Latest commit

 

History

History
1094 lines (787 loc) · 34.5 KB

COMPONENTS.md

File metadata and controls

1094 lines (787 loc) · 34.5 KB

Component Specifications

Auto-generated reference for @pigna/component-library Every component follows the same folder convention, uses SCSS Modules with CSS custom properties from _tokens.scss, and is WCAG 2.2 AA compliant.


Table of Contents


Conventions

Folder structure per component

src/components/ComponentName/
  ├── ComponentName.tsx            # Component with typed props, JSDoc, ARIA
  ├── ComponentName.module.scss    # Scoped styles using CSS custom properties
  ├── ComponentName.stories.tsx    # Storybook stories with controls + a11y
  ├── ComponentName.test.tsx       # Vitest + Testing Library tests
  └── index.ts                     # Named re-export

Form components are grouped

src/components/Form/
  ├── FormField/
  ├── Input/
  ├── Textarea/
  ├── Select/
  ├── Checkbox/
  ├── RadioGroup/
  ├── Toggle/
  └── index.ts                     # Barrel re-export for all form components

Props pattern

  • Extend native HTML attributes where possible (e.g. ButtonHTMLAttributes<HTMLButtonElement>)
  • Always include ref?: Ref<HTMLElement> (React 19 — no forwardRef needed)
  • Use JSDoc comments on every prop
  • Export the interface and any union types

Styling pattern

  • Import @use '../../styles/mixins' as *; in every .module.scss
  • Use CSS custom properties from _tokens.scss (never hardcode colors, spacing, etc.)
  • Use @include focus-ring on all interactive elements
  • Use @include sr-only for screen-reader-only text
  • Use @include reduced-motion to disable animations for users who prefer it
  • Use @include breakpoint-sm/md/lg/xl for mobile-first responsive styles

Storybook pattern

  • Import types from @storybook/react-vite
  • Use tags: ['autodocs'] for auto-generated docs
  • Create individual stories for each variant/state
  • Create a composite "All Variants" story

Test pattern

  • Use vitest + @testing-library/react + @testing-library/user-event
  • Test: renders correctly, ARIA attributes present, keyboard interaction, event callbacks, disabled/loading states

Build Order

Components are built in dependency order. Phase 1 components have no internal dependencies. Each subsequent phase may depend on components from prior phases.

Phase Components Reason
1 CloseButton, TextLink, Spinner, SkeletonLoader, NotificationBadge, ProfilePicture Leaf nodes — no internal deps
2 Banner, Dialog, Tag, Breadcrumb, Popover, ProgressIndicator Use Phase 1 components
3 FormField, Input, Textarea, Select, Checkbox, RadioGroup, Toggle Grouped form elements
4 MenuItem, MenuItemGroup, SideMenu, HamburgerMenu, NotificationPopup Navigation + notifications
5 ThemeColors Storybook-only documentation page

Phase 1 — Leaf Components

CloseButton

Reusable close/dismiss button with an × icon. Used by Dialog, Banner, NotificationPopup, Tag.

Property Detail
Folder src/components/CloseButton/
Extends ButtonHTMLAttributes<HTMLButtonElement>
Status ✅ Implemented

Props

Prop Type Default Description
size 'sm' | 'md' | 'lg' 'md' Button size. All sizes meet WCAG 2.5.8 minimum touch target.
aria-label string 'Close' Accessible label — override per context (e.g. "Dismiss banner").
ref Ref<HTMLButtonElement> — Forwarded to the native <button>.

Accessibility

  • Renders as <button type="button"> with aria-label
  • SVG icon has aria-hidden="true"
  • Keyboard focusable with visible :focus-visible ring

Theming

  • Icon color: --color-text-secondary → --color-text-primary on hover
  • Background: transparent → --color-surface on hover

TextLink

Styled anchor component. Supports external links with automatic target="_blank", rel="noopener noreferrer", and a screen-reader "(opens in new tab)" announcement.

Property Detail
Folder src/components/TextLink/
Extends AnchorHTMLAttributes<HTMLAnchorElement>

Props

Prop Type Default Description
external boolean false Adds target="_blank", rel="noopener noreferrer", and sr-only text.
children ReactNode — Link content.
ref Ref<HTMLAnchorElement> — Forwarded to the native <a>.

Accessibility

  • Renders as native <a> element
  • External links append <span class="sr-only">(opens in new tab)</span> for screen readers
  • Visible :focus-visible ring via @include focus-ring

Theming

  • Color: --color-primary
  • Underline on hover
  • Dark mode automatically adapts via --color-primary token

Spinner

Animated loading spinner with screen-reader announcement.

Property Detail
Folder src/components/Spinner/
Extends HTMLAttributes<HTMLDivElement>

Props

Prop Type Default Description
size 'sm' | 'md' | 'lg' 'md' Spinner diameter: sm=16px, md=24px, lg=40px.
label string 'Loading' Screen-reader announcement text.
ref Ref<HTMLDivElement> — Forwarded to the container <div>.

Accessibility

  • Container: role="status", aria-live="polite"
  • Hidden sr-only <span> with label text
  • Animation disabled when prefers-reduced-motion: reduce

Theming

  • Spinner stroke: --color-primary

SkeletonLoader

Placeholder loading shapes with a pulse animation. Supports text (multi-line), circle, and rectangle variants.

Property Detail
Folder src/components/SkeletonLoader/
Extends HTMLAttributes<HTMLDivElement>

Props

Prop Type Default Description
variant 'text' | 'circle' | 'rectangle' 'text' Shape of the skeleton.
width string varies CSS width. Defaults: text=100%, circle=48px, rectangle=100%.
height string varies CSS height. Defaults: text=1em, circle=48px, rectangle=200px.
lines number 1 Number of lines for text variant.
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • Container: role="status", aria-busy="true"
  • Sr-only <span> with "Loading…" text
  • Visual skeleton elements: aria-hidden="true"
  • Pulse animation disabled when prefers-reduced-motion: reduce

Theming

  • Background pulse between --color-surface and --color-surface-raised

NotificationBadge

Small count/dot badge overlaid on a child element (e.g. a bell icon).

Property Detail
Folder src/components/NotificationBadge/
Extends HTMLAttributes<HTMLSpanElement>

Props

Prop Type Default Description
count number — Numeric count to display.
max number 99 Maximum before displaying {max}+.
dot boolean false Show a dot instead of a count.
children ReactNode — The anchor element the badge overlays.
ref Ref<HTMLSpanElement> — Forwarded to the wrapper <span>.

Accessibility

  • Badge <span> has aria-label (e.g. "5 notifications")
  • When dot, sr-only text "New notifications" is used
  • When count is 0, badge is hidden and aria-label is omitted

Theming

  • Background: --color-error
  • Text: --color-text-inverse
  • Positioned absolute relative to wrapper

ProfilePicture

Avatar component with image, fallback initials, and optional status indicator.

Property Detail
Folder src/components/ProfilePicture/
Extends HTMLAttributes<HTMLDivElement>

Props

Prop Type Default Description
src string — Image URL. Falls back to initials on error or when omitted.
alt string required Accessible alt text for the image.
initials string — 1–2 character fallback when image is unavailable.
size 'sm' | 'md' | 'lg' | 'xl' 'md' Sizes: sm=32px, md=40px, lg=56px, xl=80px.
status 'online' | 'offline' | 'away' | 'busy' — Optional status dot indicator.
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • <img> has the alt prop
  • Initials fallback: <span role="img" aria-label="{alt}">
  • Status indicator: sr-only <span> with status text (e.g. "Status: online")

Theming

  • Fallback background: --color-surface-raised
  • Fallback text: --color-text-secondary
  • Status colors: online=--color-success, away=--color-warning, busy=--color-error, offline=--color-secondary
  • Circle shape: border-radius: var(--radius-full)

Phase 2 — Composite Components

Banner

Full-width informational banner with semantic variants and optional dismiss.

Property Detail
Folder src/components/Banner/
Depends on CloseButton

Props

Prop Type Default Description
variant 'info' | 'warning' | 'error' | 'success' required Semantic style variant.
dismissible boolean false Show a close button.
onDismiss () => void — Called when the dismiss button is clicked.
children ReactNode — Banner content.
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • role="alert" for error variant (assertive, interrupts screen reader)
  • role="status" for info, warning, success (polite)
  • CloseButton: aria-label="Dismiss banner"

Theming

  • Left border accent: --color-info/warning/error/success
  • Background: --color-info-subtle/warning-subtle/error-subtle/success-subtle
  • Icon color matches the variant color

Responsive

  • Stacks icon + text vertically on mobile, inline on @include breakpoint-sm

Dialog

Modal dialog using the native <dialog> element for built-in focus trapping and Escape-to-close.

Property Detail
Folder src/components/Dialog/
Depends on CloseButton, Button

Props

Prop Type Default Description
open boolean required Controls dialog visibility.
onClose () => void required Called on Escape, backdrop click, or close button.
title string required Dialog heading (linked via aria-labelledby).
children ReactNode — Custom dialog body content.
confirmLabel string 'Confirm' Label for the confirm action button.
cancelLabel string 'Cancel' Label for the cancel action button.
onConfirm () => void — Called when confirm button is clicked.
onCancel () => void — Called when cancel button is clicked.
hideActions boolean false Hide the confirm/cancel footer entirely.
ref Ref<HTMLDialogElement> — Forwarded to the <dialog>.

Accessibility

  • Native <dialog> with .showModal() — automatic focus trap, Escape handling, aria-modal="true"
  • aria-labelledby pointing to the title element
  • aria-describedby pointing to the content element
  • Backdrop click calls onClose
  • Focus returns to trigger element on close

Theming

  • ::backdrop uses --color-overlay
  • Surface: --color-surface-raised
  • Shadow: --shadow-xl
  • Z-index: --z-index-dialog

Responsive

  • Full screen on mobile (width: 100vw; height: 100vh)
  • Centered card with max-width on @include breakpoint-sm

Tag

Informational tag/chip with optional color, link, and remove action.

Property Detail
Folder src/components/Tag/
Depends on CloseButton (sm), TextLink (for link variant)

Props

Prop Type Default Description
label string required Tag text.
color string — CSS color for background tint (rendered with low opacity).
href string — Makes the tag a link (renders as <a> via TextLink).
removable boolean false Shows a remove × button.
onRemove () => void — Called when remove button is clicked.
ref Ref<HTMLSpanElement> — Forwarded to the container.

Accessibility

  • Remove button: aria-label="Remove {label}"
  • Link variant: full anchor semantics via TextLink
  • Keyboard: remove button focusable, Enter/Space triggers remove

Theming

  • Default: --color-surface background, --color-border border
  • Custom color prop overrides background with reduced opacity
  • Text: --color-text-primary

Breadcrumb

Navigation breadcrumb trail. Compound component: Breadcrumb + BreadcrumbItem.

Property Detail
Folder src/components/Breadcrumb/
Depends on TextLink

Props — Breadcrumb

Prop Type Default Description
children ReactNode — Expects BreadcrumbItem children.
separator ReactNode '/' Custom separator between items.
ref Ref<HTMLElement> — Forwarded to <nav>.

Props — BreadcrumbItem

Prop Type Default Description
href string — Link URL. Omit for the current (last) page.
children ReactNode — Item label.

Accessibility

  • <nav aria-label="Breadcrumb">
  • <ol> ordered list
  • Last item (no href): aria-current="page"
  • Separators: aria-hidden="true"

Theming

  • Separator + inactive text: --color-text-secondary
  • Active link: --color-primary
  • Current page: --color-text-primary, --font-weight-semibold

Responsive

  • Horizontal scroll with overflow-x: auto on narrow screens
  • Items truncated with @include truncate when constrained

Popover

Floating content panel triggered by click or hover, positioned relative to a trigger element.

Property Detail
Folder src/components/Popover/
Depends on none

Props

Prop Type Default Description
trigger ReactElement required The element that anchors the popover.
content ReactNode required Content rendered inside the popover panel.
triggerOn 'click' | 'hover' 'click' Interaction mode.
placement 'top' | 'bottom' | 'left' | 'right' 'bottom' Preferred position relative to trigger.
open boolean — Controlled open state (makes component controlled).
onOpenChange (open: boolean) => void — Called when open state should change.
ref Ref<HTMLDivElement> — Forwarded to the popover panel.

Accessibility

  • Trigger: aria-expanded, aria-haspopup="dialog", aria-controls="{panelId}"
  • Panel: role="dialog", id="{panelId}"
  • Click mode: close on Escape, close on outside click
  • Hover mode: close on mouse leave (with delay), close on Escape
  • Focus management: first focusable element in panel receives focus on open

Theming

  • Background: --color-surface-raised
  • Border: --color-border
  • Shadow: --shadow-lg
  • Z-index: --z-index-popover

Responsive

  • Position flips when near viewport edge (CSS-based or JS fallback)
  • Consider full-width on very small screens

ProgressIndicator

Progress display in bar or circle form with percentage or step count.

Property Detail
Folder src/components/ProgressIndicator/
Depends on none

Props

Prop Type Default Description
variant 'bar' | 'circle' 'bar' Visual shape.
value number required Current progress (0–100).
max number 100 Maximum value.
displayMode 'percentage' | 'steps' 'percentage' How to show progress text.
currentStep number — Current step (required when displayMode='steps').
totalSteps number — Total steps (required when displayMode='steps').
label string — Accessible label for the progress bar.
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • role="progressbar"
  • aria-valuenow={value}, aria-valuemin={0}, aria-valuemax={max}
  • aria-label for screen readers

Theming

  • Track: --color-surface
  • Fill: --color-primary
  • Circle: SVG with stroke-dasharray animation
  • Text: --color-text-secondary

Phase 3 — Form Components

All form components live under src/components/Form/ and share a common FormField wrapper that connects labels, error messages, and helper text via ARIA.

FormField

Wrapper component that links a label, input, error message, and helper text using ARIA attributes. Provides a useFormField() context hook for child inputs.

Property Detail
Folder src/components/Form/FormField/
Depends on none (consumed by all form inputs)

Props

Prop Type Default Description
label string required Label text.
htmlFor string required ID of the associated input element.
error string — Error message (shown below input in red).
helperText string — Helper/description text (shown below input).
required boolean false Show required indicator (*) after label.
children ReactNode — The input element.
ref Ref<HTMLDivElement> — Forwarded to the container.

Context hook: useFormField()

Returns { inputId, errorId, helperId, hasError } — used by child input components to wire aria-describedby and aria-invalid automatically.

Accessibility

  • <label htmlFor="{inputId}">
  • Error <span id="{errorId}"> — input links via aria-describedby
  • Helper <span id="{helperId}"> — input links via aria-describedby
  • Required: aria-required="true" on input, visible * indicator

Theming

  • Label: --color-text-primary, --font-weight-medium
  • Helper: --color-text-secondary, --font-size-sm
  • Error: --color-error, --font-size-sm
  • Required indicator: --color-error

Input

Text input supporting multiple types with built-in validation patterns (overridable).

Property Detail
Folder src/components/Form/Input/
Depends on FormField

Props

Prop Type Default Description
label string required Input label (passed to FormField).
inputType 'text' | 'email' | 'url' | 'tel' | 'number' | 'password' 'text' Maps to native type.
error string — Error message.
helperText string — Helper text.
ref Ref<HTMLInputElement> — Forwarded to the <input>.
...all native InputHTMLAttributes

Built-in validation (overridable via pattern prop)

inputType Pattern Description
email RFC 5322 simplified Basic email validation
url https?://... Requires http/https prefix
tel [+]?[\d\s\-().]+ Allows digits, spaces, hyphens, parens, optional +

Accessibility

  • Wraps with FormField — all ARIA wiring automatic
  • aria-invalid="true" when error is present
  • aria-describedby linked to error + helper spans
  • aria-required when required

Theming

  • Border: --color-border → --color-border-focus on focus → --color-error on error
  • Background: --color-background
  • Placeholder: --color-text-disabled
  • Full-width by default

Textarea

Multi-line text input with optional character count.

Property Detail
Folder src/components/Form/Textarea/
Depends on FormField

Props

Prop Type Default Description
label string required Textarea label.
error string — Error message.
helperText string — Helper text.
resize 'none' | 'vertical' | 'both' 'vertical' CSS resize behavior.
ref Ref<HTMLTextAreaElement> — Forwarded to the <textarea>.
...all native TextareaHTMLAttributes

Accessibility

  • Same ARIA wiring as Input via FormField
  • When maxLength is set, display character count (e.g. "45/200")

Select

Native dropdown select with options array.

Property Detail
Folder src/components/Form/Select/
Depends on FormField

Props

Prop Type Default Description
label string required Select label.
options Array<{ value: string; label: string; disabled?: boolean }> required Dropdown options.
placeholder string — Disabled first option text (e.g. "Select an option…").
error string — Error message.
helperText string — Helper text.
ref Ref<HTMLSelectElement> — Forwarded to the <select>.
...all native SelectHTMLAttributes

Accessibility

  • Native <select> — full keyboard support built-in
  • Placeholder rendered as <option disabled selected hidden>

Checkbox

Single checkbox with custom styling and indeterminate support.

Property Detail
Folder src/components/Form/Checkbox/
Depends on FormField

Props

Prop Type Default Description
label string required Checkbox label.
error string — Error message.
indeterminate boolean false Indeterminate state (e.g. "select all" partially checked).
ref Ref<HTMLInputElement> — Forwarded to the <input>.
...all native InputHTMLAttributes

Accessibility

  • Native <input type="checkbox"> with aria-checked (including "mixed" for indeterminate)
  • aria-invalid when error
  • Label via <label> wrapping the input
  • Toggle via Space key (native)

Theming

  • Custom styled via appearance: none + ::before pseudo-element
  • Checked: --color-primary background with white checkmark
  • Indeterminate: --color-primary background with – dash

RadioGroup

Group of radio buttons rendered as a <fieldset> with <legend>.

Property Detail
Folder src/components/Form/RadioGroup/
Depends on FormField concepts

Props

Prop Type Default Description
legend string required Group label rendered as <legend>.
name string required Shared name attribute for all radios.
options Array<{ value: string; label: string; disabled?: boolean }> required Radio options.
value string — Controlled selected value.
onChange (value: string) => void — Called when selection changes.
error string — Error message.
orientation 'vertical' | 'horizontal' 'vertical' Layout direction.
ref Ref<HTMLFieldSetElement> — Forwarded to the <fieldset>.

Accessibility

  • <fieldset> + <legend> (native group labeling)
  • Native <input type="radio"> — arrow key navigation built-in
  • aria-invalid on fieldset when error
  • Error linked via aria-describedby

Toggle

Toggle/switch input with on/off labels.

Property Detail
Folder src/components/Form/Toggle/
Depends on none

Props

Prop Type Default Description
label string required Accessible label.
onText string — Text shown when on (optional).
offText string — Text shown when off (optional).
ref Ref<HTMLInputElement> — Forwarded to the <input>.
...all native InputHTMLAttributes (except type)

Accessibility

  • role="switch" on the <input>
  • aria-checked reflects state
  • <label> association
  • Toggle via Space or Enter key

Theming

  • Track off: --color-secondary
  • Track on: --color-primary
  • Thumb: --color-text-inverse
  • Transition: --transition-fast
  • Min touch target: 44×24px

Phase 4 — Navigation & Notifications

MenuItem

Single navigation item for use in menus. Renders as a link or button depending on props.

Property Detail
Folder src/components/MenuItem/
Depends on none

Props

Prop Type Default Description
href string — Renders as <a>. Omit for <button>.
icon ReactNode — Leading icon.
active boolean false Current page state.
disabled boolean false Disabled state.
children ReactNode — Item label.
ref Ref<HTMLLIElement> — Forwarded to the <li>.

Accessibility

  • role="menuitem"
  • Active: aria-current="page"
  • Disabled: aria-disabled="true"
  • Keyboard focus management via tabIndex

Theming

  • Active: --color-primary text, --color-surface background
  • Hover: --color-surface background
  • Disabled: --color-text-disabled, cursor: not-allowed

MenuItemGroup

Collapsible group of MenuItems with a label/header.

Property Detail
Folder src/components/MenuItemGroup/
Depends on MenuItem

Props

Prop Type Default Description
label string required Group label (rendered as a button).
defaultOpen boolean true Initial expanded state.
collapsible boolean true Whether the group can be collapsed.
children ReactNode — MenuItem children.
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • Trigger <button>: aria-expanded, toggles on click/Enter/Space
  • Content region: role="group", aria-labelledby="{triggerId}"
  • Collapse/expand animated with max-height transition

Theming

  • Label: --font-weight-semibold, --color-text-secondary
  • Separator: --color-border

SideMenu

Sidebar navigation composed of MenuItems and MenuItemGroups.

Property Detail
Folder src/components/SideMenu/
Depends on MenuItem, MenuItemGroup

Props

Prop Type Default Description
children ReactNode — MenuItem and MenuItemGroup children.
ariaLabel string 'Side navigation' Accessible label for the <nav>.
ref Ref<HTMLElement> — Forwarded to <nav>.

Accessibility

  • <nav aria-label="{ariaLabel}">
  • Inner <ul role="menu">

Theming

  • Background: --color-surface
  • Border-right: --color-border
  • Width: fixed 260px on desktop

Responsive

  • Hidden below @include breakpoint-lg — replaced by HamburgerMenu

HamburgerMenu

Mobile hamburger toggle button + slide-in menu panel with focus trapping.

Property Detail
Folder src/components/HamburgerMenu/
Depends on CloseButton, typically wraps SideMenu as children

Props

Prop Type Default Description
open boolean — Controlled open state.
onOpenChange (open: boolean) => void — Called when open state should change.
children ReactNode — Menu content (typically SideMenu).
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • Toggle <button>: aria-expanded, aria-label="Open menu" / "Close menu"
  • Panel: role="dialog", aria-modal="true"
  • Custom focus trap (no external dependency)
  • Close on Escape
  • Close on overlay click

Theming

  • Overlay: --color-overlay
  • Panel background: --color-surface
  • Slide-in: transform: translateX(-100%) → translateX(0)
  • Animation disabled via @include reduced-motion

Responsive

  • Only visible below @include breakpoint-lg
  • Hidden on desktop (SideMenu shown directly)

NotificationPopup

Toast notification card with pop-in animation, auto-dismiss, and close button.

Property Detail
Folder src/components/NotificationPopup/
Depends on CloseButton

Props

Prop Type Default Description
variant 'info' | 'success' | 'warning' | 'error' 'info' Semantic variant (matches Banner).
title string — Optional notification heading.
children ReactNode — Notification body content.
visible boolean required Controls visibility.
autoDismiss boolean true Auto-dismiss after timeout.
autoDismissMs number 5000 Milliseconds before auto-dismiss.
onDismiss () => void — Called when dismissed (auto or manual).
ref Ref<HTMLDivElement> — Forwarded to the container.

Accessibility

  • role="alert", aria-live="assertive"
  • CloseButton: aria-label="Dismiss notification"
  • Auto-dismiss pauses on hover/focus (gives user time to read)
  • Animation disabled via @include reduced-motion

Theming

  • Background: --color-surface-raised
  • Shadow: --shadow-lg
  • Left accent color: --color-info/success/warning/error (matches variant)
  • Z-index: --z-index-notification
  • Pop-in: @keyframes slideIn from right
  • Fade-out: opacity transition

Responsive

  • Fixed bottom-right on desktop
  • Full-width top on mobile

Phase 5 — Documentation

ThemeColors

Storybook-only documentation page that displays all design tokens from _tokens.scss as visual swatches. Not exported from the public API.

Property Detail
Folder src/components/ThemeColors/
Files ThemeColors.stories.tsx + ThemeColors.module.scss only (no .tsx component)
Exported ❌ Not included in src/index.ts

Tables

A component that renders a table.

  • Table can have header, body, footer sections
  • Table can be sortable
  • Table can be paginated
  • Table can be searchable
  • Table can be filtered
  • Columns can be resizable
  • Columns can be colored (Odd or even columns)
  • Rows can be colored (Odd or even rows)
  • Table can have a color

Story structure

  • title: 'Documentation/Theme Colors'
  • Groups: Primary, Secondary, Semantic (success/warning/error/info), Surface, Text, Border
  • Each swatch shows: color sample box, CSS variable name, computed hex value
  • Renders in both light and dark themes (side by side or via Storybook theme toggle)
  • Uses getComputedStyle(document.documentElement).getPropertyValue('--color-*') to read live values

Dependency Graph

CloseButton ─────┬── Banner
                  ├── Dialog (+ Button)
                  ├── Tag (+ TextLink)
                  ├── HamburgerMenu (+ SideMenu)
                  └── NotificationPopup

TextLink ─────────┬── Tag
                  └── Breadcrumb

FormField ────────┬── Input
                  ├── Textarea
                  ├── Select
                  └── Checkbox

MenuItem ─────────┬── MenuItemGroup
                  └── SideMenu ──── HamburgerMenu

(standalone)       Spinner, SkeletonLoader, NotificationBadge,
                   ProfilePicture, Popover, ProgressIndicator,
                   RadioGroup, Toggle, ThemeColors

Barrel Export Plan (src/index.ts)

After all components are built, the root barrel file exports everything:

// Existing
export { Button } from './components/Button';
export { CloseButton } from './components/CloseButton';

// Phase 1
export { TextLink } from './components/TextLink';
export { Spinner } from './components/Spinner';
export { SkeletonLoader } from './components/SkeletonLoader';
export { NotificationBadge } from './components/NotificationBadge';
export { ProfilePicture } from './components/ProfilePicture';

// Phase 2
export { Banner } from './components/Banner';
export { Dialog } from './components/Dialog';
export { Tag } from './components/Tag';
export { Breadcrumb, BreadcrumbItem } from './components/Breadcrumb';
export { Popover } from './components/Popover';
export { ProgressIndicator } from './components/ProgressIndicator';

// Phase 3 — Form
export {
  FormField,
  Input,
  Textarea,
  Select,
  Checkbox,
  RadioGroup,
  Toggle,
} from './components/Form';

// Phase 4
export { MenuItem } from './components/MenuItem';
export { MenuItemGroup } from './components/MenuItemGroup';
export { SideMenu } from './components/SideMenu';
export { HamburgerMenu } from './components/HamburgerMenu';
export { NotificationPopup } from './components/NotificationPopup';

// + all type exports

ThemeColors is NOT exported — it only exists as a Storybook documentation page.