margaui
v0.5704.0
Published
Tailwind 4 port of daisyui that can run completely in the browser
Maintainers
Readme
margaui
CSS component library with an in-browser Tailwind v4 compiler. 68 components, 35 themes, zero build step needed for browser use.
Quick start (browser)
Import margaui.js as an ES module, call compile() with class names, and inject the returned CSS. Works directly from a static file server — no Node or bundler required.
<style id="margaui"></style>
<script type="module">
import { compile } from "./margaui.js";
const css = await compile(["btn", "btn-primary", "badge"]);
document.getElementById("margaui").textContent = css;
</script>Quick start (CLI / Tailwind v4)
For traditional Tailwind workflows, create an entry.css input file and remove the components you don't need:
/* entry.css */
@import "tailwindcss" source(none);
@source "./your-templates/**/*.html";
/* base */
@import "./base/properties.css";
@import "./base/rootcolor.css";
@import "./base/rootscrollgutter.css";
@import "./base/rootscrolllock.css";
@import "./base/scrollbar.css";
/* themes (light + dark bundled, add more individually) */
@import "./themes/theme.css";
/* components — remove the ones you don't need */
@import "./src/accordion.css";
@import "./src/alert.css";
@import "./src/avatar.css";
@import "./src/badge.css";
@import "./src/breadcrumbs.css";
@import "./src/button.css";
@import "./src/calendar.css";
@import "./src/card.css";
@import "./src/carousel.css";
@import "./src/chat.css";
@import "./src/checkbox.css";
@import "./src/collapse.css";
@import "./src/colors.css";
@import "./src/countdown.css";
@import "./src/diff.css";
@import "./src/divider.css";
@import "./src/dock.css";
@import "./src/drawer.css";
@import "./src/dropdown.css";
@import "./src/fab.css";
@import "./src/fieldset.css";
@import "./src/file-input.css";
@import "./src/filter.css";
@import "./src/footer.css";
@import "./src/hero.css";
@import "./src/hover-3d.css";
@import "./src/hover-gallery.css";
@import "./src/indicator.css";
@import "./src/input.css";
@import "./src/join.css";
@import "./src/kbd.css";
@import "./src/label.css";
@import "./src/link.css";
@import "./src/list.css";
@import "./src/loading.css";
@import "./src/mask.css";
@import "./src/menu.css";
@import "./src/mockup-browser.css";
@import "./src/mockup-code.css";
@import "./src/mockup-phone.css";
@import "./src/mockup-window.css";
@import "./src/modal.css";
@import "./src/navbar.css";
@import "./src/progress.css";
@import "./src/radial-progress.css";
@import "./src/radio.css";
@import "./src/range.css";
@import "./src/rating.css";
@import "./src/select.css";
@import "./src/skeleton.css";
@import "./src/stack.css";
@import "./src/stat.css";
@import "./src/status.css";
@import "./src/steps.css";
@import "./src/swap.css";
@import "./src/tab.css";
@import "./src/table.css";
@import "./src/text-rotate.css";
@import "./src/textarea.css";
@import "./src/theme-controller.css";
@import "./src/timeline.css";
@import "./src/toast.css";
@import "./src/toggle.css";
@import "./src/tooltip.css";
@import "./src/validator.css";Then compile with the Tailwind CLI:
npx @tailwindcss/cli -i entry.css -o style.cssAdd --watch to recompile automatically on changes:
npx @tailwindcss/cli -i entry.css -o style.css --watchHow it works
- Components are defined with Tailwind v4
@utilitydirectives insrc/*.css - Themes are CSS custom property sets under
[data-theme="name"]selectors vfs.jsbundles all CSS into a virtual filesystem as an ES moduletwcompiler.jswraps Tailwind v4's compiler, resolving imports from the VFSmargaui.jsassembles the input CSS from VFS keys and exposescompile()/getCompiler()compile(classes)returns only the CSS needed for the given class names (tree-shaken)
API
Exports from margaui.js:
compile(classes: string[]): Promise<string>
One-shot compile. Returns the CSS for the given utility class names.
const css = await compile(["btn", "btn-primary", "card", "card-body"]);getCompiler(): Promise<Compiler>
Returns the singleton compiler instance. Use compiler.build(classes) for repeated compilations without re-initializing.
const compiler = await getCompiler();
const css1 = await compiler.build(["btn"]);
const css2 = await compiler.build(["badge", "badge-primary"]);buildInput(entries): string
Advanced. Builds the raw CSS input string from VFS entries. Useful if you need to customize which files are included before creating a compiler.
Themes
35 themes available. Set the data-theme attribute on any element to switch themes:
<html data-theme="dracula"></html>The light theme is the default — it sets :root so pages work without an explicit data-theme attribute. (It is intentionally not pinned to :host; see Shadow DOM below.)
Available themes:
| | | | | | | ------ | --------- | --------- | -------- | ------------ | | light | dark | abyss | acid | aqua | | autumn | black | bumblebee | business | caramellatte | | cmyk | coffee | corporate | cupcake | cyberpunk | | dim | dracula | emerald | fantasy | forest | | garden | halloween | lemonade | lofi | luxury | | night | nord | pastel | retro | silk | | sunset | synthwave | valentine | winter | wireframe |
themes/theme.css bundles just light + dark — import it for the common pair. For additional themes, import them individually:
@import "./themes/theme.css"; /* light + dark */
@import "./themes/dracula.css"; /* add dracula */Loading themes
Theme files are not included in the default browser bundle (margaui.js / vfs.js). You need to load them separately.
Link tag — the simplest option. Just point a <link> at the theme file:
<link rel="stylesheet" href="./themes/dracula.css" />
<body data-theme="dracula"></body>Fetch at runtime — useful when switching themes dynamically:
<script type="module">
const css = await fetch("./themes/dracula.css").then((r) => r.text());
const style = document.createElement("style");
style.textContent = css;
document.head.appendChild(style);
document.body.setAttribute("data-theme", "dracula");
</script>Import in your CSS — if you use the Tailwind CLI or build your own input.css, import theme files directly:
@import "./themes/theme.css"; /* light + dark */
@import "./themes/dracula.css"; /* add more individually */Then compile as usual with npx @tailwindcss/cli -i input.css -o style.css.
Shadow DOM — set data-theme on each shadow host to theme its subtree, and reflect the page theme onto the host when it changes:
host.setAttribute("data-theme", document.documentElement.dataset.theme);Don't rely on a theme set on <html> inheriting into a shadow root: Tailwind emits margaui's default palette to :root, :host, so the :host half pins the default (light) values onto every shadow host and shadows the ambient theme that would otherwise inherit across the boundary. A data-theme attribute on the host wins over that default. The theme's CSS must be reachable from inside the shadow root — the usual pattern is a shared CSSStyleSheet in the host's adoptedStyleSheets, updated with replaceSync() on theme change (see the playground source for a working example).
Components
68 components in src/:
| | | | | | | ----------- | ------------ | ------------- | ---------------- | -------------- | | accordion | alert | aura | avatar | badge | | breadcrumbs | button | calendar | card | carousel | | chat | checkbox | collapse | colors | countdown | | diff | divider | dock | drawer | dropdown | | fab | fieldset | file-input | filter | footer | | hero | hover-3d | hover-gallery | indicator | input | | join | kbd | label | link | list | | loading | mask | megamenu | menu | mockup-browser | | mockup-code | mockup-phone | mockup-window | modal | navbar | | otp | pagination | progress | radial-progress | radio | | range | rating | select | skeleton | stack | | stat | status | steps | swap | tab | | table | text-rotate | textarea | theme-controller | timeline | | toast | toggle | tooltip | validator | |
Each component is defined using Tailwind v4's @utility directive, making them available as utility classes during compilation.
Playground
Interactive preview of all components. Shows each component with Preview, HTML, and CSS tabs, a theme switcher, and compile-time stats.
Editor
Live HTML editor with real-time preview. Write HTML in a textarea and see the compiled result instantly (100ms debounce). Includes a CSS tab showing generated output, compile-time and size stats, and a component loader to start from any existing example.
To run the playground and editor:
npm run playground # generates VFS + component tree
npx serve . # or any static file server
# open http://localhost:8000/playground/
# open http://localhost:8000/editor/Project structure
src/ Component CSS files (@utility directives)
themes/ Theme CSS files (custom property sets)
base/ Base styles (properties, scrollbar, root color)
tw/ Tailwind v4 CSS entry points
playground/ Interactive component preview app
tools/ Build scripts (VFS generator, component tree, sync checker)Development
Requires Node 22+ and npm — the entire toolchain runs through npm scripts (no bun or Python). See package.json's scripts for the full list.
| Command | Description |
| ---------------------------- | ------------------------------------------------ |
| npm run playground | Generate VFS + component tree for the playground |
| npm run gen-vfs-dev | Regenerate vfs.js from source CSS files |
| npm run gen-component-tree | Regenerate playground/components.json |
| npm run dist | Build minified distribution in dist/ |
| npm run check-sync | Verify source files and imports are in sync |
| npm run fetch-tw | Re-fetch and bundle tailwindcss.js |
License
MIT -- see LICENSE for details.
Built on the shoulders of daisyUI and Tailwind CSS. If you find this project useful, please consider supporting Tailwind CSS.
