Skip to content

Button

<button class="btn">Save changes</button>
<button class="btn btn-outline">Cancel</button>

The classes style any element, but reach for a real <button> or <a> so keyboard activation, focus order and disabled come from the platform.

Tailwind v4 switched buttons from cursor: pointer to cursor: default, and partialkit follows it. Add this to your own stylesheet to get the pointer back:

@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}
ClassHeight
.btn-xs24px
.btn-sm28px
.btn32px
.btn-lg36px
.btn-icon-xs .btn-icon-sm .btn-icon .btn-icon-lgSquare, matching the text size of the same name

Composing the sizes (.btn-icon.btn-sm) reaches the same place, for markup already written that way.

The base class carries the default variant, applied when no other one is.

No surface until it is hovered. The hover uses --muted, which is a 3% grey — deliberately quiet.

A wash of the danger colour rather than a solid fill, so the weight comes from the colour.

Underlines on hover and never takes a surface.

An icon-only button needs an aria-label or .sr-only text — there is no visible text to name it.

Mark the icon with data-icon="inline-start" or data-icon="inline-end" so the padding tightens on that side.

<button class="btn" style="border-radius: 9999px">Rounded</button>

With Tailwind in your own build, rounded-full says the same thing.

Put a spinner inside the button for a loading state, and disable the button while it runs. Give the spinner a data-icon attribute too, so the spacing matches an icon.

Sit several controls flush with the button group.

An anchor takes the classes and keeps its own semantics — it navigates, it is in the tab order, and a screen reader announces it as a link. Do not put role="button" on it to make it look right; that is exactly the semantic the anchor should keep.

Set dir="rtl" on the page or on any subtree. partialkit is written with logical properties, so padding, corners, absolute positions and menu placement all follow the reading direction — nothing to configure.

The pieces that are drawn rather than laid out — the select’s chevron and a submenu’s arrow — are mirrored explicitly, and the menu positioner flips right-start to left-start on its own.

StateHow
Disabledthe native disabled attribute — dims it and stops pointer events
Invalidaria-invalid="true" — recolours the border and ring
Pressedautomatic; the button shifts down 1px, except when it opens a menu
Focus ringautomatic, on :focus-visible — a pointer press does not ring
Expandedaria-expanded="true" — outline, secondary and ghost keep their hover surface