Derive a full UI palette from one or two brand colours — state variants, brand-tinted greys, semantic colours. Use when building or expanding a colour system.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Rest, hover, active, focus, disabled and loading states, derived from the base colour. Use when designing buttons, links, inputs or any clickable component.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
실행 점수 미검증
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
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.
| 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 |
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%
#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.
.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 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;
}
outline, not box-shadow, for focus rings — outline respects border-radius in modern browsers and does not affect layoutoutline-offset: 2–4px gives the ring breathing room from the component edgeoutline: none without a replacement focus style.btn:disabled,
.btn[aria-disabled="true"] {
opacity: 0.4;
cursor: not-allowed;
pointer-events: none;
}
pointer-events: none to prevent click events even if JS is bypassedWhen a button triggers an async action, replace the label with a spinner and prevent re-submission.
.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.
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.
.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; }
outline (not removed)?cursor: not-allowed?cursor: pointer appear on all interactive elements at rest?아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
Derive a full UI palette from one or two brand colours — state variants, brand-tinted greys, semantic colours. Use when building or expanding a colour system.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Top bar, sidebar rail, app launcher, tenant and environment cue, status bar. Use when designing the persistent shell, a collapsible sidebar, or navigation between apps behind one login.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Product visuals must show the real product with real data. Use when building landing pages, hero sections, screenshots or demo panels.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Shape language across type, radius and icons carries brand personality. Use when starting a design system, picking icons or radius tokens, or reviewing visual consistency.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Rebuild a live web page 1:1 from measurement into Figma, Penpot or code, then verify it. Use when reproducing a page or checking a rebuild against the original.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Light, dark or both, and when a theme selector is worth it. Use when defining a base palette or deciding on dark mode support.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.