A minimal, themeable CSS component library. Black and white by default.
modest-ui built on the idea that the web can now do more than we give it credit for. Heavy UI libraries solved real problems, but we forget how much browsers had quietly caught up. HTML and CSS natively gives us a lot (accessibility, device-native interactions, better performance). Build with the grain of the web and a lot comes for free.
Use it when you don’t need the overhead. Reach for something else when you do.
modest-ui styles native HTML elements automatically. Add one class to <body>, and buttons, inputs, tables, and details elements are styled instantly (no classes needed). When you need variants, add a modifier class. When you want a theme, override a few CSS custom properties.
- Developers who want styled HTML without learning a new class vocabulary
- Teams building internal tools, admin panels, or prototypes who need "good enough" defaults fast
- Anyone who wants a CSS baseline that doesn't fight their own styles
- Developers who value semantic HTML and accessibility — modest-ui uses native elements like
<dialog>,<details>, and<progress> - Projects that don't want or need JavaScript for UI components
- If you need a full design system with comprehensive layout utilities, you'll want Tailwind or similar
- If you need complex interactive components with built-in JavaScript (carousels, rich data tables, etc.), consider a framework-specific library
- If you need pixel-perfect design specification adherence — modest-ui's minimalism may feel too sparse
- If you need IE11 support — modest-ui uses modern CSS features (nesting,
:where(),color-mix())
Add the stylesheet and put .mdst-ui on the body. That's it.
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/thisismodest/modest-ui@v0.2.2/dist/modest-ui.min.css" />
</head>
<body class="mdst-ui">
<h1>Hello world</h1>
<p>This paragraph is styled automatically.</p>
<button>Click me</button>
<input type="text" placeholder="Type here" />
</body>
</html>Every native element — buttons, inputs, tables, details, typography — is styled out of the box. No classes needed.
The classless defaults cover the base appearance. When you need a variant, add a modifier class. It overrides the default automatically.
<!-- Classless — just works -->
<button>Save</button>
<table>
...
</table>
<details>...</details>
<!-- With variants — opt-in -->
<button class="mdst-button--ghost">Cancel</button>
<button class="mdst-button--inverted">Delete</button>
<table class="mdst-table--striped">
...
</table>
<details class="mdst-details--compact">...</details>You can also use the full class-based API directly — .mdst-button, .mdst-input, .mdst-table — all the explicit classes still work exactly as before. Classes always win over the classless defaults, so there's never a specificity conflict.
Classless layer — Bare elements inside .mdst-ui are styled using :where(), which keeps specificity at zero. Every native <button>, <input>, <table>, etc. gets a styled baseline for free.
Class layer — Component classes like .mdst-button and .mdst-input have normal specificity, so they always override the classless defaults when applied.
Variant classes — Modifiers like .mdst-button--ghost work directly on bare elements inside .mdst-ui. No need to pair them with the base class.
Unlike classless frameworks that style every element on the page globally, modest-ui is scoped to .mdst-ui. Third-party widgets, embedded content, and anything outside the scope are unaffected.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/thisismodest/modest-ui@v0.2.2/dist/modest-ui.min.css" />npm install github:thisismodest/modest-uiThen import in CSS:
@import "modest-ui/dist/modest-ui.css";/* Always include tokens first */
@import "modest-ui/base/tokens.css";
/* Then pick what you need */
@import "modest-ui/components/button/button.css";
@import "modest-ui/components/input/input.css";View the component documentation and examples at modest-ui.com
Override CSS custom properties to create your own theme:
:root {
--mdst-color-fg: #000;
--mdst-color-bg: #fff;
--mdst-color-border: #000;
--mdst-color-muted: #666;
--mdst-color-subtle: #f5f5f5;
--mdst-color-focus: #000;
--mdst-color-error: #dc2626;
--mdst-color-success: #16a34a;
--mdst-radius: 0;
--mdst-border-width: 0.0625em;
}Use the Theme Playground to experiment with different themes.
Typography flows through three semantic tokens: --mdst-font-text (the
reading face — prose, tables, lists inherit it from the .mdst-ui
container), --mdst-font-ui (the interface face — buttons, inputs, badges),
and --mdst-font-mono (code). Both text and ui default to the
--mdst-font-sans stack.
Retheme prose without touching components — serif blog, sans buttons, one token:
:root {
--mdst-font-text: Charter, Georgia, serif;
}Some components need state changes — tabs that switch, steps that advance, toasts that appear. Instead of shipping JavaScript, modest-ui styles every possible state via data attributes. You bring whatever JS you want — a framework, vanilla event listeners, or Alpine.js — and toggle data-state, data-variant, etc. We handle the CSS, you handle the behavior.
<!-- Toggle a tab -->
<button class="mdst-tab" data-state="active">Tab 1</button>
<div class="mdst-tabs-panel" data-state="active">...</div>
<!-- Show a toast -->
<output class="mdst-toast" data-state="visible" data-variant="success">Saved.</output>
<!-- Advance a stepper -->
<div class="mdst-stepper-step" data-state="completed">...</div>This keeps modest-ui dependency-free while letting you use whatever interaction model fits your project.
Start a local server to preview components:
npm run devThen open http://localhost:8000
- Create a folder in
components/with your component CSS and apreview.html:
components/
└── my-component/
├── my-component.css
└── preview.html
- Add the import to
index.css:
@import "./components/my-component/my-component.css";- Generate and build:
npm run generate # Updates sidebar in index.html
npm run build # Bundles CSS to dist/See CONVENTIONS.md for component and preview formatting guidelines.
The version in package.json is the single source of truth. Running npm run build propagates it everywhere — the site, this README, CDN links, and llms.txt.
- Bump the version in
package.json:
npm version patch # or minor / major- Rebuild everything:
npm run build- Commit and push:
git add -A
git commit -m "release vX.Y.Z"
git push origin main- Tag the release:
git tag vX.Y.Z
git push origin vX.Y.ZThe tag is what jsDelivr resolves against, so this step is required for the CDN to serve the new version.
Contributions are welcome — see CONTRIBUTING.md for guidelines on getting started, adding components, and submitting pull requests.
MIT