Skip to content

About

Zero-runtime UI with the shadcn aesthetic — no React, no Tailwind, no Radix, no hydration. 65 HTML/CSS components for server-rendered, static, HTMX, Astro, and AI-generated apps.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

shadcss/ui

Zero-runtime UI with the shadcn aesthetic — no React, no Tailwind, no Radix, no hydration. 65 HTML/CSS components and patterns for server-rendered, static, HTMX, Astro, and AI-generated apps. ~20.2 KB gzipped, zero dependencies, zero JS runtime. Complex widgets are clearly marked.

live demo npm gzip size no js framework components license

▶ Live demo: shadcss.vercel.app · 📖 Docs: /docs · 🤖 /llms.txt

shadcss brings the shadcn/ui visual language to plain HTML and CSS. Where shadcn uses React + Radix + Tailwind, shadcss is CSS you paste into any server-rendered template — built on :has(), the Popover API, native <dialog>, and design tokens. No JS framework. Zero dependencies. Zero framework lock-in. The CSS bundle ships 0 JS; native <dialog>/Popover/toast need a one-line native trigger (showModal()/showPopover()).

Not affiliated with, endorsed by, or sponsored by shadcn/ui. Inspired by its design language.

Quickstart

Paste this into any HTML page and open it — the button renders in the shadcn style. No build step, no JavaScript.

<link rel="stylesheet" href="https://unpkg.com/@russfranky/shadcss@0.1.12/dist/shadcss.min.css">
<button class="btn">Hello, shadcss</button>

Install

<!-- CDN — pin a version in production so a future major can't break you -->
<link rel="stylesheet" href="https://unpkg.com/@russfranky/shadcss@0.1.12/dist/shadcss.min.css">
# npm
npm install @russfranky/shadcss
/* Bare-specifier @import — resolves ONLY through a bundler (Vite, webpack,
   Parcel, esbuild, etc.) that honors the package "exports"/"style" field.
   Plain CSS has no Node resolution, so this 404s without a bundler. */
@import "@russfranky/shadcss";

/* Plain CSS / no bundler: import the built file by path instead. */
@import "@russfranky/shadcss/dist/shadcss.min.css";

CLI — own the code and keep it updatable

npx @russfranky/shadcss-cli add button card dialog   # copy components (+ deps) into ./shadcss
npx @russfranky/shadcss-cli diff button              # see what changed upstream vs your copy
npx @russfranky/shadcss-cli check ./index.html       # lint HTML for a11y/markup foot-guns
npx @russfranky/shadcss-cli list                     # all 65 components

add gives you shadcn-style ownership (the CSS lives in your repo); diff answers shadcn's #1 complaint by showing exactly what changed upstream so fixes don't silently pass you by. Zero dependencies, fetches from the CDN. See packages/cli.

Optional JS helpers (only where the platform falls short)

shadcss is zero-runtime CSS by default. For the few interactions the platform can't yet complete (menu keyboard nav, full ARIA tabs), @russfranky/shadcss-js ships tiny, dependency-free, opt-in helpers — ~1 KB each, per-element, no global runtime.

<div class="dropdown-menu" popover id="m" data-sc-menu> … </div>
<div class="tabs" data-sc-tabs> … </div>
<script type="module">import "@russfranky/shadcss-js/menu"; import "@russfranky/shadcss-js/tabs";</script>

Without them the components stay accessible native HTML (tabs are a real radiogroup); with them you get the full keyboard/ARIA pattern. See packages/shadcss-js.

Configuration

No config files, no env vars — shadcss is CSS. Theming is done by overriding design tokens (CSS custom properties), never by editing components. All tokens live in packages/shadcss/src/base/tokens.css (12 groups: surfaces, brand, status, borders, sidebar, chart, radii, typography, spacing, elevation, motion, z-index). Colors are full OKLCH values (0.2.0+), applied directly as var(--token).

:root {
  --primary: oklch(0.5 0.15 250);      /* your brand color */
  --primary-foreground: oklch(1 0 0);
  --radius: 0.75rem;
}

Dark mode ships built in — declarative or automatic:

<html data-theme="dark">   <!-- force dark -->
<html>                      <!-- follows prefers-color-scheme -->

Per-component CSS files in packages/shadcss/dist/components/ are standalone — include only what you use to shrink the bundle (see "Import only what you use" in the framework README).

Why shadcss — the common shadcn/ui complaints, answered

shadcss drops React, Radix, and Tailwind, which structurally removes most of the things people complain about with shadcn/ui:

Common shadcn/ui complaint shadcss answer
"Your component, your problem" — copy-paste code you must maintain; no npm update shadcss is both: copy a single CSS file, or npm install the package and update it like any dependency. SemVer'd.
Dependency bugs (a cmdk/Radix breaking change breaks your components) Zero dependencies. Nothing upstream can break you.
Tailwind: "ugly" HTML, utility-class soup, no selector targeting Semantic classes (<button class="btn">), real CSS, theme via CSS variables. Readable markup, no utility soup.
SSR / hydration mismatches No JavaScript runtime → no hydration, ever. Works in any SSR/SSG/server-rendered/email context.
A11y breaks when you customize, with no safety net Ships an axe-core a11y gate + markup guards you can run against your own build (npm run check, check:a11y). Light and dark themes verified at 0 axe violations.
Not responsive by default (dialogs/tables overflow on mobile) Overlays clamp to the viewport (min(size, 100vw - 2rem)); tables scroll; OTP wraps; the calendar shrinks. No horizontal overflow at 375px+.
"Too much freedom" — undisciplined edits create inconsistency Retheme by overriding design tokens, not by editing components — consistency by construction.

Honest about the trade-off: like shadcn, the components are intentionally simple, and a class of interactivity (menu keyboard nav, command search, live toggles) still needs a few lines of your JavaScript — documented, never faked. shadcss is the accessible shadcn aesthetic with zero runtime, not a drop-in Radix replacement.

Monorepo structure

This repository mirrors the shadcn-ui/ui layout:

shadcss-ui/
├── apps/
│   └── www/              ← live component showcase + docs
└── packages/
    └── shadcss/          ← the framework (published to npm as `shadcss`)
        ├── src/          ← source CSS (base + 65 components)
        ├── dist/         ← built bundles
        ├── registry.json ← machine-readable component spec
        └── AI_GUIDE.md   ← patterns for AI code generation

Develop

npm install        # install workspace deps
npm run build      # build the framework → packages/shadcss/dist
npm run dev        # rebuild on change (watch)
npm run www        # serve the showcase at http://localhost:3333

The showcase (apps/www/index.html) links the built shadcss.min.css, which the framework build generates and copies in. Run npm run build first (the www dev/build scripts now do this for you) — opening the raw index.html before any build produces an unstyled page.

Deploy the demo

VERCEL_TOKEN=xxx npm run deploy

Builds, stages a self-contained copy (CSS + doc-links repointed to GitHub), and ships it to the shadcss Vercel project → shadcss.vercel.app. The token is read from the environment (never committed); override the team/project with VERCEL_SCOPE / VERCEL_PROJECT_ID if needed.

Quality gates (run locally)

npm install
npm run build      # Lightning CSS bundle + minify (fails on invalid CSS)
npm run check      # static guards (no browser needed)

npm run check runs two no-dependency guards that encode the regressions prior QA passes found, so they can't silently return:

  • check-consistency.mjs — version sync, component count, gzip badge, "no absolutist 0-JS claim," "no false shadcn $schema" — all asserted against reality.
  • check-markup.mjs — no anchor() without an anchor-name producer, no popover on .navigation-menu-content, no static aria-selected on tabs, no invalid focus-visible-anchor, balanced markup, valid registry JSON.

Runtime accessibility (headless Chromium + axe-core), one-time browser install:

npx playwright install chromium
npx serve apps/www -l 3333 &      # serve the built showcase
npm run check:a11y                # axe-core on light AND dark themes

check:a11y scans both themes (with a settle delay so it doesn't sample mid-transition colors) and fails on any serious or critical violation; moderate/minor are reported as warnings. The showcase currently passes at 0 violations of any impact in both light and dark — the WCAG AA work (including a darkened status palette vs shadcn's defaults) is recorded in qa/iteration-3/.

Documentation

Contributing

See CONTRIBUTING.md. PRs welcome — each component is one file in packages/shadcss/src/components/; use the design tokens, not raw values; run npm run build and npm run check before pushing.

License

MIT — see LICENSE.md.

About

Zero-runtime UI with the shadcn aesthetic — no React, no Tailwind, no Radix, no hydration. 65 HTML/CSS components for server-rendered, static, HTMX, Astro, and AI-generated apps.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages