Skip to content

Button

wu-btn styles a <button> or a link as a button. Modifier words go after the root class: wu-btn brand sm.

Class Effect
wu-btn Root. Surface background, soft border, medium control height
brand Brand fill with text-on-brand text
danger Danger fill with text-on-danger text, for destructive actions
quiet Transparent background and border, tinted on hover
link Text-only button that looks like a link: no background, border or padding, text-link color, underline on hover (see Link)
subtle With brand or danger: tinted fill and role border, solid on hover (see Subtle)
icon Square button for one svg and no text (see Icon)
round Fully rounded pill. Empty or icon-only, it is a circle (see Round)
sm, lg Small and large control height, padding and font size
caret CSS-drawn caret after the label. Empty, it is a square caret-only button (see Caret)
animated Spring press scale, opt-in (see Animation)
aria-busy="true" Loading state: full opacity, pointer clicks blocked (see Loading)
<button class="wu-btn" type="button">Default</button>
<div class="flex wrap x-center gap-2">
<button class="wu-btn sm" type="button">Small</button>
<button class="wu-btn" type="button">Default</button>
<button class="wu-btn lg" type="button">Large</button>
</div>
<div class="flex wrap x-center gap-2">
<button class="wu-btn brand sm" type="button">Brand small</button>
<button class="wu-btn brand" type="button">Brand</button>
<button class="wu-btn brand lg" type="button">Brand large</button>
</div>

For destructive actions such as delete. The fill, hover and active colors come from the danger role tokens.

<div class="flex wrap x-center gap-2">
<button class="wu-btn danger sm" type="button">Delete small</button>
<button class="wu-btn danger" type="button">Delete</button>
<button class="wu-btn danger lg" type="button">Delete large</button>
</div>

A low-emphasis button with no visible background or border until hover, for toolbars and secondary actions. Keyboard focus shows the same focus ring as every button.

<div class="flex wrap x-center gap-2">
<button class="wu-btn quiet sm" type="button">Small</button>
<button class="wu-btn quiet" type="button">Cancel</button>
<button class="wu-btn quiet lg" type="button">Large</button>
</div>

quiet is the text-only button, the equivalent of the Element Plus “text” button: no background or border until hover.

subtle after brand or danger gives the tinted outline style: a light role fill, a role border and role text, turning solid on hover. Use it for secondary actions that still need the role, such as a Delete in a table row.

<div class="flex wrap x-center gap-2">
<button class="wu-btn brand subtle" type="button">Edit</button>
<button class="wu-btn danger subtle" type="button">Delete</button>
<button class="wu-btn danger subtle sm" type="button">Delete</button>
<button class="wu-btn brand subtle round" type="button">Round</button>
</div>

icon gives a square button as wide as its height for one svg and no text. Add an aria-label. Combined with round it is a circle. Any direct child svg of a button is sized to 1em and takes the text color; in an icon button it is a little larger.

<div class="flex wrap x-center gap-2">
<button class="wu-btn icon sm" type="button" aria-label="Add small"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M7.25 2h1.5v5.25H14v1.5H8.75V14h-1.5V8.75H2v-1.5h5.25z"/></svg></button>
<button class="wu-btn icon" type="button" aria-label="Add"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button>
<button class="wu-btn icon lg" type="button" aria-label="Add large"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M7.25 2h1.5v5.25H14v1.5H8.75V14h-1.5V8.75H2v-1.5h5.25z"/></svg></button>
<button class="wu-btn brand icon" type="button" aria-label="Add item"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button>
<button class="wu-btn quiet icon" type="button" aria-label="Add quietly"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M7.25 2h1.5v5.25H14v1.5H8.75V14h-1.5V8.75H2v-1.5h5.25z"/></svg></button>
<button class="wu-btn icon round" type="button" aria-label="Add round"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M7.25 2h1.5v5.25H14v1.5H8.75V14h-1.5V8.75H2v-1.5h5.25z"/></svg></button>
<button class="wu-btn brand" type="button"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>Add item</button>
</div>

round gives a fully rounded pill. It combines with every variant, size and caret. With no label (and an aria-label) or with a single svg icon and no text, it becomes a circle sized to the control height.

<div class="flex wrap x-center gap-2">
<button class="wu-btn round sm" type="button">Small</button>
<button class="wu-btn round" type="button">Default</button>
<button class="wu-btn round lg" type="button">Large</button>
<button class="wu-btn brand round" type="button">Brand</button>
<button class="wu-btn danger round" type="button">Danger</button>
<button class="wu-btn quiet round" type="button">Quiet</button>
<button class="wu-btn round caret" type="button">Actions</button>
<button class="wu-btn round caret" type="button" aria-label="Round menu"></button>
<button class="wu-btn brand round" type="button" aria-label="Add"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M7.25 2h1.5v5.25H14v1.5H8.75V14h-1.5V8.75H2v-1.5h5.25z"/></svg></button>
</div>

link is a text-only button that looks like a link. It has no background, border or horizontal padding, uses the text-link color and underlines on hover. It keeps the control height, so it lines up with other buttons in a toolbar row. It works on a <button> for actions and on an <a> for navigation, and the disabled state is the same as for every button.

Anchor
<div class="flex wrap x-center gap-2">
<button class="wu-btn link sm" type="button">Small</button>
<button class="wu-btn link" type="button">Default</button>
<button class="wu-btn link lg" type="button">Large</button>
<a class="wu-btn link" href="/components/docs/">Anchor</a>
<button class="wu-btn link" type="button" disabled>Disabled</button>
</div>

A toolbar row mixing the styles:

<div class="flex wrap x-center gap-2">
<button class="wu-btn" type="button">Default</button>
<button class="wu-btn round" type="button">Round</button>
<button class="wu-btn quiet" type="button">Quiet</button>
<button class="wu-btn link" type="button">Link</button>
</div>

caret draws a caret after the label. With no label (and an aria-label) it becomes a caret-only button, as wide as its height for sm, default and lg. The caret turns while the popover right after the button is open.

<div class="flex wrap x-center gap-2">
<button class="wu-btn caret" type="button" popovertarget="button-caret-menu">Actions</button>
<div class="wu-dropdown" id="button-caret-menu" popover>
<button class="wu-dropdown-item" type="button">Edit</button>
<button class="wu-dropdown-item" type="button">Duplicate</button>
</div>
<button class="wu-btn sm caret" type="button" aria-label="Small menu"></button>
<button class="wu-btn caret" type="button" aria-label="Default menu"></button>
<button class="wu-btn lg caret" type="button" aria-label="Large menu"></button>
</div>

A wu-btn on an <a> looks the same as a button. Use it for navigation that should read as a button.

<a class="wu-btn" href="/components/docs/">Link button</a>

Hover darkens the default button’s border and changes its background; the brand and danger buttons also have an active color. Keyboard focus shows the focus ring from the border-focus tokens.

The disabled style applies to the disabled attribute and to aria-disabled="true", which covers links. It lowers the opacity and turns off pointer events.

Link disabled
<div class="flex wrap x-center gap-2">
<button class="wu-btn" type="button" disabled>Disabled</button>
<button class="wu-btn brand" type="button" disabled>Brand disabled</button>
<a class="wu-btn" href="/components/docs/" aria-disabled="true">Link disabled</a>
</div>

Set aria-busy="true" while an action runs and put a spinner in the label. The button keeps full opacity, so it does not look disabled, and pointer events are off, so a second click does not go through. Keyboard activation still reaches the button: ignore it in your handler while the attribute is set, and remove the attribute when the action ends.

<div class="flex wrap x-center gap-2">
<button class="wu-btn brand" type="button" aria-busy="true"><span class="wu-spinner sm" aria-hidden="true"></span>Saving</button>
<button class="wu-btn danger" type="button" aria-busy="true"><span class="wu-spinner sm" aria-hidden="true"></span>Deleting</button>
</div>

Buttons are calm by default. The animated word adds a spring scale on press to one button.

Animated per button
<div class="flex wrap x-center gap-2">
<button class="wu-btn animated" type="button">Press me</button>
<button class="wu-btn brand animated" type="button">Press me</button>
</div>

wu-animated on an ancestor turns the same motion on for every button inside it, without the word on each button.

Animated section
<section class="wu-animated">
<div class="flex wrap x-center gap-2">
<button class="wu-btn sm" type="button">Small</button>
<button class="wu-btn brand" type="button">Brand</button>
<button class="wu-btn lg" type="button">Large</button>
</div>
</section>

The press animation runs only under prefers-reduced-motion: no-preference. With reduced motion set in the operating system, the buttons keep the color transitions and do not scale.

Buttons follow the document direction. The caret and the split-button divider mirror under dir="rtl".

<div dir="rtl" class="flex wrap x-center gap-2">
<button class="wu-btn" type="button">افتراضي</button>
<button class="wu-btn round caret" type="button">إجراءات</button>
<button class="wu-btn quiet" type="button">إلغاء</button>
<button class="wu-btn link" type="button">رابط</button>
<div class="wu-btn-group">
<button class="wu-btn brand" type="button">حفظ</button>
<button class="wu-btn brand caret" type="button" aria-label="المزيد"></button>
</div>
</div>
@use "@wovenui/components/button";

Use the per-component entry or the bundle, not both. See Two ways to load components.