lombokcss
v0.1.0
Published
Token-first component CSS framework. One markup, five design styles (Corporate Flat, Resonant Stark, Neo-Brutalism, Semantic Minimalist, Glassmorphism), dark mode + RTL. ~9.7KB gzipped.
Maintainers
Readme
LombokCSS
A modern, token-first component CSS framework. Drop a class, get a working component — like Bootstrap. Re-theme everything by changing one attribute — like a design system. Ships at ~9.7 KB gzipped (full build, minified).
Component-based (Bootstrap) + Token-driven theming (design systems) + Tiny (Pico/UnoCSS)Why it's different
Most frameworks bake their look into each component, so changing the visual style means overriding hundreds of rules. LombokCSS inverts that:
- Components read only semantic tokens (
--lc-surface,--lc-text,--lc-radius,--lc-shadow,--lc-accent, …). - A "design style" is just a different set of token values. Switching it never touches component CSS or your HTML.
<!-- same markup, five identities -->
<html data-style="neo-brutalism"> <!-- thick borders, hard shadows -->
<html data-style="glassmorphism"> <!-- frosted glass -->
<html data-style="resonant-stark"> <!-- Linear-style dark -->Dark mode (data-theme) and RTL (dir) are independent axes that compose
with any style.
Install
CDN (jsDelivr) — fastest start:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lombokcss/dist/lombok.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/lombokcss/dist/lombok.js"></script>npm:
npm install lombokcss
# or: yarn add lombokcss / pnpm add lombokcssimport "lombokcss/dist/lombok.min.css";
import "lombokcss/dist/lombok.js"; // optional, only for interactive componentsPin a version for production (jsDelivr):
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/lombok.min.css">Download: grab dist/lombok.min.css (+ optional dist/lombok.js) and link them directly.
The JS is optional. Accordions, modals and <dialog> work natively without
it; the script only adds dropdown/tab/toast/table-sort/navbar-toggle behavior.
Other registries. Also distributed via Composer (composer require codinglombok/lombokcss),
RubyGems (gem "lombokcss" → Lombokcss.assets_path for Sprockets), and Bower
(legacy: bower install lombokcss, prefer npm). Works with any bundler
(Vite/Webpack/Parcel) and framework (Vue/React/Svelte) — just import the CSS.
See the Getting started docs for per-tool snippets.
Documentation site
A full multi-page docs site lives in docs/ (Overview, Getting started, Theming
& styles, Components, Utilities). Open docs/index.html in a browser. The style
/ dark / RTL switchers at the top apply live and follow you across pages via
the URL query string. A single-file interactive playground is at
examples/demo.html.
Architecture
src/
variables.css → token architecture (the contract every component depends on)
core.css → reset/reboot + base element styles (= classless mode)
themes.css → token value sets per design preset (data-style)
components.css → all components (read tokens only, RTL-safe, glass-aware)
utilities.css → atomic utilities + responsive prefixes
dist/
lombok.css → bundled, unminified (readable)
lombok.min.css → bundled + minified (ship this)
lombok.js → optional interactive behaviorsToken layers
- Primitives & scale — spacing, font sizes, raw palette.
- Semantic tokens —
--lc-surface,--lc-text,--lc-border,--lc-accent,--lc-radius,--lc-shadow,--lc-blur, status colors. Components only ever reference these. - Style presets (
[data-style="…"]) re-map the semantic tokens. - Dark overlay (
[data-theme="dark"]/prefers-color-scheme) re-maps color tokens; composes with presets via[data-style][data-theme]pairs.
Glassmorphism support is built into the contract: every surface component
applies backdrop-filter: var(--lc-blur). --lc-blur is none everywhere
except the glass preset, so the property is inert until a glass theme turns it
on — no per-component glass code.
The five design styles
| data-style | Character |
|---------------------------|-----------|
| modern-corporate-flat | Clean, flat, neutral + one brand color, medium radius, soft shadows (default) |
| resonant-stark | Linear-style dark: high contrast, subtle borders, tight type, violet accent |
| neo-brutalism | Thick black borders, hard offset shadows, bold blocks, zero radius |
| semantic-minimalist | Minimal, neutral, generous spacing, serif display, readability-first |
| glassmorphism | Frosted glass over a gradient; translucent surfaces, glowing borders (with opaque fallback) |
Theming guide
Everything is a CSS variable. Override on :root (or a scope) to customize:
:root {
--lc-accent: #e11d48; /* brand color */
--lc-accent-hover: #be123c;
--lc-radius: 4px; /* tighter corners */
--lc-font-sans: "Inter", system-ui, sans-serif;
--lc-space-4: 1.1rem; /* rescale spacing */
}Dark mode — three ways, they all work together:
<html data-theme="dark"> <!-- force dark -->
<html data-theme="light"> <!-- force light -->
<html> <!-- follows the OS setting -->RTL — set the document direction; all components use logical properties:
<html dir="rtl" lang="ar">Make your own style — add a token block, no component edits:
[data-style="sunset"] {
--lc-bg:#1a0f0f; --lc-surface:#2a1818; --lc-text:#ffe;
--lc-accent:#ff7849; --lc-radius:14px; --lc-shadow:0 8px 24px rgba(0,0,0,.4);
}Component usage
<!-- Buttons -->
<button class="btn btn-primary">Primary</button>
<button class="btn btn-outline btn-lg btn-rounded">Big outline</button>
<span class="btn-group">
<button class="btn btn-secondary">L</button><button class="btn btn-secondary">R</button>
</span>
<!-- Card -->
<article class="card">
<div class="card-body">
<h3 class="card-title">Title</h3>
<p class="card-text">Body copy.</p>
</div>
<div class="card-footer">Footer</div>
</article>
<!-- Form + validation (native :user-invalid + :has) -->
<form>
<div class="field">
<label class="label" for="e">Email</label>
<input class="input" id="e" type="email" required>
<span class="help">We never share it.</span>
<span class="error-text">Enter a valid email.</span>
</div>
<label class="switch"><span>Notify me</span><input type="checkbox"><span class="track"></span></label>
</form>
<!-- Alert / Badge -->
<div class="alert alert-success"><div><div class="alert-title">Saved</div>All good.</div></div>
<span class="badge badge-primary">new</span>
<!-- Tabs (JS) -->
<div class="tabs" role="tablist">
<button role="tab" aria-selected="true" aria-controls="p1">One</button>
<button role="tab" aria-selected="false" aria-controls="p2">Two</button>
</div>
<div class="tab-panel" id="p1" role="tabpanel">…</div>
<div class="tab-panel" id="p2" role="tabpanel" hidden>…</div>
<!-- Accordion (native, no JS) -->
<div class="accordion">
<details open><summary>Q</summary><div class="accordion-body">A</div></details>
</div>
<!-- Dropdown (JS) -->
<div class="dropdown">
<button class="btn btn-soft" data-dropdown-toggle aria-expanded="false">Menu ▾</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Item</a>
<div class="dropdown-divider"></div>
<button class="dropdown-item">Action</button>
</div>
</div>
<!-- Modal (native <dialog> + tiny JS helpers) -->
<button class="btn btn-primary" data-modal-open="m">Open</button>
<dialog class="modal" id="m">
<div class="modal-card">
<div class="modal-header">Title <button class="btn btn-ghost btn-icon btn-sm" data-modal-close>✕</button></div>
<div class="modal-body">Body</div>
<div class="modal-footer"><button class="btn btn-primary" data-modal-close>OK</button></div>
</div>
</dialog>
<!-- Toast (JS API) -->
<button class="btn" onclick="Lombok.toast('Done', {variant:'success'})">Notify</button>
<!-- Sortable table (JS: click headers) -->
<div class="table-wrap">
<table class="table table-hover">
<thead><tr><th aria-sort="none">Name</th><th aria-sort="none">MRR</th></tr></thead>
<tbody><tr><td>Nadia</td><td>49</td></tr></tbody>
</table>
</div>Menu
Other components included: navbar (+ mobile toggle), breadcrumb,
pagination, avatar/avatar-group, progress, spinner, skeleton,
stat, list-group, steps, CSS-only tooltip (data-tip="…"), kbd, code/pre.
Utilities
Layout (flex, grid, items-*, justify-*, gap-*, grid-cols-1..12),
spacing (p-*, m-*, logical), sizing (w-full, max-w-*, min-h-screen),
type (text-sm..3xl, font-*, text-start/center/end), color
(bg-surface, text-muted, border), radius/shadow, position/z-index, plus
responsive prefixes sm: md: lg: xl: (e.g. md:grid-cols-3).
Classless mode
Plain semantic HTML is styled with zero classes — headings, paragraphs, links,
lists, blockquote, table, form inputs, code/pre/kbd. Good for prose
and quick prototypes.
Build process
The dist files are produced with Lightning CSS:
# bundle the modules in order
cat src/variables.css src/core.css src/themes.css \
src/components.css src/utilities.css > dist/lombok.css
# minify (autoprefix + dead-code removal)
npx lightningcss --minify --bundle dist/lombok.css -o dist/lombok.min.cssPostCSS equivalent: postcss-import + autoprefixer + cssnano.
Shrinking further
- Split core vs full: ship
variables + core + themes + componentsand addutilities.cssonly if you use utility classes. - Purge unused utilities with your bundler's CSS purge step keyed on your templates.
- Drop presets you don't use from
themes.css.
Browser support & accessibility
Modern CSS used with graceful fallbacks: :has(), :user-invalid,
accent-color, backdrop-filter (opaque fallback via @supports), logical
properties, native <dialog>/<details>. Components ship with visible
:focus-visible rings, ARIA hooks, and respect prefers-reduced-motion.
Testing
Visual-regression tests (Playwright) screenshot the buttons block across all
five styles plus dark/RTL, and the form validation state, comparing against
committed baselines in tests/**-snapshots/.
npm run test:visual # compare against baselines
npm run test:visual:update # refresh baselines after an intentional changeBaselines are browser-specific. CI runs in a pinned Playwright container
(mcr.microsoft.com/playwright:v1.56.0-noble) so pixels match. When you change
visuals on purpose, refresh baselines inside that image so they match CI:
docker run --rm -v "$PWD":/work -w /work mcr.microsoft.com/playwright:v1.56.0-noble \
bash -c "npm ci && npm run build && npm run build:docs && npm run test:visual:update"A failing run uploads a playwright-report artifact with side-by-side diffs.
Publishing (maintainers)
Full step-by-step setup (repo creation, branch protection, secrets, Pages, release-please flow, npm/CDN, troubleshooting) is in GITHUB_SETUP.md.
The package ships only dist/, src/, README.md and LICENSE (see the
files field). prepublishOnly rebuilds the bundle and enforces the size
budget, so a broken or oversized build can never be published.
Automated (recommended). Two GitHub Actions workflows are included:
.github/workflows/ci.yml— on every push/PR:npm ci,npm run build, size-budget check, and a guard that fails if committeddist/is stale..github/workflows/publish.yml— on a published GitHub Release: builds and runsnpm publish --provenance --access public. Add anNPM_TOKENrepo secret (an npm automation token); provenance uses the workflow's OIDC token.
Release flow:
npm version patch # or minor / major — bumps package.json + git tag
git push --follow-tags
# then create a GitHub Release for that tag -> publish workflow runsManual:
npm run build
npm publish --access publicCDN is automatic. Once on npm, jsDelivr and unpkg serve it with no extra
step: https://cdn.jsdelivr.net/npm/lombokcss@<version>/dist/lombok.min.css.
The jsdelivr / unpkg fields point both CDNs at the minified CSS by default.
Docs hosting (GitHub Pages). .github/workflows/pages.yml rebuilds the
docs and deploys docs/ to Pages on every push to main. Enable it once in
repo Settings → Pages → Source: GitHub Actions. Site goes live at
https://codinglombok.github.io/lombokcss/.
Need help? See SUPPORT.md.
License
MIT.
