Template
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:
1 parent
b1049d5ded
commit
47127db84b
20 files changed
+874
-57
No files matched your search
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user