Files
bellsystems-cp/frontend/src/pages/dev/CardFontSample.jsx
T

435 lines
14 KiB
React

// frontend/src/pages/dev/CardFontSample.jsx
// Standalone card header style sampler — no MainLayout, no auth required.
// View at: /dev/card-fonts
//
// Shows the stitch-reference style: Inter, uppercase, tracking-widest, icon-left.
// Each card uses a different icon to demonstrate how sections would look in practice.
import { useEffect } from 'react'
import Icon from '@/components/ui/Icon'
// ─── Shared mock body content ─────────────────────────────────────────────────
const services = [
{ name: 'API Gateway', status: 'success' },
{ name: 'Database', status: 'success' },
{ name: 'Worker Queue', status: 'warning' },
{ name: 'CDN Edge', status: 'success' },
]
const dotColor = {
success: 'var(--color-success)',
warning: 'var(--color-warning)',
}
function ServiceRow({ name, status }) {
return (
<div style={{
display: 'flex',
alignItems: 'center',
gap: 'var(--space-3)',
padding: 'var(--space-2) 0',
borderBottom: '1px solid var(--color-border)',
}}>
<span style={{
width: 7,
height: 7,
borderRadius: 'var(--radius-full)',
flexShrink: 0,
background: dotColor[status],
boxShadow: `0 0 5px ${dotColor[status]}`,
}} />
<span style={{
flex: 1,
fontSize: 'var(--font-size-sm)',
color: 'var(--color-text-secondary)',
fontFamily: 'Inter, var(--font-family-base)',
}}>{name}</span>
<span style={{
fontSize: '0.625rem',
color: dotColor[status],
fontFamily: 'var(--font-family-mono)',
textTransform: 'uppercase',
letterSpacing: '0.08em',
}}>{status === 'success' ? 'OK' : 'WARN'}</span>
</div>
)
}
const CardBody = () => (
<div>
{services.map(s => <ServiceRow key={s.name} name={s.name} status={s.status} />)}
</div>
)
const CardFooter = () => (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
<span style={{
fontSize: 'var(--font-size-xs)',
color: 'var(--color-text-muted)',
fontFamily: 'Inter, var(--font-family-base)',
}}>Last checked 2 minutes ago</span>
<button style={{
background: 'transparent',
border: '1px solid var(--color-border-strong)',
borderRadius: 'var(--radius-md)',
padding: '3px 10px',
fontSize: 'var(--font-size-xs)',
color: 'var(--color-text-secondary)',
cursor: 'pointer',
fontFamily: 'Inter, var(--font-family-base)',
fontWeight: 600,
letterSpacing: '0.03em',
transition: 'var(--transition-fast)',
}}>Refresh</button>
</div>
)
// ─── Card header style variants ───────────────────────────────────────────────
// All use Inter. They vary only in the title treatment (size, weight, case, spacing, color).
const specimens = [
{
id: 1,
icon: 'settings',
iconColor: 'var(--color-primary)',
label: '1 · Inter 600 · uppercase · tracking-widest · text-muted',
description: 'Closest to the stitch reference. Clean, restrained, professional.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.6875rem', /* 11px */
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.12em',
color: 'var(--color-text-muted)',
},
},
{
id: 2,
icon: 'devices',
iconColor: 'var(--color-primary)',
label: '2 · Inter 700 · uppercase · tracking-widest · on-surface-variant',
description: 'Heavier weight — bolder impression while keeping the muted tone.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.6875rem',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.12em',
color: 'var(--color-text-secondary)',
},
},
{
id: 3,
icon: 'dashboard',
iconColor: 'var(--color-primary)',
label: '3 · Inter 600 · uppercase · tracking-wide · primary color',
description: 'Icon and title in the same accent color — more expressive.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.6875rem',
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.10em',
color: 'var(--color-primary)',
},
},
{
id: 4,
icon: 'customers',
iconColor: 'var(--color-text-muted)',
label: '4 · Inter 500 · uppercase · tracking-widest · muted icon + muted title',
description: 'Both icon and title muted — very quiet, recedes into the card.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.6875rem',
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: '0.14em',
color: 'var(--color-text-muted)',
},
},
{
id: 5,
icon: 'firmware',
iconColor: 'var(--color-primary)',
label: '5 · Inter 600 · uppercase · tracking-widest · 12px (slightly larger)',
description: 'Same treatment at 12px — easier to read at a glance.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: 'var(--font-size-sm)', /* 12px */
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.10em',
color: 'var(--color-text-muted)',
},
},
{
id: 6,
icon: 'mail',
iconColor: 'var(--color-primary)',
label: '6 · Inter 600 · title case · 14px · no tracking',
description: 'Title case at body size — feels more conversational.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: 'var(--font-size-base)', /* 14px */
fontWeight: 600,
textTransform: 'none',
letterSpacing: '-0.01em',
color: 'var(--color-text-primary)',
},
},
{
id: 7,
icon: 'manufacturing',
iconColor: 'var(--color-primary)',
label: '7 · Inter 500 · title case · 14px · relaxed tracking',
description: 'Medium weight at 14px — softest, most editorial option.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: 'var(--font-size-base)',
fontWeight: 500,
textTransform: 'none',
letterSpacing: '0.01em',
color: 'var(--color-text-secondary)',
},
},
{
id: 8,
icon: 'mqtt',
iconColor: 'var(--color-tertiary)',
label: '8 · Inter 700 · uppercase · tracking-widest · tertiary/aqua icon',
description: 'Accent icon in aqua — useful to visually distinguish section types.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.6875rem',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.12em',
color: 'var(--color-text-muted)',
},
},
{
id: 9,
icon: 'melodies',
iconColor: 'var(--color-primary)',
label: '9 · Inter 600 · uppercase · 10px · extreme tracking',
description: 'Pushed to the limit — very spacious, laser-etched instrument-panel feel.',
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.625rem', /* 10px */
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.20em',
color: 'var(--color-text-muted)',
},
},
{
id: 10,
icon: 'staff',
iconColor: 'var(--color-primary)',
label: '10 · Inter 600 · uppercase · 11px · no icon (icon=false)',
description: 'No icon — title only. To compare how it looks without the icon.',
noIcon: true,
titleStyle: {
fontFamily: 'Inter, sans-serif',
fontSize: '0.6875rem',
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.12em',
color: 'var(--color-text-muted)',
},
},
]
// ─── Single specimen card ──────────────────────────────────────────────────────
function SpecimenCard({ s }) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
{/* The card */}
<div style={{
background: 'var(--color-bg-surface)',
border: '1px solid var(--color-border)',
borderRadius: 'var(--radius-lg)',
overflow: 'hidden',
boxShadow: 'inset 0 1px 0 rgba(192,193,255,0.05)',
display: 'flex',
flexDirection: 'column',
}}>
{/* Header */}
<div style={{
padding: 'var(--space-4) var(--space-6)',
borderBottom: '1px solid var(--color-border)',
background: 'linear-gradient(to bottom, rgba(192,193,255,0.035) 0%, transparent 100%)',
display: 'flex',
flexDirection: 'column',
gap: 'var(--space-1)',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)' }}>
{!s.noIcon && (
<Icon
name={s.icon}
size={15}
color={s.iconColor}
style={{ flexShrink: 0, marginTop: 1 }}
/>
)}
<span style={{ margin: 0, ...s.titleStyle }}>
System Status
</span>
</div>
<p style={{
margin: 0,
fontSize: 'var(--font-size-xs)',
color: 'var(--color-text-muted)',
fontFamily: 'Inter, var(--font-family-base)',
lineHeight: 1.5,
paddingLeft: s.noIcon ? 0 : 'calc(15px + var(--space-2))',
}}>
All services operational
</p>
</div>
{/* Body */}
<div style={{ padding: 'var(--space-5) var(--space-6)', flex: 1 }}>
<CardBody />
</div>
{/* Footer */}
<div style={{
padding: 'var(--space-3) var(--space-6)',
borderTop: '1px solid var(--color-border)',
background: 'rgba(0,0,0,0.08)',
}}>
<CardFooter />
</div>
</div>
{/* Label beneath card */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<span style={{
fontSize: 'var(--font-size-xs)',
fontFamily: 'var(--font-family-mono)',
color: 'var(--color-primary)',
letterSpacing: '0.02em',
}}>{s.label}</span>
<span style={{
fontSize: 'var(--font-size-xs)',
fontFamily: 'Inter, var(--font-family-base)',
color: 'var(--color-text-muted)',
}}>{s.description}</span>
</div>
</div>
)
}
// ─── Page ─────────────────────────────────────────────────────────────────────
export default function CardFontSample() {
// Inter is already loaded via tokens.css (system fallback) but ensure the
// Google Fonts version is present for the exact weights we need.
useEffect(() => {
const existing = document.querySelector('link[data-card-font-sampler]')
if (existing) return
const link = document.createElement('link')
link.rel = 'stylesheet'
link.setAttribute('data-card-font-sampler', '1')
link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'
document.head.appendChild(link)
return () => {
const el = document.querySelector('link[data-card-font-sampler]')
if (el) document.head.removeChild(el)
}
}, [])
return (
<div style={{
minHeight: '100vh',
background: 'var(--color-bg-base)',
padding: 'var(--space-12)',
}}>
{/* Page header */}
<div style={{ maxWidth: 980, margin: '0 auto var(--space-10)' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', marginBottom: 'var(--space-3)' }}>
<Icon name="dashboard" size={13} color="var(--color-primary)" />
<span style={{
fontSize: '0.625rem',
fontFamily: 'Inter, var(--font-family-base)',
color: 'var(--color-primary)',
textTransform: 'uppercase',
letterSpacing: '0.14em',
fontWeight: 600,
}}>Dev Tooling · Card Header Sampler</span>
</div>
<h1 style={{
margin: '0 0 var(--space-3)',
fontFamily: 'var(--font-family-display)',
fontSize: 'var(--font-size-xl)',
fontWeight: 700,
letterSpacing: 'var(--tracking-tight)',
color: 'var(--color-text-primary)',
lineHeight: 1.1,
}}>
Card Header Style Sampler
</h1>
<p style={{
margin: 0,
fontFamily: 'Inter, var(--font-family-base)',
fontSize: 'var(--font-size-base)',
color: 'var(--color-text-muted)',
lineHeight: 1.6,
maxWidth: 560,
}}>
All 10 cards use <strong style={{ color: 'var(--color-text-secondary)', fontWeight: 600 }}>Inter</strong> — the same font as the stitch reference.
Each varies title size, weight, case, letter-spacing, and icon treatment.
Pick a number and we'll apply it globally.
</p>
<div style={{
marginTop: 'var(--space-6)',
height: 1,
background: 'linear-gradient(to right, var(--color-border-strong), transparent)',
}} />
</div>
{/* Card grid */}
<div style={{
maxWidth: 980,
margin: '0 auto',
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(360px, 1fr))',
gap: 'var(--space-8)',
}}>
{specimens.map(s => <SpecimenCard key={s.id} s={s} />)}
</div>
{/* Footer */}
<div style={{
maxWidth: 980,
margin: 'var(--space-12) auto 0',
paddingTop: 'var(--space-6)',
borderTop: '1px solid var(--color-border)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}>
<span style={{
fontFamily: 'var(--font-family-mono)',
fontSize: 'var(--font-size-xs)',
color: 'var(--color-text-muted)',
letterSpacing: '0.04em',
}}>/dev/card-fonts</span>
<span style={{
fontFamily: 'var(--font-family-mono)',
fontSize: 'var(--font-size-xs)',
color: 'var(--color-text-muted)',
letterSpacing: '0.04em',
}}>BellSystems CP v2 · Internal only</span>
</div>
</div>
)
}