Playground

Every property, live. The generated markup below is exactly what you would paste into your own application.

Continue

            
Events appear here.

Variants

Six treatments. variant decides the shape of the treatment; tone decides its meaning.

Solid Elevated Soft Outline Ghost Link

Tones

Chosen by meaning, not colour. A delete button is danger whatever the active theme decides danger looks like.

Primary Neutral Success Warning Danger Info Accent
Primary Neutral Success Warning Danger Info Accent
Primary Neutral Success Warning Danger Info Accent
Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-button tone="primary">Primary</bmx-button>
<bmx-button tone="neutral">Neutral</bmx-button>
<bmx-button tone="success">Success</bmx-button>
<bmx-button tone="warning">Warning</bmx-button>
<bmx-button tone="danger">Danger</bmx-button>
<bmx-button tone="info">Info</bmx-button>
<bmx-button tone="accent">Accent</bmx-button>
<bmx-button variant="soft" tone="primary">Primary</bmx-button>
<!-- …and 13 more like these -->

Sizes & shapes

Five steps, in rem, so the whole library scales with the reader's browser font-size setting.

Extra small Small Medium Large Extra large
Rounded Pill Sharp
Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-button size="xs">Extra small</bmx-button>
<bmx-button size="sm">Small</bmx-button>
<bmx-button size="md">Medium</bmx-button>
<bmx-button size="lg">Large</bmx-button>
<bmx-button size="xl">Extra large</bmx-button>
<bmx-button shape="rounded">Rounded</bmx-button>
<bmx-button shape="pill">Pill</bmx-button>
<bmx-button shape="sharp">Sharp</bmx-button>
<!-- …and 1 more like these -->

Icons

Slotted, not bundled — so BMX ships no icon licence and you keep the icon set your design system already uses.

Download Next Notifications

An icon-only button without a label logs a console warning in development. A button nobody can name is a button a screen-reader user cannot use — and it looks perfectly fine to everyone else, which is why it survives to production.

Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-button>
  <svg slot="start" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3" /></svg>
  Download
</bmx-button>
<bmx-button variant="outline">
  Next
  <svg slot="end" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7" /></svg>
</bmx-button>
<bmx-button variant="soft" tone="neutral" icon-only="true" label="Settings">
  <svg slot="start" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9v.09a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" /></svg>
</bmx-button>
<bmx-button badge="12" variant="outline" tone="neutral">
  <svg slot="start" viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0" /></svg>
  Notifications
</bmx-button>

Click effects

Seven of them, radiating from wherever you pressed — and from the centre when you activate with the keyboard. Click the buttons below. Every one is transform and opacity only, so a screen full of them still runs at 60fps, and all seven are suppressed entirely under prefers-reduced-motion.

The spiral is a real Archimedean spiral, sampled once at module load and drawn with stroke-dashoffset so the line is drawn outward from the centre rather than fading in whole. Its geometry is unit tested — the shape is code, not a hand-tuned SVG someone will be frightened to touch.

Guarded actions

The part every application eventually hand-rolls, badly. One attribute each.

Confirm — click once to arm, again to fire, Escape or blur to cancel
Delete account Six-second window
Press and hold — works from the keyboard too: hold Enter or Space
Hold to wipe data Hold (0.8s)
Cooldown & async — re-entry is refused while the handler runs
Save changes Send code (3s cooldown)

States

Busy, toggled, disabled — and the WCAG 2.2 treatment of a disabled control that stays discoverable.

Saving Upload Toggle me Pressed Disabled Disabled, still reachable

The last button keeps its Tab stop and is marked aria-disabled instead of disabled. A natively disabled control vanishes from the Tab order, so a keyboard user never learns it exists — let alone why it is unavailable. This paragraph is its aria-describedby.

Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-button loading="true">Saving</bmx-button>
<bmx-button loading="true" loading-label="replace" loading-text="Uploading…" variant="soft">Upload</bmx-button>
<bmx-button toggle="true">Toggle me</bmx-button>
<bmx-button toggle="true" pressed="true" variant="soft">Pressed</bmx-button>
<bmx-button disabled="true">Disabled</bmx-button>
<bmx-button disabled="true" focusable-when-disabled="true" described-by="why-disabled" variant="outline">
  Disabled, still reachable
</bmx-button>

Forms

Real form participation from inside a shadow root, via ElementInternals. Constraint validation runs; the submit event is cancellable.

Subscribe Reset

Try submitting it empty — the browser's own validation message appears, because the button calls requestSubmit() rather than submit(). Most component libraries get this wrong and skip validation entirely.

Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-button type="submit" name="intent" value="subscribe">Subscribe</bmx-button>
<bmx-button type="reset" variant="ghost" tone="neutral">Reset</bmx-button>

Links & responsive

A button with href renders a real anchor, with link keyboard semantics: Enter activates, Space does not.

Binarymission Jump to variants
Narrow the window below 768px — these keep their icons and drop their labels
Export Print Share
Full width
Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-button href="https://binarymission.co.uk" target="_blank">
  Binarymission
  <svg slot="end" viewBox="0 0 24 24"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14 21 3" /></svg>
</bmx-button>
<bmx-button href="#buttons" variant="link">Jump to variants</bmx-button>
<bmx-button hide-label-below="md" variant="outline" tone="neutral">
  <svg slot="start" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3" /></svg>
  Export
</bmx-button>
<bmx-button hide-label-below="md" variant="outline" tone="neutral">
  <svg slot="start" viewBox="0 0 24 24"><path d="M6 9V2h12v7M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2M6 14h12v8H6z" /></svg>
  Print
</bmx-button>
<bmx-button hide-label-below="md" variant="outline" tone="neutral">
  <svg slot="start" viewBox="0 0 24 24"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8M16 6l-4-4-4 4M12 2v13" /></svg>
  Share
</bmx-button>
<bmx-button full-width="true" variant="soft">Full width</bmx-button>

Buttons that act together

Groups carries the two components built out of this one: a button group that turns several buttons into one widget with a single Tab stop, and a split button that joins a default action to a menu of alternatives. Everything on this page — the tones, the sizes, the guards — works inside both of them unchanged, which is the point of them being the same element.