Buttons

Interactive controls using semantic HTML. No class variants.

ASW styles buttons through HTML semantics, not CSS class names. The type attribute communicates intent to the browser and to assistive technology; ASW uses it to apply consistent appearance. All button variants receive identical visual treatment — the semantic distinction is in the markup, not the colour.

No class-based variants

ASW deliberately omits .primary, .secondary, .danger, and .outline button classes. If your design requires visual hierarchy between buttons, express it through layout (position, grouping) rather than colour. The framework maintains this constraint to keep agent-generated HTML readable and auditable.


Default button

A plain <button> for generic interaction. Use when the action is not a form submission.

<button>Click me</button>
<button type="button">Explicit type</button>

Live demo

A bare <button> inside a <form> defaults to type="submit". Outside a form, it defaults to type="button". Set the type explicitly when it matters for correctness.


Submit and reset

[type=submit] submits the nearest ancestor <form>. [type=reset] clears all form fields to their initial values. Both receive the same visual treatment as a default button.

<form>
  <input type="email" name="email" placeholder="you@example.com">
  <button type="submit">Subscribe</button>
  <button type="reset">Clear</button>
</form>

Live demo



Disabled state

The disabled attribute prevents interaction and reduces opacity to 50%. Disabled buttons are removed from tab order by the browser.

<button disabled>Not available</button>
<button type="submit" disabled>Submit</button>

Live demo

Do not use aria-disabled="true" as a substitute for disabled. The HTML attribute is the correct mechanism for form controls — it prevents both visual interaction and form submission. Use aria-disabled only when you need to keep the element focusable for tooltip or context purposes.


Loading state

Apply aria-busy="true" to show an inline spinner. The button becomes non-interactive (pointer-events: none) and the cursor changes to progress. The spinner is rendered as a CSS ::before pseudo-element — no additional HTML needed.

<button aria-busy="true">Saving…</button>

Live demo

Set aria-busy="false" (or remove the attribute) when the operation completes. Pair with aria-live on a status region to announce completion to screen readers.

aria-busy also works on block elements. See the Vocabulary Reference for the block-level loading overlay pattern.


Button groups

Place buttons inline with a gap — natural text flow handles spacing. For semantic groupings, wrap related buttons in a <div> or use <footer> inside a dialog or card.

<p>
  <button type="submit">Save changes</button>
  <button type="reset">Discard</button>
</p>

Live demo


Design tokens

Token Default (dark) Controls
--asw-input-padding-y 0.75rem Vertical padding inside button
--asw-input-padding-x 1rem Horizontal padding inside button
--asw-border-width 1px Border thickness
--asw-radius-md 0.375rem Corner radius
--asw-bg #0a0a0a Button background
--asw-bg-hover #1a1a1a Background on hover/active
--asw-accent-focus rgba(96,165,250,0.35) Focus ring colour
--asw-outline-width 2px Focus ring width

Accessibility

  • Always use a <button> for actions that don't navigate. Don't use <div onclick> or <span onclick>.
  • Button text must describe the action: "Save changes" not "OK". If the button has no visible label, use aria-label.
  • Focus rings are visible on :focus-visible — keyboard users see them, mouse users don't. Do not remove the focus ring styling.
  • Disabled buttons are excluded from tab order by default. If you need a disabled button to be focusable (e.g., to show a tooltip explaining why it's disabled), use aria-disabled="true" without the disabled attribute — but handle click prevention in JavaScript.