Where to start, how the system fits together, and the rules that keep it coherent.
Optimistic is named for the optimistic update, the pattern where an interface shows the result of an action the instant you take it, then quietly reconciles with the server. The design system is built in that same spirit: fast, confident, and honest when reality disagrees. Read the philosophy for the long version, or start below. Everything lives in one repository: github.com/subinsab/Optimistic.
The stack is Next.js and React, so anything that runs a modern Node app runs this. Confirm two things first: node -v and git --version.
| Requirement | Minimum |
|---|---|
| Operating system | macOS 12+, Windows 10+, or a modern Linux |
| Node.js | 18.18 or newer (20 LTS recommended) |
| Package manager | npm 9+ (bundled with Node), or pnpm / yarn |
| Git | 2.30 or newer |
| Editor | VS Code recommended, or any editor you like |
| Disk space | about 600 MB for dependencies |
Four free tools. Install Node and Git to run the project; VS Code and Claude are how you will work in it.
Runs the dev server, the production build and the component installer. Get the LTS build.
Download Node.js ↗Build with the AI workflow this system was made for: the desktop app and the Claude Code CLI.
Download Claude ↗Four steps to get the documentation site and the full component source running on your machine.
git clone https://github.com/subinsab/Optimistic.git
cd Optimistic npm install
npm run dev # ready on http://localhost:3000
npm run build npm start
The kit is shadcn-style: instead of a locked npm package, an installer copies the tokens and the components you pick straight into your project, so you own and can reshape every file.
cd Optimistic/optimistic-ui
node install.mjs button notification --dir src/ui
/* global.css */ @import "./ui/tokens.css";
import { Button } from "./ui/components/button/button";
<Button variant="warm">Ship it</Button>Once the tokens are imported, every foundation is a set of CSS variables you consume with var(--token). Nothing is hard-coded, so a change at the token layer flows to every component at once. Here is how to use each one.
Never hard-code a hex. Reach for a role variable (surface, text, border), or a core ramp step for a specific tone. Change a token once and it moves everywhere.
.card {
background: var(--bg-panel);
color: var(--text-primary);
border: 1px solid var(--border-default);
}
.card__cta { background: var(--color-brand-500); }Each text style is one font shorthand plus its tracking. Body 1 (16px) is the default reading size, Header 1 to 5 for titles.
.title { font: var(--type-header-1); letter-spacing: var(--type-header-1-tracking); }
.copy { font: var(--type-body-1); letter-spacing: var(--type-body-1-tracking); }Every gap, pad and margin is a step on the 4px scale. Reach for a token, never a raw pixel.
.panel { padding: var(--space-300); } /* 24px */
.row { display: flex; gap: var(--space-100); } /* 8px */A responsive grid: 4 columns on mobile, 8 on tablet, 12 on web, inside a 1320px container. Set how many columns each cell spans per breakpoint with --m (mobile), --t (tablet), --d (desktop).
<div class="o-container">
<div class="o-row">
<div class="o-col" style="--m:4; --t:8; --d:8">Main</div>
<div class="o-col" style="--m:4; --t:8; --d:4">Aside</div>
</div>
</div>Sharp panels, gently rounded controls, pills for chips. One token per role.
.button { border-radius: var(--radius-md); } /* 6px */
.card { border-radius: var(--radius-xl); } /* 12px */
.chip { border-radius: var(--radius-full); }On a dark field, structure comes from a 1px hairline first. Reach for a shadow only when depth carries meaning.
.divider { border-top: 1px solid var(--border-default); }
.popover { box-shadow: 0 8px 24px rgba(0, 0, 0, .42); }Layouts reflow at fixed widths. Design mobile-first, then add columns as space allows.
/* mobile-first: 768px tablet, 1024px web */
.grid { grid-template-columns: 1fr; }
@media (min-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(4, 1fr); } }The system is atomic. A decision becomes a token, tokens form components, components form products, one unbroken line from Figma to shipped software.
--color-brand-500, --space-300, --type-body-1.var(--token) and never hard-code a value.The field is monochrome so a single #FF7A00 element can carry all the attention. Never spend it twice in one view.
Structure comes from one-pixel lines and spacing, not from boxes, shadows or heavier borders.
Render success first, but roll back plainly when the server disagrees. The interface never lies to keep a promise.
If you are typing a hex or a pixel into a component, stop. The value belongs in a token so it can move everywhere at once.