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
@@ -199,7 +199,7 @@ export function DynamicPricingBreakdown({
<section className={cn('min-w-0', !compact && 'py-4')}>
{!compact && (
<div className='mb-3 flex items-center gap-2'>
<span className='inline-flex size-6 items-center justify-center rounded-lg bg-amber-100 text-amber-700 shadow-sm dark:bg-amber-500/20 dark:text-amber-300'>
<span className='bg-warning/15 text-warning inline-flex size-6 items-center justify-center rounded-lg'>
<TagIcon className='size-3.5' />
</span>
<div>
@@ -212,7 +212,7 @@ export function DynamicPricingBreakdown({
</div>
</div>
)}
<div className='text-muted-foreground mb-1 text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground mb-1 text-xs font-medium tracking-wider uppercase'>
{t('Raw expression')}
</div>
<code className='text-muted-foreground block text-xs break-all'>
@@ -234,7 +234,7 @@ export function DynamicPricingBreakdown({
<section className={cn('min-w-0', !compact && 'py-3 sm:py-4')}>
{!compact && (
<div className='mb-3 flex items-start gap-2 sm:mb-4'>
<span className='mt-0.5 inline-flex size-6 items-center justify-center rounded-lg bg-amber-100 text-amber-700 shadow-sm dark:bg-amber-500/20 dark:text-amber-300'>
<span className='bg-warning/15 text-warning mt-0.5 inline-flex size-6 items-center justify-center rounded-lg'>
<TagIcon className='size-3.5' />
</span>
<div>
@@ -271,7 +271,7 @@ export function DynamicPricingBreakdown({
key={`tier-mobile-${i}`}
className={cn(
'rounded-md border p-2',
isMatched && 'border-emerald-500/40 bg-emerald-500/10'
isMatched && 'border-success/40 bg-success/10'
)}
>
<div className='mb-1.5 flex flex-wrap items-center gap-1.5'>
@@ -284,7 +284,7 @@ export function DynamicPricingBreakdown({
{isMatched && (
<Badge
variant='secondary'
className='bg-emerald-100 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300'
className='bg-success/10 text-success'
>
{t('Matched')}
</Badge>
@@ -302,7 +302,7 @@ export function DynamicPricingBreakdown({
)
return (
<div key={v.field} className='min-w-0'>
<div className='text-muted-foreground truncate text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground truncate text-xs font-medium tracking-wider uppercase'>
{t(v.shortLabel)}
</div>
<div
@@ -337,10 +337,7 @@ export function DynamicPricingBreakdown({
const isMatched =
normalizedMatchedTierLabel !== '' &&
normalizeTierLabel(tier.label) === normalizedMatchedTierLabel
return cn(
isMatched &&
'bg-emerald-50/70 hover:bg-emerald-50/70 dark:bg-emerald-500/10 dark:hover:bg-emerald-500/10'
)
return cn(isMatched && 'bg-success/10 hover:bg-success/10')
}}
columns={[
{
@@ -369,7 +366,7 @@ export function DynamicPricingBreakdown({
{isMatched && (
<Badge
variant='secondary'
className='bg-emerald-100 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300'
className='bg-success/10 text-success'
>
{t('Matched')}
</Badge>
+2 -2
View File
@@ -96,10 +96,10 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
if (dynamicSummary.isSpecialExpression) {
priceSummary = (
<span className='min-w-0'>
<span className='text-amber-700 dark:text-amber-300'>
<span className='text-warning'>
{t('Special billing expression')}
</span>
<code className='text-muted-foreground/70 mt-0.5 line-clamp-1 block font-mono text-[11px] break-all'>
<code className='text-muted-foreground/70 mt-0.5 line-clamp-1 block font-mono text-xs break-all'>
{dynamicSummary.rawExpression}
</code>
</span>
@@ -539,7 +539,7 @@ function CodeSamplesSection(props: {
<p className='text-muted-foreground mt-2 text-xs'>
{t('Replace')}{' '}
<code className='bg-muted rounded px-1 py-0.5 font-mono text-[11px]'>
<code className='bg-muted rounded px-1 py-0.5 font-mono text-xs'>
{'<YOUR_API_KEY>'}
</code>{' '}
{t('with the API key from your token settings.')}
@@ -582,7 +582,7 @@ function SupportedParametersSection(props: { model: PricingModel }) {
{p.required && (
<Badge
variant='outline'
className='h-6 border-rose-500/40 px-2 text-sm text-rose-600 dark:text-rose-400'
className='border-destructive/40 text-destructive h-6 px-2 text-sm'
>
{t('required')}
</Badge>
@@ -711,7 +711,7 @@ function RateLimitsSection(props: { model: PricingModel }) {
},
]}
/>
<p className='text-muted-foreground mt-2 text-[11px] leading-relaxed'>
<p className='text-muted-foreground mt-2 text-xs leading-relaxed'>
{t(
'RPM = requests per minute, TPM = tokens per minute, RPD = requests per day. Limits apply per token group.'
)}
@@ -734,11 +734,11 @@ function AuthSection() {
<div className='space-y-1.5 text-xs leading-relaxed'>
<p>
{t('All requests must include')}{' '}
<code className='bg-muted rounded px-1 py-0.5 font-mono text-[11px]'>
<code className='bg-muted rounded px-1 py-0.5 font-mono text-xs'>
Authorization: Bearer &lt;TOKEN&gt;
</code>{' '}
{t('header. Anthropic-formatted endpoints accept the')}{' '}
<code className='bg-muted rounded px-1 py-0.5 font-mono text-[11px]'>
<code className='bg-muted rounded px-1 py-0.5 font-mono text-xs'>
x-api-key
</code>{' '}
{t('header instead.')}
@@ -48,16 +48,14 @@ function RankBadge(props: { rank: number }) {
const isPodium = rank <= 3
const palette =
rank === 1
? 'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300'
: rank === 2
? 'bg-slate-100 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300'
: rank === 3
? 'bg-orange-100 text-orange-700 dark:bg-orange-500/20 dark:text-orange-300'
: 'bg-muted text-muted-foreground'
? 'bg-warning/15 text-warning'
: rank <= 3
? 'bg-muted text-foreground'
: 'bg-muted text-muted-foreground'
return (
<span
className={cn(
'inline-flex size-7 shrink-0 items-center justify-center rounded-md font-mono text-xs font-bold tabular-nums',
'inline-flex size-7 shrink-0 items-center justify-center rounded-md text-xs font-bold tabular-nums',
palette
)}
>
@@ -71,16 +69,16 @@ function GrowthChip(props: { value: number }) {
const isUp = value > 0
const isDown = value < 0
const palette = isUp
? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300'
? 'bg-success/10 text-success'
: isDown
? 'bg-rose-100 text-rose-700 dark:bg-rose-500/20 dark:text-rose-300'
? 'bg-destructive/10 text-destructive'
: 'bg-muted text-muted-foreground'
const Icon = isUp ? ArrowUpRight : isDown ? ArrowDownRight : null
const formatted = `${value > 0 ? '+' : ''}${value.toFixed(1)}%`
return (
<span
className={cn(
'inline-flex items-center gap-0.5 rounded-md px-1.5 py-0.5 font-mono text-[11px] font-semibold tabular-nums',
'inline-flex items-center gap-0.5 rounded-md px-1.5 py-0.5 text-xs font-semibold tabular-nums',
palette
)}
>
@@ -125,35 +123,35 @@ export function ModelDetailsApps(props: { model: PricingModel }) {
<div className='flex flex-col gap-4'>
<div className='grid grid-cols-1 gap-2 sm:grid-cols-3'>
<div className='bg-muted/20 rounded-lg border p-3'>
<div className='text-muted-foreground text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground text-xs font-medium tracking-wider uppercase'>
{t('Tracked apps')}
</div>
<div className='text-foreground mt-1 font-mono text-lg font-semibold tabular-nums'>
<div className='text-foreground mt-1 text-lg font-semibold tabular-nums'>
{apps.length}
</div>
<p className='text-muted-foreground/70 text-[11px]'>
<p className='text-muted-foreground/70 text-xs'>
{t('Top integrations using this model')}
</p>
</div>
<div className='bg-muted/20 rounded-lg border p-3'>
<div className='text-muted-foreground text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground text-xs font-medium tracking-wider uppercase'>
{t('Monthly tokens')}
</div>
<div className='text-foreground mt-1 font-mono text-lg font-semibold tabular-nums'>
<div className='text-foreground mt-1 text-lg font-semibold tabular-nums'>
{COMPACT_NUMBER.format(totalMonthlyTokens)}
</div>
<p className='text-muted-foreground/70 text-[11px]'>
<p className='text-muted-foreground/70 text-xs'>
{t('Aggregated across the apps below')}
</p>
</div>
<div className='bg-muted/20 rounded-lg border p-3'>
<div className='text-muted-foreground text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground text-xs font-medium tracking-wider uppercase'>
{t('#1 by usage')}
</div>
<div className='text-foreground mt-1 truncate text-base font-semibold'>
{top.name}
</div>
<p className='text-muted-foreground/70 truncate text-[11px]'>
<p className='text-muted-foreground/70 truncate text-xs'>
{top.category} · {formatTokenVolume(top.monthly_tokens)}{' '}
{t('tokens / mo')}
</p>
@@ -225,7 +223,7 @@ export function ModelDetailsApps(props: { model: PricingModel }) {
]}
/>
<p className='text-muted-foreground/60 text-[11px] leading-relaxed'>
<p className='text-muted-foreground/60 text-xs leading-relaxed'>
{t(
'App rankings shown here are simulated for preview purposes and will be replaced with live usage data once the backend integration is complete.'
)}
@@ -51,22 +51,20 @@ function StatCard(props: {
const Icon = props.icon
return (
<div className='bg-background flex flex-col gap-1 rounded-lg border p-3'>
<span className='text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider uppercase'>
<span className='text-muted-foreground inline-flex items-center gap-1.5 text-xs font-medium tracking-wider uppercase'>
<Icon className='size-3' />
{props.label}
</span>
<span
className={cn(
'text-foreground font-mono text-lg font-semibold tabular-nums',
'text-foreground text-lg font-semibold tabular-nums',
props.valueClassName
)}
>
{props.value}
</span>
{props.hint && (
<span className='text-muted-foreground/70 text-[11px]'>
{props.hint}
</span>
<span className='text-muted-foreground/70 text-xs'>{props.hint}</span>
)}
</div>
)
@@ -330,7 +328,7 @@ export function ModelDetailsPerformance(props: { model: PricingModel }) {
}
accent={
incidentCount > 0 ? (
<span className='inline-flex items-center gap-1 text-amber-600 dark:text-amber-400'>
<span className='text-warning inline-flex items-center gap-1'>
<AlertTriangle className='size-3.5' />
{t('{{count}} incidents', {
count: incidentCount,
@@ -128,7 +128,7 @@ export function UptimeSparkline(props: UptimeSparklineProps) {
{showOverall && (
<span
className={cn(
'font-mono text-sm font-semibold tabular-nums',
'text-sm font-semibold tabular-nums',
getSuccessRateTextClass(overall)
)}
>
@@ -164,13 +164,11 @@ export function UptimeStatusRow(props: {
: AlertCircle
const statusColour =
status === 'operational'
? 'text-emerald-600 dark:text-emerald-400'
: status === 'minor'
? 'text-emerald-600 dark:text-emerald-400'
: status === 'degraded'
? 'text-amber-600 dark:text-amber-400'
: 'text-rose-600 dark:text-rose-400'
status === 'operational' || status === 'minor'
? 'text-success'
: status === 'degraded'
? 'text-warning'
: 'text-destructive'
const statusLabel =
status === 'operational'
+20 -20
View File
@@ -156,12 +156,12 @@ function OverviewMetric(props: {
<div className='flex min-w-0 items-center gap-2 px-3 py-2'>
<Icon className='text-muted-foreground/70 size-3.5 shrink-0' />
<div className='min-w-0 flex-1'>
<div className='text-muted-foreground truncate text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground truncate text-xs font-medium tracking-wider uppercase'>
{props.label}
</div>
<div
className={cn(
'text-foreground truncate font-mono text-sm font-semibold tabular-nums',
'text-foreground truncate text-sm font-semibold tabular-nums',
props.valueClassName
)}
>
@@ -254,7 +254,7 @@ function CatalogTextValue(props: { children: React.ReactNode }) {
function CatalogInfoCell(props: { label: string; children: React.ReactNode }) {
return (
<div className='bg-card flex min-w-0 flex-col gap-1 px-3 py-2.5'>
<span className='text-muted-foreground text-[10px] font-medium tracking-wider uppercase'>
<span className='text-muted-foreground text-xs font-medium tracking-wider uppercase'>
{props.label}
</span>
{props.children}
@@ -361,7 +361,7 @@ function ModelBackendQuickStats(props: { model: PricingModel }) {
key={stat.key}
className='bg-background flex min-w-0 flex-col gap-0.5 px-3 py-2.5'
>
<span className='text-muted-foreground inline-flex min-w-0 items-center gap-1 text-[10px] font-medium tracking-wider uppercase'>
<span className='text-muted-foreground inline-flex min-w-0 items-center gap-1 text-xs font-medium tracking-wider uppercase'>
<Icon className='size-3 shrink-0' />
<span className='truncate'>{stat.label}</span>
</span>
@@ -369,7 +369,7 @@ function ModelBackendQuickStats(props: { model: PricingModel }) {
{stat.value}
</span>
{stat.hint && (
<span className='text-muted-foreground/60 truncate text-[10px]'>
<span className='text-muted-foreground/60 truncate text-xs'>
{stat.hint}
</span>
)}
@@ -565,7 +565,7 @@ function ModelHeader(props: { model: PricingModel }) {
{model.billing_mode === 'tiered_expr' && model.billing_expr && (
<>
<span className='text-muted-foreground/30'>·</span>
<span className='rounded bg-amber-100 px-1.5 py-0.5 text-[10px] font-medium text-amber-700 dark:bg-amber-500/20 dark:text-amber-300'>
<span className='bg-warning/15 text-warning rounded px-1.5 py-0.5 text-xs font-medium'>
{isSpecialExpression
? t('Special billing expression')
: t('Dynamic Pricing')}
@@ -649,15 +649,15 @@ function PriceSection(props: {
return (
<section>
<SectionTitle>{t('Base Price')}</SectionTitle>
<div className='rounded-lg border border-amber-200/70 bg-amber-50/70 p-3 dark:border-amber-500/20 dark:bg-amber-500/10'>
<div className='text-sm font-medium text-amber-800 dark:text-amber-200'>
<div className='border-warning/30 bg-warning/10 rounded-lg border p-3'>
<div className='text-warning text-sm font-medium'>
{t('Special billing expression')}
</div>
<p className='text-muted-foreground mt-1 text-xs'>
{t('Unable to parse structured pricing')}
</p>
<div className='mt-3'>
<div className='text-muted-foreground mb-1 text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground mb-1 text-xs font-medium tracking-wider uppercase'>
{t('Raw expression')}
</div>
<code className='text-muted-foreground bg-background/80 block max-h-28 overflow-auto rounded-md border px-2 py-1.5 font-mono text-xs break-all'>
@@ -682,7 +682,7 @@ function PriceSection(props: {
<div className='text-muted-foreground text-xs'>
{t(entry.shortLabel)}
</div>
<div className='text-foreground mt-1 font-mono text-base font-semibold tabular-nums'>
<div className='text-foreground mt-1 text-base font-semibold tabular-nums'>
{entry.formatted}
<span className='text-muted-foreground/40 ml-1 text-xs font-normal'>
/ {tokenUnitLabel}
@@ -707,7 +707,7 @@ function PriceSection(props: {
<span className='text-muted-foreground/70 text-sm'>
{t(entry.shortLabel)}
</span>
<span className='text-muted-foreground font-mono text-sm tabular-nums'>
<span className='text-muted-foreground text-sm tabular-nums'>
{entry.formatted}
<span className='text-muted-foreground/40 ml-1 text-xs font-normal'>
/ {tokenUnitLabel}
@@ -730,7 +730,7 @@ function PriceSection(props: {
<span className='text-muted-foreground text-sm'>
{t('Per request')}
</span>
<span className='text-foreground font-mono text-sm font-semibold tabular-nums'>
<span className='text-foreground text-sm font-semibold tabular-nums'>
{formatFixedPrice(
props.model,
baseGroupKey,
@@ -771,7 +771,7 @@ function PriceSection(props: {
{primaryPriceTypes.map((item) => (
<div key={item.type} className='bg-muted/20 rounded-lg border p-3'>
<div className='text-muted-foreground text-xs'>{item.label}</div>
<div className='text-foreground mt-1 font-mono text-base font-semibold tabular-nums'>
<div className='text-foreground mt-1 text-base font-semibold tabular-nums'>
{renderPrice(item.type)}
</div>
</div>
@@ -788,7 +788,7 @@ function PriceSection(props: {
<span className='text-muted-foreground/70 text-sm'>
{item.label}
</span>
<span className='text-muted-foreground font-mono text-sm tabular-nums'>
<span className='text-muted-foreground text-sm tabular-nums'>
{renderPrice(item.type)}
</span>
</div>
@@ -923,7 +923,7 @@ function GroupPricingSection(props: {
}
const thClass =
'text-muted-foreground py-2 text-[10px] font-medium tracking-wider uppercase'
'text-muted-foreground py-2 text-xs font-medium tracking-wider uppercase'
if (isDynamicPricingModel(props.model)) {
const dynamicTiers = getDynamicPricingTiers(props.model)
@@ -933,8 +933,8 @@ function GroupPricingSection(props: {
<section>
<SectionTitle>{t('Pricing by Group')}</SectionTitle>
<AutoGroupChain model={props.model} autoGroups={props.autoGroups} />
<div className='rounded-lg border border-amber-200/70 bg-amber-50/70 p-3 dark:border-amber-500/20 dark:bg-amber-500/10'>
<div className='text-sm font-medium text-amber-800 dark:text-amber-200'>
<div className='border-warning/30 bg-warning/10 rounded-lg border p-3'>
<div className='text-warning text-sm font-medium'>
{t('Special billing expression')}
</div>
<p className='text-muted-foreground mt-1 text-xs'>
@@ -943,7 +943,7 @@ function GroupPricingSection(props: {
)}
</p>
<div className='mt-3'>
<div className='text-muted-foreground mb-1 text-[10px] font-medium tracking-wider uppercase'>
<div className='text-muted-foreground mb-1 text-xs font-medium tracking-wider uppercase'>
{t('Raw expression')}
</div>
<code className='text-muted-foreground bg-background/80 block max-h-28 overflow-auto rounded-md border px-2 py-1.5 font-mono text-xs break-all'>
@@ -1028,7 +1028,7 @@ function GroupPricingSection(props: {
</div>
)
})}
<p className='text-muted-foreground/40 mt-1.5 text-[10px]'>
<p className='text-muted-foreground/40 mt-1.5 text-xs'>
{t('Prices shown per')} {tokenUnitLabel} tokens
</p>
</div>
@@ -1119,7 +1119,7 @@ function GroupPricingSection(props: {
/>
<div className='-mx-4 sm:mx-0'>
{isTokenBased && (
<p className='text-muted-foreground/40 mt-1.5 px-4 text-[10px] sm:px-0'>
<p className='text-muted-foreground/40 mt-1.5 px-4 text-xs sm:px-0'>
{t('Prices shown per')} {tokenUnitLabel} tokens
</p>
)}
@@ -79,7 +79,7 @@ export const ModelPerfBadge = memo(function ModelPerfBadge(
)}
>
<div title={t('Average latency')} className='min-w-0'>
<div className='text-muted-foreground/55 text-[10px] leading-4'>
<div className='text-muted-foreground/55 text-xs leading-4'>
{t('Latency short')}
</div>
<div className='text-muted-foreground/80 font-mono text-xs leading-4 whitespace-nowrap'>
@@ -87,7 +87,7 @@ export const ModelPerfBadge = memo(function ModelPerfBadge(
</div>
</div>
<div title={t('Throughput')} className='min-w-0'>
<div className='text-muted-foreground/55 truncate text-[10px] leading-4'>
<div className='text-muted-foreground/55 truncate text-xs leading-4'>
{t('Throughput short')}
</div>
<div className='text-muted-foreground/80 font-mono text-xs leading-4 whitespace-nowrap'>
@@ -98,7 +98,7 @@ export const ModelPerfBadge = memo(function ModelPerfBadge(
title={`${t('Success rate')}: ${success_rate.toFixed(1)}%`}
className='min-w-0'
>
<div className='text-muted-foreground/55 truncate text-[10px] leading-4'>
<div className='text-muted-foreground/55 truncate text-xs leading-4'>
{t('Status short')}
</div>
<div className='flex h-4 items-center justify-end gap-0.5'>
@@ -135,13 +135,13 @@ export function usePricingColumns(
if (dynamicSummary.isSpecialExpression) {
return (
<div className='max-w-full min-w-0'>
<div className='text-xs font-medium text-amber-700 dark:text-amber-300'>
<div className='text-warning text-xs font-medium'>
{t('Special billing expression')}
</div>
<div className='text-muted-foreground text-[11px]'>
<div className='text-muted-foreground text-xs'>
{t('Unable to parse structured pricing')}
</div>
<code className='text-muted-foreground/70 mt-1 line-clamp-2 block font-mono text-[10px] leading-relaxed break-all'>
<code className='text-muted-foreground/70 mt-1 line-clamp-2 block font-mono text-xs leading-relaxed break-all'>
{dynamicSummary.rawExpression}
</code>
</div>
@@ -159,7 +159,7 @@ export function usePricingColumns(
return (
<div className='max-w-full min-w-0'>
<span className='font-mono text-sm tabular-nums'>
<span className='text-sm tabular-nums'>
{primaryEntries.map((entry, index) => (
<span key={entry.key}>
{index > 0 && (
@@ -169,7 +169,7 @@ export function usePricingColumns(
</span>
))}
</span>
<div className='text-muted-foreground/50 text-[10px]'>
<div className='text-muted-foreground/50 text-xs'>
/ {tokenUnitLabel} tokens
{dynamicSummary.tierCount > 1 &&
` · ${t('{{count}} tiers', {
@@ -208,12 +208,12 @@ export function usePricingColumns(
return (
<div className='max-w-full min-w-0'>
<span className='font-mono text-sm tabular-nums'>
<span className='text-sm tabular-nums'>
{inputPrice}
<span className='text-muted-foreground/40 mx-1'>/</span>
{outputPrice}
</span>
<div className='text-muted-foreground/50 text-[10px]'>
<div className='text-muted-foreground/50 text-xs'>
/ {tokenUnitLabel} tokens
</div>
</div>
@@ -232,8 +232,8 @@ export function usePricingColumns(
return (
<div className='max-w-full min-w-0'>
<span className='font-mono text-sm tabular-nums'>{price}</span>
<div className='text-muted-foreground/50 text-[10px]'>
<span className='text-sm tabular-nums'>{price}</span>
<div className='text-muted-foreground/50 text-xs'>
/ {t('request')}
</div>
</div>
@@ -278,10 +278,10 @@ export function usePricingColumns(
return (
<div className='max-w-full min-w-0'>
<span className='font-mono text-sm tabular-nums'>
<span className='text-sm tabular-nums'>
{stripTrailingZeros(cacheEntry.formatted)}
</span>
<div className='text-muted-foreground/50 text-[10px]'>
<div className='text-muted-foreground/50 text-xs'>
/ {tokenUnitLabel}
</div>
</div>
@@ -308,10 +308,8 @@ export function usePricingColumns(
return (
<div className='max-w-full min-w-0'>
<span className='font-mono text-sm tabular-nums'>
{cachedPrice}
</span>
<div className='text-muted-foreground/50 text-[10px]'>
<span className='text-sm tabular-nums'>{cachedPrice}</span>
<div className='text-muted-foreground/50 text-xs'>
/ {tokenUnitLabel}
</div>
</div>
@@ -115,7 +115,7 @@ function FilterChip(props: {
{(props.option.suffix || props.option.count != null) && (
<span
className={cn(
'rounded-md px-1.5 py-0.5 text-[10px]',
'rounded-md px-1.5 py-0.5 text-xs',
props.active
? 'bg-background text-foreground'
: 'bg-muted text-muted-foreground'
@@ -179,7 +179,7 @@ export function PricingToolbar(props: PricingToolbarProps) {
<Filter className='size-4' />
{t('Filter')}
{props.activeFilterCount > 0 && (
<Badge className='ml-0.5 size-5 justify-center p-0 text-[10px]'>
<Badge className='ml-0.5 size-5 justify-center p-0 text-xs'>
{props.activeFilterCount}
</Badge>
)}
+1 -1
View File
@@ -78,7 +78,7 @@ export function SearchBar(props: SearchBarProps) {
<X className='size-4' />
</Button>
) : (
<kbd className='bg-muted text-muted-foreground pointer-events-none hidden rounded border px-1.5 py-0.5 font-mono text-[10px] sm:inline-block'>
<kbd className='bg-muted text-muted-foreground pointer-events-none hidden rounded border px-1.5 py-0.5 font-mono text-xs sm:inline-block'>
K
</kbd>
)}