Wave 3 of the 2026-09-28 design overhaul. - Esc (no modifiers) cancels an in-progress dictation, recording or processing: VoiceModeService.requestEscape() queues the dead 'escape' action. Clicking the capsule cancels only while recording, so a stray click during processing no longer throws the text away. - Errors show in the user's language: bootstrap now passes error.code to the tip (the translated branch never ran), and one core classifier (voice-error-message) feeds both the tip and the main-window alert. - Capsule: bars stay flat without input (no fake "alive" wobble before the mic opens), an indeterminate bar replaces the made-up percentage, the partial transcript stays visible while processing, an Esc hint, and a brief "Inserted" state so success is visible. - All six popups share popups/_shared (Pretendard, one panel radius, selection, kbd hints, reduced motion). History/command popups get titles, listbox roles and window sizes from popup-list-geometry (empty states were clipped); the command popup's "0" key is registered; result popup explains why it appeared and can be closed; captions keep a theme-independent dark backing on purpose. - Popup themes follow the OS when set to system and the document lang follows the app language. - Site hero demo mirrors the new capsule (done state, indeterminate bar, Esc hint; 10 locales).
295 lines
7.5 KiB
CSS
295 lines
7.5 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* ── Base ─────────────────────────────────────────────── */
|
|
@layer base {
|
|
html {
|
|
scroll-behavior: smooth;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
text-rendering: optimizeLegibility;
|
|
@apply bg-surface-950 font-sans text-neutral-100;
|
|
}
|
|
|
|
body {
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
::selection {
|
|
@apply bg-brand-blue/35 text-white;
|
|
}
|
|
|
|
:focus-visible {
|
|
@apply outline outline-2 outline-offset-2 outline-brand-blue-light;
|
|
}
|
|
|
|
/* 고정 헤더(64px)가 앵커 대상의 머리를 가리지 않게 한다. */
|
|
[id] {
|
|
scroll-margin-top: 5rem;
|
|
}
|
|
|
|
/* 의미적 강조는 세미볼드까지 — 볼드(700) 금지 (design.md 타이포 원칙 1) */
|
|
strong, b { font-weight: 600; }
|
|
|
|
[hidden] { display: none !important; }
|
|
}
|
|
|
|
/* ── Components ───────────────────────────────────────── */
|
|
@layer components {
|
|
.section {
|
|
@apply py-20 md:py-28;
|
|
}
|
|
|
|
/* 버튼: 최소 44px 터치 타깃. 흰 글자 대비 4.5:1 이상을 위해 채움색은 blue-dark 계열. */
|
|
.btn {
|
|
@apply inline-flex min-h-[44px] flex-wrap items-center justify-center gap-x-2 gap-y-1 rounded-xl px-5 py-2 text-center text-base font-medium leading-snug transition-colors duration-150;
|
|
}
|
|
.btn-sm {
|
|
@apply min-h-[40px] px-4 text-sm;
|
|
}
|
|
.btn-lg {
|
|
@apply min-h-[52px] px-7;
|
|
}
|
|
.btn-primary {
|
|
@apply bg-brand-blue-dark text-white hover:bg-brand-blue-deep;
|
|
}
|
|
.btn-secondary {
|
|
@apply border border-white/15 bg-surface-800 text-neutral-100 hover:border-white/30 hover:bg-surface-700;
|
|
}
|
|
|
|
.link {
|
|
@apply text-brand-blue-light underline decoration-brand-blue-light/40 underline-offset-4 transition-colors hover:decoration-brand-blue-light;
|
|
}
|
|
|
|
.kbd {
|
|
@apply inline-flex min-h-[32px] items-center rounded-md border border-white/15 border-b-white/25 bg-surface-700 px-2.5 font-sans text-sm text-neutral-100;
|
|
}
|
|
.kbd-accent {
|
|
@apply border-brand-blue/50 border-b-brand-blue bg-brand-blue/15 text-brand-blue-light;
|
|
}
|
|
|
|
.skip-link {
|
|
@apply fixed left-4 top-3 z-[60] -translate-y-24 rounded-lg bg-surface-800 px-4 py-3 text-sm text-white shadow-chassis focus:translate-y-0;
|
|
}
|
|
}
|
|
|
|
/* ── 히어로 예시: 입력 대상 창 ─────────────────────────── */
|
|
.demo-window {
|
|
position: relative;
|
|
overflow: hidden;
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
background: #0f1116;
|
|
box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.6);
|
|
}
|
|
.demo-titlebar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
height: 36px;
|
|
padding: 0 14px;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
|
|
background: #151821;
|
|
font-size: 12px;
|
|
color: #a1a1aa;
|
|
}
|
|
.demo-app-icon {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 3px;
|
|
background: #3b82f6;
|
|
}
|
|
.demo-win-glyph {
|
|
font-size: 11px;
|
|
line-height: 1;
|
|
}
|
|
.demo-doc {
|
|
min-height: 236px;
|
|
padding: 22px 22px 108px;
|
|
font-size: 15px;
|
|
line-height: 1.7;
|
|
}
|
|
.demo-typed {
|
|
border-radius: 3px;
|
|
animation: demo-paste 1.6s ease-out;
|
|
}
|
|
@keyframes demo-paste {
|
|
0% { background: rgba(59, 130, 246, 0.35); }
|
|
100% { background: transparent; }
|
|
}
|
|
.demo-caret {
|
|
display: inline-block;
|
|
width: 2px;
|
|
height: 1.15em;
|
|
margin-left: 1px;
|
|
vertical-align: text-bottom;
|
|
background: #f4f4f5;
|
|
animation: demo-caret 1.06s steps(1) infinite;
|
|
}
|
|
@keyframes demo-caret {
|
|
50% { opacity: 0; }
|
|
}
|
|
.demo-capsule-slot {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 20px;
|
|
display: flex;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
transform: translateY(6px) scale(0.98);
|
|
transition: opacity 180ms cubic-bezier(0.16, 1, 0.3, 1), transform 180ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
pointer-events: none;
|
|
}
|
|
.demo-capsule-slot.is-visible {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
|
|
/* ── 녹음 캡슐 — 앱 popups/recording-tip/style.css(v4, 2026-09-28)와 같은 값 ───
|
|
* 판 모양(radius 10)·단색 막대·입력 없으면 막대 정지·처리 중 무한 막대·완료 "입력함".
|
|
* 색은 앱 다크 테마 원시값(packages/ui/src/theme-raw.ts)과 같다 — 값이 바뀌면 함께 고친다. */
|
|
.capsule {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
min-width: 168px;
|
|
max-width: 300px;
|
|
padding: 8px 12px;
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(148, 180, 255, 0.12);
|
|
background: #111a30;
|
|
box-shadow: 0 12px 32px rgba(3, 7, 18, 0.55);
|
|
}
|
|
.capsule.is-listening {
|
|
border-color: rgba(148, 180, 255, 0.2);
|
|
}
|
|
.capsule-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.capsule-bars {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
height: 22px;
|
|
}
|
|
.capsule-bar {
|
|
display: block;
|
|
width: 3px;
|
|
min-height: 2px;
|
|
height: 2px;
|
|
border-radius: 2px;
|
|
background: #3b82f6;
|
|
transition: height 80ms ease-out;
|
|
}
|
|
.capsule.is-thinking .capsule-bar {
|
|
background: #7e8eb0;
|
|
}
|
|
.capsule-duration {
|
|
min-width: 32px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
font-variant-numeric: tabular-nums;
|
|
color: #eef2fb;
|
|
}
|
|
.capsule-thinking {
|
|
flex: 1;
|
|
font-size: 12px;
|
|
color: #93a4c8;
|
|
white-space: nowrap;
|
|
}
|
|
.capsule-cancel {
|
|
margin-left: auto;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
height: 18px;
|
|
padding: 0 5px;
|
|
border: 1px solid rgba(148, 180, 255, 0.2);
|
|
border-radius: 4px;
|
|
font-size: 11px;
|
|
line-height: 1;
|
|
color: #7e8eb0;
|
|
white-space: nowrap;
|
|
}
|
|
.capsule-progress {
|
|
position: relative;
|
|
display: block;
|
|
height: 2px;
|
|
overflow: hidden;
|
|
border-radius: 1px;
|
|
background: rgba(148, 180, 255, 0.12);
|
|
}
|
|
.capsule-progress-bar {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 36%;
|
|
border-radius: 1px;
|
|
background: #3b82f6;
|
|
animation: capsule-indeterminate 1.1s cubic-bezier(0.22, 1, 0.36, 1) infinite;
|
|
}
|
|
@keyframes capsule-indeterminate {
|
|
from { left: -36%; }
|
|
to { left: 100%; }
|
|
}
|
|
.capsule-partial {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-end;
|
|
max-height: 38px;
|
|
overflow: hidden;
|
|
border-top: 1px solid rgba(148, 180, 255, 0.07);
|
|
padding-top: 6px;
|
|
font-size: 12px;
|
|
line-height: 1.55;
|
|
color: #eef2fb;
|
|
}
|
|
.capsule.is-thinking .capsule-partial {
|
|
color: #7e8eb0;
|
|
}
|
|
.capsule-done {
|
|
font-size: 12px;
|
|
color: #eef2fb;
|
|
}
|
|
.capsule-done-icon {
|
|
color: #34d399;
|
|
}
|
|
|
|
/* ── Grid Background Pattern (히어로) ─────────────────── */
|
|
.bg-grid {
|
|
background-image:
|
|
linear-gradient(rgba(59, 130, 246, 0.035) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(59, 130, 246, 0.035) 1px, transparent 1px);
|
|
background-size: 64px 64px;
|
|
}
|
|
|
|
/* ── Utilities ─────────────────────────────────────────── */
|
|
@layer utilities {
|
|
.text-balance {
|
|
text-wrap: balance;
|
|
}
|
|
/* 한글 어절 단위 줄바꿈. 중국어·일본어에 keep-all을 걸면 줄바꿈 기회가 사라지므로 한국어에만 적용한다. */
|
|
.keep-ko {
|
|
overflow-wrap: break-word;
|
|
}
|
|
.keep-ko:lang(ko) {
|
|
word-break: keep-all;
|
|
}
|
|
}
|
|
|
|
/* ── Reduced motion ───────────────────────────────────── */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
html {
|
|
scroll-behavior: auto;
|
|
}
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|