CSS 버튼 세트

다양한 variant, size, 상태를 갖춘 완성형 CSS 버튼 컴포넌트 시스템.

Gist
:root {
  --btn-radius: 6px;
  --btn-font-size: 0.875rem;
  --btn-transition: all 0.15s ease;
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-primary-active: #1e40af;
  --color-success: #16a34a;
  --color-danger: #dc2626;
  --color-warning: #d97706;
  --color-surface: #f3f4f6;
  --color-border: #d1d5db;
  --color-text: #374151;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  font-size: var(--btn-font-size);
  font-weight: 500;
  font-family: inherit;
  line-height: 1;
  border-radius: var(--btn-radius);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  user-select: none;
  transition: var(--btn-transition);
  -webkit-tap-highlight-color: transparent;
}
.btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.btn:active { transform: scale(0.97); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

/* Variants */
.btn-primary   { background: var(--color-primary);  color: #fff; border-color: var(--color-primary); }
.btn-primary:hover  { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
.btn-primary:active { background: var(--color-primary-active); }

.btn-success   { background: var(--color-success); color: #fff; border-color: var(--color-success); }
.btn-success:hover  { filter: brightness(1.1); }

.btn-danger    { background: var(--color-danger);  color: #fff; border-color: var(--color-danger); }
.btn-danger:hover   { filter: brightness(1.1); }

.btn-warning   { background: var(--color-warning); color: #fff; border-color: var(--color-warning); }
.btn-warning:hover  { filter: brightness(1.1); }

.btn-outline   { background: transparent; color: var(--color-primary); border-color: var(--color-primary); }
.btn-outline:hover  { background: var(--color-primary); color: #fff; }

.btn-ghost     { background: transparent; color: var(--color-text); border-color: transparent; }
.btn-ghost:hover    { background: var(--color-surface); border-color: var(--color-border); }

.btn-surface   { background: var(--color-surface); color: var(--color-text); border-color: var(--color-border); }
.btn-surface:hover  { background: #e5e7eb; }

/* Sizes */
.btn-xs { padding: 0.25rem 0.625rem; font-size: 0.75rem;  border-radius: 4px; }
.btn-sm { padding: 0.375rem 0.75rem; font-size: 0.8125rem; }
.btn-md { padding: 0.5rem 1rem;      font-size: 0.875rem;  }
.btn-lg { padding: 0.625rem 1.25rem; font-size: 1rem;      border-radius: 8px; }
.btn-xl { padding: 0.75rem 1.5rem;   font-size: 1.0625rem; border-radius: 8px; }

/* Modifiers */
.btn-full  { width: 100%; }
.btn-round { border-radius: 999px; }
.btn-icon  { padding: 0.5rem; aspect-ratio: 1; }

/* Loading state */
.btn.loading { pointer-events: none; }
.btn.loading::before {
  content: '';
  display: inline-block;
  width: 0.875em; height: 0.875em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: btn-spin 0.6s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }