Button An action. Use a <button>, or an <a href> styled as a button when the action is navigation. For navigation inside text, use Link .
Installation bun add @harmo-ui/css @harmo-ui/react npm install @harmo-ui/css @harmo-ui/react pnpm add @harmo-ui/css @harmo-ui/react yarn add @harmo-ui/css @harmo-ui/react pnpm dlx harmo-ui add button yarn dlx harmo-ui add button Usage import { Button } from "@harmo-ui/react" ;
< Button >Save changes</ Button > Examples Variants Primary for the main action, secondary for the others, and tertiary for the least prominent.
Save changes Cancel Show details
<>
< Button >Save changes</ Button >
< Button variant = "secondary" >Cancel</ Button >
< Button variant = "tertiary" >Show details</ Button >
</> < button class = "hbutton" type = "button" >
< span class = "hbutton-label" >Save changes</ span >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "button" >
< span class = "hbutton-label" >Cancel</ span >
</ button >
< button class = "hbutton" data-variant = "tertiary" type = "button" >
< span class = "hbutton-label" >Show details</ span >
</ button > Small data-size="small": 29px tall with 13px text, for notices, table rows, and cards.
Save changes Cancel Show details
<>
< Button size = "small" >Save changes</ Button >
< Button variant = "secondary" size = "small" >
Cancel
</ Button >
< Button variant = "tertiary" size = "small" >
Show details
</ Button >
</> < button class = "hbutton" data-size = "small" type = "button" >
< span class = "hbutton-label" >Save changes</ span >
</ button >
< button class = "hbutton" data-variant = "secondary" data-size = "small" type = "button" >
< span class = "hbutton-label" >Cancel</ span >
</ button >
< button class = "hbutton" data-variant = "tertiary" data-size = "small" type = "button" >
< span class = "hbutton-label" >Show details</ span >
</ button > Disabled disabled dims a button and takes it out of the tab order. aria-disabled="true" looks the same but stays focusable, so a tooltip can say why: Tab reaches the last button here.
Save changes Cancel Show details Save changes Publish
<>
< Button disabled >Save changes</ Button >
< Button variant = "secondary" disabled >
Cancel
</ Button >
< Button variant = "tertiary" disabled >
Show details
</ Button >
< Button size = "small" disabled >
Save changes
</ Button >
< Button aria-disabled = "true" >Publish</ Button >
</>
View full source
< button class = "hbutton" type = "button" disabled >
< span class = "hbutton-label" >Save changes</ span >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "button" disabled >
< span class = "hbutton-label" >Cancel</ span >
</ button >
< button class = "hbutton" data-variant = "tertiary" type = "button" disabled >
< span class = "hbutton-label" >Show details</ span >
</ button >
< button class = "hbutton" data-size = "small" type = "button" disabled >
< span class = "hbutton-label" >Save changes</ span >
</ button >
< button class = "hbutton" type = "button" aria-disabled = "true" >
< span class = "hbutton-label" >Publish</ span >
</ button >
View full source
Links When the action is navigation, put .hbutton on an <a href>: it keeps link behavior, such as opening in a new tab, with a button's look.
<>
< Button href = "/campaigns/new" >Create campaign</ Button >
< Button variant = "secondary" href = "/reports" >
View reports
</ Button >
< Button variant = "tertiary" href = "/help" >
Read more
</ Button >
</> < a class = "hbutton" href = "/campaigns/new" >
< span class = "hbutton-label" >Create campaign</ span >
</ a >
< a class = "hbutton" data-variant = "secondary" href = "/reports" >
< span class = "hbutton-label" >View reports</ span >
</ a >
< a class = "hbutton" data-variant = "tertiary" href = "/help" >
< span class = "hbutton-label" >Read more</ span >
</ a > Icon only Without a label, a button is a square as wide as it is tall, with its icon drawn at 20px. aria-label gives it the name a label would. Where the icon alone might not be clear, a Tooltip can show that name too.
<>
< Button variant = "tertiary" aria-label = "Close" >
< CloseIcon className = "hicon" />
</ Button >
< Button variant = "secondary" aria-label = "More actions" >
< MoreIcon className = "hicon" />
</ Button >
< Button aria-label = "Add contact" >
< PlusIcon className = "hicon" />
</ Button >
< Button variant = "secondary" size = "small" aria-label = "More actions" >
< MoreIcon className = "hicon" />
</ Button >
< Button aria-label = "Add contact" disabled >
< PlusIcon className = "hicon" />
</ Button >
</>
View full source
< button class = "hbutton" data-variant = "tertiary" type = "button" aria-label = "Close" >
< svg class = "hicon" width = "24" height = "24" viewBox = "0 0 25 25" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" aria-hidden = "true" focusable = "false" >
< path d = "M18.1183 18.5318L6.80463 7.21814" />
< path d = "M18.1183 7.21814L6.80463 18.5318" />
</ svg >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "button" aria-label = "More actions" >
< svg class = "hicon" width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" aria-hidden = "true" focusable = "false" >
< circle cx = "6" cy = "12" r = "1" />
< circle cx = "12" cy = "12" r = "1" />
< circle cx = "18" cy = "12" r = "1" />
</ svg >
</ button >
< button class = "hbutton" type = "button" aria-label = "Add contact" >
< svg class = "hicon" width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" aria-hidden = "true" focusable = "false" >
< path d = "M12 5V19M5 12H19" />
</ svg >
</ button >
< button class = "hbutton" data-variant = "secondary" data-size = "small" type = "button" aria-label = "More actions" >
< svg class = "hicon" width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" aria-hidden = "true" focusable = "false" >
< circle cx = "6" cy = "12" r = "1" />
< circle cx = "12" cy = "12" r = "1" />
< circle cx = "18" cy = "12" r = "1" />
</ svg >
</ button >
< button class = "hbutton" type = "button" aria-label = "Add contact" disabled >
< svg class = "hicon" width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" aria-hidden = "true" focusable = "false" >
< path d = "M12 5V19M5 12H19" />
</ svg >
</ button >
View full source
Side by side Labeled and icon-only buttons share --harmo-control-height, so they line up in a row.
Export Save changes
<>
< Button variant = "secondary" >Export</ Button >
< Button variant = "secondary" aria-label = "More actions" >
< MoreIcon className = "hicon" />
</ Button >
< Button >Save changes</ Button >
</> < button class = "hbutton" data-variant = "secondary" type = "button" >
< span class = "hbutton-label" >Export</ span >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "button" aria-label = "More actions" >
< svg class = "hicon" width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" aria-hidden = "true" focusable = "false" >
< circle cx = "6" cy = "12" r = "1" />
< circle cx = "12" cy = "12" r = "1" />
< circle cx = "18" cy = "12" r = "1" />
</ svg >
</ button >
< button class = "hbutton" type = "button" >
< span class = "hbutton-label" >Save changes</ span >
</ button >
View full source
With an arrow A ChevronToArrow turns into an arrow on hover or keyboard focus. A back arrow leads the label and a forward arrow follows it; the button balances the space on the other side.
<>
< Button variant = "secondary" >
< ChevronToArrow direction = "back" />
Go back
</ Button >
< Button >
Continue
< ChevronToArrow />
</ Button >
< Button variant = "tertiary" href = "/reports" >
All reports
< ChevronToArrow />
</ Button >
</>
View full source
< button class = "hbutton" data-variant = "secondary" type = "button" >
< svg class = "hicon hchevron-to-arrow" data-direction = "back" width = "24" height = "24" viewBox = "0 0 24 24" aria-hidden = "true" focusable = "false" >
< g class = "hchevron-to-arrow-head" >
< line class = "hchevron-to-arrow-upper" x1 = "12" y1 = "6" x2 = "12" y2 = "12" />
< line class = "hchevron-to-arrow-lower" x1 = "12" y1 = "18" x2 = "12" y2 = "12" />
</ g >
< line class = "hchevron-to-arrow-shaft" x1 = "6" y1 = "12" x2 = "15" y2 = "12" />
</ svg >
< span class = "hbutton-label" >Go back</ span >
</ button >
< button class = "hbutton" type = "button" >
< span class = "hbutton-label" >Continue</ span >
< svg class = "hicon hchevron-to-arrow" width = "24" height = "24" viewBox = "0 0 24 24" aria-hidden = "true" focusable = "false" >
< g class = "hchevron-to-arrow-head" >
< line class = "hchevron-to-arrow-upper" x1 = "12" y1 = "6" x2 = "12" y2 = "12" />
< line class = "hchevron-to-arrow-lower" x1 = "12" y1 = "18" x2 = "12" y2 = "12" />
</ g >
< line class = "hchevron-to-arrow-shaft" x1 = "6" y1 = "12" x2 = "15" y2 = "12" />
</ svg >
</ button >
< a class = "hbutton" data-variant = "tertiary" href = "/reports" >
< span class = "hbutton-label" >All reports</ span >
< svg class = "hicon hchevron-to-arrow" width = "24" height = "24" viewBox = "0 0 24 24" aria-hidden = "true" focusable = "false" >
< g class = "hchevron-to-arrow-head" >
< line class = "hchevron-to-arrow-upper" x1 = "12" y1 = "6" x2 = "12" y2 = "12" />
< line class = "hchevron-to-arrow-lower" x1 = "12" y1 = "18" x2 = "12" y2 = "12" />
</ g >
< line class = "hchevron-to-arrow-shaft" x1 = "6" y1 = "12" x2 = "15" y2 = "12" />
</ svg >
</ a >
View full source
Right to left The same buttons in Arabic. Arrows follow the reading direction: back points right and forward points left.
رجوع متابعة
<>
< Button variant = "secondary" >
< ChevronToArrow direction = "back" />
رجوع
</ Button >
< Button >
متابعة
< ChevronToArrow />
</ Button >
</> < button class = "hbutton" data-variant = "secondary" type = "button" >
< svg class = "hicon hchevron-to-arrow" data-direction = "back" width = "24" height = "24" viewBox = "0 0 24 24" aria-hidden = "true" focusable = "false" >
< g class = "hchevron-to-arrow-head" >
< line class = "hchevron-to-arrow-upper" x1 = "12" y1 = "6" x2 = "12" y2 = "12" />
< line class = "hchevron-to-arrow-lower" x1 = "12" y1 = "18" x2 = "12" y2 = "12" />
</ g >
< line class = "hchevron-to-arrow-shaft" x1 = "6" y1 = "12" x2 = "15" y2 = "12" />
</ svg >
< span class = "hbutton-label" >رجوع</ span >
</ button >
< button class = "hbutton" type = "button" >
< span class = "hbutton-label" >متابعة</ span >
< svg class = "hicon hchevron-to-arrow" width = "24" height = "24" viewBox = "0 0 24 24" aria-hidden = "true" focusable = "false" >
< g class = "hchevron-to-arrow-head" >
< line class = "hchevron-to-arrow-upper" x1 = "12" y1 = "6" x2 = "12" y2 = "12" />
< line class = "hchevron-to-arrow-lower" x1 = "12" y1 = "18" x2 = "12" y2 = "12" />
</ g >
< line class = "hchevron-to-arrow-shaft" x1 = "6" y1 = "12" x2 = "15" y2 = "12" />
</ svg >
</ button >
View full source
Long labels In a narrow container, drawn dashed here, a long label wraps and the button grows taller instead of overflowing.
Save changes and notify everyone Export every conversation
<>
< Button >Save changes and notify everyone</ Button >
< Button variant = "secondary" >Export every conversation</ Button >
</> < button class = "hbutton" type = "button" >
< span class = "hbutton-label" >Save changes and notify everyone</ span >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "button" >
< span class = "hbutton-label" >Export every conversation</ span >
</ button > Loading aria-busy with aria-disabled keeps a loading button focusable, and a spinner follows its label or takes its icon's place. Press the last button to watch it load for two seconds.
Saving changes Exporting Save changes
const [ saving , setSaving ] = useState ( false );
const save = () => {
setSaving ( true );
setTimeout (() => setSaving ( false ) , 2000 );
};
return (
<>
< Button loading >Saving changes</ Button >
< Button variant = "secondary" loading >
Exporting
</ Button >
< Button aria-label = "Adding contact" loading >
< PlusIcon className = "hicon" />
</ Button >
< Button loading = { saving } onClick = { save } >
Save changes
</ Button >
</>
);
View full source
< button class = "hbutton" type = "button" aria-busy = "true" aria-disabled = "true" >
< span class = "hbutton-label" >Saving changes</ span >
< span class = "hspinner" aria-hidden = "true" ></ span >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "button" aria-busy = "true" aria-disabled = "true" >
< span class = "hbutton-label" >Exporting</ span >
< span class = "hspinner" aria-hidden = "true" ></ span >
</ button >
< button class = "hbutton" type = "button" aria-label = "Adding contact" aria-busy = "true" aria-disabled = "true" >
< span class = "hspinner" aria-hidden = "true" ></ span >
</ button >
< button class = "hbutton" type = "button" >
< span class = "hbutton-label" >Save changes</ span >
</ button >
View full source
API reference React <Button> renders a <button type="button">, or with href a link, with its text in .hbutton-label. An icon as its first or last child stays outside the label: Harmo's <ChevronToArrow>, <Chevron>, or <ExpandChevron>, or any element with the hicon class. Other props are the button's or the link's attributes, so type="submit" makes a submit button.
HTML A <button class="hbutton"> or <a class="hbutton">, with its text in .hbutton-label. Load button.css; spinner.css for loading; and chevron-to-arrow.css, expand-chevron.css, or tooltip.css when used.
A script is only needed for aria-disabled and loading. See Button's full contract .
Accessibility aria-disabled="true" instead of disabled keeps the button focusable, so a tooltip can explain why it's unavailable.A loading button keeps focus, so a keyboard user who submitted with Enter doesn't lose their place. An icon-only button needs a name: aria-label, or a Tooltip referenced with aria-labelledby. A naming tooltip uses aria-labelledby rather than aria-describedby, so screen readers say "Close, button" once, not twice.