style(web): unify design system across default frontend

🔤 Typography
- Fix --font-sans stack: 'Public Sans Variable' was never applied (wrong family name), add CJK fallbacks (PingFang SC / Microsoft YaHei UI / Noto Sans SC)
- Sync <html lang> with i18n language for correct CJK glyph selection
- Remove dead fake font system (font-provider.tsx, config/fonts.ts, --font-inter/--font-manrope)
- Typography contract: 3-tier headings, arbitrary text-[10px]/[11px] → text-xs (46 files), no uppercase table headers, font-bold → font-semibold
- Numbers use tabular-nums only; font-mono reserved for keys/IDs/code (29 files)

🎨 Color discipline
- Retire StatusBadge string-hash rainbow (autoColor → neutral); all variants resolve to 5 semantic voices
- Add tintedBadgeClassMap as the single tinted-pill recipe
- Replace hardcoded emerald/amber/rose/sky classes with semantic tokens across ~30 files (timing badges, progress bars, system panels, pricing, rankings, log row tints)
- Drop all !important text overrides and [font-family:var(--font-body)] hacks

🧘 Motion restraint
- Remove admin entrance animations: table row stagger, card hover lift/shadow, button press scale, dashboard shimmer line
- Neuter page-transition stagger components into plain wrappers; page transitions reduced to pure opacity fade
- Decorative motion stays landing-only

📐 Shape
- Single radius source: --radius 1rem → 0.625rem
- Sparklines use solid chart tokens instead of decorative gradients
- PanelWrapper: border-only (no shadow), align radius with Card

📏 Guardrails
- Add "Design System Discipline" section (3.10.1) to web/default/AGENTS.md

 Verified: tsgo typecheck clean, rsbuild production build passes
This commit is contained in:
t0ng7u
2026-07-11 02:02:11 +08:00
parent 0cb741d8da
commit 262ab93123
97 changed files with 561 additions and 863 deletions
@@ -33,10 +33,7 @@ export function GrowthText(props: GrowthTextProps) {
if (!Number.isFinite(v) || v === 0) {
return (
<span
className={cn(
'text-muted-foreground/80 font-mono tabular-nums',
props.className
)}
className={cn('text-muted-foreground/80 tabular-nums', props.className)}
>
0%
</span>
@@ -46,10 +43,8 @@ export function GrowthText(props: GrowthTextProps) {
return (
<span
className={cn(
'font-mono tabular-nums',
isUp
? 'text-emerald-600 dark:text-emerald-400'
: 'text-rose-600 dark:text-rose-400',
'tabular-nums',
isUp ? 'text-success' : 'text-destructive',
props.className
)}
>
@@ -279,7 +279,7 @@ function VendorList(props: {
<ul>
{props.rows.map((vendor) => (
<li key={vendor.vendor} className='flex items-center gap-3 py-2.5'>
<span className='text-muted-foreground/80 w-6 shrink-0 text-right font-mono text-xs tabular-nums'>
<span className='text-muted-foreground/80 w-6 shrink-0 text-right text-xs tabular-nums'>
{vendor.rank}.
</span>
<span
@@ -296,10 +296,10 @@ function VendorList(props: {
{vendor.vendor}
</VendorLink>
<div className='shrink-0 text-right'>
<div className='text-foreground font-mono text-sm font-semibold tabular-nums'>
<div className='text-foreground text-sm font-semibold tabular-nums'>
{formatTokens(vendor.total_tokens)}
</div>
<div className='text-muted-foreground/80 font-mono text-[11px] tabular-nums'>
<div className='text-muted-foreground/80 text-xs tabular-nums'>
{formatShare(vendor.share)}
</div>
</div>
@@ -79,7 +79,7 @@ function ModelList(props: {
: 'flex items-center gap-3 py-2.5'
}
>
<span className='text-muted-foreground/80 w-6 shrink-0 text-right font-mono text-xs tabular-nums'>
<span className='text-muted-foreground/80 w-6 shrink-0 text-right text-xs tabular-nums'>
{row.rank}.
</span>
<span className='shrink-0'>
@@ -99,7 +99,7 @@ function ModelList(props: {
<p
className={
compact
? 'text-muted-foreground/80 truncate text-[11px] italic'
? 'text-muted-foreground/80 truncate text-xs italic'
: 'text-muted-foreground/80 truncate text-xs italic'
}
>
@@ -113,8 +113,8 @@ function ModelList(props: {
<div
className={
compact
? 'text-foreground font-mono text-xs font-semibold tabular-nums'
: 'text-foreground font-mono text-sm font-semibold tabular-nums'
? 'text-foreground text-xs font-semibold tabular-nums'
: 'text-foreground text-sm font-semibold tabular-nums'
}
>
{formatTokens(row.total_tokens)}
@@ -129,7 +129,7 @@ function ModelList(props: {
</div>
<GrowthText
value={row.growth_pct}
className={compact ? 'text-[10px]' : 'text-[11px]'}
className={compact ? 'text-xs' : 'text-xs'}
/>
</div>
</li>
@@ -177,10 +177,10 @@ export function ModelsSection(props: ModelsSectionProps) {
</p>
</div>
<div className='shrink-0 text-right'>
<div className='text-foreground font-mono text-2xl font-semibold tabular-nums'>
<div className='text-foreground text-2xl font-semibold tabular-nums'>
{formatTokens(totalTokens)}
</div>
<div className='text-muted-foreground/80 text-[10px] font-medium tracking-widest uppercase'>
<div className='text-muted-foreground/80 text-xs font-medium tracking-widest uppercase'>
{t('tokens')}
</div>
</div>
@@ -210,7 +210,7 @@ export function ModelsSection(props: ModelsSectionProps) {
<div className='border-t'>
<header className='px-5 pt-4 pb-2'>
<h3 className='text-foreground inline-flex items-center gap-2 text-sm font-semibold'>
<Trophy className='size-3.5 text-amber-500' />
<Trophy className='text-warning size-3.5' />
{t('LLM Leaderboard')}
</h3>
<p className='text-muted-foreground/80 mt-0.5 text-xs'>
@@ -46,7 +46,7 @@ export function PulseSection(props: PulseSectionProps) {
<PulseCard
title={t('Trending up')}
description={t('Models climbing the leaderboard')}
icon={<TrendingUp className='size-4 text-emerald-500' />}
icon={<TrendingUp className='text-success size-4' />}
>
{props.movers.length === 0 ? (
<PulseEmpty label={t('No notable climbers right now')} />
@@ -62,7 +62,7 @@ export function PulseSection(props: PulseSectionProps) {
<PulseCard
title={t('Trending down')}
description={t('Models losing positions')}
icon={<TrendingDown className='size-4 text-rose-500' />}
icon={<TrendingDown className='text-destructive size-4' />}
>
{props.droppers.length === 0 ? (
<PulseEmpty label={t('No notable drops right now')} />
@@ -119,7 +119,7 @@ function MoverRow(props: { row: RankingMover; intent: 'up' | 'down' }) {
>
{props.row.model_name}
</ModelLink>
<p className='text-muted-foreground/80 truncate text-[11px]'>
<p className='text-muted-foreground/80 truncate text-xs'>
#{props.row.current_rank} ·{' '}
<VendorLink vendor={props.row.vendor}>
{props.row.vendor.toLowerCase()}
@@ -128,10 +128,8 @@ function MoverRow(props: { row: RankingMover; intent: 'up' | 'down' }) {
</div>
<span
className={cn(
'inline-flex shrink-0 items-center gap-0.5 font-mono text-xs font-semibold tabular-nums',
props.intent === 'up'
? 'text-emerald-600 dark:text-emerald-400'
: 'text-rose-600 dark:text-rose-400'
'inline-flex shrink-0 items-center gap-0.5 text-xs font-semibold tabular-nums',
props.intent === 'up' ? 'text-success' : 'text-destructive'
)}
>
{props.intent === 'up' ? (