feat(ui): bundle Inter + design-system preview page (/app/design)
CI / backend (pull_request) Successful in 1m4s
CI / frontend (pull_request) Successful in 23s
Security / secrets (pull_request) Successful in 5s
Security / dependencies (pull_request) Successful in 1m8s

Slice 2: self-host Inter (variable) via @fontsource-variable/inter and add a
living design-system reference at /app/design showing tokens, the green ramp,
typography, and all primitives in both themes. Verified: frontend builds clean;
prod npm audit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-01 20:06:50 +02:00
parent e482d60b69
commit 666b5ada4a
6 changed files with 195 additions and 3 deletions
+10
View File
@@ -8,6 +8,7 @@
"name": "inboxintel-frontend",
"version": "0.1.0",
"dependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@radix-ui/react-avatar": "^1.2.1",
"@radix-ui/react-checkbox": "^1.3.6",
"@radix-ui/react-dialog": "^1.1.18",
@@ -764,6 +765,15 @@
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
"node_modules/@fontsource-variable/inter": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz",
"integrity": "sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+1
View File
@@ -9,6 +9,7 @@
"preview": "vite preview --host"
},
"dependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@radix-ui/react-avatar": "^1.2.1",
"@radix-ui/react-checkbox": "^1.3.6",
"@radix-ui/react-dialog": "^1.1.18",
+3 -3
View File
@@ -66,9 +66,9 @@
body {
@apply bg-background text-foreground antialiased;
/* Prefer Inter when available (bundled in a later slice); graceful system fallback. */
font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto,
Helvetica, Arial, sans-serif;
/* Inter (variable, self-hosted via @fontsource-variable/inter); system fallback. */
font-family: 'Inter Variable', 'Inter', ui-sans-serif, system-ui, -apple-system,
'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
/* Subtle, modern scrollbars that respect the theme. */
+3
View File
@@ -9,7 +9,9 @@ import Unsubscribe from './pages/Unsubscribe.jsx';
import FolderView from './pages/FolderView.jsx';
import SearchResults from './pages/SearchResults.jsx';
import Layout from './components/Layout.jsx';
import DesignSystem from './pages/DesignSystem.jsx';
import { ToastProvider, TooltipProvider } from './components/ui';
import '@fontsource-variable/inter';
import './index.css';
import './styles.css';
@@ -30,6 +32,7 @@ ReactDOM.createRoot(document.getElementById('root')).render(
<Route path="unsubscribe" element={<Unsubscribe />} />
<Route path="folder/:slug" element={<FolderView />} />
<Route path="search" element={<SearchResults />} />
<Route path="design" element={<DesignSystem />} />
</Route>
<Route path="*" element={<Navigate to="/" replace />} />
+177
View File
@@ -0,0 +1,177 @@
import React from 'react';
import {
Button, Badge, Input, Textarea, Switch, Skeleton, Separator,
Card, CardHeader, CardTitle, CardDescription, CardContent,
Tabs, TabsList, TabsTrigger, TabsContent, EmptyState, ThemeToggle,
} from '../components/ui';
/*
Design-system preview (v2). A living reference for the tokens + primitives so the
redesign can be visually verified in both themes. Route: /app/design.
Not linked in primary nav — it's a developer/design surface.
*/
const TOKENS = [
['background', 'App background'],
['card', 'Card / surface'],
['muted', 'Muted surface'],
['border', 'Border'],
['primary', 'Brand (green)'],
['success', 'Success'],
['warning', 'Warning'],
['danger', 'Danger'],
];
// Full literal class names so Tailwind's JIT includes them.
const GREEN = [
'bg-green-50', 'bg-green-100', 'bg-green-200', 'bg-green-300', 'bg-green-400',
'bg-green-500', 'bg-green-600', 'bg-green-700', 'bg-green-800', 'bg-green-900',
];
const TYPE = [
['text-3xl', '30px — Display'],
['text-2xl', '24px — Heading'],
['text-xl', '20px — Subheading'],
['text-lg', '18px — Large'],
['text-base', '16px — Body'],
['text-sm', '14px — UI base'],
['text-xs', '12px — Caption'],
];
function Swatch({ token, label }) {
return (
<div className="flex flex-col gap-1">
<div
className="h-14 w-full rounded-lg border"
style={{ background: `hsl(var(--${token}))` }}
/>
<div className="text-xs font-medium">{label}</div>
<div className="text-xs text-muted-foreground">--{token}</div>
</div>
);
}
function Section({ title, children }) {
return (
<section className="mb-10">
<h2 className="mb-3 text-lg font-semibold">{title}</h2>
{children}
</section>
);
}
export default function DesignSystem() {
return (
<div className="mx-auto max-w-4xl p-2">
<div className="mb-8 flex items-center justify-between">
<div>
<h1 className="text-2xl font-semibold">Design system</h1>
<p className="text-sm text-muted-foreground">
v2 tokens &amp; primitives green accent, warm neutrals, dark-first. Toggle the
theme to verify both.
</p>
</div>
<ThemeToggle />
</div>
<Section title="Semantic tokens">
<div className="grid grid-cols-2 gap-4 sm:grid-cols-4">
{TOKENS.map(([t, l]) => <Swatch key={t} token={t} label={l} />)}
</div>
</Section>
<Section title="Brand ramp (green, from #3ba31f)">
<div className="flex overflow-hidden rounded-lg border">
{GREEN.map((cls) => (
<div key={cls} className={`h-12 flex-1 ${cls}`} title={cls} />
))}
</div>
<div className="mt-1 flex justify-between text-xs text-muted-foreground">
<span>50</span><span>500 (brand)</span><span>900</span>
</div>
</Section>
<Section title="Typography (Inter)">
<div className="space-y-2">
{TYPE.map(([cls, label]) => (
<div key={cls} className={`${cls} font-medium`}>
{label}
</div>
))}
</div>
</Section>
<Section title="Buttons">
<div className="flex flex-wrap items-center gap-3">
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Danger</Button>
<Button disabled>Disabled</Button>
</div>
</Section>
<Section title="Badges">
<div className="flex flex-wrap items-center gap-3">
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="destructive">Danger</Badge>
</div>
</Section>
<Section title="Inputs">
<div className="grid max-w-md gap-3">
<Input placeholder="Search your inbox…" />
<Textarea placeholder="Write a reply…" rows={3} />
<label className="flex items-center gap-2 text-sm">
<Switch defaultChecked /> Use AI features
</label>
</div>
</Section>
<Section title="Card & tabs">
<Card className="max-w-md">
<CardHeader>
<CardTitle>Thread summary</CardTitle>
<CardDescription>AI-generated · local</CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="summary">
<TabsList>
<TabsTrigger value="summary">Summary</TabsTrigger>
<TabsTrigger value="actions">Actions</TabsTrigger>
</TabsList>
<TabsContent value="summary" className="pt-3 text-sm text-muted-foreground">
A concise overview of the conversation would appear here.
</TabsContent>
<TabsContent value="actions" className="pt-3 text-sm text-muted-foreground">
Reply to the client · Follow up in 3 days
</TabsContent>
</Tabs>
</CardContent>
</Card>
</Section>
<Section title="Loading & empty states">
<div className="grid gap-6 sm:grid-cols-2">
<div className="space-y-2">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
<Skeleton className="h-4 w-2/3" />
</div>
<div className="rounded-lg border p-4">
<EmptyState
title="No results"
description="Try broader terms or clear a filter."
/>
</div>
</div>
</Section>
<Separator className="my-8" />
<p className="text-xs text-muted-foreground">
InboxIntel design system · see docs/discovery/04-ux-redesign-and-design-system.md
</p>
</div>
);
}
+1
View File
@@ -48,6 +48,7 @@ export default {
},
fontFamily: {
sans: [
'Inter Variable',
'Inter',
'ui-sans-serif',
'system-ui',