diff --git a/miniapp/assets/app.js b/miniapp/assets/app.js index 66b4883..c57f26f 100644 --- a/miniapp/assets/app.js +++ b/miniapp/assets/app.js @@ -9,9 +9,13 @@ const app = document.getElementById("app"); /* ----------------- Telegram WebApp setup ----------------- */ function setupTelegram() { - // Apply theme based on Telegram color scheme even if OS prefers-color-scheme misses const scheme = tg?.colorScheme || (window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light"); document.documentElement.setAttribute("data-theme", scheme); + + // Дизайн-вариант: brand (default) / a / c — сохраняется в localStorage. + const savedVariant = (typeof localStorage !== "undefined" && localStorage.getItem("zov_variant")) || "brand"; + document.documentElement.setAttribute("data-variant", savedVariant); + if (!tg) return; try { tg.ready(); @@ -19,11 +23,35 @@ function setupTelegram() { if (tg.onEvent) tg.onEvent("themeChanged", () => { document.documentElement.setAttribute("data-theme", tg.colorScheme || "light"); }); - if (tg.setHeaderColor) tg.setHeaderColor("#003E7E"); if (tg.enableClosingConfirmation) tg.enableClosingConfirmation(); } catch (e) { console.warn(e); } } +function setVariant(variant) { + document.documentElement.setAttribute("data-variant", variant); + try { localStorage.setItem("zov_variant", variant); } catch (e) {} + // Перерисовать активную кнопку в свитчере + document.querySelectorAll(".theme-switch button").forEach(b => { + b.classList.toggle("active", b.dataset.variant === variant); + }); + haptic(); +} + +function buildThemeSwitch() { + const current = document.documentElement.getAttribute("data-variant") || "brand"; + const node = el(` +
+ + + +
+ `); + node.querySelectorAll("button").forEach(btn => { + btn.addEventListener("click", () => setVariant(btn.dataset.variant)); + }); + return node; +} + function haptic(type = "selection") { try { if (!tg?.HapticFeedback) return; @@ -90,6 +118,7 @@ function renderManager(me) { const tgId = me.user?.tg_id ? `ID ${me.user.tg_id}` : ""; app.innerHTML = ""; + app.appendChild(buildThemeSwitch()); app.appendChild(el(`
@@ -151,6 +180,7 @@ function renderClient(me) { const greetName = me.user?.full_name || "Здравствуйте"; app.innerHTML = ""; + app.appendChild(buildThemeSwitch()); app.appendChild(el(`
diff --git a/miniapp/assets/styles.css b/miniapp/assets/styles.css index 9382cfa..4829643 100644 --- a/miniapp/assets/styles.css +++ b/miniapp/assets/styles.css @@ -1,83 +1,124 @@ /* ============================================================ - ЗОВ MiniApp — design system v3 ("Architectural Clean") - Гибрид Architectural Clean (Claude.ai Frontend Design) - + бренд-палитра ЗОВ + редакторская типографика + ЗОВ MiniApp — design system v4 + Три варианта: brand (default) · a (Editorial Calm) · c (Architectural Clean) + Переключатель — ``, состояние в localStorage. ============================================================ */ +/* =========================================================== + 1. BRAND (default :root) — мой гибрид с палитрой ЗОВ + =========================================================== */ :root { - /* ====== Brand ====== */ - --zov-blue: #003E7E; - --zov-green: #76BD22; - --zov-warning: #C0392B; + --paper: var(--tg-theme-bg-color, #FAFAF7); + --paper-2: var(--tg-theme-secondary-bg-color, #F0EDE5); + --card: var(--tg-theme-section-bg-color, #FFFFFF); + --ink: var(--tg-theme-text-color, #0F0F0E); + --ink-2: #2A2622; + --muted: var(--tg-theme-hint-color, #6E6A62); + --muted-2: #A09C92; + --line: rgba(15, 15, 14, 0.08); + --line-strong: rgba(15, 15, 14, 0.16); - /* ====== Paper palette (ВАРИАНТ A — светлая «бумага») ====== */ - --paper: var(--tg-theme-bg-color, #FAFAF7); - --paper-2: var(--tg-theme-secondary-bg-color, #F0EDE5); - --card: var(--tg-theme-section-bg-color, #FFFFFF); - --ink: var(--tg-theme-text-color, #0F0F0E); - --ink-2: #2A2622; - --muted: var(--tg-theme-hint-color, #6E6A62); - --muted-2: #A09C92; - --line: rgba(15, 15, 14, 0.08); - --line-strong: rgba(15, 15, 14, 0.16); + --accent-1: #003E7E; /* brand blue */ + --accent-2: #76BD22; /* brand green */ + --accent-3: #C0392B; + --status-active: #76BD22; + --status-lapsed: #C0392B; + --status-grace: #B07E00; - /* ====== Status ====== */ - --status-active: var(--zov-green); - --status-active-bg: rgba(118, 189, 34, 0.10); - --status-lapsed: var(--zov-warning); - --status-lapsed-bg: rgba(192, 57, 43, 0.08); - --status-grace: #B07E00; - --status-grace-bg: rgba(176, 126, 0, 0.08); - - /* ====== Radii (architectural — небольшие скругления) ====== */ - --r-tag: 4px; - --r-btn: 12px; --r-card: 14px; + --r-btn: 12px; + --r-tag: 4px; --r-pill: 999px; - /* ====== Spacing ====== */ --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px; --s7: 32px; --s8: 40px; - /* ====== Type ====== */ - --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif; + --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif; --font-display: "Instrument Serif", Georgia, "Times New Roman", serif; - --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + + --ribbon: linear-gradient(90deg, var(--accent-1) 0%, var(--accent-2) 100%); } -/* ====== Dark theme — both via OS preference and Telegram-driven [data-theme="dark"] ====== */ @media (prefers-color-scheme: dark) { :root { color-scheme: dark; } } -html[data-theme="dark"], -@media (prefers-color-scheme: dark) { - :root, - html[data-theme="dark"] { - --paper: var(--tg-theme-bg-color, #14130E); - --paper-2: var(--tg-theme-secondary-bg-color, #1F1D17); - --card: var(--tg-theme-section-bg-color, #1C1A14); - --ink: var(--tg-theme-text-color, #F4F1EA); - --ink-2: #C8C3B5; - --muted: var(--tg-theme-hint-color, #A5A299); - --muted-2: #807D74; - --line: rgba(255, 255, 255, 0.10); - --line-strong: rgba(255, 255, 255, 0.20); - --status-active-bg: rgba(118, 189, 34, 0.22); - --status-lapsed-bg: rgba(220, 38, 38, 0.22); - --status-grace-bg: rgba(217, 119, 6, 0.22); - } +html[data-theme="dark"]:not([data-variant="a"]):not([data-variant="c"]) { + --paper: var(--tg-theme-bg-color, #14130E); + --paper-2: var(--tg-theme-secondary-bg-color, #1F1D17); + --card: var(--tg-theme-section-bg-color, #1C1A14); + --ink: var(--tg-theme-text-color, #F4F1EA); + --ink-2: #C8C3B5; + --muted: var(--tg-theme-hint-color, #A5A299); + --muted-2: #807D74; + --line: rgba(255, 255, 255, 0.10); + --line-strong: rgba(255, 255, 255, 0.20); } -/* Force light theme when explicitly set (overrides OS dark) */ -html[data-theme="light"] { - --paper: var(--tg-theme-bg-color, #FAFAF7); - --paper-2: var(--tg-theme-secondary-bg-color, #F0EDE5); - --card: var(--tg-theme-section-bg-color, #FFFFFF); - --ink: var(--tg-theme-text-color, #0F0F0E); - --ink-2: #2A2622; - --muted: var(--tg-theme-hint-color, #6E6A62); - --muted-2: #A09C92; - --line: rgba(15, 15, 14, 0.08); - --line-strong: rgba(15, 15, 14, 0.16); +/* =========================================================== + 2. VARIANT A — Editorial Calm + (палитра/шрифты — дословно из мокапа) + =========================================================== */ +html[data-variant="a"] { + --paper: #FBF7F0; + --paper-2: #F5EDDC; + --card: #FBF7F0; + --ink: #1F1A14; + --ink-2: #8B8275; + --muted: #8B8275; + --muted-2: #9A9085; + --line: rgba(31, 26, 20, 0.18); + --line-strong: rgba(31, 26, 20, 0.40); + + --accent-1: #3C5278; /* editorial blue */ + --accent-2: #6B4A2B; /* walnut */ + --accent-3: #B85A2D; /* terracotta */ + --status-active: #B85A2D; + --status-lapsed: #B85A2D; + --status-grace: #B85A2D; + + --r-card: 0; + --r-btn: 0; + --r-tag: 3px; + --r-pill: 100px; + + --font-ui: "Geist", "Manrope", -apple-system, system-ui, sans-serif; + --font-display: "Newsreader", "Cormorant Garamond", Georgia, serif; + --font-mono: "JetBrains Mono", ui-monospace, monospace; + + --ribbon: linear-gradient(90deg, var(--accent-1) 0%, var(--accent-2) 50%, var(--accent-3) 100%); +} + +/* =========================================================== + 3. VARIANT C — Architectural Clean + =========================================================== */ +html[data-variant="c"] { + --paper: #FAFAF7; + --paper-2: #EEEAE0; + --card: #FAFAF7; + --ink: #0F0F0E; + --ink-2: #1F1A14; + --muted: #6E6A62; + --muted-2: #A09C92; + --line: rgba(15, 15, 14, 0.18); + --line-strong: rgba(15, 15, 14, 0.40); + + --accent-1: #3A6B44; /* blueprint green */ + --accent-2: #5A3F26; /* walnut */ + --accent-3: #8C3F1E; + --status-active: #3A6B44; + --status-lapsed: #8C3F1E; + --status-grace: #5A3F26; + + --r-card: 0; + --r-btn: 0; + --r-tag: 4px; + --r-pill: 100px; + + --font-ui: "Geist", -apple-system, system-ui, sans-serif; + --font-display: "Instrument Serif", "Cormorant Garamond", Georgia, serif; + --font-mono: "JetBrains Mono", ui-monospace, monospace; + + --ribbon: linear-gradient(90deg, var(--accent-2) 0%, var(--accent-1) 100%); } /* ============================================================ @@ -97,7 +138,6 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; - font-feature-settings: "ss01", "cv01", "cv11"; } a { color: inherit; text-decoration: none; } @@ -109,10 +149,43 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: #app { max-width: 560px; margin: 0 auto; - padding: var(--s5) var(--s4) calc(var(--s8) + env(safe-area-inset-bottom)); + padding: var(--s4) var(--s4) calc(var(--s8) + env(safe-area-inset-bottom)); min-height: 100vh; } +/* ============================================================ + Theme switcher (top of page) + ============================================================ */ +.theme-switch { + display: flex; + gap: 0; + margin-bottom: var(--s5); + border: 1px solid var(--line-strong); + border-radius: var(--r-tag); + overflow: hidden; + background: var(--card); +} + +.theme-switch button { + flex: 1; + padding: 8px 6px; + font-family: var(--font-mono); + font-size: 10px; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + border-right: 1px solid var(--line-strong); + transition: all 0.12s; +} + +.theme-switch button:last-child { border-right: none; } + +.theme-switch button.active { + background: var(--ink); + color: var(--paper); +} + /* ============================================================ Loader ============================================================ */ @@ -126,19 +199,19 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: width: 32px; height: 32px; border: 1.5px solid var(--line-strong); - border-top-color: var(--zov-blue); - border-radius: 50%; + border-top-color: var(--accent-1); + border-radius: var(--r-pill); animation: spin 0.7s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* ============================================================ - Profile card (header) — editorial premium + Profile card ============================================================ */ .profile-card { background: var(--card); - border: 1px solid var(--line); + border: 1px solid var(--line-strong); border-radius: var(--r-card); padding: var(--s6) var(--s5) var(--s5); margin-bottom: var(--s7); @@ -146,15 +219,12 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: overflow: hidden; } -/* Тонкая брендовая «лента» сверху карточки */ .profile-card::before { content: ""; position: absolute; - top: 0; - left: 0; - right: 0; + top: 0; left: 0; right: 0; height: 3px; - background: linear-gradient(90deg, var(--zov-blue) 0%, var(--zov-green) 100%); + background: var(--ribbon); } .profile-card .role-tag { @@ -164,7 +234,7 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); - margin-bottom: var(--s2); + margin-bottom: var(--s3); display: inline-flex; align-items: center; gap: var(--s2); @@ -176,7 +246,6 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: width: 24px; height: 1px; background: var(--line-strong); - vertical-align: middle; } .profile-card .head-row { @@ -186,10 +255,7 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: margin-bottom: var(--s4); } -.profile-card .info { - flex: 1; - min-width: 0; -} +.profile-card .info { flex: 1; min-width: 0; } .profile-card .name { font-family: var(--font-display); @@ -211,7 +277,7 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: .profile-card .avatar { width: 52px; height: 52px; - border-radius: 50%; + border-radius: var(--r-pill); background: var(--paper-2); display: grid; place-items: center; @@ -220,7 +286,7 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: font-style: italic; font-size: 26px; font-weight: 400; - color: var(--zov-blue); + color: var(--accent-1); border: 1px solid var(--line-strong); } @@ -251,26 +317,26 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: .profile-card .status-dot { width: 7px; height: 7px; - border-radius: 50%; - background: var(--zov-green); - box-shadow: 0 0 0 3px rgba(118, 189, 34, 0.18); + border-radius: var(--r-pill); + background: var(--status-active); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-active) 22%, transparent); animation: pulse 2.5s ease-in-out infinite; } @keyframes pulse { - 0%, 100% { box-shadow: 0 0 0 3px rgba(118, 189, 34, 0.18); } - 50% { box-shadow: 0 0 0 6px rgba(118, 189, 34, 0); } + 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-active) 22%, transparent); } + 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--status-active) 0%, transparent); } } .profile-card .status-dot.lapsed { - background: var(--zov-warning); - box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.20); + background: var(--status-lapsed); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-lapsed) 22%, transparent); animation: none; } .profile-card .status-dot.grace { background: var(--status-grace); - box-shadow: 0 0 0 3px rgba(176, 126, 0, 0.20); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-grace) 22%, transparent); } /* ============================================================ @@ -294,17 +360,15 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: content: ""; flex: 1; height: 1px; - background: var(--line); + background: var(--line-strong); } -.section-label:first-child { margin-top: 0; } - /* ============================================================ - Menu (grouped list — minimal architectural) + Menu (grouped list) ============================================================ */ .menu { background: var(--card); - border: 1px solid var(--line); + border: 1px solid var(--line-strong); border-radius: var(--r-card); overflow: hidden; } @@ -350,15 +414,11 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: stroke-width: 1.6; } -/* Тонкие цветовые акценты для ключевых разделов — без «таблеток» */ -.menu-item .icon.green { color: var(--zov-green); } -.menu-item .icon.blue { color: var(--zov-blue); } -.menu-item .icon.gold { color: var(--status-grace); } +.menu-item .icon.green { color: var(--status-active); } +.menu-item .icon.blue { color: var(--accent-1); } +.menu-item .icon.gold { color: var(--accent-2); } -.menu-item .text { - flex: 1; - min-width: 0; -} +.menu-item .text { flex: 1; min-width: 0; } .menu-item .label { font-family: var(--font-ui); @@ -391,9 +451,7 @@ button { font: inherit; cursor: pointer; border: none; background: none; color: .menu-item.disabled { cursor: not-allowed; } .menu-item.disabled .label, -.menu-item.disabled .icon { color: var(--muted); opacity: 0.7; } -html[data-theme="dark"] .menu-item.disabled .label, -html[data-theme="dark"] .menu-item.disabled .icon { opacity: 0.85; } +.menu-item.disabled .icon { color: var(--muted); opacity: 0.65; } .badge { display: inline-block; @@ -410,7 +468,7 @@ html[data-theme="dark"] .menu-item.disabled .icon { opacity: 0.85; } } /* ============================================================ - Footer hint — editorial signature + Footer ============================================================ */ .footer-hint { text-align: center; @@ -435,17 +493,14 @@ html[data-theme="dark"] .menu-item.disabled .icon { opacity: 0.85; } color: var(--muted); } -.footer-hint a { color: var(--zov-blue); } -@media (prefers-color-scheme: dark) { - .footer-hint a { color: #6FA0D6; } -} +.footer-hint a { color: var(--accent-1); } /* ============================================================ - Error state + Error ============================================================ */ .error { background: var(--card); - border: 1px solid var(--line); + border: 1px solid var(--line-strong); border-radius: var(--r-card); padding: var(--s7) var(--s5); text-align: center; diff --git a/miniapp/index.html b/miniapp/index.html index bdb2362..e2cf7f3 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -10,9 +10,9 @@ ЗОВ — Кабинет - + - +
@@ -20,7 +20,7 @@
- - + +