From 97ea48bc6f70e64c8c8a87ef336d408a45f17e6f Mon Sep 17 00:00:00 2001 From: root Date: Wed, 16 Sep 2026 14:46:08 +0200 Subject: [PATCH] feat: per-theme accent + Activity WhatsApp(en)/GitHub cards, softer icon borders - theme: store accent separately for light/dark (theme-core/controls) - en contacts: WhatsApp card instead of VK (wa.me link) - Activity: add GitHub card (logo webp) in both locales - mp-card__image border uses --line to match card (less contrast) --- public/_lab/lab.js | 7 +- public/assets/misc/github_logo.webp | Bin 0 -> 6036 bytes public/assets/shared/icons/whatsapp.svg | 4 ++ public/css/main.css | 6 +- public/en/cases/ai-assistant-figma/index.html | 2 +- .../cases/ai-platform-primo-rpa-2/index.html | 2 +- .../cases/ai-platform-primo-rpa-v2/index.html | 2 +- .../en/cases/ai-platform-primo-rpa/index.html | 2 +- .../en/cases/card-columns-obsidian/index.html | 2 +- public/en/cases/lunacy/index.html | 2 +- public/en/cases/manu-game-maker/index.html | 2 +- public/en/cases/mobile-clinic-app/index.html | 2 +- .../obsidian-plugin-columns-view/index.html | 2 +- .../en/cases/orc-automation-panel/index.html | 2 +- public/en/cases/site-with-ai-ide/index.html | 2 +- .../task-distribution-concept/index.html | 2 +- public/en/cases/task-tracker/index.html | 2 +- .../cases/template-elements-demo/index.html | 2 +- public/en/index.html | 32 ++++++++- public/index.html | 30 ++++++++ public/js/modules/theme/theme-controls.js | 45 ++++++++---- public/js/modules/theme/theme-core.js | 68 ++++++++++++++---- src/partials/contact-section.en.html | 2 +- src/partials/mini-projects-section.en.html | 30 ++++++++ src/partials/mini-projects-section.html | 30 ++++++++ 25 files changed, 235 insertions(+), 47 deletions(-) create mode 100644 public/assets/misc/github_logo.webp create mode 100644 public/assets/shared/icons/whatsapp.svg diff --git a/public/_lab/lab.js b/public/_lab/lab.js index bc7eded..e8d852a 100644 --- a/public/_lab/lab.js +++ b/public/_lab/lab.js @@ -20,9 +20,10 @@ function apply(patch) { if (state.radius) root.dataset.radius = state.radius; if (state.font) root.dataset.font = state.font; - root.style.setProperty("--theme-h", String(state.h)); - root.style.setProperty("--theme-s", String(state.s)); - root.style.setProperty("--theme-l", String(state.l)); + const labAcc = state[state.mode] || state.light || { h: 210, s: 90, l: 40 }; + root.style.setProperty("--theme-h", String(labAcc.h)); + root.style.setProperty("--theme-s", String(labAcc.s)); + root.style.setProperty("--theme-l", String(labAcc.l)); ensureThemeFontLoaded(state.font); syncButtons(); diff --git a/public/assets/misc/github_logo.webp b/public/assets/misc/github_logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..2edbcd73e18064b8b946fccc678297fe3d07a720 GIT binary patch literal 6036 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 + + + diff --git a/public/css/main.css b/public/css/main.css index 5fd6634..630fc75 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -3383,7 +3383,7 @@ html[data-theme="light"] .hero__portrait-shell { height: auto; object-fit: cover; object-position: center; - border: 1px solid color-mix(in srgb, var(--line) 84%, var(--text) 16%); + border: 1px solid var(--line); border-radius: calc(var(--radius-card) - 4px); box-sizing: border-box; } @@ -4400,6 +4400,10 @@ html[data-radius="strong"] .skill-task-card__solution { --contact-brand: #0077ff; } +.contact-card--brand-whatsapp { + --contact-brand: #25D366; +} + .contact-card--brand-ya { --contact-brand: #f8604a; } diff --git a/public/en/cases/ai-assistant-figma/index.html b/public/en/cases/ai-assistant-figma/index.html index f97eefb..73d7c90 100644 --- a/public/en/cases/ai-assistant-figma/index.html +++ b/public/en/cases/ai-assistant-figma/index.html @@ -362,7 +362,7 @@

Contact

diff --git a/public/en/cases/ai-platform-primo-rpa-2/index.html b/public/en/cases/ai-platform-primo-rpa-2/index.html index 941009e..8732a1f 100644 --- a/public/en/cases/ai-platform-primo-rpa-2/index.html +++ b/public/en/cases/ai-platform-primo-rpa-2/index.html @@ -1008,7 +1008,7 @@

Contact

diff --git a/public/en/cases/ai-platform-primo-rpa-v2/index.html b/public/en/cases/ai-platform-primo-rpa-v2/index.html index 081f329..11330d2 100644 --- a/public/en/cases/ai-platform-primo-rpa-v2/index.html +++ b/public/en/cases/ai-platform-primo-rpa-v2/index.html @@ -1138,7 +1138,7 @@

Contact

diff --git a/public/en/cases/ai-platform-primo-rpa/index.html b/public/en/cases/ai-platform-primo-rpa/index.html index 701e2e5..c373262 100644 --- a/public/en/cases/ai-platform-primo-rpa/index.html +++ b/public/en/cases/ai-platform-primo-rpa/index.html @@ -1172,7 +1172,7 @@

Contact

diff --git a/public/en/cases/card-columns-obsidian/index.html b/public/en/cases/card-columns-obsidian/index.html index 2b73984..f09b3bb 100644 --- a/public/en/cases/card-columns-obsidian/index.html +++ b/public/en/cases/card-columns-obsidian/index.html @@ -758,7 +758,7 @@

Contact

diff --git a/public/en/cases/lunacy/index.html b/public/en/cases/lunacy/index.html index 68ba1c0..a601eae 100644 --- a/public/en/cases/lunacy/index.html +++ b/public/en/cases/lunacy/index.html @@ -960,7 +960,7 @@

Contact

diff --git a/public/en/cases/manu-game-maker/index.html b/public/en/cases/manu-game-maker/index.html index 2f167f3..425ddf6 100644 --- a/public/en/cases/manu-game-maker/index.html +++ b/public/en/cases/manu-game-maker/index.html @@ -702,7 +702,7 @@

Contact

diff --git a/public/en/cases/mobile-clinic-app/index.html b/public/en/cases/mobile-clinic-app/index.html index 4bc76e6..f8c7dd0 100644 --- a/public/en/cases/mobile-clinic-app/index.html +++ b/public/en/cases/mobile-clinic-app/index.html @@ -702,7 +702,7 @@

Contact

diff --git a/public/en/cases/obsidian-plugin-columns-view/index.html b/public/en/cases/obsidian-plugin-columns-view/index.html index 618e3ba..986a0ab 100644 --- a/public/en/cases/obsidian-plugin-columns-view/index.html +++ b/public/en/cases/obsidian-plugin-columns-view/index.html @@ -736,7 +736,7 @@

Contact

diff --git a/public/en/cases/orc-automation-panel/index.html b/public/en/cases/orc-automation-panel/index.html index fb64eea..a1ca989 100644 --- a/public/en/cases/orc-automation-panel/index.html +++ b/public/en/cases/orc-automation-panel/index.html @@ -841,7 +841,7 @@

Contact

diff --git a/public/en/cases/site-with-ai-ide/index.html b/public/en/cases/site-with-ai-ide/index.html index 2af9750..50be3e3 100644 --- a/public/en/cases/site-with-ai-ide/index.html +++ b/public/en/cases/site-with-ai-ide/index.html @@ -362,7 +362,7 @@

Contact

diff --git a/public/en/cases/task-distribution-concept/index.html b/public/en/cases/task-distribution-concept/index.html index bc4aa71..3bffe9e 100644 --- a/public/en/cases/task-distribution-concept/index.html +++ b/public/en/cases/task-distribution-concept/index.html @@ -702,7 +702,7 @@

Contact

diff --git a/public/en/cases/task-tracker/index.html b/public/en/cases/task-tracker/index.html index 7bcfc2c..279b554 100644 --- a/public/en/cases/task-tracker/index.html +++ b/public/en/cases/task-tracker/index.html @@ -1170,7 +1170,7 @@

Contact

diff --git a/public/en/cases/template-elements-demo/index.html b/public/en/cases/template-elements-demo/index.html index c3bffe1..0565238 100644 --- a/public/en/cases/template-elements-demo/index.html +++ b/public/en/cases/template-elements-demo/index.html @@ -1095,7 +1095,7 @@

Contact

diff --git a/public/en/index.html b/public/en/index.html index c33064e..0945068 100644 --- a/public/en/index.html +++ b/public/en/index.html @@ -1265,6 +1265,36 @@ +
  • + + +
    +

    + GitHub + +

    +

    + My projects and code experiments. +

    +
    +
    +
  • Contact diff --git a/public/index.html b/public/index.html index be3b211..dd1dda9 100644 --- a/public/index.html +++ b/public/index.html @@ -1287,6 +1287,36 @@
  • +
  • + + +
    +

    + GitHub + +

    +

    + Мои проекты и эксперименты с кодом. +

    +
    +
    +
  • + btn.addEventListener("click", () => { + if (!panel.hidden) { + syncHueSlider(); + drawSLPlane(canvas, acc().h); + updateKnob(knob, acc().s, acc().l); + } + }), + ); if (meshBgBtns.length >= 2) bindOptionButtons(meshBgBtns, "data-mesh-bg-option", MESH_BG_IDS, "meshBg"); function setPanelOpen(open) { @@ -84,8 +98,8 @@ export function initThemeControls() { btn.setAttribute("aria-label", open ? "Закрыть настройки стиля" : "Настройки стиля"); }); if (open) { - drawSLPlane(canvas, state.h); - updateKnob(knob, state.s, state.l); + drawSLPlane(canvas, acc().h); + updateKnob(knob, acc().s, acc().l); syncAriaRadioGroup(themeModeBtns, "data-theme-mode", state.mode); syncAriaRadioGroup(fontBtns, "data-font-option", state.font); if (meshBgBtns.length >= 2) syncAriaRadioGroup(meshBgBtns, "data-mesh-bg-option", state.meshBg); @@ -95,6 +109,11 @@ export function initThemeControls() { subscribePrefersColorSchemeChange(() => { if (state.mode !== "system") return; applyThemeState(state); + if (!panel.hidden) { + syncHueSlider(); + drawSLPlane(canvas, acc().h); + updateKnob(knob, acc().s, acc().l); + } }); panelBtns.forEach((btn) => { @@ -143,9 +162,10 @@ export function initThemeControls() { if (r.width <= 0 || r.height <= 0) return; const x = clampNum(((clientX - r.left) / r.width) * 100, 0, 100, 0) / 100; const y = clampNum(((clientY - r.top) / r.height) * 100, 0, 100, 0) / 100; - state.s = Math.round(PICK.sMin + x * S_RANGE); - state.l = Math.round(PICK.lMin + (1 - y) * L_RANGE); - updateKnob(knob, state.s, state.l); + const a = acc(); + a.s = Math.round(PICK.sMin + x * S_RANGE); + a.l = Math.round(PICK.lMin + (1 - y) * L_RANGE); + updateKnob(knob, a.s, a.l); persistAndApply(); } @@ -168,10 +188,11 @@ export function initThemeControls() { }); hueSlider.addEventListener("input", () => { - state.h = clampNum(hueSlider.value, 0, 360, state.h); + const a = acc(); + a.h = clampNum(hueSlider.value, 0, 360, a.h); syncHueSlider(); - drawSLPlane(canvas, state.h); - updateKnob(knob, state.s, state.l); + drawSLPlane(canvas, a.h); + updateKnob(knob, a.s, a.l); persistAndApply(); }); } diff --git a/public/js/modules/theme/theme-core.js b/public/js/modules/theme/theme-core.js index a16e4d3..63a5b4a 100644 --- a/public/js/modules/theme/theme-core.js +++ b/public/js/modules/theme/theme-core.js @@ -27,7 +27,14 @@ export const S_RANGE = PICK.sMax - PICK.sMin; export const L_RANGE = PICK.lMax - PICK.lMin; export function defaultThemeState() { - return { mode: "system", h: 210, s: 90, l: 40, radius: "medium", font: "geologica", meshBg: "on" }; + return { + mode: "system", + light: { h: 210, s: 90, l: 40 }, + dark: { h: 210, s: 90, l: 40 }, + radius: "medium", + font: "geologica", + meshBg: "on", + }; } export function effectiveThemeMode(state) { @@ -35,23 +42,53 @@ export function effectiveThemeMode(state) { return state.mode; } +export function activeAccent(state) { + const key = effectiveThemeMode(state); + return state[key] || state.light || { h: 210, s: 90, l: 40 }; +} + +function parseAccent(obj, base) { + if (!obj) return { ...base }; + return { + h: clampNum(obj.h ?? obj.hue, 0, 360, base.h), + s: clampNum(obj.s ?? obj.sat, PICK.sMin, PICK.sMax, base.s), + l: clampNum(obj.l ?? obj.light, PICK.lMin, PICK.lMax, base.l), + }; +} + export function readThemeState() { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return defaultThemeState(); const o = JSON.parse(raw); const base = defaultThemeState(); - const hRaw = o.h ?? o.hue; - const h = clampNum(hRaw, 0, 360, base.h); - const s = clampNum(o.s ?? o.sat, PICK.sMin, PICK.sMax, base.s); - const lRaw = o.l ?? o.light; - const l = clampNum(lRaw, PICK.lMin, PICK.lMax, base.l); const mode = o.mode === "light" || o.mode === "dark" || o.mode === "system" ? o.mode : base.mode; const radius = RADIUS_IDS.includes(o.radius) ? o.radius : base.radius; const font = normalizeFontId(o.font ?? base.font); const meshBg = MESH_BG_IDS.includes(o.meshBg) ? o.meshBg : base.meshBg; - return { mode, h, s, l, radius, font, meshBg }; + + let light = base.light; + let dark = base.dark; + if (o.light || o.dark) { + light = parseAccent(o.light, base.light); + dark = parseAccent(o.dark, base.dark); + } else if ( + o.h !== undefined || + o.hue !== undefined || + o.s !== undefined || + o.sat !== undefined || + o.l !== undefined || + o.light !== undefined + ) { + // legacy: единый акцент → копируем в обе темы + const h = clampNum(o.h ?? o.hue, 0, 360, base.light.h); + const s = clampNum(o.s ?? o.sat, PICK.sMin, PICK.sMax, base.light.s); + const l = clampNum(o.l ?? o.light, PICK.lMin, PICK.lMax, base.light.l); + light = { h, s, l }; + dark = { h, s, l }; + } + return { mode, light, dark, radius, font, meshBg }; } catch { return defaultThemeState(); } @@ -62,9 +99,8 @@ export function writeThemeState(state) { STORAGE_KEY, JSON.stringify({ mode: state.mode, - h: state.h, - s: state.s, - l: state.l, + light: state.light, + dark: state.dark, radius: state.radius, font: state.font, meshBg: state.meshBg, @@ -110,14 +146,16 @@ export function relativeLuminance(rgb) { export function applyThemeState(state) { const root = document.documentElement; - root.dataset.theme = effectiveThemeMode(state); + const mode = effectiveThemeMode(state); + root.dataset.theme = mode; root.dataset.radius = state.radius; root.dataset.font = state.font; root.dataset.meshBg = state.meshBg === "off" ? "off" : "on"; - root.style.setProperty("--theme-h", String(state.h)); - root.style.setProperty("--theme-s", String(state.s)); - root.style.setProperty("--theme-l", String(state.l)); - const rgb = hslToRgb(state.h, state.s, state.l); + const acc = activeAccent(state); + root.style.setProperty("--theme-h", String(acc.h)); + root.style.setProperty("--theme-s", String(acc.s)); + root.style.setProperty("--theme-l", String(acc.l)); + const rgb = hslToRgb(acc.h, acc.s, acc.l); const lum = relativeLuminance(rgb); root.style.setProperty("--on-accent", lum > 0.58 ? "hsl(220 14% 12%)" : "hsl(0 0% 98%)"); window.dispatchEvent(new CustomEvent("site-theme-applied", { bubbles: true })); diff --git a/src/partials/contact-section.en.html b/src/partials/contact-section.en.html index 163f0f3..58f581a 100644 --- a/src/partials/contact-section.en.html +++ b/src/partials/contact-section.en.html @@ -3,7 +3,7 @@

    Contact

    diff --git a/src/partials/mini-projects-section.en.html b/src/partials/mini-projects-section.en.html index b9e3a3b..adb4716 100644 --- a/src/partials/mini-projects-section.en.html +++ b/src/partials/mini-projects-section.en.html @@ -108,6 +108,36 @@
  • +
  • + + +
    +

    + GitHub + +

    +

    + My projects and code experiments. +

    +
    +
    +
  • +
  • + + +
    +

    + GitHub + +

    +

    + Мои проекты и эксперименты с кодом. +

    +
    +
    +