button-states — скилл для Claude Code: состояния кнопок
Скилл описывает шесть состояний интерактивных элементов и выводит цвета hover и active из базового цвета по HSL. Пригодится, когда кнопки в проекте выглядят незаконченными.
npx skills add dembrandt/dembrandt-skills --skill button-states -a claude-codeЧто делает button-states
Скилл задаёт шесть состояний: rest, hover, active (pressed), focus, disabled и loading. Для каждого указан триггер и визуальный сигнал — от курсора pointer в покое до спиннера при асинхронной операции. Цвета hover и active не подбираются на глаз: они считаются из базового цвета сдвигом светлоты в HSL, hover темнее на 8%, active на 14%. Для светлых кнопок на тёмном фоне логика переворачивается — светлеть при наведении.
Внутри есть готовые блоки CSS: основной и вторичный вариант кнопки, focus через outline с отступом 2–4px, disabled с opacity: 0.4 и pointer-events: none, loading с сохранением ширины кнопки, чтобы не дёргался layout. Отдельно разобран масштаб при нажатии: держите scale в диапазоне 0.95–0.98, ниже кнопка выглядит сломанной. Скилл подхватывается по путям вроде components/**, **/*.css, **/*.tsx и по фразам вроде «hover state» или «loading state».
Фокусное кольцо обязательно для навигации с клавиатуры, и на hover тут полагаться нельзя — клавиатурный пользователь его не вызывает. Поэтому правило outline: none без замены скилл считает ошибкой. Курсор во время загрузки не меняется: прогресс показывает сам элемент, а не место, где стоит мышь. В конце идёт чек-лист из восьми пунктов для ревью компонента.
Какие задачи решает
- дорисовать hover и active для кнопки из базового цвета
- сделать видимое фокусное кольцо для навигации с клавиатуры
- показать загрузку на кнопке и заблокировать повторную отправку
- привести disabled к единому виду во всех компонентах
- проверить состояния интерактивных элементов перед ревью
Примеры запросов агенту
- какого цвета должен быть hover у этой кнопки
- сделай все состояния для этой кнопки
- как показать состояние загрузки на кнопке
Как установить button-states
1Через Skills CLI (Vercel)
npx skills add dembrandt/dembrandt-skills --skill button-states -a claude-code2Вручную: положить файлы
~/.claude/skills/button-states/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/button-states/
1Через Skills CLI (Vercel)
npx skills add dembrandt/dembrandt-skills --skill button-states -a codex2Вручную: положить файлы
.agents/skills/button-states/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add dembrandt/dembrandt-skills --skill button-states -a cursor2Вручную: положить файлы
.cursor/skills/button-states/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install dembrandt/dembrandt-skills button-states2Вручную: положить файлы
.github/skills/button-states/SKILL.md1Через Skills CLI (Vercel)
npx skills add dembrandt/dembrandt-skills --skill button-states -a gemini-cli2Командой платформы
gemini skills install https://github.com/dembrandt/dembrandt-skills --path skills/button-statesВручную: положить файлы
.devin/skills/button-states/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add dembrandt/dembrandt-skills --skill button-states -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Из того же набора34
Похожие
Рядом в теме «Фронтенд и UI»
Всё по теме «Фронтенд и UI» →Вопросы о button-states
Как установить button-states в Claude?
Выполните npx skills add dembrandt/dembrandt-skills --skill button-states -a claude-code. Файл скилла окажется в ~/.claude/skills/button-states/SKILL.md.
Что делает button-states?
Описывает шесть состояний кнопок и ссылок и показывает, как вывести цвета hover и active из базового цвета через HSL. Плюс даёт готовый CSS для focus, disabled и loading.
На чём скилл ломается?
На outline: none без замены: фокусное кольцо пропадёт, и навигация с клавиатуры станет невидимой. Ещё он не советует менять курсор во время загрузки — прогресс показывает сам элемент.
Исходный текст
SKILL.md · MIT
---
name: button-states
description: Rest, hover, active, focus, disabled and loading states, derived from the base colour. Use when designing buttons, links, inputs or any clickable component.
metadata:
priority: 8
pathPatterns:
- "components/**"
- "src/components/**"
- "**/*.css"
- "**/*.scss"
- "**/tokens/**"
- "**/*.tsx"
- "**/*.jsx"
- "design-system/**"
promptSignals:
phrases:
- "hover state"
- "active state"
- "focus state"
- "disabled"
- "button states"
- "interactive states"
- "loading state"
- "pressed"
retrieval:
aliases:
- button states
- hover state
- interactive states
- focus ring
- disabled state
- loading button
intents:
- design button states
- add hover and active states
- style focus ring
- handle disabled state
- add loading state to button
examples:
- what colour should the hover state be
- design all states for this button
- how do I show a loading state on a button
- make the focus ring visible
---
# Button and Interactive Element States
Every interactive component must have a complete, visually distinct state for each interaction mode. Missing or ambiguous states make the UI feel unfinished and reduce user confidence.
## The Six States
| State | Trigger | Visual signal |
|---|---|---|
| **Rest** | Default | Base colour, cursor: pointer |
| **Hover** | Mouse over | Slightly darker, subtle background shift |
| **Active / Pressed** | Mouse down / tap | Noticeably darker, slight scale-down |
| **Focus** | Keyboard navigation | Visible focus ring, no change to fill |
| **Disabled** | Not available | Low contrast, cursor: not-allowed, no interaction |
| **Loading** | Async action in progress | Spinner or pulse, non-interactive |
## Deriving State Colours Algorithmically
State colours are not chosen independently — they are derived from the base colour by adjusting lightness in HSL. This guarantees coherence across the entire palette.
```
base: hsl(H, S%, L%)
hover: hsl(H, S%, L% - 8%) ← darken 8%
active: hsl(H, S%, L% - 14%) ← darken 14%
```
### Example: primary button `#635BFF` (hsl 243, 100%, 68%)
```css
.btn-primary {
background: hsl(243, 100%, 68%); /* rest #635BFF */
}
.btn-primary:hover {
background: hsl(243, 100%, 60%); /* hover #4A40FF */
}
.btn-primary:active {
background: hsl(243, 100%, 54%); /* active #3429FF */
}
```
For light buttons on dark backgrounds, invert the logic — lighten on hover instead of darkening.
### Secondary / outlined buttons
```css
.btn-secondary {
background: transparent;
border: 1px solid var(--color-border);
color: var(--color-text);
}
.btn-secondary:hover {
background: var(--color-grey-100); /* subtle fill */
border-color: var(--color-grey-300);
}
.btn-secondary:active {
background: var(--color-grey-200);
}
```
## Focus State
Focus is a keyboard navigation requirement (WCAG 2.2). It must be visible and must not rely on the hover style alone — keyboard users do not trigger hover.
```css
.btn:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 3px;
}
```
- Use `outline`, not `box-shadow`, for focus rings — `outline` respects `border-radius` in modern browsers and does not affect layout
- `outline-offset: 2–4px` gives the ring breathing room from the component edge
- Never use `outline: none` without a replacement focus style
## Disabled State
```css
.btn:disabled,
.btn[aria-disabled="true"] {
opacity: 0.4;
cursor: not-allowed;
pointer-events: none;
}
```
- Disabled elements are exempt from WCAG contrast requirements — low opacity is correct and intentional
- Use `pointer-events: none` to prevent click events even if JS is bypassed
- Do not change the shape or size of a disabled button — only colour and cursor change
## Loading State
When a button triggers an async action, replace the label with a spinner and prevent re-submission.
```css
.btn--loading {
pointer-events: none;
opacity: 0.7;
}
```
**The cursor does not change while loading.** It signals affordance, not progress: the arrow for ordinary content, `pointer` for something you can act on. Progress belongs to the element and the loading region, which the user is already looking at, rather than to wherever the mouse happens to rest. `cursor: wait` is also inert next to `pointer-events: none`, so the rule reads as deliberate while doing nothing.
- Keep the button width stable during loading — avoid layout shift when label is replaced by spinner
- Return to rest state on completion (success or error)
- For long-running operations, pair with a status message — a spinner alone does not tell the user what is happening
## Scale on Active (Optional)
A subtle scale-down on press adds physical feedback — borrowed from Disney's squash principle.
```css
.btn:active {
transform: scale(0.97);
transition: transform 80ms ease-out;
}
```
Keep the scale value between `0.95–0.98`. Below `0.95` feels like the button is breaking.
## Complete Button CSS Reference
```css
.btn {
cursor: pointer;
background: var(--color-primary);
color: white;
border-radius: var(--radius-button);
padding: var(--component-padding-y-md) var(--component-padding-x-md);
height: var(--component-height-md);
border: none;
transition: background 120ms ease-out, transform 80ms ease-out;
}
.btn:hover { background: var(--color-primary-hover); }
.btn:active { background: var(--color-primary-active); transform: scale(0.97); }
.btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
.btn.btn--loading { opacity: 0.7; pointer-events: none; }
```
## Review Checklist
- [ ] Does every interactive element have all six states defined?
- [ ] Are hover and active colours derived from the base by lightness adjustment (not chosen arbitrarily)?
- [ ] Is focus state visible and using `outline` (not removed)?
- [ ] Is disabled state low-opacity with `cursor: not-allowed`?
- [ ] Does loading state prevent re-submission?
- [ ] Are transition durations 80–150ms — not instant, not slow?
- [ ] Does `cursor: pointer` appear on all interactive elements at rest?
- [ ] Does the cursor stay unchanged while an action runs, with progress shown by the element itself?
© dembrandt · MIT · dembrandt/dembrandt-skills