Components
Button
Full support Supported since v125. Full support Supported since v128. Full support Supported since v18.
Variants
Change the button variant with the variant prop.
---import { Button } from "@opui/astro"---
<div class="example-row"> <Button>Text</Button> <Button disabled>Disabled</Button> <Button href="#">Link</Button></div><div class="example-row"> <Button variant="outlined">Outlined</Button> <Button variant="outlined" disabled>Disabled</Button> <Button variant="outlined" href="#">Link</Button></div><div class="example-row"> <Button variant="tonal">Tonal</Button> <Button variant="tonal" disabled>Disabled</Button> <Button variant="tonal" href="#">Link</Button></div><div class="example-row"> <Button variant="filled">Filled</Button> <Button variant="filled" disabled>Disabled</Button> <Button variant="filled" href="#">Link</Button></div><div class="example-row"> <button class="ui-button">Text</button> <button disabled class="ui-button ui-disabled">Disabled</button> <a href="#" class="ui-button"> Link </a></div><div class="example-row"> <button class="ui-button ui-outlined">Outlined</button> <button disabled class="ui-button ui-disabled ui-outlined">Disabled</button> <a href="#" class="ui-button ui-outlined"> Link </a></div><div class="example-row"> <button class="ui-button ui-tonal">Tonal</button> <button disabled class="ui-button ui-disabled ui-tonal">Disabled</button> <a href="#" class="ui-button ui-tonal"> Link </a></div><div class="example-row"> <button class="ui-button ui-filled">Filled</button> <button disabled class="ui-button ui-disabled ui-filled">Disabled</button> <a href="#" class="ui-button ui-filled"> Link </a></div>Colors
Pass color to apply a brand or destructive color: primary or critical. The default is a neutral gray.
---import { Button } from "@opui/astro"---
<div class="example-row"> <Button color="primary">Primary</Button> <Button color="primary" variant="outlined">Outlined</Button> <Button color="primary" variant="tonal">Tonal</Button> <Button color="primary" variant="filled">Filled</Button></div><div class="example-row"> <Button color="critical">Critical</Button> <Button color="critical" variant="outlined">Outlined</Button> <Button color="critical" variant="tonal">Tonal</Button> <Button color="critical" variant="filled">Filled</Button></div><div class="example-row"> <button class="ui-button ui-primary">Primary</button> <button class="ui-button ui-outlined ui-primary">Outlined</button> <button class="ui-button ui-tonal ui-primary">Tonal</button> <button class="ui-button ui-filled ui-primary">Filled</button></div><div class="example-row"> <button class="ui-button ui-critical">Critical</button> <button class="ui-button ui-outlined ui-critical">Outlined</button> <button class="ui-button ui-tonal ui-critical">Tonal</button> <button class="ui-button ui-filled ui-critical">Filled</button></div>Buttons with icon and label
Include an icon alongside text by nesting it within the component.
---import { Button } from "@opui/astro"---
<Button> Text <svg> <!-- --> </svg></Button><Button variant="outlined"> Outlined <svg> <!-- --> </svg></Button><Button variant="tonal"> Tonal <svg> <!-- --> </svg></Button><Button variant="filled"> Filled <svg> <!-- --> </svg></Button>
<Button> <svg> <!-- --> </svg> Text</Button><Button variant="outlined"> <svg> <!-- --> </svg> Outlined</Button><Button variant="tonal"> <svg> <!-- --> </svg> Tonal</Button><Button variant="filled"> <svg> <!-- --> </svg> Filled</Button><div class="example-row"> <button class="ui-button"> Text <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button class="ui-button ui-outlined"> Outlined <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button class="ui-button ui-tonal"> Tonal <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button class="ui-button ui-filled"> Filled <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button></div><div class="example-row"> <button class="ui-button"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> Text </button> <button class="ui-button ui-outlined"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> Outlined </button> <button class="ui-button ui-tonal"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> Tonal </button> <button class="ui-button ui-filled"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> Filled </button></div>Keyboard
Use the <kbd> element to provide keyboard hints within a
button.
---import { Button } from "@opui/astro"---
<Button> Search <kbd>⌘K</kbd></Button><Button variant="outlined"> Save <kbd>⌘S</kbd></Button><Button variant="tonal"> Copy <kbd>⌘C</kbd></Button><Button variant="filled"> Delete <kbd>⌘⌫</kbd></Button><button class="ui-button">Search <kbd>⌘K</kbd></button><button class="ui-button ui-outlined">Save <kbd>⌘S</kbd></button><button class="ui-button ui-tonal">Copy <kbd>⌘C</kbd></button><button class="ui-button ui-filled">Delete <kbd>⌘⌫</kbd></button>Icon-only
See Icon button documentation.
Sizes
Resize any button using the size prop.
---import { Button } from "@opui/astro"---
<Button size="small">Small</Button><Button>Default</Button><Button size="large">Large</Button>
<Button variant="filled" size="small">Small</Button><Button variant="filled">Default</Button><Button variant="filled" size="large">Large</Button>
<Button size="small" variant="outlined"> Small <svg> <!-- --> </svg></Button><Button variant="outlined"> Default <svg> <!-- --> </svg></Button><Button variant="outlined" size="large"> Large <svg> <!-- --> </svg></Button><div class="example-row"> <button class="ui-button ui-small">Small</button> <button class="ui-button">Default</button> <button class="ui-button ui-large">Large</button></div><div class="example-row"> <button class="ui-button ui-small ui-filled">Small</button> <button class="ui-button ui-filled">Default</button> <button class="ui-button ui-large ui-filled">Large</button></div><div class="example-row"> <button class="ui-button ui-small ui-outlined"> Small <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button class="ui-button ui-outlined"> Default <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button class="ui-button ui-large ui-outlined"> Large <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button></div>Disabled
Disable the button with the disabled prop.
---import { Button } from "@opui/astro"---
<Button disabled>Text</Button><div class="example-row"> <button disabled class="ui-button ui-disabled">Text</button> <button disabled class="ui-button ui-disabled ui-outlined">Outlined</button> <button disabled class="ui-button ui-disabled ui-tonal">Tonal</button> <button disabled class="ui-button ui-disabled ui-filled">Filled</button></div><div class="example-row"> <button disabled class="ui-button ui-disabled"> Text <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button disabled class="ui-button ui-disabled ui-outlined"> Outlined <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button disabled class="ui-button ui-disabled ui-tonal"> Tonal <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button> <button disabled class="ui-button ui-disabled ui-filled"> Filled <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M11.75 3a.75.75 0 0 1 .743.648l.007.102l.001 7.25h7.253a.75.75 0 0 1 .102 1.493l-.102.007h-7.253l.002 7.25a.75.75 0 0 1-1.493.101l-.007-.102l-.002-7.249H3.752a.75.75 0 0 1-.102-1.493L3.752 11h7.25L11 3.75a.75.75 0 0 1 .75-.75" ></path> </svg> </button></div>File upload
Is it a button? Is it an input? You can find the docs for it here at least.
Anatomy
- Container
- Label text (optional)
- Icon (optional)
API
| Prop | Type | Default | Description |
|---|---|---|---|
size | "small", "large" | - | The size of the button. |
variant | "outlined", "tonal", "filled" | - | The visual variant of the button. |
color | "critical", "primary" | - | The color of the button. Default is a neutral gray. |
href | string | - | Renders as an tag if an href is provided. |
disabled | boolean | - | Button disabled state. |
Browser support
Full support Supported since v125. Full support Supported since v128. Full support Supported since v18.
See also the full browser support guide.
Installation
@layer components.root { :where(.ui-button) { --_color: light-dark(var(--color-16), var(--color-1)); --_color-contrast: light-dark(var(--color-1), var(--color-16)); --_color-tonal: var(--surface-tonal);
--_accent: var(--_color); --_accent-contrast: var(--_color-contrast); --_accent-tonal: var(--_color-tonal); --_accent-tonal-contrast: var(--_color);
--_default-hover-bg-color: light-dark( oklch(from var(--_accent) l 0.01 h / 10%), oklch(from var(--_accent) l 0.01 h / 20%) ); --_default-active-bg-color: light-dark( oklch(from var(--_accent) l 0.06 h / 20%), oklch(from var(--_accent) l 0.06 h / 30%) );
--_filled-hover-bg-color: light-dark( oklch(from var(--_accent) calc(l + 0.2) c h), oklch(from var(--_accent) calc(l - 0.1) c h) ); --_filled-active-bg-color: light-dark( oklch(from var(--_accent) calc(l + 0.3) c h), oklch(from var(--_accent) calc(l - 0.15) c h) ); --_outlined-active-bg-color: var(--_filled-active-bg-color);
--_bg-color: transparent; --_border-color: transparent; --_border-width: var(--border-size-1); --_font-size: var(--font-size-1); --_min-height: var(--button-size); --_border-radius: var(--button-border-radius, var(--radius-2)); --_text-color: var(--_accent);
-webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; align-items: center; background-color: var(--_bg-color); border: var(--_border-width) solid var(--_border-color); border-radius: var(--_border-radius); color: var(--_text-color); display: inline-flex; font-size: var(--_font-size); font-weight: 700; gap: var(--size-2); justify-content: center; min-block-size: var(--_min-height); padding-block: 0.5ex; padding-inline: 1.5ex; text-align: center; text-decoration: none; transition: background-color 0.1s ease, color 0.1s ease, border-color 0.1s ease, box-shadow 0.1s ease; user-select: none;
/* Colors — shared interaction model for critical and primary. Each color modifier below only swaps --_color; the tonal surface, contrast text, and hover/active strategy are identical. The .critical scope class in theme.css redirects --palette-source so --color-6 below becomes a tint of the active color. */ &.ui-critical, &.ui-primary { --_color-contrast: var(--gray-1); --_color-tonal: var(--color-6); --_accent-tonal-contrast: var(--text-primary-contrast);
--_default-hover-bg-color: oklch(from var(--_accent) l c h / 15%); --_default-active-bg-color: oklch(from var(--_accent) l c h / 25%);
--_filled-hover-bg-color: oklch(from var(--_accent) calc(l - 0.1) c h); --_filled-active-bg-color: oklch(from var(--_accent) calc(l - 0.15) c h);
--_outlined-active-bg-color: oklch(from var(--_accent) calc(l - 0.1) c h); }
&.ui-critical { --_color: var(--critical); }
&.ui-primary { --_color: var(--primary); }
/* Disabled */ &:where([disabled]) { --_text-color: color-mix( in oklch, var(--text-muted) 50%, var(--surface-default) );
cursor: not-allowed; opacity: 0.64; }
/* Hover & Active */ &:where(:not([disabled])) { &:where(:not(:active):hover) { --_bg-color: var(--_default-hover-bg-color); }
&:where(:hover:active), &[aria-current="page"] { --_bg-color: var(--_default-active-bg-color); } }
/* Icons / Keyboard */ &:where(:has(svg), &.ui-icon-only) { gap: 1ex;
svg { color: currentColor; max-block-size: 0.7lh; } }
/* Keyboard input */ :is(kbd) { background-color: oklch(from currentColor l c h / 10%); border: 0; color: oklch(from currentColor l c h / 80%); font-size: 0.8em; font-weight: 400; line-height: 1.2; }
/* Sizes — heights aliased from the shared --control-size scale. Override --button-size-* in a parent scope to resize an entire region. */ &.ui-x-small { --_min-height: var(--button-size-x-small); --_font-size: var(--font-size-0); padding-block: 0; padding-inline: 0.5ex; }
&.ui-small { --_min-height: var(--button-size-small); --_font-size: var(--font-size-05); padding-block: 0; padding-inline: 0.75ex; }
&.ui-large { --_min-height: var(--button-size-large); padding-inline: 4ex; }
/* Variants */ &.ui-outlined { --_bg-color: transparent; --_border-color: var(--_accent); --_text-color: var(--_accent);
&:where(:not([disabled])) { &:where(:not(:active):hover) { --_bg-color: var(--_accent); --_border-color: var(--_accent); --_text-color: var(--_accent-contrast); }
&:where(:active) { --_bg-color: var(--_outlined-active-bg-color); --_border-color: var(--_outlined-active-bg-color); --_text-color: var(--_accent-contrast); } } }
&.ui-tonal { --_bg-color: var(--_accent-tonal); --_text-color: var(--_accent-tonal-contrast);
&:where(:not([disabled])) { &:where(:not(:active):hover) { --_bg-color: var(--_accent); --_text-color: var(--_accent-contrast); }
&:where(:active) { --_bg-color: var(--_outlined-active-bg-color); --_text-color: var(--_accent-contrast); } } }
&.ui-filled { --_bg-color: var(--_accent); --_text-color: var(--_accent-contrast);
&:where(:not([disabled])) { &:where(:not(:active):hover) { --_bg-color: var(--_filled-hover-bg-color); }
&:where(:active) { --_bg-color: var(--_filled-active-bg-color); } } } }
/* file input */ :where(input[type="file"]) { align-self: flex-start; border: var(--border-size-1) solid var(--surface-filled); border-radius: var(--radius-2); box-shadow: var(--inner-shadow-4); color: var(--text-muted-contrast); cursor: initial; max-inline-size: 100%; padding: 0; }
:where(input[type="file"])::-webkit-file-upload-button, :where(input[type="file"])::file-selector-button { cursor: pointer; margin-inline-end: var(--size-relative-6); }}