Skip to content

Add CalendarHeatmap component for activity visualization#40

Merged
jalexw merged 2 commits into
mainfrom
claude/determined-feynman-Ar3tB
May 12, 2026
Merged

Add CalendarHeatmap component for activity visualization#40
jalexw merged 2 commits into
mainfrom
claude/determined-feynman-Ar3tB

Conversation

@jalexw
Copy link
Copy Markdown
Contributor

@jalexw jalexw commented May 11, 2026

Summary

Introduces a new CalendarHeatmap component for visualizing daily activity data across date ranges, similar to GitHub's contribution graph. This component is useful for displaying metrics like contributions, deployments, API usage, or any time-series data where a single count per day is meaningful.

Key Changes

  • New CalendarHeatmap component (src/components/ui/calendar-heatmap/calendar-heatmap.tsx):

    • Renders a grid-based heatmap with cells representing individual days
    • Supports 5 color themes: default, primary, positive, warning, destructive
    • Offers 3 size variants: sm, md, lg
    • Configurable week start (Sunday or Monday)
    • Optional weekday labels, month labels, and legend display
    • Automatic threshold calculation based on dataset quartiles or custom thresholds
    • Accessible with proper ARIA labels and semantic HTML
    • Customizable tooltips and legend content
  • Comprehensive Storybook stories (src/components/ui/calendar-heatmap/CalendarHeatmap.stories.tsx):

    • Default, color variant, and size variant examples
    • Stories demonstrating custom thresholds and tooltips
    • Color and size matrix showcases
    • Dashboard composition example
    • Empty state handling
    • Synthetic data generation for consistent story rendering
  • Module exports (src/components/ui/calendar-heatmap/index.ts):

    • Exports component, variants, and type definitions
    • Updated main UI index to include new exports

Notable Implementation Details

  • UTC-based date handling: All dates are normalized to UTC to avoid timezone-related bugs
  • Intelligent threshold calculation: Uses quartile-based thresholds when not explicitly provided, ensuring good visual distribution across datasets of varying sizes
  • Flexible date input: Accepts both Date objects and ISO yyyy-mm-dd strings
  • Performance optimized: Uses useMemo for expensive computations (value mapping, threshold calculation, week grid generation)
  • Tailwind-safe class names: All color/level combinations are written in full to ensure Tailwind's content scanner picks them up
  • Accessible design: Includes proper ARIA labels, semantic grid structure, and keyboard-friendly tooltips
  • Responsive sizing: Dimensions scale with size variant (cell size, gap, border radius, font size)

https://claude.ai/code/session_01Y6Vm8h6hqzmRiLZYhPmMkJ

… across a date range. Fills a gap in the library: there was no primitive for rendering GitHub-style contribution graphs / activity grids — a common dashboard need (audit log frequency, schema mutations, deploy cadence, login density, API usage). The existing data-viz primitives (sparkline, number-ticker, stat-card) cover 1D series and single-number KPIs, but not 2D date-grid intensity, so this complements them without overlap. Implementation is pure SVG-free DOM (CSS-grid-style columns of day cells) so it scales crisply, prints cleanly, and avoids any new dependency. Date math is UTC-only to dodge browser timezone drift around DST boundaries; ISO `yyyy-mm-dd` strings and Date instances are both accepted. Cells use a five-step intensity scale (level 0 = no activity → level 4 = peak); thresholds are auto-derived from the dataset's quartile distribution but can be overridden via the `thresholds` prop for stable colour mapping across multiple heatmaps in the same view. cva-driven `color` variant (default brand-blue, primary, positive, warning, destructive — all sourced from @schemavaults/theme tokens with /20, /40, /65, full opacity steps so light and dark modes look correct out of the box) and `size` variant (sm/md/lg controlling cell size, gap, and label font-size). Optional weekday and month labels follow the GitHub convention (alternating weekday rows, month labels above the first column where the month transitions). `weekStart` is configurable (Sunday default, ISO Monday for European locales). `tooltipText` is a render-prop so callers can format counts however they want (e.g. multiplying by a unit, using locale formatting); falls back to a sensible default. `legendExtra` slot lets composers add date-range hints or threshold legends without bespoke layout code. Out-of-range cells in the surrounding 7×N grid are rendered as transparent spacers so the grid stays rectangular regardless of where startDate/endDate fall in the week. role=figure on the root and role=gridcell + aria-label on each active cell so screen readers can read individual day activity. Storybook story covers Default, Primary/Positive/Warning/Destructive colour variants, Small/Large sizes, MondayWeekStart, NoLabels, ShortRange (Q1 only), Sparse (low-density data), WithCustomThresholds, WithCustomTooltip, ColorVariantMatrix and SizeMatrix grid views, InsideCard (dashboard composition with totals header), and Empty (no data path).
@vercel
Copy link
Copy Markdown

vercel Bot commented May 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ui Ready Ready Preview, Comment May 11, 2026 10:56pm

Request Review

…the InsideCard Storybook story). The inner labels+grid row had no overflow handling, so when the root's `max-w-full` correctly capped its outer box at the parent's width, the fixed-pixel-width cells still bled past the right edge of the card. Two changes: (1) the inner labels+grid row is now `flex max-w-full min-w-0 overflow-x-auto`, so it becomes a horizontal scroll container when the natural grid width exceeds the available space — the legend underneath stays put and renders at the correct width; (2) `min-w-0` is added to the root variant so the heatmap can shrink below its intrinsic min-content when used as a flex/grid child elsewhere in the page. Standalone rendering (where the parent is wider than the grid) is unchanged: `w-fit` still makes the heatmap hug its content tightly, no scrollbar appears. Verified via typecheck, typecheck:storybook, lint — all clean.
@jalexw jalexw merged commit dea4606 into main May 12, 2026
8 checks passed
@jalexw jalexw deleted the claude/determined-feynman-Ar3tB branch May 12, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants