Skip to the page

MCSS-Lite 0.4.1

GitHub

Button c-button

StableComponent · since 0.1.0

Triggers an action. Apply to <button>. Apply to <a> only when the control navigates. Parts have a 2px keyline and a brick edge that drops when pressed.

Pick this part

Pick c-button for

  • The control does something on this page: submits a form, saves, deletes, opens a dialog, toggles a setting.
  • A confirming or cancelling action in a form or c-modal footer.
  • An on/off toolbar control such as Bold or Mute, with aria-pressed.

Pick another part for

  • The control goes to another page or URL: use a plain link. Apply c-button to an <a> only when a link must look like a button, such as a start-page call to action.
  • Showing a status that does nothing when clicked: use c-badge.
  • Filtering or removing items in a list of tags: that is a chip, which MCSS-Lite does not ship yet.

Build it

  1. Start with <button> and add class="c-button".
  2. Pick at most one variant modifier: --primary, --secondary, --ghost, --danger.
  3. Pick at most one size modifier: --sm, --lg.
  4. Add data-state="disabled" when the action cannot run right now. Prefer aria-disabled="true": the button stays focusable, so link a visible reason with aria-describedby and ignore activation in your handler. The disabled attribute also works on <button> but removes it from the tab order. On <a>, set aria-disabled="true" and remove href.
  5. Add data-state="loading" when the action is in progress. Set aria-busy="true" and aria-disabled="true", keep the visible label (for example "Saving…"), and ignore activation in your handler.

Add a publish date to schedule the post.

Show markup 26 lines
<div class="l-cluster">
  <button type="button" class="c-button c-button--ghost">Cancel</button>
  <button type="button" class="c-button">Save draft</button>
  <button type="button" class="c-button c-button--primary">Publish post</button>
</div>

<div class="l-cluster">
  <button type="button" class="c-button c-button--secondary c-button--sm">Export CSV</button>
  <button type="button" class="c-button c-button--secondary">Duplicate post</button>
  <button type="button" class="c-button c-button--lg">Create a project</button>
</div>

<div class="l-stack l-stack--sm">
  <div class="l-cluster">
    <button type="button" class="c-button" data-state="disabled" aria-disabled="true" aria-describedby="schedule-reason">Schedule post</button>
    <button type="button" class="c-button" data-state="loading" aria-busy="true" aria-disabled="true">Saving…</button>
  </div>
  <p class="u-margin-0" id="schedule-reason">Add a publish date to schedule the post.</p>
</div>

<div class="l-cluster" role="group" aria-label="View">
  <button type="button" class="c-button c-button--sm" aria-pressed="true">List</button>
  <button type="button" class="c-button c-button--sm" aria-pressed="false">Grid</button>
</div>

<button type="button" class="c-button c-button--danger">Delete project</button>

Check your build

Each piece that doesn't fit was run through mcss-lite validate when this page was built. The output below is real.

Fits

Use one primary button per region, placed last in its group.

Show markup 4 lines
<div class="l-cluster">
  <button type="button" class="c-button c-button--ghost">Cancel</button>
  <button type="submit" class="c-button c-button--primary">Save changes</button>
</div>

validate: 0 issues

Doesn't fit

Don't combine two variants on one button to make it stand out more.

<button type="submit" class="c-button c-button--primary c-button--danger">Save changes</button>

validate

  • error "c-button--primary" and "c-button--danger" are both variant modifiers; use only one. [exclusive-modifiers]

Fits

Keep an unavailable action focusable and say why it can't run, with aria-disabled and a linked reason.

Add a title before you publish.

Show markup 4 lines
<div class="l-stack l-stack--sm">
  <button type="button" class="c-button c-button--primary" data-state="disabled" aria-disabled="true" aria-describedby="publish-why">Publish</button>
  <p id="publish-why" class="u-margin-0">Add a title before you publish.</p>
</div>

validate: 0 issues

Doesn't fit

Don't grey out a button without saying why, or set the state without its native pair.

<button type="button" class="c-button" data-state="disabled">Publish</button>

validate

  • warning data-state="disabled" on <button> also needs aria-disabled="true" (keeps it focusable; link the reason with aria-describedby) or the disabled attribute. [state-pair]

Fits

Invent nothing: a warning-style action is c-button--danger or a plain button.

<button type="button" class="c-button c-button--danger">Delete project</button>

validate: 0 issues

Doesn't fit

Don't guess a modifier that isn't in the contract.

<button type="button" class="c-button c-button--warning">Delete project</button>

validate

  • error Unknown class "c-button--warning". Valid modifiers of c-button: --primary, --secondary, --ghost, --danger, --sm, --lg. Only classes from the MCSS-Lite contracts exist. [unknown-class]

Fits

Label the button with what happens: a verb and the thing it acts on.

<button type="submit" class="c-button c-button--primary">Create account</button>

validate: 0 issues

Doesn't fit

Don't use vague labels: "OK" or "Submit" say nothing when a screen reader lists every button on the page.

<button type="submit" class="c-button c-button--primary">Submit</button>

validate

  • warning "Submit" says nothing out of context. Name the action and its object, e.g. "Save changes" or "Download the invoice". [content-vague]

Fits

Use a real <button> for actions; it brings focus, Enter and Space for free.

<button type="button" class="c-button" hx-post="/projects/42/archive">Archive project</button>

validate: 0 issues

Doesn't fit

Don't make a div clickable: it has no role, no focus and no keyboard, even with the button classes.

<div class="c-button" hx-post="/projects/42/archive">Archive project</div>

validate

  • warning A <div> that reacts to clicks has no role, no keyboard access and no focus. Use <button type="button"> for actions or <a href> for navigation. [clickable-div]

Spec sheet

Apply to <button> <a>

Modifiers
ClassGroupWhat it does
c-button--primaryvariant (pick one)The main action in a view region. Use at most one per region.
c-button--secondaryvariant (pick one)Outlined in the action color; for important but not main actions.
c-button--ghostvariant (pick one)No border or background; for low-emphasis actions such as Cancel.
c-button--dangervariant (pick one)Destructive, hard-to-undo actions such as Delete. Brick red.
c-button--smsize (pick one)Compact button for dense UI such as tables and toolbars.
c-button--lgsize (pick one)Large button for hero or empty-state calls to action.
States
StateMeaningPair it with
data-state="disabled"The action cannot run right now. Rendered ghosted with a dashed keyline.Prefer aria-disabled="true": the button stays focusable, so link a visible reason with aria-describedby and ignore activation in your handler. The disabled attribute also works on <button> but removes it from the tab order. On <a>, set aria-disabled="true" and remove href.
data-state="loading"The action is in progress. Shows moving stripes; text keeps full contrast.Set aria-busy="true" and aria-disabled="true", keep the visible label (for example "Saving…"), and ignore activation in your handler.
Show tokens 21
Tokens
TokenValue (light / dark)
--button-background#ffffff / #14294d
--button-background-hover#eef6fc / #1b335c
--button-border#1a1d20 / #a9bacb
--button-text#1a1d20 / #e6edf4
--button-radius0.25rem
--button-shadow0 2px 0 #1a1d20, 0 3px 8px rgb(26 29 32 / 0.12) / 0 2px 0 rgb(207 218 229 / 0.45), 0 4px 10px rgb(0 0 0 / 0.35)
--button-primary-background#005a9c / #2576cc
--button-primary-background-hover#004a80 / #185da6
--button-primary-text#ffffff
--button-secondary-text#005a9c / #7cbff1
--button-secondary-border#005a9c / #2576cc
--button-ghost-background-hover#e9ecef / #27436f
--button-danger-background#c91a09
--button-danger-background-hover#a01408
--button-danger-text#ffffff
--button-pressed-background#1a1d20 / #e6edf4
--button-pressed-border#1a1d20 / #cfdae5
--button-pressed-text#ffffff / #0f2140
--button-primary-border#1a1d20 / #cfdae5
--button-danger-border#1a1d20 / #cfdae5
--button-primary-background-active#003860 / #144d8a

Accessibility

  • A button needs a visible text label, or aria-label when it only contains an icon.
  • Do not remove the :focus-visible outline.
  • Use type="button" unless the button submits a form.
  • c-button--sm looks compact but keeps a 44px tall hit area.
  • Don't disable a submit button until the form is valid. Let people submit, then show what is missing.
Keyboard
KeyWhat it does
EnterActivates the button.
SpaceActivates the button (not links).
TabMoves focus to the button, including one marked aria-disabled.

Wording

  1. Write a verb plus the thing it acts on: "Save changes", "Delete project". Never "OK", "Submit" or "Click here".
  2. Use sentence case: "Create account", not "Create Account".
  3. Keep the label the same when a toggle changes state; aria-pressed carries the state.
  4. A danger button names what is destroyed: "Delete account", not "Confirm".
  5. No full stop at the end of a label.

All content rules

Fits with

Researched from