Design tokens + agent iteration hardening

- tokens.css: full token layer (type scale rem-based, palette, spacing, radii)
- App.css rebuilt token-driven; zero hardcoded colors/px font sizes
- readability preset: --root-font-size 112%; button matrix scrolls when narrow
- App.tsx split into real components (Hero, CodeSample, RightRail + reflow)
- light theme via [data-theme=light] overrides; topbar sun/moon toggle persisted
- scripts/check-tokens.mjs + npm run check (lint, token guard, tsc, build)
- AGENTS.md: token contract, check gate, preview workflow
This commit is contained in:
mats committed 2026-09-12 10:07:07 +00:00
1 parent b1049d5ded
commit 47127db84b
20 files changed
+874 -57

No files matched your search

+56 -2
View File
@@ -1,7 +1,61 @@
import { useState } from 'react'
import { Icon } from './Icon'
type TopbarProps = { query: string; onQueryChange: (value: string) => void; onMenuToggle: () => void }
type TopbarProps = {
query: string
onQueryChange: (value: string) => void
onMenuToggle: () => void
}
/** App theme is a data attribute on <html>; tokens.css defines both palettes. */
function useTheme() {
const [theme, setTheme] = useState<'dark' | 'light'>(() =>
localStorage.getItem('ns-theme') === 'light' ? 'light' : 'dark',
)
const toggle = () => {
const next = theme === 'dark' ? 'light' : 'dark'
setTheme(next)
document.documentElement.dataset.theme = next
localStorage.setItem('ns-theme', next)
}
return { theme, toggle }
}
export function Topbar({ query, onQueryChange, onMenuToggle }: TopbarProps) {
return <header className="topbar"><div className="brand"><div className="brand-mark"><span /></div><b>northstar</b><span className="version">v0.8.2</span></div><div className="top-search"><Icon name="search" size={16} /><input value={query} onChange={e => onQueryChange(e.target.value)} placeholder="Search components, tokens, docs..." /><kbd>⌘ K</kbd></div><nav className="top-links"><a href="#docs">Docs</a><a href="#github">GitHub ↗</a><button className="icon-button" aria-label="Toggle theme"><Icon name="sun" size={17} /></button><button className="avatar">MS</button></nav><button className="mobile-menu icon-button" onClick={onMenuToggle} aria-label="Toggle navigation"><Icon name="menu" /></button></header>
const { theme, toggle } = useTheme()
return (
<header className="topbar">
<div className="brand">
<div className="brand-mark">
<span />
</div>
<b>northstar</b>
<span className="version">v0.8.2</span>
</div>
<div className="top-search">
<Icon name="search" size={16} />
<input
value={query}
onChange={e => onQueryChange(e.target.value)}
placeholder="Search components, tokens, docs..."
/>
<kbd>⌘ K</kbd>
</div>
<nav className="top-links">
<a href="#docs">Docs</a>
<a href="#github">GitHub ↗</a>
<button className="icon-button" onClick={toggle} aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}>
<Icon name={theme === 'dark' ? 'sun' : 'moon'} size={17} />
</button>
<button className="avatar">MS</button>
</nav>
<button className="mobile-menu icon-button" onClick={onMenuToggle} aria-label="Toggle navigation">
<Icon name="menu" />
</button>
</header>
)
}