vignette/apps/web/src/components/ui/ui.css

369 lines
8.7 KiB
CSS

/* =====================================================================
UI 프리미티브 스타일 — 토큰 변수만 참조(tokens.css).
철칙: border-left 강조선 0 · 카드 상단 강조선 0 · 알약 radius 금지(원형은 dot/오브/아바타만).
강조 = weight + accent 텍스트 + accent-tint 배경 + kicker(+dot).
===================================================================== */
/* ── Kicker (상단 소제목, 양산형 좌측바 대체 위계 신호) §3.6 ── */
.vg-kicker {
font-family: var(--font-num);
font-size: var(--fs-kicker);
font-weight: 600;
letter-spacing: 0.08em;
color: var(--text-accent);
display: inline-flex;
align-items: center;
gap: 8px;
text-transform: none;
}
.vg-kicker__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent-bright);
flex: none;
}
/* ── SectionHead ── */
.vg-sechead {
margin-bottom: var(--sp-5);
}
.vg-sechead__kicker {
margin-bottom: 6px;
}
.vg-sechead__title {
font-size: var(--fs-h2);
font-weight: 600;
letter-spacing: 0;
color: var(--text-strong);
line-height: 1.35;
}
.vg-sechead__desc {
font-size: var(--fs-xs);
color: var(--text-muted);
margin-top: 4px;
}
/* ── Button (위계 3단 + danger). radius 8px, 알약 금지 §7.1 ── */
.vg-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font: 600 var(--fs-sm) / 1 var(--font-sans);
padding: 10px 18px;
border-radius: var(--radius);
border: 1px solid transparent;
transition:
background var(--dur-base) var(--ease-out),
border-color var(--dur-base) var(--ease-out),
transform var(--dur-fast) var(--ease-spring),
box-shadow var(--dur-fast) var(--ease-out);
cursor: pointer;
white-space: nowrap;
user-select: none;
}
.vg-btn:active {
transform: translateY(1px) scale(0.98);
}
.vg-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.vg-btn--block {
width: 100%;
}
.vg-btn--sm {
padding: 7px 13px;
font-size: var(--fs-xs);
}
.vg-btn--lg {
padding: 13px 22px;
font-size: var(--fs-body);
}
.vg-btn--primary {
background: linear-gradient(105deg, color-mix(in srgb, var(--accent) 90%, var(--bg-surface)), var(--accent));
color: var(--text-on-accent);
border-color: color-mix(in srgb, var(--botanical-gold) 24%, var(--accent));
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg-surface) 32%, transparent);
}
.vg-btn--primary:hover:not(:disabled) {
background: var(--accent-deep);
}
.vg-btn--secondary {
background: var(--surface-gradient);
color: var(--text-accent);
border-color: color-mix(in srgb, var(--accent) 62%, var(--botanical-hair));
}
.vg-btn--secondary:hover:not(:disabled) {
background: var(--accent-tint);
}
.vg-btn--ghost {
background: transparent;
color: var(--text-body);
}
.vg-btn--ghost:hover:not(:disabled) {
background: var(--bg-surface-2);
color: var(--text-strong);
}
.vg-btn--danger {
background: var(--crit-solid);
color: var(--text-on-accent);
}
.vg-btn--danger:hover:not(:disabled) {
background: var(--crit-text);
}
/* ── Surface SSOT ── */
.vg-surface.vg-surface--panel {
background: var(--glass-surface);
border: 1px solid var(--glass-border);
border-radius: var(--radius-lg);
box-shadow: var(--glass-shadow);
}
.vg-surface.vg-surface--inset {
background: var(--bg-surface-2);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
box-shadow: var(--glass-inset-shadow);
}
.vg-surface.vg-surface--interactive {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius);
box-shadow: var(--shadow-sm);
transition:
background var(--dur-base) var(--ease-out),
border-color var(--dur-base) var(--ease-out),
box-shadow var(--dur-fast) var(--ease-out),
transform var(--dur-fast) var(--ease-out);
}
.vg-surface.vg-surface--interactive:hover {
border-color: color-mix(in srgb, var(--accent) 48%, var(--glass-border));
background: var(--glass-surface-strong);
box-shadow: var(--shadow-island);
transform: translateY(-1px);
}
/* 포커스 가시성 — outline 채널로 링을 그린다(WCAG 2.4.7). */
.vg-surface.vg-surface--interactive:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 2px;
}
.vg-surface.vg-surface--flat {
box-shadow: none;
}
.vg-surface.vg-surface--tint {
background: var(--bg-tint);
border-color: transparent;
box-shadow: none;
}
/* ── Tabs behavior primitive. 시각 표면은 만들지 않는다. ── */
.vg-tabs,
.vg-tabs__panel {
min-width: 0;
}
.vg-tabs__trigger:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
}
/* ── Card / Panel 크기 계약. 표면은 위 Surface만 소유한다. ── */
.vg-card {
padding: var(--sp-5);
}
.vg-panel {
border-radius: var(--radius-lg);
padding: var(--sp-6);
}
/* ── Badge (배경 틴트 + 텍스트, 좌측바 없음) ── */
.vg-badge {
display: inline-flex;
align-items: center;
gap: 6px;
font-family: var(--font-num);
font-size: var(--fs-xs);
font-weight: 600;
letter-spacing: 0.01em;
padding: 2px 9px;
border-radius: 100px; /* 칩/배지만 100px 관용 (DESIGN_CONCEPT 데모 일치) */
line-height: 1.5;
}
.vg-badge--neutral {
color: var(--ink-2);
background: var(--paper-2);
}
.vg-badge--accent {
color: var(--accent-deep);
background: var(--accent-tint);
}
.vg-badge--pos {
color: var(--pos-text);
background: var(--pos-tint);
}
.vg-badge--warn {
color: var(--warn-text);
background: var(--warn-tint);
}
.vg-badge--crit {
color: var(--crit-text);
background: var(--crit-tint);
}
.vg-badge--info {
color: var(--info-text);
background: var(--info-tint);
}
/* ── Dot (상태 점). 원형 예외 허용 대상 ── */
.vg-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
flex: none;
background: var(--neutral-200);
}
.vg-dot--accent {
background: var(--accent);
}
.vg-dot--pos {
background: var(--pos-solid);
}
.vg-dot--warn {
background: var(--warn-solid);
}
.vg-dot--crit {
background: var(--crit-solid);
}
.vg-dot--info {
background: var(--info-solid);
}
.vg-dot--muted {
background: var(--ink-3);
}
/* ── StatLine (요약 스탯, tabular) ── */
.vg-statline {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--sp-5);
}
.vg-statline__item {
display: inline-flex;
align-items: baseline;
gap: 7px;
}
.vg-statline__num {
font-size: 20px;
font-weight: 700;
color: var(--text-strong);
font-variant-numeric: tabular-nums;
letter-spacing: 0;
}
.vg-statline__lab {
font-size: var(--fs-xs);
color: var(--text-muted);
}
.vg-statline__sep {
width: 1px;
height: 18px;
background: var(--hair);
}
/* ── ProgressBar (가로 게이지, 라운드 없는 사각 트랙) §5.5/§6.4 ── */
.vg-progress {
width: 100%;
height: 8px;
background: var(--paper-2);
border-radius: 4px;
overflow: hidden;
}
.vg-progress--slim {
height: 4px;
}
.vg-progress__fill {
height: 100%;
border-radius: 4px;
background: var(--accent);
transition: width var(--dur-slow) var(--ease-out);
}
.vg-progress__fill--muted {
background: var(--neutral-200);
}
.vg-progress__fill--warn {
background: var(--warn-solid);
}
.vg-progress__fill--crit {
background: var(--crit-solid);
}
.vg-progress__fill--clay {
background: var(--clay);
}
/* ── Field / Input §7.2 (radius 6px, 포커스 보더색+ring, 좌측바 X) ── */
.vg-field {
display: flex;
flex-direction: column;
gap: 6px;
}
.vg-field__label {
font-size: var(--fs-sm);
font-weight: 600;
color: var(--text-strong);
}
.vg-field__hint {
font-size: var(--fs-xs);
color: var(--text-muted);
}
.vg-field__error {
font-size: var(--fs-xs);
color: var(--crit-text);
}
.vg-input {
width: 100%;
background: var(--bg-surface-2);
color: var(--text-strong);
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
padding: 10px 12px;
font: var(--fs-body) / 1.5 var(--font-sans);
transition:
border-color var(--dur-fast) var(--ease-out),
box-shadow var(--dur-fast) var(--ease-out);
}
.vg-input::placeholder {
color: var(--neutral-400);
}
.vg-input:focus {
border-color: var(--border-focus);
outline: none;
box-shadow: 0 0 0 3px var(--focus-ring);
}
.vg-input[aria-invalid="true"] {
border-color: var(--crit-solid);
}
/* ── 빈/준비중 상태 (스텁 페이지 공통) ── */
.vg-empty {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--sp-3);
max-width: var(--maxw-read);
}
.vg-empty__title {
font-size: var(--fs-h1);
font-weight: 700;
letter-spacing: 0;
color: var(--text-strong);
line-height: 1.3;
}
.vg-empty__desc {
font-size: var(--fs-lead);
color: var(--text-body);
line-height: 1.6;
}