ui-part-buttons
Buttons: hierarchy and variants, sizes and spacing, labels, icons and icon-only buttons, every state (hover, focus, pressed, toggled, loading, disabled), button or link, groups and split buttons, destructive actions, placement and phones. Read before building or restyling any button.
- License 许可
- Apache-2.0
- 所属仓库
- enowdev/enowxcli
- 最近更新
- 2026-10-06
- Stars
- ★ 67
- Forks
- 21
01 前言 Front Matter
- name
- ui-part-buttons
- description
- Buttons: hierarchy and variants, sizes and spacing, labels, icons and icon-only buttons, every state (hover, focus, pressed, toggled, loading, disabled), button or link, groups and split buttons, destructive actions, placement and phones. Read before building or restyling any button.
03 SKILL.md 正文
Buttons
The generated version: every button a gradient pill with a glow and an
arrow, two filled buttons side by side, "Submit" and "Get started", a
spinner that makes the button shrink, a greyed-out button with no reason,
and a div with a click handler. A button is a promise of one action;
this is how to rank it, word it, size it and give it every state. One part
of an interface: the principles (direction, spacing, type, colour, icons,
states, accessibility) are in the ui skill, the measures in ui-layout.
1. Hierarchy
- One primary button per view (a screen, a dialog, a card with its own task): the action most people take next. Secondary and text buttons sit below it in weight. Never two primaries side by side.
- Primary: filled with the accent,
--on-accenttext at 4.5:1. - Secondary: the surface with a 1px border that reaches 3:1, or a tinted surface, for the second most likely action.
- Tertiary (ghost): the text colour, no border, a surface on hover; for toolbars, table rows and dismissive actions. Cancel is ghost or secondary, never filled.
- Text button: looks like a link, is a
button, for inline actions ("Undo", "Edit"). - Destructive: looks it, in the danger colour. The entry point is a quiet button or a menu item in danger text; the filled danger button appears in the confirming step.
previewcounts filled buttons: five or more in one saturated colour on a screen means rows carry primaries; make them quiet (ui-page-dashboard).
2. Sizes and anatomy
| Size | Height | Side padding | Text | Icon | Where |
|---|---|---|---|---|---|
| Compact | 32px | 12px | 13 to 14px | 16px | table rows, toolbars, dense panels |
| Default | 36 to 40px | 16px | 14 to 15px | 16 to 20px | application screens, forms, dialogs |
| Large | 44 to 48px | 20 to 24px | 16px | 20px | pages, the hero, phones |
- At least 44px tall to the touch: compact and default buttons grow to
44px on phones (
previewlists anything smaller at 360px). - Padding from the spacing scale; 8px between icon and label; 8px between neighbouring buttons, 12px on touch.
- Weight 500 to 600, sentence case, one line (
white-space: nowrap), a radius from the token set (4 to 8px). Fully round only when the design language is round everywhere, not as the default look. - A minimum width (about 80px) so "Save" is not a stamp beside "Save and send".
3. Labels and icons
- The label is a verb and an object that says the outcome: "Create
invoice", "Send reminder", "Book a first appointment". Not "Submit",
"OK", "Yes", "Click here", "Get started" or "Learn more" (
ui_checkflags them), and never one word on two buttons that do different things. - A confirming button repeats the verb of its question: "Delete project?"
is answered by "Delete project", not "Yes" (
writing). - A trailing "…" when the action asks for more before it happens ("Rename…", "Export…"), the desktop convention; not on actions that complete at once.
- An icon beside the label when the product's set has a clear one for the
action (plus, download, trash): leading, 16 to 20px,
currentColor,aria-hidden="true". - A trailing chevron only on a button that opens a menu; a trailing arrow only on "Next" or a link that goes somewhere. Not an arrow on every button.
- Icon-only buttons in dense toolbars and rows: 36 to 40px square (44px on
touch), an
aria-labelthat names the action ("Delete invoice"), and a tooltip with the same name on hover and focus (ui-part-tooltips).
4. States
| State | Look | Behaviour |
|---|---|---|
| Hover | a background step of 4 to 8%, over 160ms | only under (hover: hover) |
| Focus-visible | a 2px ring in --focus, 2px offset, no transition |
reached by Tab in visual order |
| Pressed | scale(0.97) for 100ms, or a darker step |
none under reduced motion |
| Toggled | a filled surface and weight, aria-pressed="true" |
the label stays the same |
| Loading | a spinner over the label, the width kept | aria-busy="true", clicks ignored |
| Disabled | legible but clearly off, no hover | the reason stated nearby |
- Loading:
aria-busyset after about 300ms of waiting, so quick work shows no spinner; the label stays in place under it (transparent), so the width and the accessible name hold; clicks are ignored from the first one, so the form is sent once; "Saving", then "Saved" or the error, announced through arole="status"region. Keep the button focusable: making the pressed buttondisableddrops the keyboard focus, and a screen reader loses its place. - Disabled: say why near it ("Add a payment method to publish"), or leave
it enabled and explain on click. A submit button stays enabled and the
form shows what is missing (
frontend-forms). Where it must look disabled,aria-disabled="true"keeps it focusable and readable, with the click stopped in script. - After the action the result shows: the new row, or a toast ("Invoice
sent", with Undo,
ui-part-notifications). A button that reads "Saved" goes back to "Save" after 2 to 3 seconds or when the form changes.
5. Button or link
a hrefnavigates: to a page, a section, a file, another site. It opens in a new tab with a middle click, shows its URL, works before the script loads.- A
buttonelement acts: saves, sends, opens a dialog or a menu, toggles.type="button"unless it submits: inside aformthe default is submit, which sends the form by accident. - The look follows the rank, the element follows the behaviour: "Book a
session" leading to the booking page is a link that looks like a primary
button. Never a
divorspanwith a click handler.
6. Groups, split buttons, placement
- In footers and dialogs the primary sits at the end of the reading
direction (the right on desktop, in left-to-right languages), Cancel
beside it; the project's own convention wins (
ui-layout4). - Past two secondary actions, the rest go into a "More" menu button
(
aria-haspopup="menu",aria-expanded,ui-part-menus). - A split button: the main action, and beside it a chevron button with
aria-label="More save options"opening a menu ("Save as draft", "Save and send"). Both halves are real buttons. - Segmented controls pick a view or a filter: they are choices, not
buttons (
ui-part-choices). - Destructive actions stand apart from Save (a "Danger zone" at the end, or
a menu). They confirm, in a dialog naming the object
(
ui-part-dialogs), when the loss is large and permanent; they offer Undo instead when the action is frequent and reversible.
7. Phones
- Full width only on phone forms and sheets: stacked, the primary on top, Cancel below it. Elsewhere buttons keep their width.
- The screen's primary action stays in reach: in the top bar with its
word, at the end of the content, or in a bar fixed to the bottom above
the safe area (
ui-layout9). - Labels may shorten ("Add" for "Add product") but never become an unnamed icon. Rows of buttons wrap or fold into a menu; never off the edge.
8. Themes and motion
- The accent takes a lighter step on dark to keep 3:1 against the page and
4.5:1 for its label; the focus ring shows in both themes (
ui-themes). - Hover, press and focus as in
motion-interface: colour over 160ms, press at 100ms, no bounce, no shine sweeping across, no pulse.
9. A sketch
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
min-block-size: 40px; min-inline-size: 80px; padding-inline: 16px;
font: inherit; font-size: 14px; font-weight: 500; white-space: nowrap;
border: 1px solid transparent; border-radius: var(--radius-sm); cursor: pointer;
transition: background-color var(--m-fast) var(--m-ease-out),
border-color var(--m-fast) var(--m-ease-out), transform var(--m-instant) var(--m-ease-out);
}
.btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); }
.btn--ghost { background: transparent; color: var(--text); }
.btn--danger { background: var(--danger); color: var(--on-danger); }
@media (hover: hover) {
.btn--primary:hover { background: var(--accent-hover); }
.btn--secondary:hover, .btn--ghost:hover { background: var(--surface-2); }
}
.btn:not([aria-disabled="true"], [aria-busy="true"]):active { transform: scale(0.97); }
.btn[aria-disabled="true"], .btn:disabled {
background: var(--surface-2); color: var(--text-muted);
border-color: var(--border); cursor: not-allowed;
}
.btn[aria-busy="true"] { position: relative; }
.btn[aria-busy="true"] > * { color: transparent; } /* keeps the width and the name */
.btn[aria-busy="true"]::after {
content: ""; position: absolute; inset: 0; margin: auto;
inline-size: 16px; block-size: 16px; border-radius: 50%;
border: 2px solid currentColor; border-inline-end-color: transparent;
animation: spin 0.9s linear infinite;
}
@keyframes spin { to { rotate: 1turn; } }
@media (max-width: 640px) { .btn { min-block-size: 44px; } }
@media (prefers-reduced-motion: reduce) { .btn:active { transform: none; } }
<button class="btn btn--primary" type="submit" aria-busy="true">
<span>Save changes</span>
</button>
<p class="visually-hidden" role="status">Saving changes</p>
With shadcn, the same variants live in the Button's cva definition
(variant, size), used by name (ui-stack-shadcn).
Check it
previewat 360px: no button in "touch targets under 44px" or "controls without a name"; no report of five or more filled buttons in one colour; labels at 4.5:1 in both themes.ui_check: generic actions, glow, default gradients, pills everywhere, empty click handlers andhref="#".- Keyboard only: Tab reaches every button in visual order with a visible ring; Enter and Space act; after a submit, focus has not jumped away.
- Double-click a submit on a slow network: one request is sent.
- Search for
divandspanwith click handlers, andaelements that act without navigating.
Avoid
An arrow on every button; pill plus gradient plus glow as the default
look; "Submit", "Click here", "Get started"; two primaries side by side; a
primary on every row; a greyed-out button with no reason; a spinner that
changes the button's width; disabled on the button that was just
pressed; a div that acts as a button; a link that acts, or a button that
navigates; a destructive action next to Save; full-width buttons on wide
screens.
04 同仓库更多 Skills
demo-skill
Project-scope demo skill for tests.
writing
Writing words people read: the specific thing instead of claims, words to drop, sentences, interface copy (buttons, labels, errors, empty states, confirmations), page copy, notifications and emails, alt text and link text, numbers and dates, words that translate well, documentation and voice. Read before writing a page's copy, an interface's text or a document.
ui
Designing or restyling an interface: direction, layout, type, colour, icons, components, states, responsive behaviour. Read before building a page, a screen or a set of components.
ui-themes
Light and dark themes, and colour that holds in both: tokens with a value per theme, and what each kind of content needs to stay visible (icons, SVG drawings and line art, logos, photos, screenshots, charts, code, maps, borders, shadows, focus, form controls), switching without a flash, and checking both. Read before building a second theme or a theme toggle, and before any drawing, logo or chart on a page that has two themes.
ui-structure
Structure before styling: the blueprint written before any interface code (the screen's job, the regions its kind of page needs, their order and why, the layout skeleton in numbers, the components and their states), the anatomy and order of a homepage, a dashboard, a list, a detail page, settings, a form, sign-in, pricing, docs, an article and a checkout, and how to turn the blueprint into code (tokens, layout primitives, components, sections, page). Read first, before building or rebuilding any page or screen.
ui-stack-vue
Building interfaces in Vue 3 and Nuxt: project layout, script setup with typed defineProps, defineEmits and defineModel, attribute fallthrough, slots and scoped slots, template refs, ref, reactive and computed, watch versus watchEffect, composables and VueUse, provide and inject, Pinia, keyed v-for, transitions, Nuxt 4 (app directory, pages, layouts, useFetch and useAsyncData keys, $fetch in handlers, server routes, runtime config, useSeoMeta, NuxtImg, middleware), VeeValidate forms, Reka UI and Nuxt UI, testing, vue-tsc, and the traps. Read when the project uses Vue or Nuxt.