Files
VoiceStudio/frontend/src/ui/Button.css
T
debpalashandClaude Opus 4.7 e1966bdcd9 feat(ui): chrome theme + retina typography + launchpad charisma + export drawer + setup wizard + projects + logs footer
## Typography system
- Add Fontsource variable fonts: Inter, IBM Plex Mono, Source Serif 4
- Unify via --font-sans / --font-mono / --font-serif / --font-display
  tokens in ui/tokens.css
- Enable OpenType features globally: cv11 (single-story a), ss01, ss03,
  zero, tabular-nums slashed-zero; font-optical-sizing: auto;
  font-synthesis: none
- Alias legacy --chrome-font-mono to var(--font-mono); purge Google Fonts
  @import and hardcoded "ui-monospace, Menlo" refs across 43+ rules

## Chrome design tokens
- Migrate every panel/button/input to --chrome-* tokens with
  color-mix(in srgb, … N%, transparent) tone tints
- Flat-chromed .glass-panel, .studio-panel, .main-content, .launchpad,
  sidebar, segments, clone/design textarea, settings subsurfaces,
  export drawer, footer, modals
- Kill radial gradients, paper-grain noise, 30s mesh animation
- Rewrite Badge.css, Button.css, Tabs.css, Table.css to chrome tokens

## Launchpad charisma
- Aurora backdrop: three drifting blurred blobs (pink 22s, green 28s,
  amber 32s) behind everything at z=0
- Hero halo + animated sweep line under H1
- Wave bars with per-bar --bar-delay / --bar-dur for breathing stagger
- Action cards: single --card-hue drives bg/border/glow/spotlight
- Cursor-tracked spotlight via onMouseMove → --mx/--my custom props
  → radial-gradient paint (no JS re-renders)
- Eternal breath ring via .lp-glow-layer::after, staggered across
  three cards via :nth-child animation-delays
- All animations gated by @media (prefers-reduced-motion: reduce)

## Export drawer (new)
- ExportModal.jsx/css: 4-tab bottom drawer (Video / Audio / Subtitles
  / Package) rendered via createPortal
- Presets (YouTube, Archive, Web, Podcast, Study), per-track checklist
  with All/None/Dubs-only, MP3 bitrate 128/192/256/320
- Non-blocking: pointer-events: none on outer, auto on sheet; ESC + click
  outside to close
- DubTab: FooterBtn → React.forwardRef; export trigger opens drawer

## Dubbing ETA
- Show elapsed time during generation; genElapsed state + fmtDur helper
  renders .dub-gen-overlay__stats block

## Setup wizard (new)
- SetupWizard.jsx/css: first-run flow for engine probing, model downloads
- Projects.jsx/css: dedicated projects browser page
- LogsFooter.jsx/css: slide-up logs panel
- api/setup.ts: client for setup router endpoints
- App.jsx, NavRail, Header, store wired to new pages

## Version
- frontend/package.json 0.2.0; refresh bun.lock

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:49:44 +05:30

168 lines
5.0 KiB
CSS

/* ── Button primitive — chrome tokens only ────────────────────────── */
.ui-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-3);
border: 1px solid transparent;
border-radius: var(--chrome-radius-pill);
background: transparent;
color: var(--chrome-fg);
font-family: var(--font-sans);
font-weight: 500;
letter-spacing: 0.02em;
cursor: pointer;
white-space: nowrap;
transition:
background var(--dur-fast) var(--ease-out),
border-color var(--dur-fast) var(--ease-out),
color var(--dur-fast) var(--ease-out);
position: relative;
overflow: hidden;
user-select: none;
}
.ui-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
filter: none;
}
.ui-btn:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.ui-btn__spinner { animation: spin 1s linear infinite; }
.ui-btn__label { line-height: 1; }
.ui-btn--block { width: 100%; }
.ui-btn.is-loading { cursor: wait; }
/* ── Size scale ──────────────────────────────────────────────────── */
.ui-btn--size-sm { padding: 3px 10px; font-size: var(--text-xs); border-radius: var(--chrome-radius-pill); }
.ui-btn--size-md { padding: 6px 12px; font-size: var(--text-md); border-radius: var(--chrome-radius-pill); }
/* ── Variants ────────────────────────────────────────────────────── */
/* primary — flat chrome accent, no gradient / shimmer */
.ui-btn--primary {
background: var(--chrome-accent-bg);
color: var(--chrome-accent);
border-color: var(--chrome-accent-border);
box-shadow: none;
}
.ui-btn--primary::before { display: none; }
.ui-btn--primary:hover:not(:disabled) {
filter: none;
background: color-mix(in srgb, var(--chrome-accent) 22%, transparent);
box-shadow: none;
transform: none;
}
.ui-btn--primary:active:not(:disabled) {
transform: none;
filter: none;
box-shadow: none;
}
/* subtle — default neutral surface action */
.ui-btn--subtle {
background: transparent;
border-color: var(--chrome-border);
color: var(--chrome-fg-muted);
}
.ui-btn--subtle:hover:not(:disabled) {
background: var(--chrome-hover-bg);
color: var(--chrome-fg);
border-color: var(--chrome-border-strong);
transform: none;
}
.ui-btn--subtle:active:not(:disabled) { transform: none; }
/* ghost — transparent, hover surfaces */
.ui-btn--ghost {
background: transparent;
border-color: transparent;
color: var(--chrome-fg-muted);
}
.ui-btn--ghost:hover:not(:disabled) {
background: var(--chrome-hover-bg);
color: var(--chrome-fg);
}
/* danger — destructive */
.ui-btn--danger {
background: color-mix(in srgb, var(--chrome-severity-err) 10%, transparent);
border-color: color-mix(in srgb, var(--chrome-severity-err) 45%, transparent);
color: var(--chrome-severity-err);
}
.ui-btn--danger:hover:not(:disabled) {
background: color-mix(in srgb, var(--chrome-severity-err) 18%, transparent);
border-color: var(--chrome-severity-err);
}
/* chip — selectable tag */
.ui-btn--chip {
padding: 2px 8px;
font-size: var(--text-xs);
border-radius: var(--chrome-radius-pill);
background: transparent;
border-color: var(--chrome-border);
color: var(--chrome-fg-muted);
font-family: var(--font-sans);
font-weight: 500;
}
.ui-btn--chip:hover:not(:disabled) {
background: var(--chrome-hover-bg);
color: var(--chrome-fg);
border-color: var(--chrome-border-strong);
transform: none;
}
.ui-btn--chip.is-active {
background: color-mix(in srgb, var(--chrome-severity-ok) 12%, transparent);
color: var(--chrome-severity-ok);
border-color: color-mix(in srgb, var(--chrome-severity-ok) 45%, transparent);
}
/* preset — neutral chrome tile, no violet tint */
.ui-btn--preset {
padding: 3px 8px;
font-size: var(--text-xs);
border-radius: var(--chrome-radius-pill);
background: transparent;
border-color: var(--chrome-border);
color: var(--chrome-fg-muted);
text-align: left;
justify-content: flex-start;
}
.ui-btn--preset:hover:not(:disabled) {
background: var(--chrome-hover-bg);
color: var(--chrome-fg);
border-color: var(--chrome-border-strong);
transform: none;
box-shadow: none;
}
.ui-btn--preset.is-active {
background: var(--chrome-accent-bg);
border-color: var(--chrome-accent-border);
color: var(--chrome-accent);
}
/* icon — square, centered, for toolbar/inline actions */
.ui-btn--icon {
padding: 0;
border-radius: var(--chrome-radius-pill);
background: transparent;
border-color: var(--chrome-border);
color: var(--chrome-fg-muted);
}
.ui-btn--icon-sm { width: 20px; height: 20px; }
.ui-btn--icon-md { width: 22px; height: 22px; }
.ui-btn--icon:hover:not(:disabled) {
background: var(--chrome-hover-bg);
color: var(--chrome-fg);
border-color: var(--chrome-border-strong);
}
.ui-btn--icon.is-active {
background: var(--chrome-accent-bg);
color: var(--chrome-accent);
border-color: var(--chrome-accent-border);
}