Skip to content

Commit 5115cc3

Browse files
boramyi-tsclaude
andauthored
feat: SW-2159 add text-2xs (10px) font-size token (#169)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 34ba183 commit 5115cc3

4 files changed

Lines changed: 124 additions & 206 deletions

File tree

DESIGN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
### Typography
2525

2626
- **Font family**: Inter Variable (`@fontsource-variable/inter`), variable weight 100–900
27-
- Scale follows Tailwind defaults (`text-xs``text-4xl`)
27+
- Scale follows Tailwind defaults (`text-xs``text-4xl`), plus a kit-added `text-2xs` (10px) for dense UI
2828
- No custom type scale — lean on Tailwind utilities directly
2929

3030
### Spacing & Radius

src/components/Typography.stories.tsx

Lines changed: 121 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,173 @@
11
import type { Meta, StoryObj } from "@storybook/react-vite"
2+
import type { ReactNode } from "react"
23

34
// ---------------------------------------------------------------------------
4-
// Typescale definitions — sourced from src/index.tailwind.css
5+
// Typography reference — Inter Variable on the Tailwind font-size scale.
6+
// The kit intentionally has NO custom type scale (see DESIGN.md); components
7+
// compose Tailwind's `text-*` size + `font-*` weight utilities directly, plus
8+
// the `text-2xs` (10px) extension for dense UI. SW-2254 retired the former
9+
// (unused) MD3 typescale utilities.
510
// ---------------------------------------------------------------------------
611

7-
interface TypescaleLevel {
8-
name: string
9-
role: string
10-
size: string
12+
const SAMPLE_TEXT = "The quick brown fox jumps over the lazy dog"
13+
14+
interface FontSize {
1115
cssClass: string
12-
lineHeight: string
13-
weight: number
14-
tracking: string
16+
size: string
17+
note: string
18+
tag?: "New" | "Default"
1519
}
1620

17-
const TYPESCALE_LEVELS: TypescaleLevel[] = [
18-
{ name: "Display Large", role: "display", size: "57px", cssClass: "text-display-lg", lineHeight: "64px", weight: 400, tracking: "-0.25px" },
19-
{ name: "Display Medium", role: "display", size: "45px", cssClass: "text-display-md", lineHeight: "52px", weight: 400, tracking: "0px" },
20-
{ name: "Display Small", role: "display", size: "36px", cssClass: "text-display-sm", lineHeight: "44px", weight: 400, tracking: "0px" },
21-
{ name: "Headline Large", role: "headline", size: "32px", cssClass: "text-headline-lg", lineHeight: "40px", weight: 400, tracking: "0px" },
22-
{ name: "Headline Medium", role: "headline", size: "28px", cssClass: "text-headline-md", lineHeight: "36px", weight: 400, tracking: "0px" },
23-
{ name: "Headline Small", role: "headline", size: "24px", cssClass: "text-headline-sm", lineHeight: "32px", weight: 400, tracking: "0px" },
24-
{ name: "Title Large", role: "title", size: "22px", cssClass: "text-title-lg", lineHeight: "28px", weight: 500, tracking: "0px" },
25-
{ name: "Title Medium", role: "title", size: "16px", cssClass: "text-title-md", lineHeight: "24px", weight: 500, tracking: "0.15px" },
26-
{ name: "Title Small", role: "title", size: "14px", cssClass: "text-title-sm", lineHeight: "20px", weight: 500, tracking: "0.1px" },
27-
{ name: "Body Large", role: "body", size: "16px", cssClass: "text-body-lg", lineHeight: "24px", weight: 400, tracking: "0.5px" },
28-
{ name: "Body Medium", role: "body", size: "14px", cssClass: "text-body-md", lineHeight: "20px", weight: 400, tracking: "0.25px" },
29-
{ name: "Body Small", role: "body", size: "12px", cssClass: "text-body-sm", lineHeight: "16px", weight: 400, tracking: "0.4px" },
30-
{ name: "Label Large", role: "label", size: "14px", cssClass: "text-label-lg", lineHeight: "20px", weight: 500, tracking: "0.1px" },
31-
{ name: "Label Medium", role: "label", size: "12px", cssClass: "text-label-md", lineHeight: "16px", weight: 500, tracking: "0.5px" },
32-
{ name: "Label Small", role: "label", size: "11px", cssClass: "text-label-sm", lineHeight: "16px", weight: 500, tracking: "0.5px" },
21+
// Tailwind v4 built-in font sizes; only `text-2xs` is custom (src/index.tailwind.css).
22+
const FONT_SIZES: FontSize[] = [
23+
{ cssClass: "text-2xs", size: "10px", note: "Dense labels, chips, chart axis ticks, table metadata", tag: "New" },
24+
{ cssClass: "text-xs", size: "12px", note: "Captions, badges, secondary metadata" },
25+
{ cssClass: "text-sm", size: "14px", note: "Default body & UI text — the most common size", tag: "Default" },
26+
{ cssClass: "text-base", size: "16px", note: "Inputs, emphasized body, small titles" },
27+
{ cssClass: "text-lg", size: "18px", note: "Sub-headings" },
28+
{ cssClass: "text-xl", size: "20px", note: "Section headings" },
29+
{ cssClass: "text-2xl", size: "24px", note: "Page titles" },
30+
{ cssClass: "text-3xl", size: "30px", note: "Display headings" },
31+
{ cssClass: "text-4xl", size: "36px", note: "Largest — hero / marketing" },
3332
]
3433

35-
const SAMPLE_TEXT = "The quick brown fox jumps over the lazy dog"
36-
37-
const ROLE_COLORS: Record<string, string> = {
38-
display: "bg-primary/10 text-primary",
39-
headline: "bg-accent/10 text-accent",
40-
title: "bg-positive/10 text-positive",
41-
body: "bg-info/10 text-info",
42-
label: "bg-warning/10 text-warning",
34+
interface FontWeight {
35+
cssClass: string
36+
weight: number
37+
note: string
4338
}
4439

40+
// Inter Variable ships the full 100–900 range; these are the weights the kit uses.
41+
const FONT_WEIGHTS: FontWeight[] = [
42+
{ cssClass: "font-normal", weight: 400, note: "Body text" },
43+
{ cssClass: "font-medium", weight: 500, note: "Labels, buttons, emphasis" },
44+
{ cssClass: "font-semibold", weight: 600, note: "Section headings" },
45+
{ cssClass: "font-bold", weight: 700, note: "Page titles" },
46+
]
47+
4548
// ---------------------------------------------------------------------------
4649
// Components
4750
// ---------------------------------------------------------------------------
4851

52+
function Code({ children }: { children: ReactNode }) {
53+
return <code className="rounded bg-muted px-1 py-0.5 text-xs">{children}</code>
54+
}
55+
4956
function CopyButton({ text }: { text: string }) {
5057
return (
5158
<button
5259
type="button"
5360
onClick={() => navigator.clipboard.writeText(text)}
54-
className="inline-flex items-center rounded-md border border-border bg-transparent px-2 py-0.5 text-xs font-mono text-muted-foreground hover:bg-muted cursor-pointer select-all transition-colors"
61+
className="inline-flex items-center whitespace-nowrap rounded-md border border-border bg-transparent px-2 py-0.5 text-xs font-mono text-muted-foreground hover:bg-muted cursor-pointer select-all transition-colors"
5562
title={`Copy: ${text}`}
5663
>
5764
{text}
5865
</button>
5966
)
6067
}
6168

62-
function TypographyPage() {
63-
let lastRole = ""
69+
function TagBadge({ tag }: { tag: "New" | "Default" }) {
70+
const cls = tag === "New" ? "bg-positive/10 text-positive" : "bg-info/10 text-info"
71+
return <span className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${cls}`}>{tag}</span>
72+
}
73+
74+
const TH = "px-4 py-2.5 text-left font-medium text-muted-foreground"
75+
const TD = "px-4 py-2.5"
76+
const TR = "border-b border-border last:border-b-0 hover:bg-muted/30 transition-colors"
6477

78+
function TypographyPage() {
6579
return (
6680
<div className="mx-auto max-w-7xl space-y-10 p-12">
6781
<div className="space-y-2">
68-
<h1 className="text-2xl font-bold text-foreground">Typography Scale</h1>
69-
<p className="text-sm text-muted-foreground max-w-2xl">
70-
Material Design 3 typescale with 15 levels across 5 roles (Display, Headline, Title, Body, Label),
71-
tuned for Inter Variable. Each level is available as a single Tailwind utility class that sets
72-
font-size, line-height, font-weight, and letter-spacing.
82+
<h1 className="text-2xl font-bold text-foreground">Typography</h1>
83+
<p className="max-w-2xl text-sm text-muted-foreground">
84+
The UI kit uses <span className="font-medium text-foreground">Inter Variable</span> on Tailwind's
85+
native font-size scale — there is no custom type scale. Compose size and weight utilities directly
86+
(e.g. <Code>text-sm font-medium</Code>). The one addition is <Code>text-2xs</Code> (10px) for dense UI.
7387
</p>
7488
</div>
7589

76-
{/* Quick reference table */}
90+
{/* Font family */}
91+
<section className="space-y-3">
92+
<h2 className="text-lg font-semibold text-foreground">Font Family</h2>
93+
<div className="space-y-1 rounded-lg border border-border bg-card p-6">
94+
<p className="text-3xl text-foreground">Inter Variable</p>
95+
<p className="text-sm text-muted-foreground">
96+
Self-hosted variable font (weights 100–900), applied globally via <Code>--font-sans</Code> /{" "}
97+
<Code>font-sans</Code>.
98+
</p>
99+
</div>
100+
</section>
101+
102+
{/* Font sizes */}
77103
<section className="space-y-3">
78-
<h2 className="text-lg font-semibold text-foreground">Reference</h2>
104+
<h2 className="text-lg font-semibold text-foreground">Font Sizes</h2>
105+
<p className="max-w-2xl text-sm text-muted-foreground">
106+
Tailwind's built-in <Code>text-*</Code> scale, plus the kit's <Code>text-2xs</Code>. Sized for
107+
data-dense scientific UIs — <Code>text-sm</Code> is the de-facto body size.
108+
</p>
79109
<div className="overflow-x-auto rounded-lg border border-border bg-card">
80110
<table className="w-full text-sm">
81111
<thead>
82112
<tr className="border-b border-border bg-muted/50">
83-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Role</th>
84-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Level</th>
85-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Tailwind Class</th>
86-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Size</th>
87-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Line Height</th>
88-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Weight</th>
89-
<th className="px-4 py-2.5 text-left font-medium text-muted-foreground">Tracking</th>
113+
<th className={TH}>Tailwind Class</th>
114+
<th className={TH}>Size</th>
115+
<th className={TH}>Sample</th>
116+
<th className={TH}>Usage</th>
90117
</tr>
91118
</thead>
92119
<tbody>
93-
{TYPESCALE_LEVELS.map((level) => {
94-
const showRole = level.role !== lastRole
95-
lastRole = level.role
96-
return (
97-
<tr key={level.cssClass} className="border-b border-border last:border-b-0 hover:bg-muted/30 transition-colors">
98-
<td className="px-4 py-2.5">
99-
{showRole && (
100-
<span className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${ROLE_COLORS[level.role]}`}>
101-
{level.role}
102-
</span>
103-
)}
104-
</td>
105-
<td className="px-4 py-2.5 font-medium text-foreground">{level.name}</td>
106-
<td className="px-4 py-2.5"><CopyButton text={level.cssClass} /></td>
107-
<td className="px-4 py-2.5 font-mono text-xs text-muted-foreground">{level.size}</td>
108-
<td className="px-4 py-2.5 font-mono text-xs text-muted-foreground">{level.lineHeight}</td>
109-
<td className="px-4 py-2.5 font-mono text-xs text-muted-foreground">{level.weight}</td>
110-
<td className="px-4 py-2.5 font-mono text-xs text-muted-foreground">{level.tracking}</td>
111-
</tr>
112-
)
113-
})}
120+
{FONT_SIZES.map((s) => (
121+
<tr key={s.cssClass} className={TR}>
122+
<td className={TD}>
123+
<div className="flex items-center gap-2">
124+
<CopyButton text={s.cssClass} />
125+
{s.tag && <TagBadge tag={s.tag} />}
126+
</div>
127+
</td>
128+
<td className={`${TD} font-mono text-xs text-muted-foreground`}>{s.size}</td>
129+
<td className={TD}>
130+
<span className={`${s.cssClass} text-foreground`}>{SAMPLE_TEXT}</span>
131+
</td>
132+
<td className={`${TD} text-xs text-muted-foreground`}>{s.note}</td>
133+
</tr>
134+
))}
114135
</tbody>
115136
</table>
116137
</div>
117138
</section>
118139

119-
{/* Visual samples */}
140+
{/* Font weights */}
120141
<section className="space-y-3">
121-
<h2 className="text-lg font-semibold text-foreground">Samples</h2>
122-
<div className="space-y-6">
123-
{TYPESCALE_LEVELS.map((level) => (
124-
<div key={level.cssClass} className="space-y-1">
125-
<div className="flex items-center gap-2">
126-
<span className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${ROLE_COLORS[level.role]}`}>
127-
{level.role}
128-
</span>
129-
<span className="text-xs text-muted-foreground">{level.name}</span>
130-
<CopyButton text={level.cssClass} />
131-
</div>
132-
<p className={`${level.cssClass} text-foreground`}>
133-
{SAMPLE_TEXT}
134-
</p>
135-
</div>
136-
))}
142+
<h2 className="text-lg font-semibold text-foreground">Font Weights</h2>
143+
<p className="max-w-2xl text-sm text-muted-foreground">
144+
Inter Variable supports 100–900; these are the weights the kit uses. Pair with any size.
145+
</p>
146+
<div className="overflow-x-auto rounded-lg border border-border bg-card">
147+
<table className="w-full text-sm">
148+
<thead>
149+
<tr className="border-b border-border bg-muted/50">
150+
<th className={TH}>Tailwind Class</th>
151+
<th className={TH}>Weight</th>
152+
<th className={TH}>Sample</th>
153+
<th className={TH}>Usage</th>
154+
</tr>
155+
</thead>
156+
<tbody>
157+
{FONT_WEIGHTS.map((w) => (
158+
<tr key={w.cssClass} className={TR}>
159+
<td className={TD}>
160+
<CopyButton text={w.cssClass} />
161+
</td>
162+
<td className={`${TD} font-mono text-xs text-muted-foreground`}>{w.weight}</td>
163+
<td className={TD}>
164+
<span className={`text-lg ${w.cssClass} text-foreground`}>{SAMPLE_TEXT}</span>
165+
</td>
166+
<td className={`${TD} text-xs text-muted-foreground`}>{w.note}</td>
167+
</tr>
168+
))}
169+
</tbody>
170+
</table>
137171
</div>
138172
</section>
139173
</div>

src/components/composed/DataAppShell/DataAppShell.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ function WorkflowPanel({
256256
<Icon className="w-4 h-4" />
257257
</span>
258258
)}
259-
<span className={cn("text-title-sm truncate min-w-0", !step.isActive && "font-light")}>
259+
<span className={cn("text-sm font-medium truncate min-w-0", !step.isActive && "font-light")}>
260260
{step.label}
261261
</span>
262262
</button>

0 commit comments

Comments
 (0)