Files
smart-home-tablet/app/globals.css
Cosmo e328055851
All checks were successful
Deploy / deploy (push) Successful in 3m8s
feat(design): FocusCard hero, CountdownCard, data-* palette, swipe, touch-targets
Big design pass across Home + tokens + components.

— globals.css: new data-* palette (cool/warm/hot/good/info/rose/violet/mood)
  with theme-aware variants, .grain overlay utility, .num-display
  typography helper, .hit-zone 44px wrapper, .eyebrow label, .focus-card
  base, focus-visible outline-offset 3px, space/touch scale vars.
— FocusCard.tsx: context engine — пять состояний (morning-outfit,
  tram-imminent, event-upcoming, countdown, bill-due, night, quiet).
  Auto-rotates by hour + live data. 96px display numbers, accent-mixed
  surfaces, grain overlay.
— CountdownCard.tsx + /api/countdowns: rotating 8s list, persistent
  /data/tablet-countdowns.json, full CRUD. Default seeded with Токио.
— HomeTab: replaced plain Weather hero with FocusCard, added Row 4
  with CountdownCard. Pulls trams + countdowns for the Focus context.
— Swipe between tabs: pointer-level detection on <main>, data-swipe-ignore
  bails out inside modals + note swipe-to-delete + voice overlay.
— Touch-target sweep: TopBar HA dot → 44px hit-zone, sensor chip 44px
  min-height, forecast day buttons 92px min, DeviceCard toggle 60x36,
  CalendarTab prev/next/close/list all 44x44, NotesTab buttons 44x44,
  TimerHomeWidget + 44x44, WeatherDayModal chevrons 48x48, close 48.
— Hardcoded hex → data-* tokens: TopBar sensors, TransportWidget routes
  (via color-mix), DeviceCard full rewrite (per-kind accent, glass
  removed in favor of color-mix surfaces + proper mock-state treatment),
  NotesTab palette refreshed to match dark theme.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:24:23 +00:00

411 lines
14 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* ——————————————————————————————
Tokens — dark (default)
—————————————————————————————— */
:root {
/* Surfaces */
--bg: #0b0b14;
--bg-secondary: #101020;
--surface-1: #15152a;
--surface-2: #1c1c36;
--surface-3: #242444;
--surface-hover: #1e1e3c;
/* Borders */
--border-subtle: rgba(255, 255, 255, 0.06);
--border-strong: rgba(255, 255, 255, 0.12);
--hairline: rgba(255, 255, 255, 0.08);
/* Text */
--text-primary: rgba(255, 255, 255, 0.95);
--text-secondary: rgba(255, 255, 255, 0.6);
--text-tertiary: rgba(255, 255, 255, 0.38);
/* Accents — UI */
--accent: #818cf8;
--accent-strong: #6366f1;
--accent-secondary: #22d3ee;
--accent-glow: rgba(129, 140, 248, 0.22);
/* Data palette — semantic, theme-aware */
--data-cool: #38bdf8; /* небо, влажность, охлаждение */
--data-info: #60a5fa; /* ссылки, second-accent */
--data-good: #34d399; /* успех, здоровье, streak */
--data-warm: #fbbf24; /* свет, тепло, утро */
--data-hot: #fb923c; /* обострение, urgent-minor */
--data-danger: #f87171; /* критичное, истёк, ошибка */
--data-rose: #f472b6; /* Света, романтика */
--data-violet: #a78bfa; /* Cosmo, индиго-вариант */
--data-mood: #8b5cf6; /* секреты, события 2-го уровня */
/* Soft backgrounds for data (color-mix with surface-2) */
--data-cool-bg: color-mix(in srgb, var(--data-cool) 12%, var(--surface-2));
--data-good-bg: color-mix(in srgb, var(--data-good) 12%, var(--surface-2));
--data-warm-bg: color-mix(in srgb, var(--data-warm) 12%, var(--surface-2));
--data-hot-bg: color-mix(in srgb, var(--data-hot) 14%, var(--surface-2));
--data-danger-bg: color-mix(in srgb, var(--data-danger) 12%, var(--surface-2));
--data-rose-bg: color-mix(in srgb, var(--data-rose) 12%, var(--surface-2));
--data-violet-bg: color-mix(in srgb, var(--data-violet) 12%, var(--surface-2));
/* Brand gradients */
--gradient-primary: linear-gradient(135deg, #6366f1, #8b5cf6);
--gradient-warm: linear-gradient(135deg, #f59e0b, #ef4444);
--gradient-cool: linear-gradient(135deg, #06b6d4, #3b82f6);
--gradient-green: linear-gradient(135deg, #10b981, #34d399);
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35);
--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.45);
--shadow-xl: 0 24px 60px rgba(0, 0, 0, 0.5);
/* State */
--on-color: #818cf8;
--off-color: rgba(255, 255, 255, 0.15);
/* Radius */
--radius-xs: 8px;
--radius-sm: 12px;
--radius-md: 16px;
--radius-lg: 22px;
--radius-xl: 28px;
/* Layout rhythm */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 22px;
--space-6: 28px;
/* Touch */
--touch-min: 44px;
--touch-comfy: 56px;
/* Legacy aliases */
--sidebar-bg: var(--bg);
--card-bg: var(--surface-1);
--card-bg-hover: var(--surface-2);
--card-border: var(--border-subtle);
--card-border-hover: var(--border-strong);
--glass: var(--surface-1);
--glass-border: var(--border-subtle);
}
/* ——————————————————————————————
Tokens — light
—————————————————————————————— */
.light {
--bg: #f6f7fb;
--bg-secondary: #eef0f5;
--surface-1: #ffffff;
--surface-2: #f3f4f8;
--surface-3: #eaebf0;
--surface-hover: #f8f9fc;
--border-subtle: rgba(15, 20, 40, 0.06);
--border-strong: rgba(15, 20, 40, 0.12);
--hairline: rgba(15, 20, 40, 0.08);
--text-primary: rgba(15, 20, 40, 0.94);
--text-secondary: rgba(15, 20, 40, 0.62);
--text-tertiary: rgba(15, 20, 40, 0.38);
--accent: #5b63e0;
--accent-strong: #4f46e5;
--accent-secondary: #0891b2;
--accent-glow: rgba(91, 99, 224, 0.14);
/* Data palette — slightly darker in light theme for contrast */
--data-cool: #0284c7;
--data-info: #2563eb;
--data-good: #059669;
--data-warm: #d97706;
--data-hot: #ea580c;
--data-danger: #dc2626;
--data-rose: #db2777;
--data-violet: #7c3aed;
--data-mood: #6d28d9;
/* Soft backgrounds — lighter mix for light theme */
--data-cool-bg: color-mix(in srgb, var(--data-cool) 9%, var(--surface-1));
--data-good-bg: color-mix(in srgb, var(--data-good) 9%, var(--surface-1));
--data-warm-bg: color-mix(in srgb, var(--data-warm) 9%, var(--surface-1));
--data-hot-bg: color-mix(in srgb, var(--data-hot) 10%, var(--surface-1));
--data-danger-bg: color-mix(in srgb, var(--data-danger) 9%, var(--surface-1));
--data-rose-bg: color-mix(in srgb, var(--data-rose) 9%, var(--surface-1));
--data-violet-bg: color-mix(in srgb, var(--data-violet) 9%, var(--surface-1));
--shadow-sm: 0 1px 2px rgba(15, 20, 40, 0.05);
--shadow-md: 0 2px 6px rgba(15, 20, 40, 0.06), 0 12px 28px -8px rgba(15, 20, 40, 0.1);
--shadow-lg: 0 4px 10px rgba(15, 20, 40, 0.06), 0 24px 60px -12px rgba(15, 20, 40, 0.14);
--shadow-xl: 0 8px 16px rgba(15, 20, 40, 0.06), 0 40px 100px -24px rgba(15, 20, 40, 0.2);
--on-color: #5b63e0;
--off-color: rgba(15, 20, 40, 0.12);
}
/* ——————————————————————————————
Base
—————————————————————————————— */
html, body {
background: var(--bg);
color: var(--text-primary);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', system-ui, sans-serif;
height: 100%;
overflow: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
transition: background 0.3s ease, color 0.3s ease;
}
#__next, main { height: 100%; }
/* Better tabular numbers across the app */
.num, .num-display {
font-variant-numeric: tabular-nums slashed-zero;
font-feature-settings: "tnum", "zero";
}
.num-display {
letter-spacing: -0.02em;
font-weight: 800;
line-height: 0.95;
}
/* ——————————————————————————————
Grain overlay
——— subtle SVG-noise; cheap, no image asset needed
—————————————————————————————— */
.grain {
position: relative;
}
.grain::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
border-radius: inherit;
opacity: 0.035;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
background-size: 120px 120px;
}
.light .grain::after {
opacity: 0.05;
mix-blend-mode: multiply;
filter: invert(1);
}
/* ——————————————————————————————
Aurora
—————————————————————————————— */
.bg-ambient {
position: fixed;
inset: 0;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.bg-ambient::before,
.bg-ambient::after {
content: '';
position: absolute;
border-radius: 50%;
filter: blur(80px);
}
.bg-ambient::before {
width: 560px; height: 560px;
background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%);
top: -180px; right: -80px;
animation: float1 22s ease-in-out infinite;
}
.bg-ambient::after {
width: 480px; height: 480px;
background: radial-gradient(circle, rgba(139, 92, 246, 0.14) 0%, transparent 70%);
bottom: -140px; left: -60px;
animation: float2 27s ease-in-out infinite;
}
.light .bg-ambient::after {
background: radial-gradient(circle, rgba(99, 102, 241, 0.1) 0%, transparent 70%);
}
@keyframes float1 {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, 40px) scale(1.05); }
66% { transform: translate(-20px, 20px) scale(0.95); }
}
@keyframes float2 {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(-40px, -30px) scale(1.1); }
66% { transform: translate(20px, -10px) scale(0.9); }
}
/* ——————————————————————————————
Cards
—————————————————————————————— */
.card {
background: var(--surface-1);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
.card-raised {
background: var(--surface-1);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
}
.card-hero {
background: var(--surface-1);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
}
/* Focus-card styling — for the context-aware hero */
.focus-card {
position: relative;
background: var(--surface-1);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
overflow: hidden;
}
.focus-card-accent::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, var(--accent-glow), transparent 60%);
pointer-events: none;
}
.glass-card {
background: var(--surface-1);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
.glass-card:hover { background: var(--surface-hover); }
/* Hairline divider helper */
.divider {
height: 1px;
width: 100%;
background: var(--hairline);
}
/* Chip / hit-zone — wraps small icons into 44px tap surface */
.hit-zone {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: var(--touch-min);
min-height: var(--touch-min);
border-radius: 12px;
}
/* Small uppercase label */
.eyebrow {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-tertiary);
}
.gradient-text {
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
button {
cursor: pointer;
border: none;
outline: none;
background: none;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
font-family: inherit;
color: inherit;
}
button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
border-radius: 14px;
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }
/* ——————————————————————————————
Motion
—————————————————————————————— */
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px var(--accent-glow); }
50% { box-shadow: 0 0 30px var(--accent-glow); }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-up { animation: slideUp 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
@keyframes fan-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes light-pulse {
0%, 100% { filter: drop-shadow(0 0 4px rgba(251, 191, 36, 0.3)); }
50% { filter: drop-shadow(0 0 12px rgba(251, 191, 36, 0.6)); }
}
@keyframes device-breathe { 0%, 100% { opacity: 0.7; } 50% { opacity: 1; } }
.fan-spinning { animation: fan-spin 2s linear infinite; }
.light-on-pulse { animation: light-pulse 3s ease-in-out infinite; }
.device-active-breathe { animation: device-breathe 3s ease-in-out infinite; }
@keyframes spin-slow { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes cloud-float { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } }
@keyframes rain-fall {
0% { transform: translateY(0); opacity: 0.7; }
80% { opacity: 0.7; }
100% { transform: translateY(16px); opacity: 0; }
}
@keyframes snow-fall {
0% { transform: translateY(0) translateX(0); opacity: 0.9; }
25% { transform: translateY(5px) translateX(2px); opacity: 0.8; }
50% { transform: translateY(10px) translateX(-1px); opacity: 0.7; }
75% { transform: translateY(15px) translateX(3px); opacity: 0.4; }
100% { transform: translateY(22px) translateX(0); opacity: 0; }
}
@keyframes thunder-flash {
0%, 100% { opacity: 0; }
5%, 7% { opacity: 1; }
6% { opacity: 0.3; }
50%, 52% { opacity: 0.8; }
51% { opacity: 0.2; }
}
@keyframes fog-drift {
0%, 100% { transform: translateX(0); opacity: 0.4; }
50% { transform: translateX(8px); opacity: 0.6; }
}
/* Weather ambient tints via accent-glow */
.weather-bg-clear { --accent-glow: rgba(251, 191, 36, 0.18); }
.weather-bg-cloudy { --accent-glow: rgba(148, 163, 184, 0.18); }
.weather-bg-rain { --accent-glow: rgba(59, 130, 246, 0.18); }
.weather-bg-snow { --accent-glow: rgba(186, 230, 253, 0.22); }
.weather-bg-thunder { --accent-glow: rgba(139, 92, 246, 0.22); }
.weather-bg-night { --accent-glow: rgba(67, 56, 202, 0.16); }
.light.weather-bg-clear { --accent-glow: rgba(234, 169, 27, 0.16); }
.light.weather-bg-cloudy { --accent-glow: rgba(100, 116, 139, 0.14); }
.light.weather-bg-rain { --accent-glow: rgba(37, 99, 235, 0.14); }
.light.weather-bg-snow { --accent-glow: rgba(125, 211, 252, 0.22); }
.light.weather-bg-thunder { --accent-glow: rgba(109, 40, 217, 0.14); }
.light.weather-bg-night { --accent-glow: rgba(55, 48, 163, 0.14); }