Initial Switch to V2. Completely Overhauled Backend, Frontend and General Structure.
This commit is contained in:
@@ -0,0 +1,434 @@
|
||||
// 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>
|
||||
)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user