base design system

This commit is contained in:
mats committed 2026-09-12 11:29:13 +02:00
1 parent cd952029fe
commit fbd29de863
29 files changed
+1705

No files matched your search

+32
View File
@@ -0,0 +1,32 @@
:root{--bg:#0b0b10;--panel:#111118;--panel-2:#16161f;--panel-3:#1b1b25;--line:#292933;--text:#f1f0f7;--muted:#92919e;--subtle:#666572;--accent:#a69bff;--positive:#73dfae;--warning:#ffc777;--critical:#ff8585;--pink:#e58bff;--blue:#9bcbff;--mono:'DM Mono',monospace}.app-shell{min-height:100vh;background:var(--bg);background-image:radial-gradient(circle at 71% 1%,rgba(139,125,255,.09),transparent 28rem)}.topbar{height:68px;border-bottom:1px solid var(--line);background:rgba(11,11,16,.82);backdrop-filter:blur(18px);display:flex;align-items:center;padding:0 34px;gap:32px;position:sticky;top:0;z-index:10}.brand,.top-links,.top-search,.hero-actions,.showcase-foot,.status-line,.code-head,.section-heading,.release-icon,.input-with-action,.toggle-field{display:flex;align-items:center}.brand{gap:10px;white-space:nowrap}.brand-mark{width:26px;height:26px;border:1px solid var(--accent);border-radius:8px;display:grid;place-items:center;transform:rotate(45deg)}.brand-mark span{width:9px;height:9px;background:var(--accent);border-radius:3px}.brand-mark.large{width:42px;height:42px;border-radius:12px}.brand-mark.large span{width:14px;height:14px}.brand b{font-weight:800;letter-spacing:-.04em;font-size:17px}.version,.api-pill,.nav-count{font:11px var(--mono);color:var(--muted);border:1px solid var(--line);padding:3px 7px;border-radius:5px}.top-search{max-width:380px;flex:1;margin-left:auto;margin-right:auto;gap:10px;color:var(--subtle);background:var(--panel);border:1px solid var(--line);border-radius:7px;padding:8px 11px}.top-search:focus-within{border-color:#56517b}.top-search input{background:none;border:0;outline:0;color:var(--text);min-width:0;flex:1;font-size:12px}.top-search kbd{font:10px var(--mono);color:var(--subtle);border:1px solid var(--line);border-radius:4px;padding:3px 5px}.top-links{gap:22px;font-size:12px;color:var(--muted)}.top-links a:hover,.text-button:hover{color:var(--text)}.icon-button,.avatar{border:0;background:none;color:var(--muted);display:grid;place-items:center;padding:7px;border-radius:6px}.icon-button:hover{background:var(--panel-3);color:var(--text)}.avatar{width:28px;height:28px;background:#312e50;color:#c9c4ff;font-size:10px;font-weight:700}.mobile-menu{display:none}.body-layout{display:grid;grid-template-columns:240px minmax(0,1fr)}.sidebar{border-right:1px solid var(--line);min-height:calc(100vh - 68px);padding:33px 18px 24px;display:flex;flex-direction:column;position:sticky;top:68px;height:calc(100vh - 68px)}.sidebar-intro{padding:0 12px 30px;border-bottom:1px solid var(--line);margin-bottom:26px}.sidebar-intro p{font-size:11px;line-height:1.6;color:var(--subtle);margin:8px 0 0;max-width:150px}.eyebrow,.section-index,.nav-label{font:10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--subtle)}.accent{color:var(--accent)}.nav-group{margin-bottom:27px}.nav-label{display:block;padding:0 12px;margin-bottom:9px}.nav-item{width:100%;border:0;background:none;color:var(--muted);text-align:left;padding:8px 12px;border-radius:6px;font-size:12px;display:flex;align-items:center;gap:10px;transition:.2s}.nav-item:hover{color:var(--text);background:var(--panel)}.nav-item.active{color:var(--text);background:var(--panel-3);box-shadow:inset 2px 0 var(--accent)}.nav-dot{width:4px;height:4px;border-radius:50%;background:var(--subtle)}.active .nav-dot{background:var(--accent)}.nav-count{margin-left:auto;padding:2px 5px;color:var(--accent);border-color:#433e68}.sidebar-bottom{margin-top:auto;border-top:1px solid var(--line);padding:18px 12px 0}.status-line{gap:8px;color:var(--muted);font-size:10px;margin-bottom:7px}.status-dot{width:6px;height:6px;background:var(--positive);box-shadow:0 0 10px var(--positive);border-radius:50%}.mono{font-family:var(--mono);font-size:10px}.muted{color:var(--subtle)}.main-content{min-width:0}.breadcrumbs{max-width:1180px;margin:0 auto;padding:27px 48px 0;display:flex;gap:10px;font:10px var(--mono);color:var(--subtle)}.breadcrumbs strong{color:var(--accent);font-weight:400}.hero-section{max-width:1180px;margin:0 auto;padding:67px 48px 78px;display:flex;justify-content:space-between;gap:40px;min-height:386px}.hero-copy{max-width:590px}.hero-copy h1{font-size:clamp(42px,5vw,70px);line-height:1.02;letter-spacing:-.07em;margin:17px 0 19px;font-weight:800}.hero-copy p{color:var(--muted);font-size:16px;line-height:1.7;max-width:510px}.hero-actions{gap:11px;margin-top:31px}.button{border:1px solid transparent;border-radius:6px;padding:9px 14px;color:var(--text);font-size:11px;display:inline-flex;align-items:center;gap:8px;transition:.2s;white-space:nowrap}.button.primary{background:var(--accent);color:#171526;font-weight:700}.button.primary:hover{filter:brightness(1.12);transform:translateY(-1px)}.button.secondary{background:var(--panel-3);border-color:var(--line)}.button.secondary:hover,.button.hover-state{background:#252535}.button.danger{background:rgba(255,133,133,.12);color:var(--critical);border-color:rgba(255,133,133,.18)}.button.ghost{background:transpaLine truncated
@media(max-width:1020px){.topbar{padding:0 20px;gap:16px}.top-links a{display:none}.body-layout{grid-template-columns:200px minmax(0,1fr)}.hero-section,.content-grid{padding-left:28px;padding-right:28px}.content-grid{gap:30px;grid-template-columns:minmax(0,1fr) 190px}.hero-orbit{transform:scale(.82);transform-origin:right top;margin-right:0}}
@media(max-width:760px){.topbar{height:60px}.top-search{order:3;position:absolute;left:18px;right:18px;top:68px;max-width:none;display:none}.top-links{margin-left:auto}.mobile-menu{display:grid}.body-layout{display:block}.sidebar{display:none;position:fixed;z-index:9;top:60px;left:0;right:0;bottom:0;height:auto;background:rgba(11,11,16,.98);border:0}.menu-open .sidebar{display:flex}.breadcrumbs{padding:24px 20px 0}.hero-section{display:block;padding:54px 20px 58px}.hero-copy h1{font-size:50px}.hero-orbit{display:none}.content-grid{display:block;padding:0 20px}.right-rail{display:none}.section-block{padding:34px 0}.section-heading{display:block}.api-pill{display:inline-block;margin-top:18px}.button-row{grid-template-columns:70px 1fr 1fr;gap:8px}.button-row .icon-only,.button-row .loading{display:none}.color-grid{grid-template-columns:1fr 1fr}.forms-grid{grid-template-columns:1fr}.toggle-field{grid-column:auto}footer{padding:24px 20px 30px;display:block}footer .mono{display:block;margin-top:8px}}
@media(max-width:450px){.version{display:none}.brand b{font-size:15px}.hero-copy h1{font-size:42px}.hero-actions{align-items:stretch;flex-direction:column}.hero-actions .button{justify-content:center}.button-row{grid-template-columns:58px 1fr 1fr}.button{padding:8px 9px;font-size:10px}.showcase-foot{flex-wrap:wrap}.foot-check{margin-left:0;width:100%;margin-top:6px}.color-card{grid-template-columns:28px 1fr}.color-card .swatch{width:28px;height:28px}.color-card .copy-icon{display:none}}
/* Markdown and CodeMirror surfaces */
.markdown-card,.editor-card{margin-top:24px;border:1px solid var(--line);border-radius:9px;overflow:hidden;background:var(--panel)}
.markdown-toolbar,.editor-tabs{display:flex;align-items:center;justify-content:space-between;padding:12px 17px;border-bottom:1px solid var(--line);background:#0f0f16}
.markdown-body{max-width:700px;margin:0 auto;padding:34px 38px 40px;color:#b8b6c4;font-size:13px;line-height:1.75}.md-kicker{font:10px var(--mono);letter-spacing:.13em;color:var(--accent)}.markdown-body h3{color:var(--text);font-size:25px;letter-spacing:-.05em;line-height:1.2;margin:13px 0 10px}.markdown-body h4{color:var(--text);font-size:13px;margin:25px 0 7px}.markdown-body p{margin:0 0 16px}.markdown-body code{font:11px var(--mono);color:var(--accent);background:#25223a;border:1px solid #38335a;border-radius:4px;padding:2px 5px}.md-callout{display:flex;gap:12px;padding:13px 15px;margin:22px 0;border:1px solid #3b3861;border-left:2px solid var(--accent);border-radius:6px;background:linear-gradient(100deg,#1a1829,#15151d)}.md-callout p{margin:0;font-size:12px;line-height:1.6}.md-callout strong{color:var(--text)}.callout-mark{color:var(--accent);font-size:16px}.markdown-body ul{margin:0 0 21px;padding-left:20px}.markdown-body li{padding-left:5px}.markdown-body li::marker{color:var(--accent)}.md-table{border:1px solid var(--line);border-radius:6px;overflow:hidden;font-size:11px}.md-table>div{display:grid;grid-template-columns:1.1fr 1fr 1.4fr;gap:10px;padding:9px 12px;border-bottom:1px solid var(--line)}.md-table>div:last-child{border-bottom:0}.md-table>div:first-child{color:var(--subtle);font:10px var(--mono);text-transform:uppercase;letter-spacing:.08em;background:#15151d}.md-table code{font-size:10px;background:none;border:0;padding:0}.md-accent{color:var(--accent)}
.editor-card{background:#0d0e14;border-color:#292b3c;box-shadow:0 16px 35px rgba(0,0,0,.18)}.editor-tabs{justify-content:flex-start;gap:0;padding:0 13px;background:#171820}.editor-dot{width:8px;height:8px;border-radius:50%;background:var(--positive);box-shadow:0 0 8px rgba(115,223,174,.6);margin-right:14px}.editor-tab{padding:12px 15px;color:var(--subtle);font:10px var(--mono);border-right:1px solid #292b3c}.editor-tab.active{color:#d9d7e5;background:#0d0e14;border-top:2px solid var(--accent);padding-top:10px}.editor-actions{margin-left:auto;color:#515363;font:9px var(--mono);letter-spacing:3px}.editor-body{display:grid;grid-template-columns:43px 1fr;min-height:218px;background:linear-gradient(90deg,rgba(20,21,30,.7),transparent 16%)}.line-numbers{padding:17px 10px 17px 0;border-right:1px solid #252735;text-align:right;color:#515363;font:11px/1.72 var(--mono);user-select:none}.editor-body pre{margin:0;padding:17px 20px;font:12px/1.72 var(--mono);overflow:auto;color:#c7c8d7}.cm-comment{color:#626779;font-style:italic}.cm-keyword{color:#e58bff}.cm-variable{color:#a69bff}.cm-property{color:#9bcbff}.cm-string{color:#73dfae}.editor-status{display:flex;gap:18px;padding:8px 14px;border-top:1px solid #252735;background:#171820;color:#666b7c;font:10px var(--mono)}.editor-ready{margin-left:auto;color:var(--positive);display:flex;align-items:center;gap:5px}
@media(max-width:760px){.markdown-body{padding:26px 20px 30px}.md-table{font-size:10px}.editor-body pre{padding:17px 12px}.editor-status{gap:10px;font-size:9px}.editor-status span:nth-child(2){display:none}}
/* Motion & finishing touches */
@keyframes rise-in{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
@keyframes soft-glow{0%,100%{box-shadow:0 0 0 rgba(166,155,255,0)}50%{box-shadow:0 0 34px rgba(166,155,255,.16)}}
@keyframes orbit-drift{from{transform:rotate(-22deg) rotate(0deg)}to{transform:rotate(-22deg) rotate(360deg)}}
@keyframes orbit-drift-reverse{from{transform:rotate(58deg) rotate(360deg)}to{transform:rotate(58deg) rotate(0deg)}}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@keyframes pulse-dot{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.72)}}
.hero-section{animation:rise-in .7s cubic-bezier(.2,.8,.2,1) both}.hero-copy h1{background:linear-gradient(110deg,#f5f3ff 15%,#aaa0ff 55%,#f5f3ff 85%);background-size:220% auto;background-clip:text;-webkit-background-clip:text;color:transparent;animation:shimmer 9s linear infinite}.hero-copy p{animation:rise-in .7s .1s cubic-bezier(.2,.8,.2,1) both}.hero-actions{animation:rise-in .7s .18s cubic-bezier(.2,.8,.2,1) both}.hero-orbit{animation:rise-in .9s .18s cubic-bezier(.2,.8,.2,1) both}.orbit-one{animation:orbit-drift 24s linear infinite}.orbit-two{animation:orbit-drift-reverse 30s linear infinite}.orbit-core{animation:soft-glow 4s ease-in-out infinite}.orbit-label{animation:rise-in .7s .7s both}
.section-block{animation:rise-in .65s .1s cubic-bezier(.2,.8,.2,1) both}.section-block:nth-child(2){animation-delay:.16s}.section-block:nth-child(3){animation-delay:.22s}.section-block:nth-child(4){animation-delay:.28s}.button-row{transition:background .25s ease,transform .25s ease}.button-row:hover{background:rgba(166,155,255,.035);transform:translateX(3px)}.button{position:relative;overflow:hidden}.button:not(:disabled)::after,.copy-button::after{content:"";position:absolute;inset:0;background:linear-gradient(110deg,transparent 25%,rgba(255,255,255,.13) 50%,transparent 75%);transform:translateX(-120%);transition:transform .55s ease}.button:hover::after,.copy-button:hover::after{transform:translateX(120%)}.button:focus-visible,.tab:focus-visible,.nav-item:focus-visible,.density:focus-visible,.segmented button:focus-visible,.text-button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.button.primary{box-shadow:0 5px 18px rgba(166,155,255,.18)}.button.primary:active{transform:translateY(1px) scale(.98)}.button.secondary:hover,.button.ghost:hover,.density:hover{border-color:#4a4760}.swatch{transition:transform .3s ease,box-shadow .3s ease}.color-card{transition:transform .25s ease,border-color .25s ease,background .25s ease}.color-card:hover{transform:translateY(-4px);border-color:#514c72;background:#15151e}.color-card:hover .swatch{transform:scale(1.08);box-shadow:0 0 18px color-mix(in srgb,var(--accent) 30%,transparent),inset 0 0 0 1px rgba(255,255,255,.2)}.nav-item{position:relative}.nav-item::after{content:"";position:absolute;right:10px;width:0;height:1px;background:var(--accent);transition:width .25s ease}.nav-item:hover::after{width:12px}.status-dot{animation:pulse-dot 2.5s ease-in-out infinite}.field input,.input-with-action{transition:border-color .25s ease,box-shadow .25s ease,background .25s ease}.field input:hover,.input-with-action:hover{background:#191923;border-color:#393847}.field input:focus,.input-with-action:focus-within{box-shadow:0 0 0 3px rgba(166,155,255,.08),0 0 20px rgba(166,155,255,.06)}.toggle{transition:background .25s ease,transform .2s ease}.toggle:active{transform:scale(.94)}.release{transition:transform .3s ease,box-shadow .3s ease}.release:hover{transform:translateY(-4px);box-shadow:0 14px 38px rgba(0,0,0,.2),0 0 25px rgba(166,155,255,.06)}.release-icon{transition:transform .4s cubic-bezier(.2,.8,.2,1)}.release:hover .release-icon{transform:rotate(-8deg) scale(1.08)}.code-card{transition:border-color .3s ease,box-shadow .3s ease}.code-card:hover{border-color:#413d5c;box-shadow:0 15px 40px rgba(0,0,0,.14)}.copy-button{position:relative;overflow:hidden}.top-search{transition:border-color .25s ease,box-shadow .25s ease}.top-search:hover{border-color:#393847}.top-search:focus-within{box-shadow:0 0 0 3px rgba(166,155,255,.06)}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}.hero-copy h1{background:none;color:var(--text)}}
/* Refined navigation rail: editorial, flat, and easy to scan. */
.sidebar{padding:22px 0 18px;background:linear-gradient(180deg,#0d0d13 0%,#0b0b10 76%)}
.sidebar-heading{display:flex;align-items:center;justify-content:space-between;padding:0 20px 20px;border-bottom:1px solid var(--line)}
.sidebar-library{display:flex;align-items:center;gap:8px;color:var(--text);font:500 11px var(--mono);letter-spacing:.05em;text-transform:uppercase}.sidebar-library svg{color:var(--accent)}
.sidebar-collapse{border:0;background:none;color:var(--subtle);font:11px var(--mono);padding:4px 0}.sidebar-collapse:hover{color:var(--text)}
.sidebar-intro{padding:20px 20px 22px;margin:0;border-bottom:1px solid var(--line)}.sidebar-intro p{max-width:180px;margin-top:8px}
.sidebar nav{padding-top:22px}.nav-group{margin-bottom:24px}.nav-label{padding:0 20px;margin-bottom:8px;color:#555462;font-size:9px}.nav-item{position:relative;width:100%;border-radius:0;padding:9px 20px;gap:11px;letter-spacing:-.01em}.nav-item:hover{background:#13131b}.nav-item.active{background:linear-gradient(90deg,rgba(166,155,255,.15),rgba(166,155,255,.035));box-shadow:inset 2px 0 var(--accent);color:#f5f3ff}.nav-item.active::before{content:"";position:absolute;right:20px;width:5px;height:5px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px rgba(166,155,255,.8)}.nav-item::after{display:none}.nav-glyph{width:13px;color:#555462;font:14px var(--mono);line-height:1;text-align:center}.nav-item.active .nav-glyph{color:var(--accent)}.nav-count{border:0;padding:0;margin-left:auto;color:#716aab;background:none;font-size:10px}.sidebar-bottom{padding:16px 20px 0}.sidebar-bottom .status-line{font-size:9px}
@media(max-width:760px){.sidebar{padding-top:18px}.sidebar-heading{padding-left:20px;padding-right:20px}.sidebar-intro{padding-top:18px}.sidebar nav{padding-top:20px}.nav-item{padding-top:10px;padding-bottom:10px}}
+45
View File
@@ -0,0 +1,45 @@
import { useState } from 'react'
import './App.css'
import { Icon } from './components/Icon'
import { Sidebar } from './components/Sidebar'
import { Topbar } from './components/Topbar'
import { Button } from './components/Button'
import { ButtonMatrix } from './components/ButtonMatrix'
import { CodeEditor } from './components/CodeEditor'
import { FormField, TextInput } from './components/FormField'
import { MarkdownRenderer } from './components/MarkdownRenderer'
import { Section } from './components/Section'
import { TokenGrid } from './components/TokenGrid'
import { buttonExamples, buttonTabs, colorTokens, navigationGroups } from './data/library'
function App() {
const [active, setActive] = useState('Buttons')
const [query, setQuery] = useState('')
const [buttonTab, setButtonTab] = useState('Preview')
const [copied, setCopied] = useState(false)
const [visible, setVisible] = useState(false)
const [menu, setMenu] = useState(false)
const copy = () => { navigator.clipboard?.writeText('pnpm add @northstar/ui @northstar/tokens'); setCopied(true); setTimeout(() => setCopied(false), 1600) }
return <div className="app-shell">
<Topbar query={query} onQueryChange={setQuery} onMenuToggle={() => setMenu(!menu)} />
<div className={`body-layout ${menu ? 'menu-open' : ''}`}>
<Sidebar active={active} groups={navigationGroups} onSelect={item => { setActive(item); setMenu(false) }} />
<main className="main-content">
<div className="breadcrumbs"><span>Components</span><span>/</span><strong>{active}</strong></div>
<section className="hero-section"><div className="hero-copy"><span className="eyebrow accent">Component library</span><h1>Build with clarity.</h1><p>A considered set of accessible, composable components for building calm, capable interfaces.</p><div className="hero-actions"><Button variant="primary" onClick={() => document.getElementById('button-matrix')?.scrollIntoView({ behavior: 'smooth' })}>Explore components <Icon name="arrow" size={16} /></Button><Button variant="ghost">Read the principles</Button></div></div><div className="hero-orbit"><div className="orbit orbit-one" /><div className="orbit orbit-two" /><div className="orbit-core"><div className="brand-mark large"><span /></div><span>system<br /><b>ready</b></span></div><span className="orbit-label label-one">TOKENS <b>48</b></span><span className="orbit-label label-two">A11Y <b>AAA</b></span><span className="orbit-label label-three">RUNTIME <b>FAST</b></span></div></section>
<div className="content-grid"><div className="content-main">
<Section id="button-matrix" index="01 / COMPONENTS" title="Buttons" description="Actions with a clear hierarchy and dependable feedback." meta={<span className="api-pill">Button · 12 variants</span>}><ButtonMatrix examples={buttonExamples} tabs={buttonTabs} filter={query} activeTab={buttonTab} onTabChange={setButtonTab} /></Section>
<Section index="02 / FOUNDATIONS" title="Color tokens" description="Functional color roles keep every surface intentional." meta={<button className="text-button">View all tokens <Icon name="arrow" size={15} /></button>}><TokenGrid tokens={colorTokens} onCopy={copy} /></Section>
<Section index="03 / PRIMITIVES" title="Form controls" description="Inputs designed for focus, flow, and the occasional messy edge case."><div className="forms-grid"><FormField label="Project name" hint="Use a memorable name for your workspace."><TextInput defaultValue="Northstar Console" /></FormField><FormField label="API key" readOnlyLabel="read only" hint="Stored securely in your environment."><div className="input-with-action"><TextInput type={visible ? 'text' : 'password'} defaultValue="nsk_live_8f2a1d90" readOnly /><button onClick={() => setVisible(!visible)} aria-label="Show API key"><Icon name="eye" size={16} /></button></div></FormField><label className="toggle-field"><span><b>Enable notifications</b><small>Get updates about deployments and incidents.</small></span><button className="toggle on"><span /></button></label></div></Section>
<Section index="04 / CODE" title="Composable by default" description="Small APIs. Strong defaults. No surprises."><div className="code-card"><div className="code-head"><span className="mono muted">button.tsx</span><button className="copy-button" onClick={copy}><Icon name={copied ? 'check' : 'copy'} size={14} /> {copied ? 'Copied' : 'Copy code'}</button></div><pre><code><span className="pink">import</span> {'{ Button }'} <span className="pink">from</span> <span className="green">'@northstar/ui'</span>{'\n\n'}<span className="pink">export function</span> <span className="blue">DeployAction</span>() {'{'}{'\n'} <span className="pink">return</span> ({'\n'} &lt;<span className="blue">Button</span> variant=<span className="green">"primary"</span>&gt;Deploy changes&lt;/<span className="blue">Button</span>&gt;{'\n'} ){ '\n'}{'}'}</code></pre></div></Section>
<Section index="05 / CONTENT" title="Markdown rendering" description="A readable editorial layer for changelogs, docs, and product notes." meta={<span className="api-pill">Markdown · styled</span>}><MarkdownRenderer /></Section>
<Section index="06 / DEVELOPER TOOLING" title="CodeMirror theme" description="A focused editor palette for examples, playgrounds, and embedded code." meta={<span className="api-pill">codemirror · dark</span>}><CodeEditor /></Section>
</div><aside className="right-rail"><div className="rail-card"><span className="eyebrow">On this page</span>{['Buttons', 'Color tokens', 'Form controls', 'Markdown rendering', 'CodeMirror theme'].map((x, i) => <a className={i === 0 ? 'rail-link active' : 'rail-link'} href={i === 0 ? '#button-matrix' : '#'} key={x}><span>0{i + 1}</span>{x}</a>)}</div><div className="rail-card release"><div className="release-icon"><Icon name="layers" size={18} /></div><strong>Designed to be extended</strong><p>Bring your brand, keep the rhythm. Tokens are portable and components stay unopinionated.</p><button className="text-button">Read the docs <Icon name="arrow" size={14} /></button></div><div className="rail-card"><span className="eyebrow">Need a hand?</span><p className="rail-copy">Check the contribution guide or say hello in the community.</p><Button variant="secondary" className="full">Open community <span>↗</span></Button></div></aside></div>
<footer><span>northstar design system</span><span className="mono">made for the long haul · 2025</span></footer>
</main>
</div>
</div>
}
export default App
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="77" height="47" fill="none" aria-labelledby="vite-logo-title" viewBox="0 0 77 47"><title id="vite-logo-title">Vite</title><style>.parenthesis{fill:#000}@media (prefers-color-scheme:dark){.parenthesis{fill:#fff}}</style><path fill="#9135ff" d="M40.151 45.71c-.663.844-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.493c-.92 0-1.457-1.04-.92-1.788l7.479-10.471c1.07-1.498 0-3.578-1.842-3.578H15.443c-.92 0-1.456-1.04-.92-1.788l9.696-13.576c.213-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.472c-1.07 1.497 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.087.89 1.83L40.153 45.712z"/><mask id="a" width="48" height="47" x="14" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M40.047 45.71c-.663.843-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.389c-.92 0-1.457-1.04-.92-1.788l7.479-10.472c1.07-1.497 0-3.578-1.842-3.578H15.34c-.92 0-1.456-1.04-.92-1.788l9.696-13.575c.213-.297.556-.474.92-.474H53.93c.92 0 1.456 1.04.92 1.788L47.37 13.03c-1.07 1.498 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.088.89 1.831L40.049 45.712z"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#eee6ff" rx="5.508" ry="14.704" transform="rotate(269.814 20.96 11.29)scale(-1 1)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#eee6ff" rx="10.399" ry="29.851" transform="rotate(89.814 -16.902 -8.275)scale(1 -1)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#8900ff" rx="5.508" ry="30.487" transform="rotate(89.814 -19.197 -7.127)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.928 4.177)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.738 5.52)scale(1 -1)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#eee6ff" rx="14.072" ry="22.078" transform="rotate(93.35 31.245 55.578)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx="14.592" cy="9.743" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(39.51 14.592 9.743)"/></g><g filter="url(#k)"><ellipse cx="61.728" cy="-5.321" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 61.728 -5.32)"/></g><g filter="url(#l)"><ellipse cx="55.618" cy="7.104" fill="#00c2ff" rx="5.971" ry="9.665" transform="rotate(37.892 55.618 7.104)"/></g><g filter="url(#m)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#n)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#o)"><ellipse cx="49.857" cy="30.678" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 49.857 30.678)"/></g><g filter="url(#p)"><ellipse cx="52.623" cy="33.171" fill="#00c2ff" rx="5.971" ry="15.297" transform="rotate(37.892 52.623 33.17)"/></g></g><path d="M6.919 0c-9.198 13.166-9.252 33.575 0 46.789h6.215c-9.25-13.214-9.196-33.623 0-46.789zm62.424 0h-6.215c9.198 13.166 9.252 33.575 0 46.789h6.215c9.25-13.214 9.196-33.623 0-46.789" class="parenthesis"/><defs><filter id="b" width="60.045" height="41.654" x="-5.564" y="16.92" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-40.407" y="-6.762" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-35.435" y="2.801" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-30.84" y="20.8" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-29.307" y="21.949" color-interpolation-filters="sRGB" filterUnits="usLine truncated

After

Width:  |  Height:  |  Size: 8.5 KiB

+9
View File
@@ -0,0 +1,9 @@
import type { ReactNode, ButtonHTMLAttributes } from 'react'
type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost'
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & { variant?: ButtonVariant; children: ReactNode }
/** Shared action primitive. Visual variants stay in App.css so themes can override them. */
export function Button({ variant = 'secondary', className = '', children, ...props }: ButtonProps) {
return <button className={`button ${variant} ${className}`.trim()} {...props}>{children}</button>
}
+10
View File
@@ -0,0 +1,10 @@
import { Button } from './Button'
import { Icon } from './Icon'
export type ButtonExample = { name: string; label: string; variant: 'primary' | 'secondary' | 'danger' | 'ghost' }
type ButtonMatrixProps = { examples: ButtonExample[]; tabs: string[]; filter?: string; activeTab?: string; onTabChange?: (tab: string) => void }
export function ButtonMatrix({ examples, tabs, filter = '', activeTab, onTabChange }: ButtonMatrixProps) {
const visible = examples.filter(example => `${example.name} ${example.label}`.toLowerCase().includes(filter.toLowerCase()))
return <><div className="tabs">{tabs.map(tab => <button className={tab === activeTab ? 'tab active' : 'tab'} key={tab} onClick={() => onTabChange?.(tab)}>{tab}</button>)}</div><div className="showcase"><div className="showcase-head"><span className="eyebrow">Interactive states</span><span className="mono muted">button.tsx</span></div><div className="button-matrix">{visible.map(example => <div className="button-row" key={example.name}><span className="row-label">{example.name}</span><Button variant={example.variant}>{example.label}</Button><Button variant={example.variant} className="hover-state">{example.label}</Button><Button variant={example.variant} className="icon-only" aria-label={`${example.name} icon button`}><Icon name="copy" size={15} /></Button><Button variant={example.variant} className="loading"><span className="spinner" />Loading</Button></div>)}</div><div className="showcase-foot"><span className="muted">Density</span><button className="density active">Compact <span>32px</span></button><button className="density">Default <span>40px</span></button><span className="foot-check"><Icon name="check" size={14} /> Keyboard ready</span></div></div></>
}
+7
View File
@@ -0,0 +1,7 @@
import { Icon } from './Icon'
export type CodeEditorProps = { fileName?: string; language?: string }
export function CodeEditor({ fileName = 'theme.ts', language = 'TypeScript' }: CodeEditorProps) {
return <div className="editor-card"><div className="editor-tabs"><span className="editor-dot" /><span className="editor-tab active">{fileName}</span><span className="editor-tab">tokens.css</span><span className="editor-actions">● ● ●</span></div><div className="editor-body"><div className="line-numbers">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8</div><pre><code><span className="cm-comment">// A calm editor for focused work</span>{'\n'}<span className="cm-keyword">export const</span> <span className="cm-variable">theme</span> = {'{'}{'\n'} <span className="cm-property">name</span>: <span className="cm-string">'northstar-dark'</span>,{'\n'} <span className="cm-property">colors</span>: {'{'}{'\n'} <span className="cm-property">accent</span>: <span className="cm-string">'#a69bff'</span>,{'\n'} <span className="cm-property">keyword</span>: <span className="cm-string">'#e58bff'</span>,{'\n'} <span className="cm-property">comment</span>: <span className="cm-string">'#666572'</span>{'\n'} {'}'}{'\n'}{'}'}</code></pre></div><div className="editor-status"><span>Ln 4, Col 18</span><span>{language} · UTF-8</span><span className="editor-ready"><Icon name="check" size={12} /> No issues</span></div></div>
}
+5
View File
@@ -0,0 +1,5 @@
import type { InputHTMLAttributes, ReactNode } from 'react'
type FormFieldProps = { label: string; hint: string; children: ReactNode; readOnlyLabel?: string }
export function FormField({ label, hint, children, readOnlyLabel }: FormFieldProps) { return <label className="field"><span>{label}{readOnlyLabel && <em>{readOnlyLabel}</em>}</span>{children}<small>{hint}</small></label> }
export function TextInput(props: InputHTMLAttributes<HTMLInputElement>) { return <input {...props} /> }
+9
View File
@@ -0,0 +1,9 @@
type IconProps = { name: string; size?: number }
const paths: Record<string, string> = {
search: 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm6-2 4 4', sun: 'M12 3v2m0 14v2M4 12h2m12 0h2M12 8a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z', copy: 'M8 8h10v12H8zM6 16H4V4h12v2', check: 'm5 12 4 4L19 6', eye: 'M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Zm10 3a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z', arrow: 'M5 12h14m-6-6 6 6-6 6', menu: 'M4 6h16M4 12h16M4 18h16', layers: 'm12 3 9 5-9 5-9-5 9-5Zm-9 9 9 5 9-5M3 16l9 5 9-5',
}
export function Icon({ name, size = 18 }: IconProps) {
return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={paths[name] || paths.layers} /></svg>
}
+7
View File
@@ -0,0 +1,7 @@
import type { ReactNode } from 'react'
export type MarkdownRendererProps = { title?: string; kicker?: string; children?: ReactNode }
export function MarkdownRenderer({ title = 'Quiet power, shipped weekly.', kicker = 'NORTHSTAR / RELEASE NOTES' }: MarkdownRendererProps) {
return <div className="markdown-card"><div className="markdown-toolbar"><span className="eyebrow">Preview</span><span className="mono muted">release-notes.md</span></div><article className="markdown-body"><span className="md-kicker">{kicker}</span><h3>{title}</h3><p>Our components help teams move quickly without making the interface feel rushed.</p><div className="md-callout"><span className="callout-mark">✦</span><p><strong>New in 0.8.2</strong><br />Button focus rings now respect your theme while keeping a dependable contrast ratio.</p></div><h4>What changed</h4><ul><li>Added <code>accent-subtle</code> surface token</li><li>Improved keyboard navigation in menus</li><li>Reduced layout shift during loading states</li></ul><div className="md-table"><div><span>Token</span><span>Value</span><span>Role</span></div><div><code>surface-1</code><span>#111118</span><span>Elevated panels</span></div><div><code>accent</code><span className="md-accent">#A69BFF</span><span>Interactive focus</span></div></div></article></div>
}
+7
View File
@@ -0,0 +1,7 @@
import type { ReactNode } from 'react'
type SectionProps = { index: string; title: string; description: string; meta?: ReactNode; children: ReactNode; id?: string; className?: string }
export function Section({ index, title, description, meta, children, id, className = '' }: SectionProps) {
return <section className={`section-block ${className}`.trim()} id={id}><div className="section-heading"><div><span className="section-index">{index}</span><h2>{title}</h2><p>{description}</p></div>{meta}</div>{children}</section>
}
+9
View File
@@ -0,0 +1,9 @@
import { Icon } from './Icon'
export type NavigationItem = { label: string; count?: number }
export type NavigationGroup = { label: string; items: NavigationItem[] }
type SidebarProps = { active: string; groups: NavigationGroup[]; onSelect: (item: string) => void }
export function Sidebar({ active, groups, onSelect }: SidebarProps) {
return <aside className="sidebar"><div className="sidebar-heading"><div className="sidebar-library"><Icon name="layers" size={13} /> Library</div><button className="sidebar-collapse" aria-label="Collapse sidebar">⌘</button></div><div className="sidebar-intro"><span className="eyebrow">Northstar system</span><p>Principles and primitives for product teams.</p></div><nav aria-label="Documentation navigation">{groups.map(group => <div className="nav-group" key={group.label}><span className="nav-label">{group.label}</span>{group.items.map(item => <button key={item.label} className={active === item.label ? 'nav-item active' : 'nav-item'} onClick={() => onSelect(item.label)}><span className="nav-glyph" aria-hidden="true">{item.count ? '◈' : '·'}</span><span>{item.label}</span>{item.count !== undefined && <span className="nav-count">{item.count}</span>}</button>)}</div>)}</nav><div className="sidebar-bottom"><div className="status-line"><span className="status-dot" />All systems operational</div><span className="mono muted">Updated 2 min ago</span></div></aside>
}
+8
View File
@@ -0,0 +1,8 @@
import { Icon } from './Icon'
export type Token = { name: string; color: string; value: string }
type TokenGridProps = { tokens: Token[]; onCopy?: (value: string) => void }
export function TokenGrid({ tokens, onCopy }: TokenGridProps) {
return <div className="color-grid">{tokens.map(token => <div className="color-card" key={token.name}><div className="swatch" style={{ background: token.color }} /><div><strong>{token.name}</strong><span className="mono">{token.value}</span></div><button className="copy-icon" onClick={() => onCopy?.(token.value)} aria-label={`Copy ${token.name} token`}><Icon name="copy" size={14} /></button></div>)}</div>
}
+7
View File
@@ -0,0 +1,7 @@
import { Icon } from './Icon'
type TopbarProps = { query: string; onQueryChange: (value: string) => void; onMenuToggle: () => void }
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>
}
+28
View File
@@ -0,0 +1,28 @@
import type { ButtonExample } from '../components/ButtonMatrix'
import type { Token } from '../components/TokenGrid'
import type { NavigationGroup } from '../components/Sidebar'
export const navigationGroups: NavigationGroup[] = [
{ label: 'Start here', items: [{ label: 'Overview' }, { label: 'Principles' }, { label: 'Quickstart' }] },
{ label: 'Foundations', items: [{ label: 'Color & theme' }, { label: 'Typography' }, { label: 'Spacing' }, { label: 'Elevation' }] },
{ label: 'Components', items: [{ label: 'Buttons', count: 12 }, { label: 'Form controls' }, { label: 'Badges' }, { label: 'Cards' }, { label: 'Data table' }] },
{ label: 'Patterns', items: [{ label: 'Command palette' }, { label: 'Empty states' }, { label: 'Settings layouts' }] },
]
export const buttonTabs = ['Preview', 'API reference', 'Usage']
export const pageSections = ['Buttons', 'Color tokens', 'Form controls', 'Markdown rendering', 'CodeMirror theme']
export const buttonExamples: ButtonExample[] = [
{ name: 'Primary', label: 'Deploy changes', variant: 'primary' },
{ name: 'Secondary', label: 'View activity', variant: 'secondary' },
{ name: 'Destructive', label: 'Delete project', variant: 'danger' },
{ name: 'Ghost', label: 'Cancel', variant: 'ghost' },
]
export const colorTokens: Token[] = [
{ name: 'Accent', color: 'var(--accent)', value: '#9b8cff' },
{ name: 'Positive', color: 'var(--positive)', value: '#73dfae' },
{ name: 'Warning', color: 'var(--warning)', value: '#ffc777' },
{ name: 'Critical', color: 'var(--critical)', value: '#ff8585' },
]
+3
View File
@@ -0,0 +1,3 @@
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Manrope:wght@400;500;600;700;800&display=swap');
:root { font-family:'Manrope',sans-serif; color:#f1f0f7; background:#0b0b10; font-synthesis:none; text-rendering:optimizeLegibility; -webkit-font-smoothing:antialiased; }
* { box-sizing:border-box; } html { scroll-behavior:smooth; } body { margin:0; min-width:320px; } button,input { font:inherit; } button { cursor:pointer; }
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)