Skip to content

Chart and Element Guide

snoo edited this page Apr 7, 2026 · 1 revision

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.


Chart Selection Matrix

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

Forbidden Chart Types

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

Area Chart

Best for: time-series trends (revenue, traffic, stock price).

Style Rules

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)

Code Example

<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>

Bar Chart

Best for: comparing discrete items or periods.

Style Rules

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

Key Rule: Only the Max Value Gets Color

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.


Donut Chart

Best for: part-of-whole comparisons (3-4 categories).

Style Rules

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

Legend List

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

Interaction

  • 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

Progress Bar

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

Segment Bar

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

Pairing Charts with Elements

Chart + Bottom Stats (Most Common)

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>

Chart + Period Toggle (Header)

<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>

Donut + Legend Side by Side

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>

Title Margins by Content Type

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

Using TS Tokens in Charts

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 }} />