Design System
Token Catalog
Complete reference for every design token, component variant, and visual foundation in the aiChemist platform.
Color Palette
All color tokens organized by category. Each swatch shows the CSS custom property name and resolved hex value.
Backgrounds
dark
#0a0a14
card
#13131f
elevated
#1e1e2e
input
#0f0f1a
Primary
base
#a78bfa
hover
#8b5cf6
active
#7c3aed
muted
#6366f1
Accent
gold
#f59e0b
goldHover
#d97706
CTA
green
#22c55e
greenHover
#16a34a
greenActive
#15803d
Text
primary
#ffffff
secondary
#a1a1aa
muted
#6b7280
accent
#a78bfa
Status
success
#22c55e
warning
#f59e0b
error
#ef4444
info
#3b82f6
Border
default
#3f3f5a
focus
#a78bfa
subtle
rgba(167,139,250,0.2)
Typography Scale
Font sizes, weights, and families from the token pipeline.
Font Sizes
Font Weights
Font Families
Spacing Scale
Visual representation of every spacing token. Bar width corresponds to the token value.
Interactive Playground
Explore all 66 shared components with live prop controls. Adjust variants, sizes, counts, and content to see how each component responds in real time.
Button
Component Metadata
ButtonProps (variant: primary|secondary, size: sm|md|lg, children, ...ButtonHTMLAttributes)- Minimum 4.5:1 contrast ratio
- Visible focus indicator
- Keyboard activatable
- Full-width on mobile (< 640px)
- Inline on desktop
- Hover scale transition 150ms ease
components.button.primary.backgroundcomponents.button.primary.backgroundHovercomponents.button.primary.textcomponents.button.primary.borderRadiuscomponents.button.primary.paddingXcomponents.button.primary.paddingYcomponents.button.secondary.backgroundcomponents.button.secondary.bordercomponents.button.secondary.text
- Button label from CMS text field
- Link target from CMS URL field
- Renders primary variant
- Renders secondary variant
- Handles click events
- Disabled state blocks interaction
Card
Card Preview
This is a card body. Cards render token-driven borders, padding, and background.
Component Metadata
CardProps (title?: string, children, ...HTMLAttributes<HTMLDivElement>)- Semantic heading level in title
- Sufficient contrast for card content
- Full-width on mobile
- Grid-based on desktop
components.card.backgroundcomponents.card.borderRadiuscomponents.card.paddingcomponents.card.shadow
- Title from CMS string field
- Body from CMS portableText
- Renders with title
- Renders without title
- Passes className through
Tabs
The overview tab content with component descriptions.
Component Metadata
TabsProps (tabs: TabItem[], defaultIndex?: number, className?: string)- role=tablist on container
- role=tab on triggers
- role=tabpanel on content
- aria-selected state
- Horizontal tabs on desktop
- Scrollable on mobile
components.tabs.borderColorcomponents.tabs.triggerColorcomponents.tabs.triggerHoverColorcomponents.tabs.triggerActiveColorcomponents.tabs.triggerActiveBordercomponents.tabs.panelColorcomponents.tabs.paddingXcomponents.tabs.paddingY
- Tab labels from CMS string array
- Tab content from CMS portableText array
- Renders all tabs
- Switches active tab on click
- Defaults to specified index
Alert
Component Metadata
AlertProps (variant: AlertVariant, children, dismissible?: boolean, onDismiss?: () => void)- role=alert for screen reader announcement
- Dismiss button has aria-label
- Full-width on all breakpoints
components.alert.borderRadiuscomponents.alert.paddingcomponents.alert.paddingXcomponents.alert.success.backgroundcomponents.alert.success.bordercomponents.alert.success.textcomponents.alert.warning.backgroundcomponents.alert.warning.bordercomponents.alert.warning.textcomponents.alert.error.backgroundcomponents.alert.error.bordercomponents.alert.error.textcomponents.alert.info.backgroundcomponents.alert.info.bordercomponents.alert.info.text
- Alert message from CMS text field
- Variant from CMS select field
- Renders each variant with correct icon
- Dismissible hides on click
- Calls onDismiss callback
Pricing Table
Starter
- ✓1 design system
- ✓50 tokens
- ✓2 surfaces
Pro
- ✓Unlimited systems
- ✓Unlimited tokens
- ✓ADW pipeline
- ✓Priority support
Enterprise
- ✓Everything in Pro
- ✓SSO
- ✓SLA
- ✓Dedicated account
Component Metadata
PricingTableProps (tiers: PricingTier[], onTierSelect?: (tier) => void)- Semantic list for features
- Button for CTA (not link)
- Sufficient contrast on featured badge
- Single column on mobile
- Multi-column grid on desktop
- Hover elevation on pricing cards
components.pricingCard.backgroundcomponents.pricingCard.backgroundFeaturedcomponents.pricingCard.borderRadiuscomponents.pricingCard.paddingcomponents.pricingCard.bordercomponents.pricingCard.borderFeaturedcomponents.pricingCard.priceColorcomponents.pricingCard.labelColorcomponents.pricingCard.featureColorcomponents.pricingCard.checkColorcomponents.pricingCard.shadowcomponents.pricingCard.shadowFeatured
- Tier data from CMS JSON field
- Feature lists from CMS text arrays
- Renders all tiers
- Featured tier has badge
- onTierSelect fires on CTA click
Testimonial Wall
Jane Smith
CTO, Example Corp
“This platform transformed our design workflow.”
John Doe
Design Lead, Agency X
“Token-driven components saved us weeks of work.”
Component Metadata
TestimonialWallProps (testimonials: Testimonial[])- Star rating has aria-label
- Blockquote for quotes
- Avatar alt text
- Single column on mobile
- 2-3 column grid on desktop
- Hover background transition on cards
components.testimonial.backgroundcomponents.testimonial.backgroundHovercomponents.testimonial.borderRadiuscomponents.testimonial.paddingcomponents.testimonial.bordercomponents.testimonial.avatarBordercomponents.testimonial.avatarSizecomponents.testimonial.quoteColorcomponents.testimonial.nameColorcomponents.testimonial.roleColorcomponents.testimonial.starColorcomponents.testimonial.starEmpty
- Testimonials from CMS collection with name/role/quote/rating fields
- Renders all testimonials
- Avatar placeholder when no URL
- Star rating reflects count
Metrics Panel
Revenue
$48K
Users
2.8K
Churn
3.2%
Component Metadata
MetricsPanelProps (metrics: MetricItem[], columns?: 2|3|4)- Trend arrows use aria-hidden
- Status conveyed by color and text
- 2 columns on mobile
- configurable columns (2-4) on desktop
components.metricsPanel.backgroundcomponents.metricsPanel.borderRadiuscomponents.metricsPanel.paddingcomponents.metricsPanel.bordercomponents.metricsPanel.valueColorcomponents.metricsPanel.labelColorcomponents.metricsPanel.trendUpcomponents.metricsPanel.trendDowncomponents.metricsPanel.trendFlatcomponents.metricsPanel.statusGoodcomponents.metricsPanel.statusWarningcomponents.metricsPanel.statusCritical
- Metrics from CMS JSON field or API endpoint
- Renders all metrics
- Correct trend arrow direction
- Status class applied
Author Bio Card
Component Metadata
AuthorBioCardProps (author: AuthorProfile, layout?: vertical|horizontal)- Social links have title attribute
- Avatar has alt text
- External links have rel=noopener
- Vertical layout on mobile
- Horizontal layout option on desktop
- Social link hover color transition
components.authorBio.backgroundcomponents.authorBio.borderRadiuscomponents.authorBio.paddingcomponents.authorBio.bordercomponents.authorBio.avatarSizecomponents.authorBio.avatarBordercomponents.authorBio.nameColorcomponents.authorBio.roleColorcomponents.authorBio.bioColorcomponents.authorBio.socialColorcomponents.authorBio.socialHovercomponents.authorBio.badgeBackgroundcomponents.authorBio.badgeText
- Author data from EmDash authors collection
- Renders vertical layout
- Renders horizontal layout
- Shows article count badge
- Social links open in new tab
Feature Comparison Table
| Feature | Basic | Pro |
|---|---|---|
| Tokens | ◐ | ✓ |
| Components | ✓ | ✓ |
| ADW Pipeline | ✗ | ✓ |
Component Metadata
FeatureComparisonTableProps (columns: ComparisonColumn[], features: ComparisonFeature[])- role=table on table element
- scope=col on header cells
- aria-label on support icons
- Scrollable region has tabIndex and aria-label
- Horizontally scrollable on mobile
- Full table on desktop
components.comparisonTable.backgroundcomponents.comparisonTable.headerBackgroundcomponents.comparisonTable.borderRadiuscomponents.comparisonTable.bordercomponents.comparisonTable.rowStripecomponents.comparisonTable.headerTextcomponents.comparisonTable.cellTextcomponents.comparisonTable.checkColorcomponents.comparisonTable.crossColorcomponents.comparisonTable.partialColorcomponents.comparisonTable.featureLabelColorcomponents.comparisonTable.tooltipBackgroundcomponents.comparisonTable.tooltipText
- Feature data from CMS JSON field
- Column definitions from CMS collection
- Renders all columns and rows
- Highlighted column has class
- Tooltip displays on hover
- Alternating row stripes
Timeline
Production deploy
All routes 200, PM2 restarted
SuccessDesign approved
WarningDocs published
Architecture docs + 6 ADRs
Component Metadata
TimelineProps (events: TimelineEvent[], filters?: EventType[], className?: string)- role=feed on container
- aria-posinset/aria-setsize on events
- aria-pressed on filter buttons
- Keyboard navigable filters
- Full-width on all breakpoints
- Stacked layout
- Filter transition 150ms ease
components.timeline.lineColorcomponents.timeline.eventBackgroundcomponents.timeline.eventBordercomponents.timeline.eventBorderRadiuscomponents.timeline.eventPaddingcomponents.timeline.iconBackgroundcomponents.timeline.iconColorcomponents.timeline.titleColorcomponents.timeline.descriptionColorcomponents.timeline.timestampColorcomponents.timeline.badgeSuccessBgcomponents.timeline.badgeErrorBgcomponents.timeline.badgeWarningBgcomponents.timeline.badgePendingBgcomponents.timeline.badgeTextcomponents.timeline.filterActiveBgcomponents.timeline.filterActiveText
- Event data from CMS collection
- Filter types from CMS config
- Renders all events
- Filters by event type
- Shows status badges
- Empty state message
Form Wizard
Component Metadata
FormWizardProps (steps: WizardStep[], onComplete?: () => void, className?: string)- aria-current=step on active step circle
- aria-live=polite on content region
- aria-labelledby linking content to step indicator
- Disabled back button on first step
- Full-width on all breakpoints
- Step labels hidden on mobile if needed
- Step circle transition 150ms ease
- Button hover transition 150ms ease
components.formWizard.backgroundcomponents.formWizard.borderRadiuscomponents.formWizard.paddingcomponents.formWizard.stepActiveBackgroundcomponents.formWizard.stepActiveBordercomponents.formWizard.stepActiveTextcomponents.formWizard.stepCompletedBackgroundcomponents.formWizard.stepCompletedTextcomponents.formWizard.stepInactiveBackgroundcomponents.formWizard.stepInactiveBordercomponents.formWizard.stepInactiveTextcomponents.formWizard.stepConnectorActivecomponents.formWizard.stepConnectorInactivecomponents.formWizard.stepLabelActivecomponents.formWizard.stepLabelInactivecomponents.formWizard.fieldBackgroundcomponents.formWizard.fieldBordercomponents.formWizard.fieldBorderFocuscomponents.formWizard.fieldBorderErrorcomponents.formWizard.fieldTextcomponents.formWizard.fieldPlaceholdercomponents.formWizard.labelColorcomponents.formWizard.errorTextcomponents.formWizard.checkboxCheckedBgcomponents.formWizard.checkboxBordercomponents.formWizard.navCounterColor
- Step definitions from CMS collection
- Form schema from CMS config
- Renders all step indicators
- Navigates forward
- Navigates backward
- Calls onComplete on last step
- Back disabled on first step
Score Gauge
Score
Component Metadata
ScoreGaugeProps (score: number, label: string, size?: sm|md|lg, status?: default|good|warning|critical, trend?: up|down|flat, trendValue?: string)- SVG ring is aria-hidden
- Score value is visible text
- Trend arrows use aria-hidden with text fallback
- Inline-flex, scales with container
- Size prop controls diameter (80/120/200)
- stroke-dasharray transition 400ms ease
components.scoreGauge.trackColorcomponents.scoreGauge.fillDefaultcomponents.scoreGauge.fillGoodcomponents.scoreGauge.fillWarningcomponents.scoreGauge.fillCriticalcomponents.scoreGauge.scoreColorcomponents.scoreGauge.labelColorcomponents.scoreGauge.trendUpColorcomponents.scoreGauge.trendDownColorcomponents.scoreGauge.trendFlatColorcomponents.scoreGauge.backgroundcomponents.scoreGauge.borderRadiuscomponents.scoreGauge.strokeWidthcomponents.scoreGauge.glowOpacity
- Score from CMS number field
- Label from CMS string field
- Renders score and label
- Clamps score to 0-100
- Applies size class
- Applies status class
- Shows trend indicator
Radar Chart
Behavioral Profile
Component Metadata
RadarChartProps (dimensions: RadarDimension[], title?: string, overallScore?: number, size?: number)- SVG is aria-hidden
- Title is visible text heading
- Score values in labels are visible text
- max-width 320px SVG, scales with container
- Labels adjust text-anchor by position
- Fill opacity transition 300ms ease
components.radarChart.backgroundcomponents.radarChart.borderRadiuscomponents.radarChart.gridColorcomponents.radarChart.gridStrokeWidthcomponents.radarChart.axisColorcomponents.radarChart.fillColorcomponents.radarChart.fillOpacitycomponents.radarChart.strokeColorcomponents.radarChart.strokeWidthcomponents.radarChart.dotColorcomponents.radarChart.dotRadiuscomponents.radarChart.labelColorcomponents.radarChart.valueColorcomponents.radarChart.titleColorcomponents.radarChart.scoreColorcomponents.radarChart.scoreBgColor
- Dimension data from CMS JSON field
- Title from CMS string field
- Renders all dimension labels
- Renders title when provided
- Renders overall score in center
- Clamps values to 0-100
- Passes className through
Progress Bar
Component Metadata
ProgressBarProps (value?: number, segments?: ProgressBarSegment[], status?: default|success|warning|error, size?: sm|md|lg, showLabel?: boolean, striped?: boolean, animated?: boolean, indeterminate?: boolean, label?: string)- role=progressbar
- aria-valuenow for determinate
- aria-valuemin and aria-valuemax
- aria-label for context
- Full-width on all breakpoints
- Height scales with size prop
- Fill width transition 0.4s ease
- Stripe animation 1.5s linear infinite
- Indeterminate bounce 1.5s ease-in-out infinite
components.progressBar.trackBackgroundcomponents.progressBar.trackBorderRadiuscomponents.progressBar.fillDefaultcomponents.progressBar.fillSuccesscomponents.progressBar.fillWarningcomponents.progressBar.fillErrorcomponents.progressBar.fillBorderRadiuscomponents.progressBar.labelColorcomponents.progressBar.labelFontSizecomponents.progressBar.heightSmcomponents.progressBar.heightMdcomponents.progressBar.heightLgcomponents.progressBar.segmentGapcomponents.progressBar.glowOpacitycomponents.progressBar.stripeAnglecomponents.progressBar.animationDuration
- Value from CMS number field
- Segments from CMS JSON
- Renders with correct width for value
- Clamps value to 0-100
- Applies status class
- Applies size class
- Shows label in lg size
- Renders striped pattern class
- Renders indeterminate animation
- Renders segments
Pagination
Component Metadata
PaginationProps (totalPages: number, currentPage?: number, onPageChange?: (page) => void, maxVisible?: number, showCounter?: boolean)- nav element with aria-label=Pagination
- aria-current=page on active button
- aria-label on each page button
- Disabled buttons have disabled attribute
- Centered on all breakpoints
- Compact layout scales with button count
- Background/color transition 150ms ease on hover
components.pagination.backgroundcomponents.pagination.borderRadiuscomponents.pagination.buttonBackgroundcomponents.pagination.buttonBackgroundHovercomponents.pagination.buttonBackgroundActivecomponents.pagination.buttonTextcomponents.pagination.buttonTextHovercomponents.pagination.buttonTextActivecomponents.pagination.buttonBorderRadiuscomponents.pagination.buttonSizecomponents.pagination.disabledColorcomponents.pagination.ellipsisColorcomponents.pagination.counterColorcomponents.pagination.navColorcomponents.pagination.navColorHover
- Total pages from CMS collection count
- Current page from URL parameter
- Renders all page buttons
- Highlights current page
- Prev disabled on first page
- Next disabled on last page
- Shows ellipsis for long ranges
- Fires onPageChange callback
Reading Time
Component Metadata
ReadingTimeProps (wordCount: number, wordsPerMinute?: number, showWordCount?: boolean, progress?: number)- Progress bar has role=progressbar with aria-valuenow/min/max
- Clock icon is aria-hidden
- Inline-flex, scales with container
- Progress bar fill transition 200ms ease
components.readingTime.backgroundcomponents.readingTime.borderRadiuscomponents.readingTime.textColorcomponents.readingTime.iconColorcomponents.readingTime.separatorColorcomponents.readingTime.progressTrackcomponents.readingTime.progressFillcomponents.readingTime.progressHeight
- Word count from CMS text field length calculation
- Progress from scroll position
- Calculates reading time from word count
- Shows word count when enabled
- Hides word count when disabled
- Renders progress bar when progress provided
- Passes className through
AI Avatar Tooltip
Component Metadata
AiAvatarTooltipProps (name?: string, greeting?: string, status?: AiStatus, messages?: AiMessage[], open?: boolean, onOpenChange?: (open: boolean) => void, placeholder?: string, onSend?: (message: string) => void, badge?: string, position?: 'bottom-right' | 'bottom-left')- Chat bubble has role=dialog with aria-label
- Trigger has aria-expanded
- Input has aria-label
- Close button has aria-label
- Send button has aria-label
- Typing indicator has aria-label
- Bubble width 320px on desktop, scales to fit mobile viewports
- Avatar glow pulse 3s ease-in-out (idle), 1.2s (typing)
- Bubble enter animation 250ms ease-out
- Typing dots bounce 1.4s staggered
components.aiAvatarTooltip.avatarSizecomponents.aiAvatarTooltip.avatarBackgroundcomponents.aiAvatarTooltip.avatarGlowColorcomponents.aiAvatarTooltip.avatarGlowActiveColorcomponents.aiAvatarTooltip.avatarIconColorcomponents.aiAvatarTooltip.bubbleBackgroundcomponents.aiAvatarTooltip.bubbleBordercomponents.aiAvatarTooltip.bubbleShadowcomponents.aiAvatarTooltip.bubbleBorderRadiuscomponents.aiAvatarTooltip.headerTextColorcomponents.aiAvatarTooltip.bodyTextColorcomponents.aiAvatarTooltip.inputBackgroundcomponents.aiAvatarTooltip.inputBorderColorcomponents.aiAvatarTooltip.inputFocusBorderColorcomponents.aiAvatarTooltip.inputTextColorcomponents.aiAvatarTooltip.inputPlaceholderColorcomponents.aiAvatarTooltip.typingDotColorcomponents.aiAvatarTooltip.goldAccentcomponents.aiAvatarTooltip.closeButtonColorcomponents.aiAvatarTooltip.closeButtonHoverColor
- AI persona from CMS config
- Greeting from CMS localization
- Renders trigger button with aria-expanded=false
- Opens bubble on trigger click
- Displays name and greeting in bubble
- Shows typing indicator when status=typing
- Renders messages
- Calls onSend on Enter key
- Shows gold badge when provided
- Passes className through
Live Readouts
Component Metadata
LiveReadoutsProps (metrics: ReadoutMetric[], title?: string, columns?: 2|3|4)- Region has role=region with aria-label
- Status dot has aria-label
- Trend arrow has aria-label
- Sparkline is aria-hidden
- Auto-fit grid (min 160px)
- Optional fixed 2/3/4 columns
- Active card pulse 2s ease-in-out infinite
- Critical status blink 1s
- Card box-shadow transition 300ms
components.liveReadouts.cardBackgroundcomponents.liveReadouts.cardBorderRadiuscomponents.liveReadouts.cardBordercomponents.liveReadouts.valueColorcomponents.liveReadouts.valueFontFamilycomponents.liveReadouts.labelColorcomponents.liveReadouts.unitColorcomponents.liveReadouts.trendUpColorcomponents.liveReadouts.trendDownColorcomponents.liveReadouts.trendFlatColorcomponents.liveReadouts.statusNormalcomponents.liveReadouts.statusWarningcomponents.liveReadouts.statusCriticalcomponents.liveReadouts.sparklineColorcomponents.liveReadouts.pulseGlowColor
- Metrics from CMS biometric data
- Thresholds from CMS config
- Renders metric cards with labels
- Displays value and unit
- Shows trend arrow with correct direction
- Renders status dot with correct class
- Shows sparkline when data provided
- Applies active pulse class
- Renders title when provided
- Passes className through
Wellness Matrix
Component Metadata
WellnessMatrixProps (compositeScore?: number, categories: WellnessCategory[], columns?: 2|3)- Status text distinguishable without color alone
- Score ring decorative (aria-hidden)
- Semantic heading for hero label if used in page context
- 3-column on desktop
- 2-column on tablet
- 1-column on mobile
- Critical card pulse 2s ease-in-out
- Bar fill transition 0.4s ease
- Ring stroke transition 0.6s ease
components.wellnessMatrix.heroBackgroundcomponents.wellnessMatrix.heroScoreColorcomponents.wellnessMatrix.heroLabelColorcomponents.wellnessMatrix.heroRingTrackcomponents.wellnessMatrix.heroRingFillcomponents.wellnessMatrix.cardBackgroundcomponents.wellnessMatrix.cardBorderRadiuscomponents.wellnessMatrix.cardBordercomponents.wellnessMatrix.categoryIconColorcomponents.wellnessMatrix.scoreColorcomponents.wellnessMatrix.labelColorcomponents.wellnessMatrix.sparklineColorcomponents.wellnessMatrix.barFillColorcomponents.wellnessMatrix.barTrackColorcomponents.wellnessMatrix.statusOptimalcomponents.wellnessMatrix.statusAttentioncomponents.wellnessMatrix.statusCriticalcomponents.wellnessMatrix.glowShadow
- Category names from CMS text fields
- Scores from CMS number fields
- Renders hero composite score with ring
- Hides hero when compositeScore is undefined
- Renders all category cards
- Shows status badge with correct class
- Renders sparkline when data provided
- Renders subcategory mini bars
- Applies critical pulse class
- Passes className through
Toast / Snackbar
Operation succeeded
Detailed notification description.
Component Metadata
ToastProps (toast: ToastItem, onDismiss?: (id) => void) + ToastContainerProps (toasts: ToastItem[], onDismiss?, position?: top-right|top-left|bottom-right|bottom-left)- role=alert for error variant
- role=status for non-error variants
- aria-live=assertive for errors, polite for others
- Close button has aria-label
- Container has aria-label
- Max-width 400px
- Full-width on small viewports
- Stacks vertically
- Enter animation 250ms ease-out
- Progress bar linear shrink
components.toast.backgroundcomponents.toast.borderRadiuscomponents.toast.paddingcomponents.toast.shadowcomponents.toast.textColorcomponents.toast.descriptionColorcomponents.toast.closeColorcomponents.toast.closeHoverColorcomponents.toast.iconSuccesscomponents.toast.iconWarningcomponents.toast.iconErrorcomponents.toast.iconInfocomponents.toast.progressTrackcomponents.toast.progressFillcomponents.toast.maxWidthcomponents.toast.gap
- Notification content from CMS event system
- Variant from CMS config
- Renders title
- Renders description when provided
- Applies variant class
- Shows variant icon
- Shows close button when onDismiss provided
- Calls onDismiss with toast id
- Shows progress bar when duration set
- Container renders multiple toasts
- Uses role=alert for error variant
Breadcrumbs
Component Metadata
BreadcrumbsProps (items: BreadcrumbItem[], separator?: string, showHome?: boolean, truncate?: boolean, maxVisible?: number)- nav element with aria-label=Breadcrumb
- aria-current=page on last item
- Separators are aria-hidden
- Home icon link has aria-label
- Wraps on narrow viewports
- Truncation for long labels
- Link hover color transition 150ms ease
components.breadcrumbs.backgroundcomponents.breadcrumbs.separatorColorcomponents.breadcrumbs.linkColorcomponents.breadcrumbs.linkHoverColorcomponents.breadcrumbs.currentColorcomponents.breadcrumbs.fontSizecomponents.breadcrumbs.gapcomponents.breadcrumbs.paddingcomponents.breadcrumbs.truncateMaxWidthcomponents.breadcrumbs.homeIconColorcomponents.breadcrumbs.homeIconHoverColorcomponents.breadcrumbs.ellipsisColor
- Path from CMS navigation tree
- Labels from CMS page titles
- Renders all breadcrumb items
- Last item has aria-current=page
- Shows home icon by default
- Hides home icon when showHome=false
- Shows ellipsis when maxVisible set
- Has nav with aria-label
- Custom separator renders
- Passes className through
Badge / Tag
Component Metadata
BadgeProps (children: ReactNode, variant?: default|success|warning|error|info, size?: sm|md, dot?: boolean, removable?: boolean, onRemove?: () => void)- Remove button has aria-label
- Dot indicator is aria-hidden
- Text content conveyed by label
- Inline-flex, wraps naturally in flex containers
- Remove button hover color transition 150ms
components.badge.fontSizecomponents.badge.fontWeightcomponents.badge.paddingXcomponents.badge.paddingYcomponents.badge.borderRadiuscomponents.badge.defaultBackgroundcomponents.badge.defaultTextcomponents.badge.successBackgroundcomponents.badge.successTextcomponents.badge.warningBackgroundcomponents.badge.warningTextcomponents.badge.errorBackgroundcomponents.badge.errorTextcomponents.badge.infoBackgroundcomponents.badge.infoTextcomponents.badge.dotSizecomponents.badge.removeColorcomponents.badge.removeHoverColor
- Label from CMS string field
- Variant from CMS select field
- Renders children
- Applies variant class
- Applies size class
- Shows dot when dot=true
- Shows remove button when removable
- Calls onRemove on click
- Hides remove button when not removable
- Passes className through
Skeleton
Component Metadata
SkeletonProps (variant?: text|circle|rectangle, width?: string|number, height?: string|number, lines?: number)- All skeleton elements are aria-hidden=true
- Actual loading content should use aria-busy on parent
- Width fills container by default
- Circle maintains aspect ratio
- Pulse animation 1.5s ease-in-out infinite
components.skeleton.baseColorcomponents.skeleton.highlightColorcomponents.skeleton.borderRadiuscomponents.skeleton.animationDurationcomponents.skeleton.textHeightcomponents.skeleton.textSpacingcomponents.skeleton.circleSizecomponents.skeleton.opacity
- Used as placeholder while CMS content loads
- Renders text variant by default
- Renders circle variant
- Renders rectangle variant
- Renders multiple lines
- Last line is shorter in multi-line
- Has aria-hidden
- Applies custom width
- Passes className through
Accordion
Component Metadata
AccordionProps (items: AccordionItem[], multiple?: boolean, defaultOpen?: string[])- Button trigger with aria-expanded
- aria-controls linking trigger to panel
- Panel has role=region with aria-labelledby
- hidden attribute when collapsed
- Full-width on all breakpoints
- Touch-friendly trigger target
- Chevron rotate 180deg transition
- Background hover transition
components.accordion.backgroundcomponents.accordion.borderRadiuscomponents.accordion.bordercomponents.accordion.headerPaddingcomponents.accordion.headerColorcomponents.accordion.headerHoverBackgroundcomponents.accordion.contentPaddingcomponents.accordion.contentColorcomponents.accordion.chevronColorcomponents.accordion.chevronActiveColorcomponents.accordion.dividerColorcomponents.accordion.animationDuration
- FAQ items from CMS collection
- Section content from CMS portableText
- Renders all items
- Expands on click
- Collapses previously open in single mode
- Multiple can be open in multi mode
- Has aria-expanded
- Panel has role=region
- Default open items are expanded
- Passes className through
Tooltip
Component Metadata
TooltipProps (content: ReactNode, children: ReactElement, placement?: TooltipPlacement, delay?: number)- role=tooltip on tooltip element
- aria-describedby on trigger linking to tooltip
- Keyboard accessible via focus/blur
- Dismiss on Escape (planned)
- Max-width constraint prevents overflow
- Touch devices show on focus
- Fade-in animation on show
- Configurable animation duration via token
components.tooltip.backgroundcomponents.tooltip.textColorcomponents.tooltip.borderRadiuscomponents.tooltip.paddingcomponents.tooltip.fontSizecomponents.tooltip.maxWidthcomponents.tooltip.shadowcomponents.tooltip.bordercomponents.tooltip.arrowSizecomponents.tooltip.arrowColorcomponents.tooltip.zIndexcomponents.tooltip.animationDurationcomponents.tooltip.offsetDistance
- Tooltip text from CMS field
- Help text from content collection
- Hidden by default
- Shows on hover
- Hides on mouse leave
- Shows on focus
- Has role=tooltip
- Applies placement class
- Renders rich content
- Passes className through
Avatar
Component Metadata
AvatarProps (src?: string, alt: string, size?: AvatarSize, status?: AvatarStatus) + AvatarGroupProps (children, max?: number)- role=img on avatar with aria-label
- Image aria-hidden when role=img provides label
- Status dot aria-label for status
- AvatarGroup role=group with aria-label
- Fixed size via tokens
- Group wraps naturally in flex
- No animation by default
components.avatar.borderRadiuscomponents.avatar.borderColorcomponents.avatar.borderWidthcomponents.avatar.backgroundcomponents.avatar.textColorcomponents.avatar.fontWeightcomponents.avatar.sizeXscomponents.avatar.sizeSmcomponents.avatar.sizeMdcomponents.avatar.sizeLgcomponents.avatar.sizeXlcomponents.avatar.statusDotSizecomponents.avatar.statusOnlinecomponents.avatar.statusOfflinecomponents.avatar.statusBusycomponents.avatar.groupOverlap
- User profile image from CMS
- Author avatar from collection
- Renders initials from name
- Renders image when src provided
- Applies size class
- Shows status dot
- Has role=img with aria-label
- AvatarGroup renders children
- AvatarGroup shows overflow count
- Passes className through
Divider
Component Metadata
DividerProps (orientation?: DividerOrientation, variant?: DividerVariant, label?: string)- role=separator with aria-orientation
- Decorative label not separately announced
- Horizontal fills container width
- Vertical stretches to parent height
- No animation
components.divider.colorcomponents.divider.thicknesscomponents.divider.spacingcomponents.divider.labelColorcomponents.divider.labelFontSizecomponents.divider.labelBackgroundcomponents.divider.labelPaddingcomponents.divider.accentColor
- Section divider label from CMS field
- Renders horizontal by default
- Renders vertical orientation
- Applies accent variant
- Shows label text
- Has role=separator
- Has aria-orientation
- Label mode has two lines
- Passes className through
Switch
Component Metadata
SwitchProps (checked?: boolean, onChange?: (checked: boolean) => void, label?: string, disabled?: boolean)- role=switch with aria-checked
- aria-disabled when disabled
- Keyboard toggle via Space and Enter
- Focus ring on keyboard navigation
- Fixed track size from tokens
- Label wraps naturally
- Thumb slides with transition
- Track background fades
components.switch.trackWidthcomponents.switch.trackHeightcomponents.switch.trackBorderRadiuscomponents.switch.trackOffBackgroundcomponents.switch.trackOnBackgroundcomponents.switch.trackDisabledBackgroundcomponents.switch.thumbSizecomponents.switch.thumbColorcomponents.switch.thumbOffsetcomponents.switch.labelColorcomponents.switch.labelFontSizecomponents.switch.labelGapcomponents.switch.animationDurationcomponents.switch.focusRing
- Toggle label from CMS field
- Default state from CMS boolean
- Renders unchecked by default
- Renders checked state
- Toggles on click
- Shows label text
- Disabled prevents toggle
- Has role=switch
- Has aria-checked
- Passes className through
Chip
Component Metadata
ChipProps (children: ReactNode, selected?: boolean, onClick?: () => void, removable?: boolean, onRemove?: () => void, icon?: ReactNode)- role=option with aria-selected for interactive chips
- Keyboard selection via Space and Enter
- Remove button with aria-label
- Icon aria-hidden
- Wraps in flex container
- Fixed height from padding tokens
- Background color transition on hover
- Remove icon color transition
components.chip.backgroundcomponents.chip.textColorcomponents.chip.borderRadiuscomponents.chip.paddingXcomponents.chip.paddingYcomponents.chip.fontSizecomponents.chip.fontWeightcomponents.chip.gapcomponents.chip.selectedBackgroundcomponents.chip.selectedTextColorcomponents.chip.hoverBackgroundcomponents.chip.removeColorcomponents.chip.removeHoverColor
- Tag list from CMS taxonomy
- Category filters from collection
- Renders children as label
- Applies selected class
- Calls onClick on click
- Shows remove button when removable
- Calls onRemove on remove click
- Renders icon
- Has role=option when interactive
- Passes className through
Dropdown
Component Metadata
DropdownProps (options: DropdownOption[], value?: string, onChange?: (value: string) => void, placeholder?: string)- Trigger has aria-haspopup=listbox and aria-expanded
- Menu has role=listbox with aria-label
- Items have role=option with aria-selected
- Keyboard: Enter/Space toggle, Escape close, ArrowDown open
- Min-width from CSS
- Menu matches trigger width
- No animation by default
components.dropdown.triggerBackgroundcomponents.dropdown.triggerBordercomponents.dropdown.triggerBorderRadiuscomponents.dropdown.triggerPaddingcomponents.dropdown.triggerColorcomponents.dropdown.triggerFontSizecomponents.dropdown.menuBackgroundcomponents.dropdown.menuBordercomponents.dropdown.menuBorderRadiuscomponents.dropdown.menuShadowcomponents.dropdown.menuMaxHeightcomponents.dropdown.itemPaddingcomponents.dropdown.itemColorcomponents.dropdown.itemHoverBackgroundcomponents.dropdown.itemSelectedColorcomponents.dropdown.placeholderColorcomponents.dropdown.chevronColorcomponents.dropdown.zIndex
- Option list from CMS taxonomy
- Default value from CMS field
- Renders trigger with placeholder
- Opens menu on click
- Shows all options
- Selects option on click
- Closes on selection
- Shows selected label
- Has aria-haspopup
- Passes className through
Input
Component Metadata
InputProps extends InputHTMLAttributes (label?: string, helper?: string, error?: string)- Label linked via htmlFor/id
- aria-invalid when error
- aria-describedby linking to error or helper
- Error message has role=alert
- Full-width by default
- Max-width set by parent container
- Border color and box-shadow transition on focus
components.input.backgroundcomponents.input.borderColorcomponents.input.borderRadiuscomponents.input.borderWidthcomponents.input.paddingcomponents.input.fontSizecomponents.input.textColorcomponents.input.placeholderColorcomponents.input.focusBorderColorcomponents.input.focusRingcomponents.input.errorBorderColorcomponents.input.errorColorcomponents.input.labelColorcomponents.input.labelFontSizecomponents.input.helperColorcomponents.input.helperFontSizecomponents.input.disabledOpacity
- Field label from CMS schema
- Validation message from CMS
- Renders input element
- Shows label text
- Shows helper text
- Shows error message
- Error has role=alert
- Has aria-invalid when error
- Disabled state applied
- Passes className through
Modal / Dialog
Component Metadata
ModalProps (open: boolean, onClose: () => void, title?: string, children: ReactNode, className?: string)- role=dialog with aria-modal=true
- aria-label from title or fallback 'Dialog'
- Close button with aria-label='Close dialog'
- Escape key closes modal
- Max-width constrained with 1rem margin
- Centers vertically and horizontally
- Overlay fade-in animation
- Content slide-up animation
components.modal.overlayBackgroundcomponents.modal.backgroundcomponents.modal.borderRadiuscomponents.modal.bordercomponents.modal.shadowcomponents.modal.paddingcomponents.modal.maxWidthcomponents.modal.titleColorcomponents.modal.titleFontSizecomponents.modal.bodyColorcomponents.modal.closeColorcomponents.modal.closeHoverColorcomponents.modal.zIndexcomponents.modal.animationDuration
- Dialog title from CMS
- Dialog body content from CMS
- Renders when open
- Does not render when closed
- Shows title when provided
- Renders children
- Has aria-modal attribute
- Calls onClose when close button clicked
- Has close button with aria-label
- Passes className through
Textarea
Component Metadata
TextareaProps extends TextareaHTMLAttributes (label?: string, helper?: string, error?: string)- Label linked via htmlFor/id
- aria-invalid when error
- aria-describedby linking to error or helper
- Error message has role=alert
- Full-width by default
- Min-height set by token
- Vertical resize only
- Border color and box-shadow transition on focus
components.textarea.backgroundcomponents.textarea.borderColorcomponents.textarea.borderRadiuscomponents.textarea.borderWidthcomponents.textarea.paddingcomponents.textarea.fontSizecomponents.textarea.textColorcomponents.textarea.placeholderColorcomponents.textarea.focusBorderColorcomponents.textarea.focusRingcomponents.textarea.errorBorderColorcomponents.textarea.errorColorcomponents.textarea.labelColorcomponents.textarea.labelFontSizecomponents.textarea.helperColorcomponents.textarea.helperFontSizecomponents.textarea.minHeightcomponents.textarea.disabledOpacitycomponents.textarea.resizeColor
- Field label from CMS schema
- Validation message from CMS
- Renders textarea element
- Shows label text
- Shows helper text
- Shows error message
- Error has role=alert
- Has aria-invalid when error
- Disabled state applied
- Passes className through
Select
Component Metadata
SelectProps extends SelectHTMLAttributes (label?: string, helper?: string, error?: string, placeholder?: string, options: SelectOption[])- Label linked via htmlFor/id
- aria-invalid when error
- aria-describedby linking to error or helper
- Error message has role=alert
- Full-width by default
- Custom chevron icon via SVG
- Border color and box-shadow transition on focus
components.select.backgroundcomponents.select.borderColorcomponents.select.borderRadiuscomponents.select.borderWidthcomponents.select.paddingcomponents.select.fontSizecomponents.select.textColorcomponents.select.placeholderColorcomponents.select.focusBorderColorcomponents.select.focusRingcomponents.select.errorBorderColorcomponents.select.errorColorcomponents.select.labelColorcomponents.select.labelFontSizecomponents.select.helperColorcomponents.select.helperFontSizecomponents.select.chevronColorcomponents.select.disabledOpacity
- Field label from CMS schema
- Options from CMS enum fields
- Validation from CMS
- Renders select element
- Shows label text
- Shows helper text
- Shows error message
- Error has role=alert
- Has aria-invalid when error
- Renders all options
- Passes className through
Table
| Component | Family | Tokens |
|---|---|---|
| Button | Action | 9 |
| Card | Layout | 4 |
| Table | Data | 14 |
Component Metadata
TableProps (columns: TableColumn[], data: Record<string, ReactNode>[], caption?: string, striped?: boolean, className?: string)- role=table on table element
- scope=col on header cells
- Caption element for table description
- Semantic thead/tbody structure
- Horizontal scroll on overflow
- Border radius on container
- Row hover background transition
components.table.backgroundcomponents.table.borderColorcomponents.table.borderRadiuscomponents.table.headerBackgroundcomponents.table.headerColorcomponents.table.headerFontSizecomponents.table.headerFontWeightcomponents.table.cellPaddingcomponents.table.cellColorcomponents.table.cellFontSizecomponents.table.rowHoverBackgroundcomponents.table.stripeBackgroundcomponents.table.captionColorcomponents.table.captionFontSize
- Column definitions from CMS schema
- Row data from CMS collections
- Renders table element
- Renders column headers
- Renders row data
- Shows caption when provided
- Applies striped class to odd rows
- Header cells have scope=col
- Renders correct number of rows
- Passes className through
Empty State
No components yet
Create your first component to populate this section.
Component Metadata
EmptyStateProps (icon?: ReactNode, title: string, description?: string, action?: ReactNode, className?: string)- role=status for live region semantics
- aria-label from title
- Icon container is aria-hidden
- Action slot is fully interactive
- Centers content
- Max-width on description for readability
- No animation — static placeholder
components.emptyState.backgroundcomponents.emptyState.borderColorcomponents.emptyState.borderRadiuscomponents.emptyState.paddingcomponents.emptyState.iconColorcomponents.emptyState.iconSizecomponents.emptyState.titleColorcomponents.emptyState.titleFontSizecomponents.emptyState.descriptionColorcomponents.emptyState.descriptionFontSizecomponents.emptyState.gap
- Title from CMS
- Description from CMS
- Action label from CMS
- Renders with title
- Has role=status
- Shows description when provided
- Renders icon when provided
- Renders action when provided
- Has aria-label from title
- Icon is aria-hidden
- Passes className through
Spinner
Component Metadata
SpinnerProps (size?: SpinnerSize, label?: string, className?: string)- role=status for live region
- aria-label defaults to 'Loading'
- Custom label overrides aria-label
- SVG is aria-hidden
- Inline-flex for inline usage
- Size controlled by tokens
- Continuous rotation animation at token-controlled speed
components.spinner.colorcomponents.spinner.trackColorcomponents.spinner.sizeSmcomponents.spinner.sizeMdcomponents.spinner.sizeLgcomponents.spinner.strokeWidthcomponents.spinner.speedcomponents.spinner.labelColorcomponents.spinner.labelFontSizecomponents.spinner.gap
- Loading message from CMS
- Renders spinner
- Has aria-label Loading by default
- Uses custom label as aria-label
- Shows label text
- Renders SVG
- Applies size class
- SVG is aria-hidden
- Passes className through
Tag
Component Metadata
TagProps (children: ReactNode, variant?: TagVariant, icon?: ReactNode, className?: string)- Icon container is aria-hidden
- Text content is readable by screen readers
- Color is not the only differentiator (text labels required)
- Inline-flex, wraps naturally in flex containers
- No animation — static label
components.tag.backgroundcomponents.tag.textColorcomponents.tag.borderRadiuscomponents.tag.paddingXcomponents.tag.paddingYcomponents.tag.fontSizecomponents.tag.fontWeightcomponents.tag.gapcomponents.tag.successBackgroundcomponents.tag.successColorcomponents.tag.warningBackgroundcomponents.tag.warningColorcomponents.tag.errorBackgroundcomponents.tag.errorColor
- Tag label from CMS taxonomy
- Variant from CMS status field
- Renders with children
- Applies default variant class
- Applies success variant
- Applies warning variant
- Applies error variant
- Renders icon when provided
- Icon is aria-hidden
- Passes className through
Sidebar
Component Metadata
SidebarProps (header?: string, sections: SidebarSection[], className?: string)- role=navigation with aria-label
- aria-current=page on active link
- Icon containers are aria-hidden
- Semantic list structure (ul/li)
- Fixed width from token
- Full height within container
- Link hover color and background transition
components.sidebar.backgroundcomponents.sidebar.borderColorcomponents.sidebar.widthcomponents.sidebar.paddingcomponents.sidebar.headerColorcomponents.sidebar.headerFontSizecomponents.sidebar.linkColorcomponents.sidebar.linkHoverColorcomponents.sidebar.linkHoverBackgroundcomponents.sidebar.linkActiveColorcomponents.sidebar.linkActiveBackgroundcomponents.sidebar.linkPaddingcomponents.sidebar.linkBorderRadiuscomponents.sidebar.linkFontSizecomponents.sidebar.sectionGapcomponents.sidebar.sectionLabelColorcomponents.sidebar.sectionLabelFontSize
- Navigation structure from CMS
- Active state from current route
- Renders navigation
- Renders header when provided
- Renders section labels
- Renders all links
- Active link has aria-current=page
- Inactive link has no aria-current
- Has aria-label for navigation
- Passes className through
Component Catalog
Every variant and size of the shared UI components.
Button
Primary Variant
Secondary Variant
Disabled States
Component Metadata
ButtonProps (variant: primary|secondary, size: sm|md|lg, children, ...ButtonHTMLAttributes)- Minimum 4.5:1 contrast ratio
- Visible focus indicator
- Keyboard activatable
- Full-width on mobile (< 640px)
- Inline on desktop
- Hover scale transition 150ms ease
components.button.primary.backgroundcomponents.button.primary.backgroundHovercomponents.button.primary.textcomponents.button.primary.borderRadiuscomponents.button.primary.paddingXcomponents.button.primary.paddingYcomponents.button.secondary.backgroundcomponents.button.secondary.bordercomponents.button.secondary.text
- Button label from CMS text field
- Link target from CMS URL field
- Renders primary variant
- Renders secondary variant
- Handles click events
- Disabled state blocks interaction
Card
Default (no title)
A card without a title prop renders only the body content.
With Title
Card Title
A card with a title prop renders a header section above the body.
Nested Content
Outer Card
Cards support arbitrary nested content:
Inner Card
This card is nested inside the outer card.
Component Metadata
CardProps (title?: string, children, ...HTMLAttributes<HTMLDivElement>)- Semantic heading level in title
- Sufficient contrast for card content
- Full-width on mobile
- Grid-based on desktop
components.card.backgroundcomponents.card.borderRadiuscomponents.card.paddingcomponents.card.shadow
- Title from CMS string field
- Body from CMS portableText
- Renders with title
- Renders without title
- Passes className through
Tabs
Interactive Demo
The component catalog demonstrates every shared component from the packages/ui library. Each component consumes CSS custom properties from the token pipeline.
Component Metadata
TabsProps (tabs: TabItem[], defaultIndex?: number, className?: string)- role=tablist on container
- role=tab on triggers
- role=tabpanel on content
- aria-selected state
- Horizontal tabs on desktop
- Scrollable on mobile
components.tabs.borderColorcomponents.tabs.triggerColorcomponents.tabs.triggerHoverColorcomponents.tabs.triggerActiveColorcomponents.tabs.triggerActiveBordercomponents.tabs.panelColorcomponents.tabs.paddingXcomponents.tabs.paddingY
- Tab labels from CMS string array
- Tab content from CMS portableText array
- Renders all tabs
- Switches active tab on click
- Defaults to specified index
Alerts
All Variants
Dismissible
Component Metadata
AlertProps (variant: AlertVariant, children, dismissible?: boolean, onDismiss?: () => void)- role=alert for screen reader announcement
- Dismiss button has aria-label
- Full-width on all breakpoints
components.alert.borderRadiuscomponents.alert.paddingcomponents.alert.paddingXcomponents.alert.success.backgroundcomponents.alert.success.bordercomponents.alert.success.textcomponents.alert.warning.backgroundcomponents.alert.warning.bordercomponents.alert.warning.textcomponents.alert.error.backgroundcomponents.alert.error.bordercomponents.alert.error.textcomponents.alert.info.backgroundcomponents.alert.info.bordercomponents.alert.info.text
- Alert message from CMS text field
- Variant from CMS select field
- Renders each variant with correct icon
- Dismissible hides on click
- Calls onDismiss callback
Pricing Table
3-Tier Layout
Starter
- ✓1 design system
- ✓50 tokens
- ✓2 surfaces
Pro
- ✓Unlimited systems
- ✓Unlimited tokens
- ✓ADW pipeline
Enterprise
- ✓Everything in Pro
- ✓SSO
- ✓SLA
Component Metadata
PricingTableProps (tiers: PricingTier[], onTierSelect?: (tier) => void)- Semantic list for features
- Button for CTA (not link)
- Sufficient contrast on featured badge
- Single column on mobile
- Multi-column grid on desktop
- Hover elevation on pricing cards
components.pricingCard.backgroundcomponents.pricingCard.backgroundFeaturedcomponents.pricingCard.borderRadiuscomponents.pricingCard.paddingcomponents.pricingCard.bordercomponents.pricingCard.borderFeaturedcomponents.pricingCard.priceColorcomponents.pricingCard.labelColorcomponents.pricingCard.featureColorcomponents.pricingCard.checkColorcomponents.pricingCard.shadowcomponents.pricingCard.shadowFeatured
- Tier data from CMS JSON field
- Feature lists from CMS text arrays
- Renders all tiers
- Featured tier has badge
- onTierSelect fires on CTA click
Testimonial Wall
Social Proof Grid
Jane Smith
CTO, Example Corp
“This platform transformed our design workflow.”
John Doe
Design Lead, Agency X
“Token-driven components saved us weeks of work.”
Component Metadata
TestimonialWallProps (testimonials: Testimonial[])- Star rating has aria-label
- Blockquote for quotes
- Avatar alt text
- Single column on mobile
- 2-3 column grid on desktop
- Hover background transition on cards
components.testimonial.backgroundcomponents.testimonial.backgroundHovercomponents.testimonial.borderRadiuscomponents.testimonial.paddingcomponents.testimonial.bordercomponents.testimonial.avatarBordercomponents.testimonial.avatarSizecomponents.testimonial.quoteColorcomponents.testimonial.nameColorcomponents.testimonial.roleColorcomponents.testimonial.starColorcomponents.testimonial.starEmpty
- Testimonials from CMS collection with name/role/quote/rating fields
- Renders all testimonials
- Avatar placeholder when no URL
- Star rating reflects count
Metrics Panel
KPI Grid
Revenue
$48K
Users
2.8K
Churn
3.2%
Component Metadata
MetricsPanelProps (metrics: MetricItem[], columns?: 2|3|4)- Trend arrows use aria-hidden
- Status conveyed by color and text
- 2 columns on mobile
- configurable columns (2-4) on desktop
components.metricsPanel.backgroundcomponents.metricsPanel.borderRadiuscomponents.metricsPanel.paddingcomponents.metricsPanel.bordercomponents.metricsPanel.valueColorcomponents.metricsPanel.labelColorcomponents.metricsPanel.trendUpcomponents.metricsPanel.trendDowncomponents.metricsPanel.trendFlatcomponents.metricsPanel.statusGoodcomponents.metricsPanel.statusWarningcomponents.metricsPanel.statusCritical
- Metrics from CMS JSON field or API endpoint
- Renders all metrics
- Correct trend arrow direction
- Status class applied
Author Bio Card
Vertical Layout
Component Metadata
AuthorBioCardProps (author: AuthorProfile, layout?: vertical|horizontal)- Social links have title attribute
- Avatar has alt text
- External links have rel=noopener
- Vertical layout on mobile
- Horizontal layout option on desktop
- Social link hover color transition
components.authorBio.backgroundcomponents.authorBio.borderRadiuscomponents.authorBio.paddingcomponents.authorBio.bordercomponents.authorBio.avatarSizecomponents.authorBio.avatarBordercomponents.authorBio.nameColorcomponents.authorBio.roleColorcomponents.authorBio.bioColorcomponents.authorBio.socialColorcomponents.authorBio.socialHovercomponents.authorBio.badgeBackgroundcomponents.authorBio.badgeText
- Author data from EmDash authors collection
- Renders vertical layout
- Renders horizontal layout
- Shows article count badge
- Social links open in new tab
Feature Comparison Table
Accessible Data Table
| Feature | Basic | Pro |
|---|---|---|
| Tokens | ◐ | ✓ |
| Components | ✓ | ✓ |
| ADW Pipeline | ✗ | ✓ |
Component Metadata
FeatureComparisonTableProps (columns: ComparisonColumn[], features: ComparisonFeature[])- role=table on table element
- scope=col on header cells
- aria-label on support icons
- Scrollable region has tabIndex and aria-label
- Horizontally scrollable on mobile
- Full table on desktop
components.comparisonTable.backgroundcomponents.comparisonTable.headerBackgroundcomponents.comparisonTable.borderRadiuscomponents.comparisonTable.bordercomponents.comparisonTable.rowStripecomponents.comparisonTable.headerTextcomponents.comparisonTable.cellTextcomponents.comparisonTable.checkColorcomponents.comparisonTable.crossColorcomponents.comparisonTable.partialColorcomponents.comparisonTable.featureLabelColorcomponents.comparisonTable.tooltipBackgroundcomponents.comparisonTable.tooltipText
- Feature data from CMS JSON field
- Column definitions from CMS collection
- Renders all columns and rows
- Highlighted column has class
- Tooltip displays on hover
- Alternating row stripes
Timeline
Vertical chronological feed — commits, deploys, approvals, publishes, pipeline runs. First component produced by the design-to-code pipeline (Stitch → Tokens → React).
Activity Feed with Filters
Design-to-Code Pipeline Run
Timeline component — full E2E automation
SuccessProduction Deploy
PM2 restart, all routes 200
Successtest(ui): unit tests
33 tests passing via vitest
Drift Score: 100/100
SuccessArchitecture Docs Published
Component Metadata
TimelineProps (events: TimelineEvent[], filters?: EventType[], className?: string)- role=feed on container
- aria-posinset/aria-setsize on events
- aria-pressed on filter buttons
- Keyboard navigable filters
- Full-width on all breakpoints
- Stacked layout
- Filter transition 150ms ease
components.timeline.lineColorcomponents.timeline.eventBackgroundcomponents.timeline.eventBordercomponents.timeline.eventBorderRadiuscomponents.timeline.eventPaddingcomponents.timeline.iconBackgroundcomponents.timeline.iconColorcomponents.timeline.titleColorcomponents.timeline.descriptionColorcomponents.timeline.timestampColorcomponents.timeline.badgeSuccessBgcomponents.timeline.badgeErrorBgcomponents.timeline.badgeWarningBgcomponents.timeline.badgePendingBgcomponents.timeline.badgeTextcomponents.timeline.filterActiveBgcomponents.timeline.filterActiveText
- Event data from CMS collection
- Filter types from CMS config
- Renders all events
- Filters by event type
- Shows status badges
- Empty state message
Score Gauge
Circular SVG gauge with score, label, trend, and status. Third pipeline-generated component — first to complete the full Stitch → Penpot → Tokens → Code pipeline.
All Sizes
Small
Medium
Large
Status Variants
Default
Good
Warning
Critical
Component Metadata
ScoreGaugeProps (score: number, label: string, size?: sm|md|lg, status?: default|good|warning|critical, trend?: up|down|flat, trendValue?: string)- SVG ring is aria-hidden
- Score value is visible text
- Trend arrows use aria-hidden with text fallback
- Inline-flex, scales with container
- Size prop controls diameter (80/120/200)
- stroke-dasharray transition 400ms ease
components.scoreGauge.trackColorcomponents.scoreGauge.fillDefaultcomponents.scoreGauge.fillGoodcomponents.scoreGauge.fillWarningcomponents.scoreGauge.fillCriticalcomponents.scoreGauge.scoreColorcomponents.scoreGauge.labelColorcomponents.scoreGauge.trendUpColorcomponents.scoreGauge.trendDownColorcomponents.scoreGauge.trendFlatColorcomponents.scoreGauge.backgroundcomponents.scoreGauge.borderRadiuscomponents.scoreGauge.strokeWidthcomponents.scoreGauge.glowOpacity
- Score from CMS number field
- Label from CMS string field
- Renders score and label
- Clamps score to 0-100
- Applies size class
- Applies status class
- Shows trend indicator
Reading Time
Reading time estimate with clock icon, word count, and optional progress bar. Self-Healing Loop component — addresses blog detail page finding.
Short Article
Long Article with Word Count
With Progress Bar
Without Word Count
Component Metadata
ReadingTimeProps (wordCount: number, wordsPerMinute?: number, showWordCount?: boolean, progress?: number)- Progress bar has role=progressbar with aria-valuenow/min/max
- Clock icon is aria-hidden
- Inline-flex, scales with container
- Progress bar fill transition 200ms ease
components.readingTime.backgroundcomponents.readingTime.borderRadiuscomponents.readingTime.textColorcomponents.readingTime.iconColorcomponents.readingTime.separatorColorcomponents.readingTime.progressTrackcomponents.readingTime.progressFillcomponents.readingTime.progressHeight
- Word count from CMS text field length calculation
- Progress from scroll position
- Calculates reading time from word count
- Shows word count when enabled
- Hides word count when disabled
- Renders progress bar when progress provided
- Passes className through
Pagination
Page navigation with numbered buttons, prev/next arrows, ellipsis, and counter. Fifth pipeline-generated component — addresses blog scalability from E2E Test 2.
Short Range (5 pages)
Long Range with Ellipsis
Boundary States
Component Metadata
PaginationProps (totalPages: number, currentPage?: number, onPageChange?: (page) => void, maxVisible?: number, showCounter?: boolean)- nav element with aria-label=Pagination
- aria-current=page on active button
- aria-label on each page button
- Disabled buttons have disabled attribute
- Centered on all breakpoints
- Compact layout scales with button count
- Background/color transition 150ms ease on hover
components.pagination.backgroundcomponents.pagination.borderRadiuscomponents.pagination.buttonBackgroundcomponents.pagination.buttonBackgroundHovercomponents.pagination.buttonBackgroundActivecomponents.pagination.buttonTextcomponents.pagination.buttonTextHovercomponents.pagination.buttonTextActivecomponents.pagination.buttonBorderRadiuscomponents.pagination.buttonSizecomponents.pagination.disabledColorcomponents.pagination.ellipsisColorcomponents.pagination.counterColorcomponents.pagination.navColorcomponents.pagination.navColorHover
- Total pages from CMS collection count
- Current page from URL parameter
- Renders all page buttons
- Highlights current page
- Prev disabled on first page
- Next disabled on last page
- Shows ellipsis for long ranges
- Fires onPageChange callback
Radar Chart
Pure SVG radar/spider chart with configurable dimensions. Fourth pipeline-generated component via Stitch Aetheris Observatory design (teal #44e5c2).
5-Dimension (Pentagon)
Behavioral Profile
4-Dimension (Square)
Project Health
Component Metadata
RadarChartProps (dimensions: RadarDimension[], title?: string, overallScore?: number, size?: number)- SVG is aria-hidden
- Title is visible text heading
- Score values in labels are visible text
- max-width 320px SVG, scales with container
- Labels adjust text-anchor by position
- Fill opacity transition 300ms ease
components.radarChart.backgroundcomponents.radarChart.borderRadiuscomponents.radarChart.gridColorcomponents.radarChart.gridStrokeWidthcomponents.radarChart.axisColorcomponents.radarChart.fillColorcomponents.radarChart.fillOpacitycomponents.radarChart.strokeColorcomponents.radarChart.strokeWidthcomponents.radarChart.dotColorcomponents.radarChart.dotRadiuscomponents.radarChart.labelColorcomponents.radarChart.valueColorcomponents.radarChart.titleColorcomponents.radarChart.scoreColorcomponents.radarChart.scoreBgColor
- Dimension data from CMS JSON field
- Title from CMS string field
- Renders all dimension labels
- Renders title when provided
- Renders overall score in center
- Clamps values to 0-100
- Passes className through
Form Wizard
Multi-step form with progress indicator, field helpers, and navigation. Second component produced by the design-to-code pipeline.
Onboarding Flow (4 Steps)
Component Metadata
FormWizardProps (steps: WizardStep[], onComplete?: () => void, className?: string)- aria-current=step on active step circle
- aria-live=polite on content region
- aria-labelledby linking content to step indicator
- Disabled back button on first step
- Full-width on all breakpoints
- Step labels hidden on mobile if needed
- Step circle transition 150ms ease
- Button hover transition 150ms ease
components.formWizard.backgroundcomponents.formWizard.borderRadiuscomponents.formWizard.paddingcomponents.formWizard.stepActiveBackgroundcomponents.formWizard.stepActiveBordercomponents.formWizard.stepActiveTextcomponents.formWizard.stepCompletedBackgroundcomponents.formWizard.stepCompletedTextcomponents.formWizard.stepInactiveBackgroundcomponents.formWizard.stepInactiveBordercomponents.formWizard.stepInactiveTextcomponents.formWizard.stepConnectorActivecomponents.formWizard.stepConnectorInactivecomponents.formWizard.stepLabelActivecomponents.formWizard.stepLabelInactivecomponents.formWizard.fieldBackgroundcomponents.formWizard.fieldBordercomponents.formWizard.fieldBorderFocuscomponents.formWizard.fieldBorderErrorcomponents.formWizard.fieldTextcomponents.formWizard.fieldPlaceholdercomponents.formWizard.labelColorcomponents.formWizard.errorTextcomponents.formWizard.checkboxCheckedBgcomponents.formWizard.checkboxBordercomponents.formWizard.navCounterColor
- Step definitions from CMS collection
- Form schema from CMS config
- Renders all step indicators
- Navigates forward
- Navigates backward
- Calls onComplete on last step
- Back disabled on first step
AI Avatar Tooltip
Floating AI assistant indicator with glassmorphic chat bubble, pulsing glow states, typing indicator, and message thread. Sixth pipeline component from the Kaleido Ether design system.
Status States (Idle / Active / Typing)
Expanded with Messages
Component Metadata
AiAvatarTooltipProps (name?: string, greeting?: string, status?: AiStatus, messages?: AiMessage[], open?: boolean, onOpenChange?: (open: boolean) => void, placeholder?: string, onSend?: (message: string) => void, badge?: string, position?: 'bottom-right' | 'bottom-left')- Chat bubble has role=dialog with aria-label
- Trigger has aria-expanded
- Input has aria-label
- Close button has aria-label
- Send button has aria-label
- Typing indicator has aria-label
- Bubble width 320px on desktop, scales to fit mobile viewports
- Avatar glow pulse 3s ease-in-out (idle), 1.2s (typing)
- Bubble enter animation 250ms ease-out
- Typing dots bounce 1.4s staggered
components.aiAvatarTooltip.avatarSizecomponents.aiAvatarTooltip.avatarBackgroundcomponents.aiAvatarTooltip.avatarGlowColorcomponents.aiAvatarTooltip.avatarGlowActiveColorcomponents.aiAvatarTooltip.avatarIconColorcomponents.aiAvatarTooltip.bubbleBackgroundcomponents.aiAvatarTooltip.bubbleBordercomponents.aiAvatarTooltip.bubbleShadowcomponents.aiAvatarTooltip.bubbleBorderRadiuscomponents.aiAvatarTooltip.headerTextColorcomponents.aiAvatarTooltip.bodyTextColorcomponents.aiAvatarTooltip.inputBackgroundcomponents.aiAvatarTooltip.inputBorderColorcomponents.aiAvatarTooltip.inputFocusBorderColorcomponents.aiAvatarTooltip.inputTextColorcomponents.aiAvatarTooltip.inputPlaceholderColorcomponents.aiAvatarTooltip.typingDotColorcomponents.aiAvatarTooltip.goldAccentcomponents.aiAvatarTooltip.closeButtonColorcomponents.aiAvatarTooltip.closeButtonHoverColor
- AI persona from CMS config
- Greeting from CMS localization
- Renders trigger button with aria-expanded=false
- Opens bubble on trigger click
- Displays name and greeting in bubble
- Shows typing indicator when status=typing
- Renders messages
- Calls onSend on Enter key
- Shows gold badge when provided
- Passes className through
Live Readouts
Real-time biometric readout panel with metric cards, sparklines, trend arrows, and status indicators. Seventh pipeline component from the Clinical Sentinel design system.
Normal Vitals (5 metrics)
Critical State (blinking indicators)
Component Metadata
LiveReadoutsProps (metrics: ReadoutMetric[], title?: string, columns?: 2|3|4)- Region has role=region with aria-label
- Status dot has aria-label
- Trend arrow has aria-label
- Sparkline is aria-hidden
- Auto-fit grid (min 160px)
- Optional fixed 2/3/4 columns
- Active card pulse 2s ease-in-out infinite
- Critical status blink 1s
- Card box-shadow transition 300ms
components.liveReadouts.cardBackgroundcomponents.liveReadouts.cardBorderRadiuscomponents.liveReadouts.cardBordercomponents.liveReadouts.valueColorcomponents.liveReadouts.valueFontFamilycomponents.liveReadouts.labelColorcomponents.liveReadouts.unitColorcomponents.liveReadouts.trendUpColorcomponents.liveReadouts.trendDownColorcomponents.liveReadouts.trendFlatColorcomponents.liveReadouts.statusNormalcomponents.liveReadouts.statusWarningcomponents.liveReadouts.statusCriticalcomponents.liveReadouts.sparklineColorcomponents.liveReadouts.pulseGlowColor
- Metrics from CMS biometric data
- Thresholds from CMS config
- Renders metric cards with labels
- Displays value and unit
- Shows trend arrow with correct direction
- Renders status dot with correct class
- Shows sparkline when data provided
- Applies active pulse class
- Renders title when provided
- Passes className through
Wellness Matrix
Biometric wellness dashboard grid — first component built via the fully automated Stitch→Penpot→Tokens→Code pipeline. Precision Noir design system.
Full Dashboard (hero + 6 categories)
Component Metadata
WellnessMatrixProps (compositeScore?: number, categories: WellnessCategory[], columns?: 2|3)- Status text distinguishable without color alone
- Score ring decorative (aria-hidden)
- Semantic heading for hero label if used in page context
- 3-column on desktop
- 2-column on tablet
- 1-column on mobile
- Critical card pulse 2s ease-in-out
- Bar fill transition 0.4s ease
- Ring stroke transition 0.6s ease
components.wellnessMatrix.heroBackgroundcomponents.wellnessMatrix.heroScoreColorcomponents.wellnessMatrix.heroLabelColorcomponents.wellnessMatrix.heroRingTrackcomponents.wellnessMatrix.heroRingFillcomponents.wellnessMatrix.cardBackgroundcomponents.wellnessMatrix.cardBorderRadiuscomponents.wellnessMatrix.cardBordercomponents.wellnessMatrix.categoryIconColorcomponents.wellnessMatrix.scoreColorcomponents.wellnessMatrix.labelColorcomponents.wellnessMatrix.sparklineColorcomponents.wellnessMatrix.barFillColorcomponents.wellnessMatrix.barTrackColorcomponents.wellnessMatrix.statusOptimalcomponents.wellnessMatrix.statusAttentioncomponents.wellnessMatrix.statusCriticalcomponents.wellnessMatrix.glowShadow
- Category names from CMS text fields
- Scores from CMS number fields
- Renders hero composite score with ring
- Hides hero when compositeScore is undefined
- Renders all category cards
- Shows status badge with correct class
- Renders sparkline when data provided
- Renders subcategory mini bars
- Applies critical pulse class
- Passes className through
Toolbar
Horizontal action toolbar with toggle buttons, dividers, and ARIA toolbar role. 40th component in the shared UI library.
Formatting Toolbar
View Toolbar
Component Metadata
ToolbarProps (items: ToolbarItem[], ariaLabel?: string, className?: string)- role=toolbar with aria-label
- aria-pressed on toggle buttons
- Dividers are aria-hidden
- Button title for tooltip
- Horizontal flex layout
- Wraps when constrained
- Button hover color and background transition
components.toolbar.backgroundcomponents.toolbar.borderColorcomponents.toolbar.paddingcomponents.toolbar.gapcomponents.toolbar.buttonColorcomponents.toolbar.buttonHoverColorcomponents.toolbar.buttonHoverBackgroundcomponents.toolbar.buttonActiveColorcomponents.toolbar.buttonActiveBackgroundcomponents.toolbar.buttonPaddingcomponents.toolbar.buttonBorderRadiuscomponents.toolbar.buttonFontSizecomponents.toolbar.dividerColor
- Action list from CMS config
- Active state from application state
- Renders toolbar with role=toolbar
- Renders all button items
- Active button has aria-pressed=true
- Inactive button has aria-pressed=false
- Renders divider item
- Divider is aria-hidden
- Has aria-label
- Passes className through
Radio Group
Radio button group with native fieldset, custom styled circles, option descriptions, error/disabled states, and focus ring. 41st component.
Plan Selection
With Disabled Option
Component Metadata
RadioGroupProps (label?: string, options: RadioOption[], value?: string, onChange?: (value) => void, name?: string, error?: string)- Native fieldset/legend for group labeling
- role=radiogroup with aria-label
- Native radio inputs (visually hidden) for keyboard support
- aria-invalid when error
- Error message has role=alert
- Focus ring on keyboard navigation
- Full-width by default
- Options stack vertically
- Border color transition on check
- Focus ring shadow transition
components.radioGroup.labelColorcomponents.radioGroup.labelFontSizecomponents.radioGroup.gapcomponents.radioGroup.radioSizecomponents.radioGroup.radioBordercomponents.radioGroup.radioCheckedBordercomponents.radioGroup.radioCheckedDotcomponents.radioGroup.radioDotSizecomponents.radioGroup.optionColorcomponents.radioGroup.optionFontSizecomponents.radioGroup.descriptionColorcomponents.radioGroup.descriptionFontSizecomponents.radioGroup.focusRingcomponents.radioGroup.errorColorcomponents.radioGroup.errorFontSizecomponents.radioGroup.disabledOpacity
- Options from CMS enum field
- Label from CMS schema
- Error from CMS validation
- Renders all radio options
- Shows group label
- Checked option has checked class
- Calls onChange with value
- Shows error message
- Error has role=alert
- Disabled option has disabled class
- Passes className through
Checkbox
Styled checkbox with custom SVG checkmark, indeterminate state, label, description, and focus ring. 42nd component.
States
Component Metadata
CheckboxProps (label?: string, description?: string, checked?: boolean, indeterminate?: boolean, onChange?: (checked) => void, disabled?: boolean)- Native checkbox (visually hidden) for keyboard support
- aria-checked=mixed for indeterminate
- Label linked via htmlFor
- Focus ring on keyboard navigation
- Inline-flex by default
- Label wraps naturally
- Background and border transition on check
components.checkbox.sizecomponents.checkbox.borderRadiuscomponents.checkbox.bordercomponents.checkbox.checkedBackgroundcomponents.checkbox.checkedBordercomponents.checkbox.checkmarkColorcomponents.checkbox.labelColorcomponents.checkbox.labelFontSizecomponents.checkbox.descriptionColorcomponents.checkbox.descriptionFontSizecomponents.checkbox.gapcomponents.checkbox.focusRingcomponents.checkbox.disabledOpacitycomponents.checkbox.indeterminateBackground
- Checkbox label from CMS field
- Default state from CMS boolean
- Renders checkbox
- Shows label text
- Checked state has checked class
- Calls onChange on click
- Indeterminate has mixed aria-checked
- Shows description text
- Disabled has disabled class
- Passes className through
Search Input
Search input with magnifying glass icon, clear button, focus ring, and role=search landmark. 43rd component.
Empty / Pre-filled
Component Metadata
SearchInputProps (value?: string, onChange?: (value) => void, placeholder?: string, ariaLabel?: string)- role=search on container
- aria-label on input
- Clear button with aria-label
- Search icon is aria-hidden
- Full-width by default
- Focus ring on focus-within
- Border color and box-shadow transition on focus
components.searchInput.backgroundcomponents.searchInput.borderColorcomponents.searchInput.borderRadiuscomponents.searchInput.paddingcomponents.searchInput.fontSizecomponents.searchInput.textColorcomponents.searchInput.placeholderColorcomponents.searchInput.iconColorcomponents.searchInput.iconSizecomponents.searchInput.focusBorderColorcomponents.searchInput.focusRingcomponents.searchInput.clearColorcomponents.searchInput.clearHoverColor
- Placeholder from CMS config
- Search query value from app state
- Renders search input
- Has role=search
- Shows placeholder
- Has aria-label
- Shows clear button when value present
- Hides clear button when empty
- Calls onChange on input
- Passes className through
Slider
Range slider with custom track, fill, label, value display, and formatter. 44th component.
With Value Display
With Formatter
Component Metadata
SliderProps (value?: number, min?: number, max?: number, step?: number, onChange?: (value) => void, label?: string, showValue?: boolean, formatValue?: (v) => string, disabled?: boolean)- Native range input for keyboard support
- aria-label from label prop
- aria-valuemin/max/now/text
- Focus ring on keyboard navigation
- Full-width by default
- Track fills container
- Fill width transition
- Thumb grab cursor
components.slider.trackHeightcomponents.slider.trackBackgroundcomponents.slider.trackBorderRadiuscomponents.slider.fillColorcomponents.slider.thumbSizecomponents.slider.thumbColorcomponents.slider.thumbBordercomponents.slider.thumbShadowcomponents.slider.labelColorcomponents.slider.labelFontSizecomponents.slider.valueColorcomponents.slider.valueFontSizecomponents.slider.focusRingcomponents.slider.disabledOpacity
- Min/max from CMS config
- Label from CMS field
- Renders range input
- Shows label text
- Shows value when showValue
- Calls onChange on input
- Has aria-valuemin/max/now
- Formats value with formatter
- Disabled has disabled class
- Passes className through
Number Input
Numeric stepper with +/- buttons, min/max clamping, label, and disabled state. 45th component.
Quantity (1-99)
Disabled
Component Metadata
NumberInputProps (value?: number, min?: number, max?: number, step?: number, onChange?: (value) => void, label?: string, disabled?: boolean)- Decrease button with aria-label
- Increase button with aria-label
- Input has aria-label from label prop
- Buttons disabled at min/max bounds
- Flexible width
- Buttons fixed width
- Border and shadow transition on focus
- Button hover color transition
components.numberInput.backgroundcomponents.numberInput.borderColorcomponents.numberInput.borderRadiuscomponents.numberInput.paddingcomponents.numberInput.fontSizecomponents.numberInput.textColorcomponents.numberInput.labelColorcomponents.numberInput.labelFontSizecomponents.numberInput.buttonColorcomponents.numberInput.buttonHoverColorcomponents.numberInput.buttonHoverBackgroundcomponents.numberInput.focusBorderColorcomponents.numberInput.focusRingcomponents.numberInput.disabledOpacity
- Min/max from CMS config
- Label from CMS schema
- Renders number input
- Shows label text
- Shows current value
- Increment button increases value
- Decrement button decreases value
- Clamps to min
- Clamps to max
- Passes className through
Popover
Floating content panel with click trigger, outside-click dismiss, Escape close, and role=dialog. 46th component.
With Title / Without Title
Component Metadata
PopoverProps (trigger: ReactElement, title?: string, children: ReactNode, open?: boolean, onOpenChange?: (open) => void)- Trigger has aria-haspopup=dialog and aria-expanded
- Panel has role=dialog with aria-label
- Close button with aria-label
- Escape key closes panel
- Absolute positioned below trigger
- Max-width constrained
- No animation by default
components.popover.backgroundcomponents.popover.borderColorcomponents.popover.borderRadiuscomponents.popover.paddingcomponents.popover.shadowcomponents.popover.maxWidthcomponents.popover.zIndexcomponents.popover.titleColorcomponents.popover.titleFontSizecomponents.popover.bodyColorcomponents.popover.bodyFontSizecomponents.popover.closeColorcomponents.popover.closeHoverColor
- Panel content from CMS
- Title from CMS field
- Hidden by default
- Opens on trigger click
- Shows title when provided
- Renders children content
- Has role=dialog
- Trigger has aria-haspopup
- Close button closes panel
- Passes className through
FileUpload
Drag-and-drop file upload zone with file list and progress bars.
Upload Zone with Files
Drag & drop files here, or click to browse
- tokens.json12.1 KB
- mockup.png1.7 MB
Disabled
Uploads disabled
Component Metadata
FileUploadProps (accept?: string, multiple?: boolean, maxSize?: number, onFiles?: (files) => void, files?: FileUploadFile[], helpText?: string, disabled?: boolean)- Zone has role=button with aria-label
- File input is aria-hidden
- File list has aria-label
- Progress bars have role=progressbar with aria-valuenow/min/max
- Full-width by default
- Centered content in zone
- Border color and background transition on drag/hover
components.fileUpload.backgroundcomponents.fileUpload.borderColorcomponents.fileUpload.borderColorActivecomponents.fileUpload.borderRadiuscomponents.fileUpload.borderStylecomponents.fileUpload.paddingcomponents.fileUpload.iconColorcomponents.fileUpload.iconSizecomponents.fileUpload.textColorcomponents.fileUpload.textFontSizecomponents.fileUpload.linkColorcomponents.fileUpload.linkHoverColorcomponents.fileUpload.fileNameColorcomponents.fileUpload.fileSizeColorcomponents.fileUpload.progressBackgroundcomponents.fileUpload.progressFillcomponents.fileUpload.progressHeight
- Help text from CMS config
- Accept types from CMS field
- Renders upload zone
- Shows help text
- Zone has role=button
- File input is hidden
- Displays file list
- Shows file size formatted
- Progress bar has correct value
- Passes className through
CodeBlock
Code display with line numbers, language header, and copy button.
TypeScript / JSON
1const greeting = "Hello, World!";2console.log(greeting);{ "name": "cms-aichemist", "version": "1.0.0"}Component Metadata
CodeBlockProps (code: string, language?: string, showLineNumbers?: boolean, showCopy?: boolean)- Pre element has role=region with aria-label
- Line numbers are aria-hidden
- Copy button has aria-label that updates on copy
- Full-width by default
- Horizontal scroll on overflow
- Copy button color transition on hover
components.codeBlock.backgroundcomponents.codeBlock.borderColorcomponents.codeBlock.borderRadiuscomponents.codeBlock.paddingcomponents.codeBlock.fontFamilycomponents.codeBlock.fontSizecomponents.codeBlock.lineHeightcomponents.codeBlock.textColorcomponents.codeBlock.lineNumberColorcomponents.codeBlock.lineNumberWidthcomponents.codeBlock.headerBackgroundcomponents.codeBlock.headerColorcomponents.codeBlock.headerFontSizecomponents.codeBlock.copyColorcomponents.codeBlock.copyHoverColor
- Code content from CMS field
- Language from CMS metadata
- Renders code content
- Shows language header
- Has line numbers
- Copy button present
- Pre has role=region
- Line numbers are aria-hidden
- Hides line numbers when disabled
- Passes className through
KanbanBoard
Column-based kanban board with cards, tags, and counts.
Component Metadata
KanbanBoardProps (columns: KanbanColumn[], onCardClick?: (cardId, columnId) => void)- Board has role=region with aria-label
- Columns have role=list with aria-label
- Cards have role=listitem
- Cards are focusable and keyboard-accessible
- Horizontal scroll on overflow
- Min-width per column
- Border-color transition on card hover
components.kanbanBoard.columnBackgroundcomponents.kanbanBoard.columnBorderRadiuscomponents.kanbanBoard.columnPaddingcomponents.kanbanBoard.columnGapcomponents.kanbanBoard.columnMinWidthcomponents.kanbanBoard.headerColorcomponents.kanbanBoard.headerFontSizecomponents.kanbanBoard.countBackgroundcomponents.kanbanBoard.countColorcomponents.kanbanBoard.cardBackgroundcomponents.kanbanBoard.cardBorderRadiuscomponents.kanbanBoard.cardPaddingcomponents.kanbanBoard.cardBordercomponents.kanbanBoard.cardGapcomponents.kanbanBoard.titleColorcomponents.kanbanBoard.titleFontSizecomponents.kanbanBoard.descColorcomponents.kanbanBoard.descFontSize
- Columns from CMS workflow states
- Cards from CMS items
- Renders columns
- Has role=region
- Columns have role=list
- Cards have role=listitem
- Shows column titles
- Shows card count
- Clicking card calls onCardClick
- Passes className through
TreeView
Collapsible hierarchical tree with depth indentation and selection.
File Explorer
- src
- components
- Button.tsx
- Card.tsx
- index.ts
- package.json
Component Metadata
TreeViewProps (nodes: TreeNode[], selected?: string, onSelect?: (id) => void, defaultExpanded?: string[])- Root has role=tree with aria-label
- Items have role=treeitem
- Expandable items have aria-expanded
- Selected items have aria-selected
- Children groups have role=group
- Full-width by default
- Indent scales with depth
- Chevron rotation on expand
- Background transition on hover
components.treeView.indentSizecomponents.treeView.itemPaddingcomponents.treeView.itemBorderRadiuscomponents.treeView.itemHoverBackgroundcomponents.treeView.itemSelectedBackgroundcomponents.treeView.labelColorcomponents.treeView.labelFontSizecomponents.treeView.iconColorcomponents.treeView.iconSizecomponents.treeView.branchLineColorcomponents.treeView.gap
- Tree data from CMS hierarchy
- Expanded state from app
- Renders root nodes
- Has role=tree
- Items have role=treeitem
- Expand shows children
- Selected item has selected class
- Clicking calls onSelect
- Default expanded works
- Passes className through
StepIndicator
Multi-step progress indicator with numbered circles and connector lines.
Mid-progress / All Complete
- Info
- Payment
- 3Review
- 4Done
- Info
- Payment
- Review
- Done
Component Metadata
StepIndicatorProps (steps: Step[], currentStep: number)- Container has role=navigation with aria-label
- Uses ordered list (ol)
- Active step has aria-current=step
- Checkmark icon is aria-hidden
- Full-width flex layout
- Steps share equal space
- No animation by default
components.stepIndicator.circleSizecomponents.stepIndicator.circleBordercomponents.stepIndicator.circleBackgroundcomponents.stepIndicator.circleColorcomponents.stepIndicator.activeBackgroundcomponents.stepIndicator.activeBordercomponents.stepIndicator.activeColorcomponents.stepIndicator.completedBackgroundcomponents.stepIndicator.completedBordercomponents.stepIndicator.completedColorcomponents.stepIndicator.lineColorcomponents.stepIndicator.lineActiveColorcomponents.stepIndicator.lineHeightcomponents.stepIndicator.labelColorcomponents.stepIndicator.labelActiveColorcomponents.stepIndicator.labelFontSizecomponents.stepIndicator.fontSize
- Step labels from CMS workflow
- Current step from app state
- Renders all steps
- Has role=navigation
- Active step has aria-current
- Completed steps show checkmark
- Active step has active class
- Shows step labels
- Shows step numbers for pending
- Passes className through
Drawer
Sliding panel overlay from left or right edge with backdrop and dismiss.
Drawer requires interactive state — see /drawer route
Drawers use open/close state and portals. Visit the dedicated Drawer showcase for interactive demos.
Component Metadata
DrawerProps (open: boolean, onClose: () => void, title?: string, position?: 'left'|'right', children: ReactNode)- Panel has role=dialog with aria-modal=true
- Panel has aria-label
- Close button with aria-label
- Escape key closes drawer
- Overlay click closes drawer
- Max-width 90vw on small screens
- Full height
- Slide-in animation from edge
components.drawer.backgroundcomponents.drawer.overlayColorcomponents.drawer.widthcomponents.drawer.paddingcomponents.drawer.headerColorcomponents.drawer.headerFontSizecomponents.drawer.borderColorcomponents.drawer.closeColorcomponents.drawer.closeHoverColorcomponents.drawer.zIndexcomponents.drawer.shadowcomponents.drawer.transitionDuration
- Drawer title from CMS
- Content from CMS section
- Hidden when closed
- Visible when open
- Has role=dialog
- Shows title
- Close button fires onClose
- Overlay click fires onClose
- Has aria-modal=true
- Passes className through
ColorSwatch
Color palette swatches with selection ring, labels, and hex values.
With Labels
Without Labels
Component Metadata
ColorSwatchProps (colors: ColorSwatchColor[], selected?: string, onSelect?: (color) => void, showLabel?: boolean)- Container has role=listbox with aria-label
- Each swatch has role=option with aria-selected
- Swatch buttons have aria-label with color name/value
- Flex-wrap by default
- Gap-controlled spacing
- Scale transform on hover
- Box-shadow transition on select
components.colorSwatch.sizecomponents.colorSwatch.borderRadiuscomponents.colorSwatch.bordercomponents.colorSwatch.selectedRingcomponents.colorSwatch.labelColorcomponents.colorSwatch.labelFontSizecomponents.colorSwatch.hexColorcomponents.colorSwatch.hexFontSizecomponents.colorSwatch.gapcomponents.colorSwatch.tooltipBackgroundcomponents.colorSwatch.tooltipColorcomponents.colorSwatch.tooltipFontSizecomponents.colorSwatch.checkerBackground
- Color values from CMS token set
- Color labels from CMS config
- Renders swatch buttons
- Has role=listbox
- Swatch has role=option
- Selected swatch has selected class
- Clicking calls onSelect
- Shows labels when showLabel is true
- Shows hex values
- Passes className through
DatePicker
Calendar-based date picker with month navigation and today highlight.
Default / Pre-selected
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Component Metadata
DatePickerProps (value?: Date, onChange?: (date) => void)- Container has role=group with aria-label
- Grid has role=grid with aria-label
- Day buttons have aria-pressed for selection
- Today has aria-current=date
- Month label has aria-live=polite
- Nav buttons have aria-label
- Inline-block by default
- 7-column grid auto-sizes
- Background transition on day hover
components.datePicker.backgroundcomponents.datePicker.borderColorcomponents.datePicker.borderRadiuscomponents.datePicker.paddingcomponents.datePicker.shadowcomponents.datePicker.headerColorcomponents.datePicker.headerFontSizecomponents.datePicker.dayColorcomponents.datePicker.dayHoverBackgroundcomponents.datePicker.daySelectedBackgroundcomponents.datePicker.daySelectedColorcomponents.datePicker.dayTodayBordercomponents.datePicker.daySizecomponents.datePicker.dayFontSizecomponents.datePicker.weekdayColorcomponents.datePicker.navColorcomponents.datePicker.navHoverColor
- Default date from CMS field
- Date format from CMS config
- Renders calendar grid
- Shows current month label
- Has weekday headers
- Clicking a day calls onChange
- Selected day has selected class
- Today has today class
- Previous month navigation works
- Passes className through
Countdown
Countdown timer with days, hours, minutes, and seconds.
Live Timer
Component Metadata
CountdownProps (targetDate: Date, label?: string, onComplete?: () => void, showLabels?: boolean)- Container has role=timer
- aria-label from label prop
- Separators are aria-hidden
- Flex wrap for segments
- Min-width per segment
- Digits update every second via setInterval
components.countdown.bgcomponents.countdown.borderColorcomponents.countdown.borderRadiuscomponents.countdown.paddingcomponents.countdown.gapcomponents.countdown.digitColorcomponents.countdown.digitFontSizecomponents.countdown.labelColorcomponents.countdown.labelFontSizecomponents.countdown.separatorColorcomponents.countdown.separatorFontSize
- Target date from CMS datetime field
- Renders four segments
- Has role=timer
- Shows label when provided
- Shows digit labels by default
- Hides labels when showLabels=false
- Shows colons between segments
- Digits are zero-padded
- Passes className through
Meter
Visual meter bar with range-based color coding.
Range Colors
Component Metadata
MeterProps (value: number, min?: number, max?: number, low?: number, high?: number, label?: string, showValue?: boolean)- Track has role=meter with aria-valuenow, aria-valuemin, aria-valuemax
- aria-label from label prop
- Full width by default
- Height from token
- Width transition (0.3s ease)
components.meter.heightcomponents.meter.borderRadiuscomponents.meter.trackBgcomponents.meter.fillLowcomponents.meter.fillMediumcomponents.meter.fillHighcomponents.meter.fillOverflowcomponents.meter.labelColorcomponents.meter.labelFontSizecomponents.meter.valueColorcomponents.meter.valueFontSizecomponents.meter.gap
- Value from CMS metric
- Thresholds from CMS config
- Renders meter track
- Has role=meter
- Shows label
- Shows value text
- Low range has low class
- High range has high class
- Fill width reflects value
- Passes className through
CommandPalette
Searchable command overlay with keyboard shortcuts.
Interactive — see /command route
CommandPalette uses modal state and focus management. Visit the dedicated Command Palette showcase for interactive demos.
Component Metadata
CommandPaletteProps (open: boolean, onClose: () => void, items: CommandItem[], onSelect: (item: CommandItem) => void, placeholder?: string)- Dialog has role=dialog with aria-modal
- Search input has aria-label
- Items have role=option in role=listbox
- Escape key closes palette
- Max width 560px, centered
- Responsive via padding
- No enter/exit animation by default
components.commandPalette.overlayBgcomponents.commandPalette.bgcomponents.commandPalette.borderColorcomponents.commandPalette.borderRadiuscomponents.commandPalette.maxWidthcomponents.commandPalette.inputBgcomponents.commandPalette.inputColorcomponents.commandPalette.inputFontSizecomponents.commandPalette.inputPaddingcomponents.commandPalette.itemBgcomponents.commandPalette.itemBgHovercomponents.commandPalette.itemColorcomponents.commandPalette.itemPaddingcomponents.commandPalette.itemFontSizecomponents.commandPalette.shortcutColorcomponents.commandPalette.shortcutBgcomponents.commandPalette.emptyColor
- Commands from CMS configuration
- Labels from CMS data
- Renders when open
- Hidden when closed
- Has role=dialog
- Shows search input
- Shows command items
- Shows shortcut badge
- Shows empty state for no results
- Passes className through
Stat
KPI stat card with label, value, and trend indicator.
Dashboard Stats
Component Metadata
StatProps (label: string, value: string | number, trend?: StatTrend, trendText?: string, icon?: ReactNode)- Container has role=group with aria-label
- Icon is aria-hidden
- Trend arrows are aria-hidden
- Flex column layout
- Stacks naturally in grid
- No motion by default
components.stat.bgcomponents.stat.borderColorcomponents.stat.borderRadiuscomponents.stat.paddingcomponents.stat.labelColorcomponents.stat.labelFontSizecomponents.stat.valueColorcomponents.stat.valueFontSizecomponents.stat.trendUpColorcomponents.stat.trendDownColorcomponents.stat.trendFontSizecomponents.stat.trendIconSize
- Value from CMS metric field
- Trend from CMS calculated field
- Renders label
- Renders value
- Has role=group
- Up trend has up class
- Down trend has down class
- Shows trend text
- Renders icon when provided
- Passes className through
Banner
Announcement/notification banner with variant styling and optional dismiss.
Variants
Component Metadata
BannerProps (variant?: BannerVariant, children: ReactNode, onClose?: () => void, icon?: ReactNode)- Container has role=banner
- Close button has aria-label
- Icon is aria-hidden
- Full width, flex layout wraps content
- No motion by default
components.banner.bgcomponents.banner.borderColorcomponents.banner.borderRadiuscomponents.banner.paddingcomponents.banner.textColorcomponents.banner.fontSizecomponents.banner.iconSizecomponents.banner.gapcomponents.banner.infoBgcomponents.banner.infoBordercomponents.banner.successBgcomponents.banner.successBordercomponents.banner.warningBgcomponents.banner.warningBordercomponents.banner.errorBgcomponents.banner.errorBordercomponents.banner.closeColorcomponents.banner.closeSize
- Banner text from CMS field
- Variant from CMS status
- Renders children
- Has role=banner
- Info variant has info class
- Error variant has error class
- Shows close button when onClose provided
- Close button has aria-label
- Renders icon when provided
- Passes className through
ToggleGroup
Segmented button group for single or multi-select toggling.
Single Select
With Disabled
Component Metadata
ToggleGroupProps (options: ToggleOption[], value: string | string[], onChange: (value: string | string[]) => void, multiple?: boolean)- Container has role=group
- Each button has role=radio with aria-checked
- Disabled options marked with disabled attribute
- Inline-flex, wraps naturally
- Background and color transitions (0.15s)
components.toggleGroup.bgcomponents.toggleGroup.borderRadiuscomponents.toggleGroup.gapcomponents.toggleGroup.paddingcomponents.toggleGroup.buttonBgcomponents.toggleGroup.buttonBgActivecomponents.toggleGroup.buttonColorcomponents.toggleGroup.buttonColorActivecomponents.toggleGroup.buttonRadiuscomponents.toggleGroup.buttonPaddingXcomponents.toggleGroup.buttonPaddingYcomponents.toggleGroup.fontSize
- Options from CMS enum/select field
- Renders all options
- Has role=group
- Active option has active class
- Buttons have role=radio
- aria-checked on active button
- Disabled option has disabled attribute
- Inactive buttons lack active class
- Passes className through
Carousel
Content carousel with arrow navigation and dot indicators.
Interactive — see /carousel route
Carousel uses stateful navigation. Visit the dedicated Carousel showcase for interactive demos.
Component Metadata
CarouselProps (slides: CarouselSlide[], showDots?: boolean, showArrows?: boolean, autoPlay?: number)- Container has role=region with aria-roledescription=carousel
- Each slide has role=group with aria-roledescription=slide
- Dots have role=tablist with aria-selected
- Arrows have aria-label for previous/next
- Inactive slides have aria-hidden
- Full width by default
- Touch-friendly arrows
- CSS translateX transition (configurable duration)
- Dot background transition
components.carousel.gapcomponents.carousel.arrowSizecomponents.carousel.arrowBgcomponents.carousel.arrowBgHovercomponents.carousel.arrowColorcomponents.carousel.arrowRadiuscomponents.carousel.dotSizecomponents.carousel.dotColorcomponents.carousel.dotActiveColorcomponents.carousel.dotGapcomponents.carousel.transitionDurationcomponents.carousel.borderRadius
- Slides from CMS collection
- Image/content from CMS fields
- Renders slides
- Has role=region
- Shows arrow buttons
- Shows dot indicators
- Active dot has active class
- Hides arrows for single slide
- Slides have roledescription
- Passes className through
Rating
Star rating input with hover preview, numeric display, and disabled state.
Values
Disabled
Locked4/5Component Metadata
RatingProps (value?: number, max?: number, onChange?: (value: number) => void, showValue?: boolean, label?: string, disabled?: boolean)- Container has role=group with aria-label
- Each star is a button with aria-label
- Stars use aria-pressed for current value
- Disabled buttons prevent interaction
- Inline-flex by default
- Stars wrap naturally
- Fill color transition on hover (0.15s)
components.rating.starSizecomponents.rating.gapcomponents.rating.filledColorcomponents.rating.emptyColorcomponents.rating.hoverColorcomponents.rating.labelColorcomponents.rating.labelFontSizecomponents.rating.valueColorcomponents.rating.valueFontSizecomponents.rating.disabledOpacity
- Rating value from CMS field
- Label from CMS data
- Renders 5 stars by default
- Has role=group
- Shows label text
- Filled stars have filled class
- Empty stars lack filled class
- Shows numeric value when showValue
- Disabled adds disabled class
- Passes className through
StatusDot
Inline status indicator with colored dot, label, and optional pulse animation.
Variants
With Pulse
Component Metadata
StatusDotProps (variant?: StatusDotVariant, label?: string, pulse?: boolean)- Container has role=status
- aria-label combines variant and label
- Dot is aria-hidden
- Inline-flex by default
- Wraps with text
- Optional pulse animation on dot
components.statusDot.dotSizecomponents.statusDot.gapcomponents.statusDot.labelColorcomponents.statusDot.labelFontSizecomponents.statusDot.successColorcomponents.statusDot.warningColorcomponents.statusDot.errorColorcomponents.statusDot.infoColorcomponents.statusDot.neutralColorcomponents.statusDot.pulseScale
- Status from CMS field
- Label from CMS data
- Renders dot
- Has role=status
- Shows label text
- Success variant has success class
- Error variant has error class
- Dot is aria-hidden
- Pulse adds pulse class
- Passes className through
CopyButton
Click-to-copy button with clipboard API, success feedback, and auto-reset.
Default / Custom Labels
Component Metadata
CopyButtonProps (text: string, label?: string, successLabel?: string)- Button has aria-label with copy target text
- Success state updates aria-label
- Icons are aria-hidden
- Inline-flex by default
- Background/color transition (0.15s)
- Auto-reset after 2s
components.copyButton.bgcomponents.copyButton.bgHovercomponents.copyButton.bgSuccesscomponents.copyButton.colorcomponents.copyButton.colorSuccesscomponents.copyButton.borderColorcomponents.copyButton.borderRadiuscomponents.copyButton.paddingcomponents.copyButton.iconSizecomponents.copyButton.fontSizecomponents.copyButton.gap
- Copy text from CMS field
- Renders with label
- Has button role
- Shows copy icon
- Has aria-label with text
- Shows custom label
- Shows custom success label text in DOM
- Default label is Copy
- Passes className through
InlineEdit
Click-to-edit inline text field with Enter to save and Escape to cancel.
With Value / Empty / Disabled
Component Metadata
InlineEditProps (value: string, onSave?: (value) => void, onCancel?: () => void, placeholder?: string, disabled?: boolean)- View mode has role=button with aria-label
- Edit mode input has aria-label
- Keyboard: Enter activates edit, Enter saves, Escape cancels
- Edit icon is aria-hidden
- Inline-flex by default
- Input min-width 120px
- Background transition on hover (0.15s)
- Focus ring on input
components.inlineEdit.bgcomponents.inlineEdit.bgEditingcomponents.inlineEdit.colorcomponents.inlineEdit.colorPlaceholdercomponents.inlineEdit.borderColorcomponents.inlineEdit.borderColorFocuscomponents.inlineEdit.borderRadiuscomponents.inlineEdit.paddingcomponents.inlineEdit.fontSizecomponents.inlineEdit.iconSizecomponents.inlineEdit.iconColorcomponents.inlineEdit.hoverBg
- Value from CMS field
- Placeholder from CMS config
- Renders value text
- Has role=button in view mode
- Shows edit icon
- Shows placeholder when empty
- Disabled has disabled class
- Edit icon is aria-hidden
- Has aria-label in view mode
- Passes className through
DataList
Key-value pair list with optional dividers and striped rows.
With Dividers
- Components
- 66
- Tokens
- 983
- Tests
- 483
- Drift Score
- 100/100
Striped
- Framework
- Next.js 15
- CMS
- EmDash + Astro 6
- Design
- Penpot
Component Metadata
DataListProps (items: DataListItem[], dividers?: boolean, striped?: boolean)- Uses semantic dl/dt/dd elements
- Container has role=list
- Rows have role=listitem
- Full width by default
- Label width fixed, value flexible
- No animation
components.dataList.gapcomponents.dataList.labelColorcomponents.dataList.labelFontSizecomponents.dataList.labelWidthcomponents.dataList.valueColorcomponents.dataList.valueFontSizecomponents.dataList.dividerColorcomponents.dataList.stripedBgcomponents.dataList.padding
- Items from CMS key-value fields
- Renders all items
- Has role=list
- Rows have role=listitem
- Shows dt labels
- Shows dd values
- Dividers class when enabled
- Striped class when enabled
- Passes className through
Shadows
Box shadow tokens from subtle to glow effects.
sm
--shadow-sm
md
--shadow-md
lg
--shadow-lg
glow
--shadow-glow
glowGreen
--shadow-glowGreen
Border Radii
Border radius tokens from sharp to fully rounded.
none
0px
sm
4px
md
8px
lg
12px
xl
16px
2xl
24px
full
9999px