@noahwright/design
v1.0.8
Published
Noah Wright's hyper-minimal React design system. Learning in public; use at your own risk.
Readme
@noahwright/design
This is a hyper-minimal React design system I'm using as a playground. That means:
- 💻 Modern browsers only. Makes things a lot simpler and supports most people.
- ⚠️ I'm "learning in public" and may break things along the way. Use at your own risk.
- 🤖 Agents will do most of the heavy lifting. Guiding them to build the site is half the project.
Install
npm i @noahwright/design
# or: pnpm add @noahwright/designPeer deps required in your app:
- react
- react-dom
What’s included
- Core components:
Button,Link,Heading,Text,Header,Footer,Layout,Container,Card,Modal - Utility components:
Avatar,HamburgerMenu,Menu,MenuItem - Base CSS:
src/styles/theme.csswith semantic color tokens (--foreground,--background,--primary,--secondary,--confirm,--danger,--overlay) - Theme utilities:
Themetype,buildThemeCss(theme)function, light/dark toggle (setThemeMode,toggleThemeMode,getThemeMode)
Usage (components)
import {
Button,
Link,
Heading,
Text,
Header,
Card,
Modal,
Menu,
MenuItem,
setThemeMode,
toggleThemeMode,
} from "@noahwright/design";
import { useState } from "react";
export function Example() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<>
<Header
left={<span>Logo</span>}
center={<Heading level={2}>My App</Heading>}
right={<Button onClick={() => toggleThemeMode()}>🌙</Button>}
mobileMenu={<Menu trigger="≡" items={[]} />}
/>
<main>
<Card title="Welcome" subtitle="To the design system">
<Text>A hyper-minimal, highly opinionated system.</Text>
<Button onClick={() => setIsModalOpen(true)}>Learn More</Button>
</Card>
<Modal
open={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="About This System"
actions={[{ label: "Got it", variant: "primary" }]}
>
<Text>Built for learning, built in public, and built by agents.</Text>
</Modal>
</main>
</>
);
}Theming (typed, build-time + runtime toggle)
In your app repo, create a typed theme.ts:
// theme.ts
import type { Theme } from "@noahwright/design";
export const theme: Theme = {
foreground: "#111111",
background: "#ffffff",
primary: "#2563eb",
secondary: "#9333ea",
confirm: "#16a34a",
danger: "#dc2626",
overlay: "rgba(15, 23, 42, 0.6)",
};Generate public/theme.css at build time:
// scripts/build-theme.ts
import { writeFileSync } from "node:fs";
import { buildThemeCss } from "@noahwright/design";
import { theme } from "../theme";
writeFileSync("public/theme.css", buildThemeCss(theme), "utf8");Light/Dark Theme Toggle
Use setThemeMode, getThemeMode, or toggleThemeMode:
import { toggleThemeMode } from "@noahwright/design";
export function ThemeToggle() {
return (
<button onClick={() => toggleThemeMode()}>
{getThemeMode() === "dark" ? "☀️" : "🌙"}
</button>
);
}The system applies a data-theme="dark" attribute to <html> and provides dark-mode colors via CSS variable overrides. Customize dark colors in your theme or update src/styles/theme.css.
Old browser redirect (optional)
<script>
if (!("CSS" in window) || !CSS.supports("color", "color-mix(in srgb, white 50%, black)")) {
location.href = "/fallback.html";
}
</script>SSR (build-time theme, optional inline style)
For static/build-time theme injection:
// scripts/build-theme.ts
import { writeFileSync } from "node:fs";
import { buildThemeCss } from "@noahwright/design";
import { theme } from "../theme";
writeFileSync("public/theme.css", buildThemeCss(theme), "utf8");Or inline via SSR:
// Next.js Server Component layout
import { buildThemeCss } from "@noahwright/design";
import { theme } from "../theme";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<style id="nw-theme" dangerouslySetInnerHTML={{ __html: buildThemeCss(theme) }} />
</head>
<body>{children}</body>
</html>
);
}No runtime side effects at module level; fully SSR-safe.
Philosophy & Constraints
- No Tailwind/SCSS/LESS/styled-components/emotion/CSS-in-JS/PostCSS.
- No layout/spacing/animation utilities yet.
- No extra props: the wrappers are intentionally tiny.
- Add capabilities only when needed in your apps.
Dev / Storybook
npm i
npm run storybookBuild the library
npm run buildThis produces ESM + CJS + types in dist/. Publish with:
npm publish --access publicRepository notes
- Agent guidance: See
AGENTS.mdfor machine-focused instructions and conventions. - Changelog: Add entries to
CHANGELOG.mdbefore committing changes or opening a PR.
