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
@@ -294,7 +294,7 @@ export function CheckinCalendarCard({
{t('Daily Check-in')}
</h3>
{checkedToday && (
<div className='inline-flex items-center gap-1 rounded-md bg-emerald-500/10 px-2 py-0.5 text-[11px] font-medium text-emerald-600 sm:gap-1.5 sm:px-2.5 sm:text-xs dark:text-emerald-400'>
<div className='bg-success/10 text-success inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium sm:gap-1.5 sm:px-2.5'>
<Sparkles className='h-2.5 w-2.5 sm:h-3 sm:w-3' />
{t('Checked in')}
</div>
@@ -337,7 +337,7 @@ export function CheckinCalendarCard({
<div className='text-xl font-semibold tracking-tight tabular-nums sm:text-2xl'>
{checkinData?.stats?.total_checkins || 0}
</div>
<div className='text-muted-foreground mt-0.5 text-[10px] font-medium sm:mt-1 sm:text-xs'>
<div className='text-muted-foreground mt-0.5 text-xs font-medium sm:mt-1 sm:text-xs'>
{t('Total check-ins')}
</div>
</div>
@@ -345,7 +345,7 @@ export function CheckinCalendarCard({
<div className='text-xl font-semibold tracking-tight tabular-nums sm:text-2xl'>
{formatQuotaWithCurrency(monthlyQuota, { digitsLarge: 0 })}
</div>
<div className='text-muted-foreground mt-0.5 text-[10px] font-medium sm:mt-1 sm:text-xs'>
<div className='text-muted-foreground mt-0.5 text-xs font-medium sm:mt-1 sm:text-xs'>
{t('This month')}
</div>
</div>
@@ -358,7 +358,7 @@ export function CheckinCalendarCard({
}
)}
</div>
<div className='text-muted-foreground mt-0.5 text-[10px] font-medium sm:mt-1 sm:text-xs'>
<div className='text-muted-foreground mt-0.5 text-xs font-medium sm:mt-1 sm:text-xs'>
{t('Total earned')}
</div>
</div>
@@ -398,7 +398,7 @@ export function CheckinCalendarCard({
{weekDays.map((day) => (
<div
key={day}
className='text-muted-foreground flex h-7 items-center justify-center text-[10px] font-medium sm:h-8 sm:text-xs'
className='text-muted-foreground flex h-7 items-center justify-center text-xs font-medium sm:h-8 sm:text-xs'
>
{day}
</div>
@@ -430,7 +430,7 @@ export function CheckinCalendarCard({
>
<span className='tabular-nums'>{dayNum}</span>
{isCheckedIn && !isToday && (
<span className='absolute bottom-0.5 h-1 w-1 rounded-full bg-emerald-500 sm:bottom-1' />
<span className='bg-success absolute bottom-0.5 h-1 w-1 rounded-full sm:bottom-1' />
)}
</Button>
)
@@ -458,7 +458,7 @@ export function CheckinCalendarCard({
</div>
{/* Footer hint */}
<div className='text-muted-foreground border-t pt-3 text-center text-[11px] sm:pt-4 sm:text-xs'>
<div className='text-muted-foreground border-t pt-3 text-center text-xs sm:pt-4 sm:text-xs'>
{t('You can only check in once per day')}
</div>
@@ -335,7 +335,7 @@ export function PasskeyCard({ loading: pageLoading }: PasskeyCardProps) {
{showUnsupportedNotice && (
<div className='bg-muted/60 text-muted-foreground flex items-start gap-3 rounded-md p-4 text-sm'>
<ShieldAlert className='mt-0.5 h-4 w-4 flex-shrink-0 text-amber-500' />
<ShieldAlert className='text-warning mt-0.5 h-4 w-4 flex-shrink-0' />
<div>
<p className='text-foreground font-medium'>
{t('Passkey not supported on this device')}
@@ -163,7 +163,7 @@ export function ProfileHeader({ profile, loading }: ProfileHeaderProps) {
</div>
</div>
<div className='text-foreground mt-1.5 truncate font-mono text-lg font-bold tracking-tight tabular-nums sm:mt-2 sm:text-2xl'>
<div className='text-foreground mt-1.5 truncate text-lg font-bold tracking-tight tabular-nums sm:mt-2 sm:text-2xl'>
{item.value}
</div>
<div className='text-muted-foreground/60 mt-1 hidden text-xs md:block'>