mak UI web components
@acme/elements is mak UI as framework-agnostic custom elements (built with Lit), for Vue,
Svelte, Angular and plain HTML. They read the same token CSS variables as the React components,
so every theme mode (light, dark, high contrast) works with no extra setup.
Install
pnpm add @acme/elements @acme/tokensLoad the tokens once, then import the package to register every <acme-*> element:
import '@acme/tokens/variables.css'
import '@acme/elements'Without a bundler, use the single-file build (@acme/elements/bundle, 37 KB minified):
<link rel="stylesheet" href="/tokens/variables.css" />
<script type="module" src="/makenzie-ui.js"></script>Vue
Tell the Vue compiler that acme- tags are custom elements, and import @acme/elements before
createApp(...).mount() so Vue sets properties rather than attributes.
// vite.config.ts
vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('acme-') } } })<acme-switch :checked="digest" @change="digest = $event.target.checked">Weekly digest</acme-switch>Svelte
No configuration needed. Props and onchange / onclick handlers work as on native elements.
Svelte's a11y lint can't see the native button inside <acme-button>, so a click handler on it
needs <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->.
<acme-switch checked={digest} onchange={(e) => (digest = e.currentTarget.checked)}>Weekly digest</acme-switch>Elements
| Element | Attributes | Notes |
|---|---|---|
acme-button | variant (primary, secondary, outline, ghost, destructive), size (sm, md, lg), type, disabled, loading, label | type="submit" submits its form, including through the form attribute. label names an icon-only button. |
acme-input | label, description, error, type, name, value, placeholder, autocomplete, required, disabled | Label, description and error share a shadow root with the input, so they are associated. |
acme-checkbox | name, value, checked, indeterminate, disabled | Text content is the label. Fires change. |
acme-switch | name, value, checked, disabled | role="switch". Applies immediately; use a checkbox inside forms. |
acme-dialog | heading, open | Native modal <dialog>: focus trap, Esc, focus return. show() / close(). Buttons in the footer slot with data-close close it. Fires close. |
acme-alert | variant (info, success, warning, destructive) | title slot. Warnings and errors are role="alert"; info and success are role="status". |
acme-card | none | title, description and footer slots. Put a real heading in title. |
acme-badge | variant (default, secondary, outline, destructive, success, warning, info) | The text must carry the meaning on its own. |
Forms
Inputs, checkboxes, switches and buttons are form-associated. They submit with FormData,
take part in native validation (an invalid field blocks submit and shows its message), and reset
with the form. No framework bindings are needed:
form.addEventListener('submit', (e) => {
e.preventDefault()
const data = new FormData(form) // includes every <acme-*> field with a name
})Styling
Elements are styled only with token variables, so theme with the tokens, not by overriding
element CSS. Box styles live inside the shadow root, so page resets such as Tailwind's preflight
don't strip them. For a one-off adjustment, each element exposes a part: ::part(button),
::part(input), ::part(label) (checkbox, switch), ::part(dialog), ::part(card), ::part(badge),
::part(alert).
Limits
- Client-rendered only. With SSR frameworks (Nuxt, SvelteKit), render the elements on the client.
- Fewer components than React: these eight cover forms, feedback and layout. Use the React components where you need the full set.
Reference apps
apps/ref-vue (Vue 3) and apps/ref-svelte (Svelte 5) build the same account-settings page from
these elements. Both run one shared Playwright suite (tests/reference-apps): axe in all four
theme modes, 320px reflow, form validation and submit, and the modal dialog.
pnpm --filter ref-vue build && pnpm --filter ref-vue test:a11yTests
pnpm --filter @acme/elements test:a11y runs the elements in Chromium in all four theme modes:
axe (WCAG 2.2 AA), keyboard, form submission and validation, the dialog, and styling under a page
CSS reset.