Skip to content

FRD: Chart Card

Document Summary

FieldDetails
Feature NameChart Card
StatusDraft
OwnerDavid Holmes
ContributorsDesign, Engineering
Target Releasev2.0.0 (P2)
Related LinksRoadmap item #47
Last Updated2026-05-26

Introduction

Overview

Chart Card combines a line, bar, or area chart with a card shell that includes a title, period selector, and delta badge. It provides a self-contained analytics widget that product teams can drop into dashboards without assembling chart, card, and metric primitives by hand. The existing charts.tsx (BarChart, LineChart, AreaChart) and metric-card.tsx provide the building blocks; Chart Card composes them into a single, opinionated widget.

Goals

  • Deliver a single <ChartCard> component that renders a chart inside a metric-card-like container with title, period selector, and delta badge.
  • Support line, bar, and area chart types via a chartType prop.
  • Use design-system tokens for all colors, spacing, and typography so the widget adapts to themes.
  • Ship Storybook stories covering all chart types, period selections, and edge cases.

Non-Goals

  • Real-time streaming data or WebSocket integration.
  • Server-side data fetching or query-layer concerns.
  • Custom chart types beyond line, bar, and area.
  • Drill-down or click-through interactions on chart data points.

Scope

In Scope

AreaDescription
Component<ChartCard> widget composing BarChart/LineChart/AreaChart + card shell
Period SelectorBuilt-in segmented control or select for time period switching (7d, 30d, 90d, 1y, custom)
Delta BadgeBadge showing absolute or percentage change with up/down/neutral trend styling
Token AlignmentAll visual properties use design-system color, spacing, and typography tokens
StoriesStorybook stories for each chart type, period states, loading/empty/error, and themed variants

Out of Scope

AreaReason
Data fetchingConsumer responsibility; widget accepts data via props
Animation libraryUses existing CSS transition tokens; no new motion library
Interactive tooltips beyond hoverDeferred to a future chart-interaction enhancement
Export-to-imageSeparate concern, not part of the card widget

Users and Pain Points

User Groups

UserDescriptionNeeds
DevelopersEngineers building SaaS dashboardsA composable, token-aligned chart widget with minimal setup
DesignersDesign-system consumersConsistent chart presentation that matches the DS visual language
Product ManagersDashboard stakeholdersQuick insight into metric trends at a glance

Pain Points

UserPain PointImpact
DevelopersMust manually compose chart + card + badge + period selector for every dashboard metricRepeated boilerplate, inconsistent layouts across products
DesignersCharts rendered with ad-hoc colors and spacing break visual consistencyBrand dilution and increased review burden

Definitions

TermDefinition
Chart CardA self-contained widget that pairs a chart visualization with metric metadata (title, delta, period) inside a card shell
Delta BadgeA small indicator showing the change in a metric value, styled with trend direction (up/down/neutral)
Period SelectorA segmented control or dropdown allowing the user to switch the displayed time range
Chart TypeOne of line, bar, or area determining the visualization style

Current State

Existing Behavior

The design system ships BarChart, LineChart, and AreaChart in charts.tsx, and MetricCard in metric-card.tsx. Developers combine these manually to build dashboard cards. There is no single widget that unifies a chart with a title, delta badge, and period selector.

Current Limitations

  • No composition primitive that pairs a chart with metric metadata.
  • Period selection must be implemented ad hoc by each consumer.
  • Delta badges on MetricCard are disconnected from the chart data.

Existing Workarounds

  • Developers wrap MetricCard and BarChart in a custom div and wire state manually.
  • Period selectors are built from scratch using SegmentedControl or Select without a shared pattern.

Proposed Solution

Summary

Introduce a <ChartCard> widget that accepts a chartType, data array, title, optional delta/deltaDirection, and a periods config. The component renders a card shell with a header row (title + period selector + delta badge) and a chart body. The period selector calls an onPeriodChange callback so the consumer can swap data.

Key Capabilities

  • Render line, bar, or area chart from a single component.
  • Built-in period selector (segmented control for up to 4 options, select dropdown for more).
  • Delta badge with automatic trend coloring via deltaDirection.
  • Loading skeleton, empty state, and error state built in.

User Experience

Users see a card with a descriptive title, a period toggle in the header, the current delta, and a chart filling the card body. Switching periods triggers a brief skeleton transition while data loads.

Developer Experience

<ChartCard
title="Revenue"
chartType="area"
data={revenueData}
delta="+12.4%"
deltaDirection="up"
periods={["7d", "30d", "90d"]}
activePeriod="30d"
onPeriodChange={setPeriod}
/>

Requirements

IDRequirementPriorityNotes
FR-001ChartCard renders line, bar, or area chart based on chartType propMustDelegates to existing chart components
FR-002ChartCard displays a title in the card headerMust-
FR-003ChartCard includes a period selector when periods is providedMust-
FR-004ChartCard shows a delta badge when delta prop is setShould-
FR-005ChartCard supports loading, empty, and error statesMust-
FR-006All colors and spacing use design-system tokensMust-

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001chartType prop accepts "line", "bar", or "area" and renders the corresponding chartSingle API for multiple chart typesMust
FUNC-002periods prop renders a segmented control (up to 4 items) or select (5+ items)Familiar period switching UXMust
FUNC-003onPeriodChange fires when user selects a new periodConsumer controls data fetchingMust
FUNC-004delta and deltaDirection render a badge with color-coded trend indicatorAt-a-glance metric performanceShould
FUNC-005When data is empty and not loading, display an empty state messageClear feedback when no data existsMust
FUNC-006When isLoading is true, display a skeleton matching the chart shapeSmooth loading transitionMust
FUNC-007When error prop is set, display an error state with retry optionRecoverable error handlingMust

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001Chart Card renders in under 50ms for datasets up to 365 data pointsPerformanceMust
NFR-002Component tree-shakes unused chart types when bundledPerformanceShould
NFR-003All interactive elements are keyboard-navigableAccessibilityMust
NFR-004Component works in light and dark themes without additional configurationThemingMust
NFR-005No new runtime dependencies beyond existing chart primitivesMaintainabilityMust

API / Interface Requirements

Public API

NameTypeDescriptionRequired
titlestringCard heading textYes
chartType"line" | "bar" | "area"Chart visualization typeYes
dataChartDataPoint[]Array of label/value data pointsYes
deltastringChange text displayed in badgeNo
deltaDirection"up" | "down" | "neutral"Trend direction for badge coloringNo
periodsstring[]Available time period optionsNo
activePeriodstringCurrently selected periodNo
onPeriodChange(period: string) => voidCallback when period changesNo
isLoadingbooleanShow loading skeletonNo
errorstringError message to displayNo
heightnumberChart height in pixelsNo
classNamestringAdditional CSS classesNo

Example Usage

import { ChartCard } from "@/components/ui/chart-card";
<ChartCard
title="Monthly Revenue"
chartType="line"
data={[
{ label: "Jan", value: 4200 },
{ label: "Feb", value: 4800 },
{ label: "Mar", value: 5100 },
]}
delta="+8.2%"
deltaDirection="up"
periods={["7d", "30d", "90d"]}
activePeriod="30d"
onPeriodChange={(p) => fetchData(p)}
/>

API Notes

  • Reuses ChartDataPoint interface from charts.tsx.
  • Period selector auto-selects segmented control or select based on option count.
  • height defaults to 220px matching existing chart defaults.

Accessibility Requirements

IDRequirementNotes
A11Y-001Chart region has role="img" with descriptive aria-labelDelegates to underlying chart component
A11Y-002Period selector is keyboard-navigable with arrow keysUses existing SegmentedControl/Select a11y
A11Y-003Delta badge conveys trend direction to screen readersaria-label includes direction text, not just color
A11Y-004Loading state announces to assistive technologyaria-busy="true" on card during loading
A11Y-005Error state is announced as an alertrole="alert" on error message

Checklist

  • Keyboard support is defined.
  • Focus behavior is defined.
  • Screen reader behavior is defined.
  • Color contrast requirements are met.
  • Reduced motion behavior is considered.
  • Semantic HTML expectations are documented.
  • ARIA usage is defined only where needed.

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001Storybook docs page with overview, props table, and usage examplesStorybookMust
DOC-002”When to use / When not to use” guidanceStorybook docsMust
DOC-003Example stories for each chart type with realistic dataStorybookMust
DOC-004Loading, empty, and error state storiesStorybookMust

Dependencies

DependencyTypeOwnerStatusNotes
charts.tsx (BarChart, LineChart, AreaChart)EngineeringDesign SystemReadyExisting components
metric-card.tsx (MetricCard)EngineeringDesign SystemReadyReference for card shell pattern
SegmentedControlEngineeringDesign SystemReadyUsed for period selector
SelectEngineeringDesign SystemReadyFallback for 5+ periods
BadgeEngineeringDesign SystemReadyDelta badge
Design tokens (color, spacing, typography)DesignDesign SystemReady-

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
Composing three chart types in one component increases bundle size for consumers who only need oneLarger import sizeTree-shaking via conditional imports; document single-chart usage pattern
Period selector pattern may not cover all consumer date-range needsConsumers with custom ranges must build their ownAccept periodSelector render prop as escape hatch
Chart data format is coupled to ChartDataPointConsumers with different data shapes must transformProvide a transformData utility or document the expected shape clearly

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should the period selector support a custom date range picker?David HolmesOpen
Q-002Should chartType support "sparkline" for compact inline usage?David HolmesOpen
Q-003Should the card support a footer slot for supplementary actions?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001&lt;ChartCard chartType="line"&gt; renders a line chart inside a card shellFR-001
AC-002&lt;ChartCard chartType="bar"&gt; renders a bar chart inside a card shellFR-001
AC-003&lt;ChartCard chartType="area"&gt; renders an area chart inside a card shellFR-001
AC-004Period selector renders and fires onPeriodChange on selectionFUNC-002, FUNC-003
AC-005Delta badge displays with correct trend color for up/down/neutralFUNC-004
AC-006Empty state displays when data is empty and isLoading is falseFUNC-005
AC-007Loading skeleton displays when isLoading is trueFUNC-006
AC-008Error state displays with message when error is setFUNC-007
AC-009All Storybook stories render without errorsDOC-003
AC-010Component passes axe accessibility auditA11Y-001

LLM Handoff Instructions

Expected LLM Behavior

  • Follow the requirements and acceptance criteria in this document.
  • Do not expand scope beyond the In Scope section.
  • Respect the Out of Scope section.
  • Compose from existing chart components in charts.tsx and patterns from metric-card.tsx.
  • Use SegmentedControl for period selector when periods count is 4 or fewer; use Select for 5+.
  • Use Badge for delta display with appropriate tone based on deltaDirection.
  • Preserve existing ChartDataPoint interface; do not modify charts.tsx.
  • Add stories under the SaaS Widgets Storybook section.
  • Add or update tests that map to the acceptance criteria.

LLM Should Not

  • Invent undocumented product behavior.
  • Add charting libraries (recharts, chart.js, etc.) — use existing CSS-based chart components.
  • Modify existing chart or metric-card components.
  • Change unrelated components.

Decision Log

DateDecisionReasonOwner
2026-05-26Compose from existing chart primitives rather than introducing a charting libraryKeeps bundle small, maintains token alignment, avoids new dependencyDavid Holmes
2026-05-26Use segmented control for period selector with select fallbackMatches existing DS patterns for option switchingDavid Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft