Skip to the content

Kongens Nytorv

Metro M1, M2 and M3.

Variants and states

RestHoverPressedFocusDisabled
Solid · neutral
Solid · brand
Outline · neutral
Outline · brand
  • Rest
  • Hover
  • Pressed
  • Focus
  • Disabled

Sizes

Mediumsize.button · 36 px
Smallsize.button-sm · 30 px

With icons

An icon before the label, in reading order, so it moves to the right in right-to-left text. Hidden from screen readers: the label names the button.

Props

NameTypeDefaultDescription
variant'solid' | 'outline''solid'How much the button stands out: solid is filled, for the action a panel is for; outline is an edge only, for the actions beside it.
color'neutral' | 'brand''neutral'The intent: neutral is ink, for every action but the main one; brand is the brand yellow, for the one main action of a screen.
size'small' | 'medium''medium'The button's height: medium is size.button, small is size.button-sm, for dense rows and panels.
disabledbooleanfalseWhether the button ignores the pointer and the keys. A disabled button leaves the tab order unless focusableWhenDisabled is set.
fullWidthbooleanfalseWhether the button fills the width of its container, such as a bottom sheet's main action. Sets data-full-width.
selectedboolean—Whether the button, a toggle such as "Follow", is on: true or false sets aria-pressed and draws the button on or off, and true sets data-selected. Left out, the button is no toggle and has no aria-pressed. The caller keeps the state and flips it in onClick.
startIconReactNode—An icon before the label, in reading order, so it moves to the right in right-to-left text. Hidden from screen readers: the label names the button.
endIconReactNode—An icon after the label, in reading order. Hidden from screen readers: the label names the button.
childrenReactNode—The label: the words that name the action, in sentence case. Required in practice: a button with no words is an IconButton.
classNamestring—Reaches the root. className is merged after pn-Button.
styleCSSProperties—Reaches the root. className is merged after pn-Button.
refRef<HTMLButtonElement>—Reaches the root. className is merged after pn-Button.
variant'solid' | 'outline', default 'solid'
How much the button stands out: solid is filled, for the action a panel is for; outline is an edge only, for the actions beside it.
color'neutral' | 'brand', default 'neutral'
The intent: neutral is ink, for every action but the main one; brand is the brand yellow, for the one main action of a screen.
size'small' | 'medium', default 'medium'
The button's height: medium is size.button, small is size.button-sm, for dense rows and panels.
disabledboolean, default false
Whether the button ignores the pointer and the keys. A disabled button leaves the tab order unless focusableWhenDisabled is set.
fullWidthboolean, default false
Whether the button fills the width of its container, such as a bottom sheet's main action. Sets data-full-width.
selectedboolean, default —
Whether the button, a toggle such as "Follow", is on: true or false sets aria-pressed and draws the button on or off, and true sets data-selected. Left out, the button is no toggle and has no aria-pressed. The caller keeps the state and flips it in onClick.
startIconReactNode, default —
An icon before the label, in reading order, so it moves to the right in right-to-left text. Hidden from screen readers: the label names the button.
endIconReactNode, default —
An icon after the label, in reading order. Hidden from screen readers: the label names the button.
childrenReactNode, default —
The label: the words that name the action, in sentence case. Required in practice: a button with no words is an IconButton.
classNamestring, default —
Reaches the root. className is merged after pn-Button.
styleCSSProperties, default —
Reaches the root. className is merged after pn-Button.
refRef<HTMLButtonElement>, default —
Reaches the root. className is merged after pn-Button.

Use it

import { Button } from '@mylder/perron'

Ask your agent

get_reference({ library: "base-ui", component: "Button" })

Keyboard and ARIA

From the Base UI reference for Button and the installed useButton. "From" says who does it.

KeyDoesFrom
Tab, Shift+TabMoves focus to the button and away; a disabled button is skipped unless focusableWhenDisabledThe browser, and Base UI
EnterActivates the button: fires onClick, or submits the form with type="submit"The browser; Base UI for a non-button render
SpaceActivates the button on releaseThe browser; Base UI for a non-button render
ElementRoleAccessible name fromStates announced
rootbutton, native; role="button" from Base UI when render gives a non-button elementIts text content: the label's children; aria-label or aria-labelledby from the caller winDisabled: the native disabled, or aria-disabled with focusableWhenDisabled. Pressed or not pressed, from aria-pressed, when selected is given
startIcon, endIconnone: aria-hidden="true"nonenone

What axe won't catch, from the contract, each with how this component meets it:

  • Everything the eye is told, the ear is told, for every shape each prop accepts: the label is text the button is named by; the icons are aria-hidden, since an icon beside a word adds nothing the word doesn't say (Icons); color and variant carry emphasis, not meaning, and disabled is announced by the native attribute; a toggle's on and off are aria-pressed, the attribute the look is read from (CR-15), which says the state in words to the ear, so the label stays "Follow" on and off and the state is heard once, "Follow, toggle button, pressed" (Accessibility, "State in words").
  • Every landmark and named region has an accessible name: none here; a button is neither.
  • Every default label can be overridden: none here; Button owns no strings.
  • Every shape the type accepts renders, in every variant: children, startIcon and endIcon render a string, an element and a list alike, in every variant, color and size.
  • A hidden copy kept for animation is aria-hidden, and a collapsed region is inert: none here; Button keeps no hidden copy and collapses nothing.
  • Native semantics first: a native <button> from Base UI's Button; with render and nativeButton={false}, Base UI's role="button".
  • Focus moves in reading order and returns on close: the button is one tab stop in document order, and opens nothing of its own to return from.
  • Every target is at least size.target-min square: the smallest target is a small button, size.button-sm (30 px) tall, and at least that wide with its space.300 padding on each side and a word between.
  • Loops stop when the system asks for less motion: none here; Button has no loop and no transition.

Owned strings

No owned strings: every word comes from the caller, as children.

Do and don't

For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.

RuleLevelDoDon'tChecked by
DD-1mustGive one button per screen color="brand", for its main action; the default neutral ink is for everything else, so the yellow keeps meaning the main actionPut two brand buttons on one screen, or use brand for emphasis: a second yellow action leaves neither the main onereview
DD-2mustName the action in sentence case, as a verb and what it acts on: "Show departures", "Show whole line"Write "OK", "Click here", Title Case or an exclamation mark (Content design)review
DD-3mustUse IconButton for a button with an icon and no words, so it gets its required labelPass only a startIcon and no children: the icon is aria-hidden, so the button has no namereview
DD-4shouldUse solid for the action a panel is for and outline for the actions beside itMark the main action with outline, or set every action in a row solidreview
DD-5mustTake variant from solid and outline, color from neutral and brand, and size from medium and smallWrite MUI's variant="contained", color="primary" or size="large": Button has none of them, and brand is its main action's colourtypes
DD-6mustSet type="submit" on a form's submit button; Base UI's default is type="button"Rely on a button submitting its form: without type="submit" it doesn'treview
DD-7mustPass nativeButton={false} with render when it gives an element that isn't a <button>Render a link with render={<a href="/map" />} for navigation: LinkButton is that component, and a button that navigates misleads screen readersreview
DD-8shouldKeep a brand button out of rows of line and mode badgesSet a brand button among bus badges, whose fill and ink label it shares, where it reads as onereview
DD-9mustGive a toggle selected, true or false, and flip it in onClick; leave it out on every button that isn't oneSet selected={false} on a plain button: it makes the button a toggle that is off, and a screen reader says "not pressed"review
DD-10mustKeep a toggle's label the same off and on, "Follow", and let aria-pressed say the state (Accessibility)Change the label of a toggle that uses selected, to "Following" or "Unfollow": with aria-pressed the state is heard twice, "Following, pressed", or the opposite way, "Unfollow, pressed"review
DD-11shouldUse FilterChip for a filter that turns on and off, and Switch for a settingUse a Button toggle in a row of filters, where FilterChip's pill shows they belong togetherreview
DD-12mustSet disabled when the action can't run, with a reason in words nearby, or focusableWhenDisabled so the button can carry itRestyle a disabled button with opacity: disabled is color.text.tertiary, a colourreview