button-states
Every interactive element needs a complete set of visual states — rest, hover, active/pressed, focus, disabled, and loading. States should be derived algorithmically from the base colour, not chosen arbitrarily. Use when designing buttons, links, inputs, or any clickable component.
pinned to #08cf3b0updated 2 weeks ago
Ask your AI client: “install skills/button-states”.
Requires the metahub MCP server installed in your client. Set up MCP.
mh install skills/button-statesmetahub onboarded this repo on the author's behalf.
If you own github.com/dembrandt/dembrandt-skills on GitHub, claim the listing to take over publishing. Your claim preserves the existing eval history and badges; only the curator label is replaced with verified-publisher on your next publish.
Stars
25
Last commit
2 weeks ago
Latest release
published
- #accessibility
- #agent-skills
- #ai
- #ai-agents
- #anthropic
- #claude-code-skills
- #claude-skills
- #cursor-skills
- #design-system
- #design-tokens
- #enterprise-ux
- #gestalt
- #interaction-design
- #mcp
- #skills-sh
- #typography
- #ui-design
- #ux
- #wcag
About this skill
Pulled from SKILL.md at publish time.
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.
Evaluation report
WarningsAutomated checks the publisher passed at publish time — structure, docs, safety, and whether the artifact behaves as claimed.08cf3b0· 2 weeks ago
Kind-specific
31Skill: SKILL.md present
found at skills/button-states/SKILL.md · frontmatter source: SKILL.md
Skill: body content present
700 words · 4,864 chars · 10 sections · 8 code blocks
Skill: triggers declaredwarn
No `trigger` phrases in SKILL.md frontmatter
Add `trigger:` lines so Claude knows when to activate this skill — e.g. `when building MCP servers` or `for diagram creation`.
Skill: allowed-tools scope
no allowed-tools restriction (Claude may use anything)
Release history
1- releasecurrent08cf3b0warn2 weeks ago
Contents
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%)
.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
.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.
.btn:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 3px;
}
- Use
outline, notbox-shadow, for focus rings —outlinerespectsborder-radiusin modern browsers and does not affect layout outline-offset: 2–4pxgives the ring breathing room from the component edge- Never use
outline: nonewithout a replacement focus style
Disabled State
.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: noneto 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.
.btn--loading {
pointer-events: none;
cursor: wait;
opacity: 0.7;
}
- 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.
.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
.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; cursor: wait; 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: pointerappear on all interactive elements at rest?
Reviews
No reviews yet. Be the first.
Related
Gpt Researcher
An autonomous agent that conducts deep research on any data using any LLM providers
Planning With Files
Claude Code skill implementing Manus-style persistent markdown planning — the workflow pattern behind the $2B acquisition.
Browser Use
🌐 Make websites accessible for AI agents. Automate tasks online with ease.
mh install skills/button-states