@akonwi/mica
v0.15.0
Published
Custom elements. Native behavior. Nearly no JavaScript.
Maintainers
Readme
mica
Custom elements. Native behavior. Nearly no JavaScript.
Mica is a progressive component library of custom elements — progressive as in progressive enhancement: every component starts from markup that works, and each layer (CSS, the browser's native behavior, an optional JS module) enhances it. No runtime, no build step, no framework. View source — there's nothing there but HTML and a stylesheet.
<link rel="stylesheet" href="mica.css">
<m-vstack gap="lg">
<h1>That's it</h1>
<m-hstack gap="sm">
<button data-variant="primary">Save</button>
<button data-variant="ghost">Cancel</button>
</m-hstack>
</m-vstack><m-vstack> is never registered — an unknown custom element is a valid,
stylable element. The behavior everywhere else (dialogs, accordions, menus,
form validation) comes from the browser, not from a runtime.
Getting it
Copy the file — mica is one dependency-free CSS file. Vendoring is a first-class distribution channel, not a workaround:
curl -O https://raw.githubusercontent.com/akonwi/mica/v0.2.1/mica.cssnpm — for toolchains:
npm install @akonwi/micaimport "@akonwi/mica/mica.css";CDN — for trying it out:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@akonwi/[email protected]/mica.css">Progressive by design
Every component states where its behavior comes from (see PROGRESSIVE.md):
- CSS-only — CSS is the behavior. Layout primitives:
m-vstack,m-hstack,m-zstack,m-center,m-box,m-grid,m-sidecar,m-switcher,m-reel. Zero JS, work with JS disabled. - Native behavior — the browser is the behavior. Styled native elements:
buttons, forms,
<dialog>,<details>accordions, popover menus, tooltips, toasts. Delete the stylesheet and everything still works. - JS-enhanced — script, honestly. Where accessibility genuinely requires
JS:
tabs.js,combobox.js,field.js(declarative validation),select.js,toast.js,header.js,sidebar.js(responsive navigation). Each is a tiny standalone module that enhances working markup — never renders it. There is no shared runtime.
m-avatar accepts authored initials or a direct native img, both CSS-only.
avatar.js is a separate opt-in visual enhancement: importing it locally
derives deterministic color and features from the blobatar seed without a
network request.
Your CSS always wins
All of mica ships inside @layer mica.*. Unlayered user CSS beats layered
CSS by spec — overriding mica never requires specificity games. Theming is
swapping token values (--hue, semantic color roles, spacing scale); dark
mode is light-dark() — automatic, no classes.
Browser support
Mica targets Baseline widely-available. Newer features (anchor positioning, customizable select) are used behind graceful fallbacks — each component's docs page notes what degrades and how. Nothing breaks; some things get plainer.
Docs
akonwi.io/mica — every component, built with mica itself (view source). The demo is the whole library on one page.
Read VISION.md for the philosophy and PROGRESSIVE.md for how components are allowed to work.
License
Nested header navigation uses nav[data-navigation-menu] with native
details/summary groups and authored [data-nav-panel] content. Import
header.js for floating desktop panels and inline mobile disclosures. See
the navigation menu docs.
Bottom navigation uses nav[data-bottom-nav] and native links, with optional
consumer-authored icons. No JavaScript module is needed. See the
bottom navigation docs
for a shell that keeps content clear of the bar.
Breadcrumbs use nav[data-breadcrumbs] with an ordered list of links and a
current-page label. Optional breadcrumbs.js enhances an authored ancestor
disclosure. See the breadcrumbs docs.
Data collections compose native table[data-table], a stable filter/selection
toolbar, and nav[data-pagination]. The app owns all data operations; optional
table.js only adds overflow indicators to m-table-scroll. See the
data table and
pagination docs.
Inline alerts and guidance use CSS-only m-callout with neutral, success,
warning, and danger treatments. Icons and actions are optional authored
content; see callout docs.
Cards use CSS-only m-card with optional m-card-header, m-card-body,
and m-card-footer parts. A flush body composes with native ul/ol[data-list]
without double padding. Lists use inset dividers by default;
data-dividers="none" opts out. See cards
and lists.
Running tests
Install development dependencies with bun install and browser engines with
bun run snapshot:setup, then run:
bun run test # all behavior, docs, and snapshot checks
bun run test -- sidebar # one component
bun run test -- menu # menu and menu-touch
bun run test -- field drawer # multiple suites
bun run test -- --list # available suitesThe runner discovers tools/*-check.ts and tools/*.test.ts, builds the docs
for browser checks, and starts a temporary server on port 8471 when needed.
An existing server serving this checkout is reused and left running. Failures
are collected and return a nonzero exit status. Snapshot baselines are never
updated by tests; use bun run snapshot explicitly to accept intentional changes.
