Getting started
BMX components are standards-based custom elements. The browser renders them, so they behave the same in plain HTML, React, Angular, Vue, Svelte, Blazor, ASP.NET MVC, or a twelve-year-old server-rendered application with no build step at all.
The one-file route
Copy bmx-components.min.js next to your pages and add one tag:
<script src="/assets/bmx-components.min.js"></script>
<bmx-button tone="primary">Save</bmx-button>
That is the whole installation. The runtime registers every <bmx-*> element
and injects its own design tokens, so there is no second stylesheet to
remember and nothing to configure.
Elements upgrade whenever they appear, so markup rendered later — by a templating engine, an htmx swap, a jQuery plugin — works without being told.
The bundler route
If your application already has a build step:
import { defineCustomElements } from 'bmx-webcomponents/loader';
import 'bmx-webcomponents/dist/bmx-tokens.css';
defineCustomElements();
Components are lazy-loaded on this route: an application that uses two of them downloads two, not twenty.
Attributes and properties
Simple values go through attributes, exactly as they look:
<bmx-button variant="outline" tone="danger" size="lg">Delete</bmx-button>
Anything that is not a string is a property, because an HTML attribute can
only ever be a string — an array pushed through one arrives as
"[object Object]", which is the single most common integration problem with
any Web Component:
document.querySelector('bmx-split-button').items = [
{ id: 'save-as', label: 'Save as…' },
{ separator: true },
{ id: 'delete', label: 'Delete', danger: true },
];
The generated framework wrappers do this for you.
Events
Every component's events are CustomEvents carrying a detail:
button.addEventListener('bmxActivate', event => {
console.log(event.detail.via); // 'pointer' | 'keyboard' | 'hold' | 'programmatic'
});
bmx-button also lets the ordinary click event through, so existing code
keeps working. It is suppressed only when one of the activation guards
(confirm, hold, cooldown) has decided the click does not count — which is
what makes confirm work with no code at all.
Frameworks
React
import { BmxButton } from 'bmx-webcomponents-react';
<BmxButton tone="danger" confirm="Click again to delete" onBmxActivate={remove}>
Delete
</BmxButton>
Angular
<bmx-button tone="danger" [confirm]="'Click again'" (bmxActivate)="remove()">
Delete
</bmx-button>
Vue
<BmxButton tone="danger" confirm="Click again" @bmxActivate="remove">Delete</BmxButton>
The wrappers contain no component code. They render the tag and bridge properties and events, which is why one wrapper serves both the demo and the commercial runtime.
Server-side rendering
The components guard every document and window access, so importing them
in a Node render pass is safe. They render on the client; nothing throws on the
server.