Button
When to use it
Section titled “When to use it”From the Lumo.Web.Component.V1.0.Complete library — theUse this component when need CTA.
button description, read 2026-10-19.Where it goes
Section titled “Where it goes”- in a card footer action row, and in an empty state Action slot
When not to use it
Section titled “When not to use it”- Don’t use it for opening external link.
- No alternative named. The source states the prohibition without saying what to reach for instead.
What each variant is for
Section titled “What each variant is for”| Property | Value | What it is for |
|---|---|---|
type |
fill |
not stated in the source |
type |
outline |
not stated in the source |
type |
transparent |
not stated in the source |
state |
default |
not stated in the source |
state |
hovered |
not stated in the source |
state |
focused |
not stated in the source |
state |
disabled |
not stated in the source |
state |
error |
not stated in the source |
Accessibility
Section titled “Accessibility”- state=“disabled” sets the real DOM disabled attribute, and rest props are spread first so a caller cannot reach past the component and re-enable it
src/components/button/button.jsx:73 - an icon-only button takes its accessible name from label via aria-label, because no text is drawn
src/components/button/button.jsx:74 - the icons are aria-hidden decoration
src/components/button/buttonIcon.jsx:17 - htmlType defaults to “button”, so it cannot submit a form by accident
src/components/button/button.jsx:41
Composition
Section titled “Composition”Nothing in the stories shows this component composed with another.
What this version promises
Section titled “What this version promises”See Versioning. At 0.1.0 the public surface is the named exports of src/index.ts; this component is one of them.
This component’s release verdict is Cleared and its Development status reads Released. A Cleared verdict is a recommendation with reasons, recorded in its release review — a human decides whether a release happens, and no agent approves its own work.
Every story that is not a variant-matrix row, embedded live from the deployed Storybook in both themes. The matrix itself is on the Design tab.
Fill Default
Section titled “Fill Default”Fill Hovered
Section titled “Fill Hovered”Fill Focused
Section titled “Fill Focused”Fill Disabled
Section titled “Fill Disabled”Fill Error
Section titled “Fill Error”Outline Default
Section titled “Outline Default”Outline Hovered
Section titled “Outline Hovered”Outline Focused
Section titled “Outline Focused”Outline Disabled
Section titled “Outline Disabled”Outline Error
Section titled “Outline Error”Transparent Default
Section titled “Transparent Default”Transparent Hovered
Section titled “Transparent Hovered”Transparent Focused
Section titled “Transparent Focused”Transparent Disabled
Section titled “Transparent Disabled”Transparent Error
Section titled “Transparent Error”Content Permutations
Section titled “Content Permutations”Icon Names
Section titled “Icon Names”Transparent — on a surface
Section titled “Transparent — on a surface”Interactive
Section titled “Interactive”Disabled — genuinely inert
Section titled “Disabled — genuinely inert”Icon only — still labelled
Section titled “Icon only — still labelled”Import
Section titled “Import”import { Button } from '@ds-house/lumo-design-system';| Prop | Type | Default | Notes |
|---|---|---|---|
type |
`‘fill’ | ‘outline’ | ‘transparent’` |
state |
`‘default’ | ‘hovered’ | ‘focused’ |
label |
string |
'Button' |
Figma label. The button text. |
text |
boolean |
true |
Figma text. Shows the label. |
iconLeft |
boolean |
true |
Figma iconLeft. Shows the leading icon. |
iconRight |
boolean |
true |
Figma iconRight. Shows the trailing icon. |
iconLeftName |
string |
'add' |
Material Symbols glyph for the leading icon. |
iconRightName |
string |
'add' |
Material Symbols glyph for the trailing icon. |
htmlType |
`‘button’ | ‘submit’ | ‘reset’` |
onClick |
() => void |
— | |
className |
string |
'' |
Union types
Section titled “Union types”type:'fill'|'outline'|'transparent'state:'default'|'hovered'|'focused'|'disabled'|'error'htmlType:'button'|'submit'|'reset'
Tokens it needs
Section titled “Tokens it needs”| Token | Light | Dark |
|---|---|---|
--border-radius-input |
not in the build | — |
--border-width-default |
not in the build | — |
--border-width-focused |
not in the build | — |
--button-button1 |
not in the build | — |
--color-bg-base |
not in the build | — |
--color-bg-disabled |
not in the build | — |
--color-bg-negative-idle |
not in the build | — |
--color-bg-primary-hovered |
not in the build | — |
--color-bg-primary-idle |
not in the build | — |
--color-bg-primary-pressed |
not in the build | — |
--color-bg-primary-tonal-idle |
not in the build | — |
--color-border-brand-hovered |
not in the build | — |
--color-border-primary-disabled |
not in the build | — |
--color-border-primary-focused |
not in the build | — |
--color-icon-actionpressed |
not in the build | — |
--color-icon-brand |
not in the build | — |
--color-icon-disabled |
not in the build | — |
--color-icon-negative |
not in the build | — |
--color-icon-onaction |
not in the build | — |
--color-icon-onfocus |
not in the build | — |
--color-icon-onhovered |
not in the build | — |
--color-text-accent-aqualight |
not in the build | — |
--color-text-actionpressed |
not in the build | — |
--color-text-brand |
not in the build | — |
--color-text-disabled |
not in the build | — |
--color-text-negative |
not in the build | — |
--color-text-onaction |
not in the build | — |
--color-text-onfocus |
not in the build | — |
--color-text-onhovered |
not in the build | — |
--motion-duration-fast |
not in the build | — |
--size-icon-md |
not in the build | — |
--spacing-padding-sm |
not in the build | — |
--typography-fontweight-regular |
not in the build | — |
A dash under Dark means the token does not change between themes.
Storybook’s own props table
Section titled “Storybook’s own props table”The Figma node
Section titled “The Figma node”The variant matrix
Section titled “The variant matrix”Each row as the set draws it, in both themes.
All Variants
Section titled “All Variants”What Figma never bound, and the gaps recorded against it
Section titled “What Figma never bound, and the gaps recorded against it”— reported, not filled in
-
transparent/focusedhas no focus indicator. Node29:1800renders no border and no ring at all — the variant is distinguished only by a darker label. (get_variable_defsdoes listborder/width/focusedagainst the node, butget_design_contextreturns no border properties, so there is no edge to resolve a colour for. Don’t go looking for a half-finished binding; there is nothing there to complete.) A focusable control with no visible focus indicator fails WCAG 2.4.7. Thestate="focused"prop reproduces the node exactly so QA can compare, but real keyboard focus gets the ring that fill and outline already use (border-primary-focusedatborder-width-focused) rather than an invented value. A designer should decide what transparent focus looks like. -
Outline stroke alignment is inconsistent. Four outline variants are 120x38 (1px outside stroke);
outline/focusedis 118x36 (2px inside). These cannot both be right. The component uses an inside stroke throughout — see above — but the node needs fixing either way. -
Two accent-aqua variables share one description. The library has both
color/text/accent/aqua(aqua/800,#176e82) andcolor/text/accent/aquaLight(aqua/600,#16bee3), and both carry the identical description “lumo.semantic.color.text.accent.aqua — Accent Colors · aqua foreground”. The Figma MCP compounds this by rendering both as--lumo-semantic-color-text-accent-aqua, with only the resolved value to tell them apart. Button bindsaquaLight; this was caught by measuring the rendered colour against the node, not by reading the name. The descriptions should be made distinct before this trips someone else. -
Icon tokens are doing border duty.
outline/defaulttakes its border fromcolor/icon/brandandoutline/errorfromcolor/icon/negative, whilecolor/border/brand/defaultandcolor/border/negative/defaultboth exist and are unused here. The component follows what the node binds. Worth a designer’s pass — the border roles exist for this. -
transparent/focusedcolours the label with a background token.color/bg/primary/pressedis used as a text colour. It resolves to a readable value, but the role is wrong;color/text/actionPressedis whatoutline/focuseduses for the same job. -
Gap uses a padding token. Every variant sets its flex gap from
spacing/padding/smrather thanspacing/gap/horizontal/sm. Both are 8px so nothing renders wrong, but the roles are not interchangeable.
Where Figma and this repo disagree across the system, see the disagreements record.
Every commit touching this component and its subcomponents — a change to a subcomponent changes the component, and a log that reads only the parent file misses it.
| Commit | Date | Shipped in | Subject |
|---|---|---|---|
2b94809 |
2026-10-19 | unknown | Write the ten component intent files |
c8bc699 |
2026-10-18 | unknown | Add the test suite, and retire the deliberate stop |
9525e0f |
2026-10-17 | unknown | Make the repo a publishable npm package |
d501bea |
2026-10-03 | unknown | Fix QA findings F1, F2, F3, F5 on Button |
74ef51a |
2026-10-03 | unknown | Add Button component, built from Figma node 26:1657 |
7ccfbbb53757.