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
@@ -62,19 +62,19 @@ function formatGroupRatio(
function getRatioBadgeClassName(ratio: ApiKeyGroupOption['ratio']) {
if (typeof ratio !== 'number') {
return 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300'
return 'border-success/25 bg-success/10 text-success'
}
if (ratio > 5) {
return 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300'
return 'border-destructive/25 bg-destructive/10 text-destructive'
}
if (ratio > 3) {
return 'border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900/60 dark:bg-orange-950/40 dark:text-orange-300'
return 'border-warning/30 bg-warning/10 text-warning'
}
if (ratio > 1) {
return 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/60 dark:bg-blue-950/40 dark:text-blue-300'
return 'border-info/25 bg-info/10 text-info'
}
return 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300'
return 'border-success/25 bg-success/10 text-success'
}
function GroupRatioBadge({ ratio }: { ratio: ApiKeyGroupOption['ratio'] }) {
@@ -87,7 +87,7 @@ function GroupRatioBadge({ ratio }: { ratio: ApiKeyGroupOption['ratio'] }) {
<Badge
variant='outline'
className={cn(
'max-w-24 shrink-0 truncate text-[10px] sm:max-w-none sm:text-xs',
'max-w-24 shrink-0 truncate text-xs sm:max-w-none sm:text-xs',
getRatioBadgeClassName(ratio)
)}
>
@@ -149,7 +149,7 @@ export function ApiKeyGroupCombobox({
{selectedOption?.label || placeholder || t('Select a group')}
</span>
{selectedOption?.desc && (
<span className='text-muted-foreground block truncate text-[11px] sm:text-xs'>
<span className='text-muted-foreground block truncate text-xs sm:text-xs'>
{selectedOption.desc}
</span>
)}
@@ -44,9 +44,10 @@ import {
import { DataTableRowActions } from './data-table-row-actions'
function getQuotaProgressColor(percentage: number): string {
if (percentage <= 10) return '[&_[data-slot=progress-indicator]]:bg-rose-500'
if (percentage <= 30) return '[&_[data-slot=progress-indicator]]:bg-amber-500'
return '[&_[data-slot=progress-indicator]]:bg-emerald-500'
if (percentage <= 10)
return '[&_[data-slot=progress-indicator]]:bg-destructive'
if (percentage <= 30) return '[&_[data-slot=progress-indicator]]:bg-warning'
return '[&_[data-slot=progress-indicator]]:bg-success'
}
function useGroupRatios(): Record<string, number> {
@@ -254,7 +255,7 @@ export function useApiKeysColumns(): ColumnDef<ApiKey>[] {
accessorKey: 'created_time',
header: t('Created'),
cell: ({ row }) => (
<span className='text-muted-foreground block truncate font-mono text-xs tabular-nums'>
<span className='text-muted-foreground block truncate text-xs tabular-nums'>
{formatTimestampToDate(row.getValue('created_time'))}
</span>
),
@@ -270,7 +271,7 @@ export function useApiKeysColumns(): ColumnDef<ApiKey>[] {
return <span className='text-muted-foreground text-xs'>-</span>
}
return (
<span className='text-muted-foreground block truncate font-mono text-xs tabular-nums'>
<span className='text-muted-foreground block truncate text-xs tabular-nums'>
{formatTimestampToDate(accessedTime)}
</span>
)
@@ -296,7 +297,7 @@ export function useApiKeysColumns(): ColumnDef<ApiKey>[] {
return (
<span
className={cn(
'block truncate font-mono text-xs tabular-nums',
'block truncate text-xs tabular-nums',
isExpired ? 'text-destructive' : 'text-muted-foreground'
)}
>
@@ -140,7 +140,7 @@ function ApiKeysMobileList({
<div className='truncate text-sm font-semibold'>
{apiKey.name}
</div>
<div className='text-muted-foreground text-[11px]'>
<div className='text-muted-foreground text-xs'>
{t('API Key')}
</div>
</div>
@@ -203,7 +203,7 @@ export function DataTableRowActions<TData>({
className={
isEnabled
? 'text-destructive hover:text-destructive'
: 'text-emerald-600 hover:text-emerald-600 dark:text-emerald-400 dark:hover:text-emerald-400'
: 'text-success hover:text-success'
}
/>
}