-
Notifications
You must be signed in to change notification settings - Fork 36
Chart and Element Guide
StyleSeed supports 5 visualization types. This guide covers when to use each, how to style them, and how to pair them with surrounding elements.
| Data Question | Chart Type | Example |
|---|---|---|
| "How has X changed over time?" | Area Chart | Revenue trend, weight over 30 days |
| "Which period/item is biggest?" | Bar Chart | Daily sales comparison |
| "What % does each part contribute?" | Donut Chart | Category breakdown, asset allocation |
| "How close to 100% is X?" | Progress Bar | Goal completion, inventory level |
| "How many of N steps are done?" | Segment Bar | Tasks completed (8/10) |
| "What is the exact number?" | Big Metric (no chart) | When precision matters more than shape |
No 3D charts
No dual Y-axis
No stacked bar charts
No radar/spider charts
No treemaps
No candlestick charts
No one card with 2+ charts
Best for: time-series trends (revenue, traffic, stock price).
| Property | Value |
|---|---|
| Line stroke |
#721FE5 (accent) |
| Line width | strokeWidth={2.5} |
| Fill | Gradient: accent at 15% opacity -> 0% (top to bottom) |
| Dots | Hidden (dot={false}) |
| X-axis labels | 10px, #7A7A7A, no tick marks, no axis line |
| Y-axis | Completely hidden |
| Height |
h-40 (160px) |
| Card margin |
-mx-2 (bleed slightly beyond card padding) |
<ChartCard title="Revenue Trend" periods={["1W", "1M", "3M"]} activePeriod="1M"
stats={[
{ label: "Average", value: "42K", unit: "USD" },
{ label: "Peak", value: "68K", unit: "USD" },
{ label: "Current", value: "55K", unit: "USD" },
]}>
<div className="h-40 -mx-2">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={data}>
<defs>
<linearGradient id="grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#721FE5" stopOpacity={0.15} />
<stop offset="100%" stopColor="#721FE5" stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="date" tick={{ fontSize: 10, fill: "#7A7A7A" }}
axisLine={false} tickLine={false} />
<YAxis hide />
<Area type="monotone" dataKey="value" stroke="#721FE5"
strokeWidth={2.5} fill="url(#grad)" dot={false} />
</AreaChart>
</ResponsiveContainer>
</div>
</ChartCard>Best for: comparing discrete items or periods.
| Property | Value |
|---|---|
| Max bar color |
#721FE5 (accent) -- only the highest bar |
| Other bars |
#E8E6E1 (surface-muted) |
| Corner radius |
radius={[8, 8, 0, 0]} (top corners only) |
| Axis lines | Hidden |
| Tick marks | Hidden |
| Height |
h-44 (176px) |
| Card margin | -mx-1 |
const barData = data.map(d => ({
...d,
fill: d.value === maxValue ? tokens.colors.brand : tokens.colors.surface.muted,
}))This draws immediate attention to the peak without overwhelming the chart.
Best for: part-of-whole comparisons (3-4 categories).
| Property | Value |
|---|---|
| Container | 128x128px (size-32) |
| Inner radius | 50px (39% of container) |
| Outer radius | 64px (50% of container) |
| Ring thickness | 14px |
| Segment gap |
paddingAngle={4} (4 degrees) |
| Corner radius | cornerRadius={8} |
| Selected color |
#721FE5 (accent) |
| Unselected colors |
#D4D4D4, #A8A8A8, #8B8B8B, #6B6B6B
|
| Unselected opacity | 0.3 |
| Center number | 24px bold |
| Center label | 10px medium uppercase |
| Property | Value |
|---|---|
| Color dot |
size-3 rounded-full (12px) |
| Name | 13px semibold #2A2A2A
|
| Value | 15px bold #2A2A2A
|
| Row gap | space-y-3.5 |
| Dot-to-name gap | gap-2.5 |
| Clickable |
cursor-pointer, opacity transition duration-300
|
- Tap a segment: it becomes accent color at full opacity, others go gray at 0.3
- Tap a legend row: same selection behavior
- Selected segment gets glow:
box-shadow: 0 0 0 2px #721FE540 - Legend row of unselected item: opacity 0.4
Best for: continuous percentage (0-100%).
{/* Standard progress bar */}
<div className="bg-surface-muted rounded-full h-4 overflow-hidden">
<div className="bg-brand h-full w-[68%] rounded-full" />
</div>
{/* Progress bar with label */}
<div className="flex items-center gap-2">
<div className="flex-1 bg-surface-muted rounded-full h-4">
<div className="bg-brand h-full w-[68%] rounded-full" />
</div>
<span className="text-[11px] text-text-primary font-bold">68%</span>
</div>| Property | Value |
|---|---|
| Track height |
h-4 (16px) |
| Track color |
bg-surface-muted (#E8E6E1) |
| Track corners | rounded-full |
| Fill color |
bg-brand (#721FE5) |
| Fill corners | rounded-full |
Best for: discrete progress (n out of N steps).
<div className="flex gap-1">
{[...Array(10)].map((_, i) => (
<div key={i} className={`h-6 flex-1 rounded ${i < filled ? 'bg-brand' : 'bg-surface-muted'}`} />
))}
</div>| Property | Value |
|---|---|
| Segment height |
h-6 (24px) |
| Segment gap |
gap-1 (4px) |
| Segment corners |
rounded (4px) |
| Active color | bg-brand |
| Inactive color | bg-surface-muted |
Use border-t to separate the chart from a stats grid below it:
<div className="h-40 -mx-2 mb-6">
{/* Chart */}
</div>
<div className="grid grid-cols-3 gap-3 pt-5 border-t border-surface-muted">
<div className="text-center">
<p className="text-[11px] text-text-secondary mb-1.5 font-medium uppercase">LABEL</p>
<p className="text-text-primary font-bold text-[18px] leading-none whitespace-nowrap">
1,648<span className="text-[10px] ms-0.5">/L</span>
</p>
</div>
{/* more stat cells */}
</div><div className="flex items-center justify-between mb-6">
<h3 className="text-[18px] font-bold text-text-primary">Revenue</h3>
<div className="flex gap-1 bg-surface-muted p-1 rounded-full">
<button className="px-4 py-1.5 text-[11px] font-bold rounded-full bg-brand text-white shadow-sm">1W</button>
<button className="px-4 py-1.5 text-[11px] font-bold rounded-full text-text-disabled">1M</button>
<button className="px-4 py-1.5 text-[11px] font-bold rounded-full text-text-disabled">3M</button>
</div>
</div>For compact layouts, place the donut and legend in a flex row:
<div className="flex items-center gap-6">
<div className="size-32 flex-shrink-0">{/* Donut */}</div>
<div className="space-y-3.5 flex-1">{/* Legend rows */}</div>
</div>The space between a section title and its content depends on what follows:
| Content After Title | Margin | Class |
|---|---|---|
| List | 16px | mb-4 |
| Table | 20px | mb-5 |
| Chart | 24px | mb-6 |
| Donut + legend | 16px | mb-4 |
Recharts and other charting libraries need color values as strings, not CSS classes. Use the TypeScript token export:
import { tokens } from "@/tokens"
// In Recharts
<Area stroke={tokens.colors.brand} />
<Bar fill={isMax ? tokens.colors.brand : tokens.colors.surface.muted} />
// For dynamic styles
<div style={{ boxShadow: tokens.shadows.card }} />