✨ 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:
@@ -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' ? (
|
||||
|
||||
Reference in New Issue
Block a user