feat(chart): add @hm-design/chart package with Bar, Line, Donut, Pie, Radar, Area and Radial charts

- new workspace package packages/chart (shadcn/ui Chart + Recharts, HM Theme tokens), examples entry with sample data
- Storybook Chart group (src/chart), consumer example (examples/chart), build:chart / build:chart-example scripts
- README rewritten as the AI usage guide (install, CSS wiring, chart selection, data rules, verification)
- docs: package count/list, Submodule guide (chart workspace, React dedupe), shared AI_GUIDE row, MAINTAINING, story inventory
This commit is contained in:
shkim4 committed 2026-11-06 15:52:47 +09:00
1 parent 228944a06b
commit dc9258599c
56 files changed
+3979 -28

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
/* Chart mapping: the shadcn/ui Chart (Recharts) wired to the HM Theme. HM has no separate chart spec yet, so series colors are
derived from existing HM palette Semantic tokens (green / brown / warm neutral); no HEX value is defined here.
Requires @hm-design/ui/theme.css (the --hm-sem-* tokens). Runtime Theme overrides of the palette flow through these aliases. */
:root, [data-hm-theme] {
/* Series order alternates green and brown so neighbouring series stay distinguishable (bar, line and donut slices). */
--hm-chart-1: var(--hm-sem-palette-green-light-2);
--hm-chart-2: var(--hm-sem-palette-brown-light-2);
--hm-chart-3: var(--hm-sem-palette-green-light-3);
--hm-chart-4: var(--hm-sem-palette-brown-light-4);
--hm-chart-5: var(--hm-sem-palette-warm-neutral-mid);
/* Light end of a stacked segment: the next palette step(s) lighter in the same family as the series color.
Brown Light 4 is the lightest brown token, so series 4 keeps its own color (a flat segment) instead of borrowing another family. */
--hm-chart-1-light: var(--hm-sem-palette-green-light-3);
--hm-chart-2-light: var(--hm-sem-palette-brown-light-3);
--hm-chart-3-light: var(--hm-sem-palette-green-light-4);
--hm-chart-4-light: var(--hm-sem-palette-brown-light-4);
--hm-chart-5-light: var(--hm-sem-palette-warm-neutral-light);
--hm-chart-tooltip-background: var(--hm-sem-bg-surface);
--hm-chart-tooltip-border-color: var(--hm-sem-stroke-default);
--hm-chart-tooltip-radius: var(--hm-radius-container);
--hm-chart-tooltip-shadow: var(--hm-sem-floating-shadow);
--hm-chart-tooltip-text: var(--hm-sem-fg-primary);
--hm-chart-tooltip-label: var(--hm-sem-fg-secondary);
--hm-chart-grid-color: var(--hm-sem-stroke-default);
--hm-chart-axis-text: var(--hm-sem-fg-secondary);
--hm-chart-center-value: var(--hm-sem-fg-primary);
--hm-chart-font-family: var(--hm-font-family-base);
}
[data-slot="chart"] {
font-family: var(--hm-chart-font-family);
/* The container may sit in a flex row or a narrow card: let it shrink instead of overflowing. */
min-width: 0;
max-width: 100%;
}
[data-slot="chart"] .recharts-cartesian-axis-tick text {
fill: var(--hm-chart-axis-text);
}
[data-slot="chart"] .recharts-cartesian-grid line {
stroke: var(--hm-chart-grid-color);
stroke-opacity: 0.6;
}
[data-slot="chart-tooltip-content"] {
border: 1px solid var(--hm-chart-tooltip-border-color);
border-radius: var(--hm-chart-tooltip-radius);
background: var(--hm-chart-tooltip-background);
color: var(--hm-chart-tooltip-text);
box-shadow: var(--hm-chart-tooltip-shadow);
}
[data-slot="chart-tooltip-content"] .text-muted-foreground {
color: var(--hm-chart-tooltip-label);
}
[data-slot="chart-legend-content"] {
color: var(--hm-chart-tooltip-label);
}
/* Donut center total. */
[data-slot="chart-center-value"] {
fill: var(--hm-chart-center-value);
font-size: 24px;
font-weight: var(--hm-font-weight-bold);
}
[data-slot="chart-center-label"] {
fill: var(--hm-chart-axis-text);
font-size: 12px;
}
/* "Sample data" marker used by the @hm-design/chart/examples entry. */
[data-slot="chart-sample-notice"] {
color: var(--hm-chart-axis-text);
font-size: 12px;
}
/* Horizontal bar chart labels. The item name sits on a gradient that starts at 30% of the series color, so the dark text gets
a light halo (the card surface) to stay readable from the faint left end to the opaque right end. */
:root, [data-hm-theme] {
--hm-chart-bar-name-text: var(--hm-sem-fg-primary);
--hm-chart-bar-name-halo: var(--hm-sem-bg-surface);
--hm-chart-bar-value-text: var(--hm-sem-fg-primary);
}
[data-slot="chart-bar-name"],
[data-slot="chart-bar-value"] {
font-size: 12px;
font-weight: var(--hm-font-weight-medium);
pointer-events: none;
}
[data-slot="chart-bar-name"] {
fill: var(--hm-chart-bar-name-text);
stroke: var(--hm-chart-bar-name-halo);
stroke-width: 3px;
stroke-linejoin: round;
paint-order: stroke;
}
[data-slot="chart-bar-value"] {
fill: var(--hm-chart-bar-value-text);
}
/* Name of a bar too short to hold it: shown after the value on the card surface, so it uses the secondary text color. */
[data-slot="chart-bar-outside-name"] {
fill: var(--hm-chart-axis-text);
font-size: 12px;
pointer-events: none;
}
/* 0 line of the positive / negative bar chart: clearly darker than the regular gridlines. */
:root, [data-hm-theme] {
--hm-chart-zero-line: var(--hm-sem-fg-secondary);
}
/* Value label above a line chart Dot; its color is set per point (series color or the point's own color). */
[data-slot="chart-point-label"] {
font-size: 12px;
font-weight: var(--hm-font-weight-medium);
pointer-events: none;
}
/* Pie: outside "name value" labels, value labels inside slices, and the ring key of the stacked pie. */
[data-slot="chart-pie-label"] {
fill: var(--hm-chart-axis-text);
font-size: 12px;
pointer-events: none;
}
[data-slot="chart-slice-label"] {
fill: var(--hm-chart-bar-name-text);
stroke: var(--hm-chart-bar-name-halo);
stroke-width: 3px;
stroke-linejoin: round;
paint-order: stroke;
font-size: 12px;
font-weight: var(--hm-font-weight-medium);
pointer-events: none;
}
/* Radar: custom axis names. */
[data-slot="chart-axis-label"] {
fill: var(--hm-chart-axis-text);
font-size: 12px;
}
/* Radial: item names curved along the start of each bar. The halo keeps the text readable on pale bar colors. */
.hm-radial-name {
fill: var(--hm-chart-bar-name-text);
stroke: var(--hm-chart-bar-name-halo);
stroke-width: 2px;
stroke-linejoin: round;
paint-order: stroke;
font-size: 11px;
font-weight: var(--hm-font-weight-medium);
pointer-events: none;
}