Add V1/V2 design version toggle

Introduces data-design-version attribute on <html> with localStorage
persistence and anti-flicker inline script. V2 overrides implement the
design audit: Cormorant serif headings, #7D021D wine palette, warm
#FFFDFA base, refreshed product card, mega-menu, contacts, and form
success state. V1 remains untouched.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Alexey S 2026-04-22 01:33:41 +03:00
parent 24228a41be
commit 6ff7e97046
6 changed files with 558 additions and 2 deletions

View File

@ -4,8 +4,9 @@
{
"name": "11ty dev server (live reload)",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"port": 8080
"runtimeArgs": ["run", "dev", "--", "--port=8088"],
"port": 8088,
"autoPort": false
},
{
"name": "Python HTTP Server (_site)",

View File

@ -4,7 +4,18 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ title }}</title>
<script>
(function () {
try {
var v = localStorage.getItem('dp-design-version') || 'v1';
document.documentElement.setAttribute('data-design-version', v);
} catch (e) {
document.documentElement.setAttribute('data-design-version', 'v1');
}
})();
</script>
<link rel="stylesheet" href="css/{{ css | default('site') }}.css" />
<link rel="stylesheet" href="css/v2.css" />
<!-- Privacy-friendly analytics by Plausible -->
<script async src="https://metrika.in20.ru/js/pa-LKQHOT3En5k9NYpn80cYX.js"></script>
<script>
@ -15,5 +26,6 @@
<body{% if bodyClass %} class="{{ bodyClass }}"{% endif %}>
{{ content | safe }}
<script src="js/inspector.js"></script>
<script src="js/design-switcher.js" defer></script>
</body>
</html>

View File

@ -10,6 +10,10 @@
</form>
<div class="header-actions">
<div class="design-toggle" role="group" aria-label="Версия дизайна">
<button type="button" class="design-toggle__btn" data-version="v1">V1</button>
<button type="button" class="design-toggle__btn" data-version="v2">V2</button>
</div>
<a class="phone-link" href="tel:+74959379460">+7 (495) 937-94-60</a>
<a class="header-icon header-icon--air" href="#" aria-label="Аккаунт">
<svg viewBox="0 0 24 24" aria-hidden="true">

View File

@ -638,6 +638,7 @@
--color-neutral-gray-300: #d9dee6;
--color-background-base: #f4f6f9;
--color-surface: #ffffff;
--color-surface-warm: #F5F0E8;
--color-accent-gold: #b9965b;
--color-accent-blue: #1f3476;
@ -663,3 +664,22 @@
--shadow-lift: 0 20px 52px rgba(75, 15, 36, 0.14);
--container: 1240px;
}
/* ── V2 design version (design audit followup) ───────────────────────── */
:root[data-design-version="v2"] {
--color-primary-wine-100: #7D021D;
--color-primary-wine-80: #9b1a33;
--color-background-base: #FFFDFA;
--color-surface: #ffffff;
--color-surface-warm: #F5F0E8;
--color-accent-blue: #7D021D;
--font-heading: "Cormorant Garamond", Georgia, serif;
--font-body: "Manrope", "Inter", Arial, sans-serif;
--font-heading-retail: "Cormorant Garamond", Georgia, serif;
--radius-sm: 4px;
--radius-md: 8px;
--shadow-lift: 0 20px 52px rgba(125, 2, 29, 0.12);
}

399
src/css/v2.css Normal file
View File

@ -0,0 +1,399 @@
/* =========================================================================
DP Trade V2 overrides (design audit followup)
Activates when <html data-design-version="v2">. V1 stays untouched.
========================================================================= */
/* ── Design toggle (visible in both versions) ─────────────────────────── */
.design-toggle {
display: inline-flex;
padding: 2px;
border: 1px solid rgba(22, 22, 22, 0.18);
border-radius: 999px;
background: rgba(255, 255, 255, 0.6);
gap: 2px;
}
.design-toggle__btn {
appearance: none;
border: 0;
background: transparent;
padding: 6px 14px;
border-radius: 999px;
font: 600 12px/1 var(--font-body);
letter-spacing: 0.06em;
color: var(--color-neutral-gray-700);
cursor: pointer;
}
.design-toggle__btn.is-active {
background: var(--color-primary-wine-100);
color: #fff;
}
/* V2-only DOM nodes hidden under V1 */
[data-design-version="v1"] [data-v2-only] { display: none !important; }
[data-design-version="v2"] [data-v2-hide] { display: none !important; }
/* Breadcrumbs */
.breadcrumbs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
padding-top: 24px;
padding-bottom: 8px;
font-size: 13px;
color: var(--color-neutral-gray-600);
}
.breadcrumbs a {
color: var(--color-neutral-gray-600);
text-decoration: none;
}
.breadcrumbs a:hover { color: var(--color-primary-wine-100); }
.breadcrumbs [aria-current="page"] { color: var(--color-neutral-black); }
/* Form success */
.form-success {
margin-top: 12px;
padding: 14px 16px;
border-radius: var(--radius-sm);
background: #F5F0E8;
color: var(--color-primary-wine-100);
font-weight: 500;
}
/* =========================================================================
V2 SCOPE everything below only applies when data-design-version="v2"
========================================================================= */
/* ── Global typography ────────────────────────────────────────────────── */
[data-design-version="v2"] body {
font-family: var(--font-body);
background: var(--color-background-base);
}
[data-design-version="v2"] h1,
[data-design-version="v2"] h2,
[data-design-version="v2"] h3,
[data-design-version="v2"] h4 {
font-family: var(--font-heading);
text-transform: none !important;
font-weight: 500;
letter-spacing: 0;
}
[data-design-version="v2"] .brand-logo,
[data-design-version="v2"] .brand-logo span:first-child {
text-transform: none;
}
[data-design-version="v2"] .brand-logo small { font-size: 12px; }
[data-design-version="v2"] .phone-link {
color: var(--color-primary-wine-100);
font-weight: 500;
}
/* ── Hero (home) ──────────────────────────────────────────────────────── */
[data-design-version="v2"] .hero h1 {
font-family: var(--font-heading);
font-size: clamp(48px, 6vw, 80px);
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
line-height: 1.05;
}
[data-design-version="v2"] .section-heading h2 {
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
/* ── Product card (grid) ──────────────────────────────────────────────── */
[data-design-version="v2"] .product-card {
background: var(--color-surface-warm);
border-color: rgba(22, 22, 22, 0.08);
}
[data-design-version="v2"] .product-media,
[data-design-version="v2"] .product-media--amber,
[data-design-version="v2"] .product-media--green {
background: #F5F0E8 !important;
}
[data-design-version="v2"] .product-card h3 {
font-family: var(--font-heading);
font-size: 20px;
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
[data-design-version="v2"] .product-footer {
padding-top: 14px;
border-top: 1px solid rgba(22, 22, 22, 0.08);
flex-direction: column;
align-items: stretch;
gap: 10px;
}
[data-design-version="v2"] .product-footer strong {
font-family: var(--font-body);
font-size: 24px;
font-weight: 700;
color: var(--color-primary-wine-100);
}
[data-design-version="v2"] .product-footer .button,
[data-design-version="v2"] .product-card .button--primary {
background: transparent;
color: var(--color-primary-wine-100);
border: 1px solid var(--color-primary-wine-100);
width: 100%;
justify-content: center;
font-weight: 500;
}
[data-design-version="v2"] .product-footer .button:hover,
[data-design-version="v2"] .product-card .button--primary:hover {
background: var(--color-primary-wine-100);
color: #fff;
}
/* Compact grid on home */
[data-design-version="v2"] .product-grid--compact .product-card h3 {
font-size: 17px;
}
/* List cards (catalog) — same typography refresh */
[data-design-version="v2"] .product-card--list h3 {
font-family: var(--font-heading);
text-transform: none !important;
letter-spacing: 0;
font-weight: 500;
}
/* ── Catalog head ─────────────────────────────────────────────────────── */
[data-design-version="v2"] .catalog-head h1 {
font-family: var(--font-heading);
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
/* ── Mega menu ────────────────────────────────────────────────────────── */
[data-design-version="v2"] .mega-intro h2 {
font-size: 18px;
font-weight: 600;
text-transform: none !important;
letter-spacing: 0;
font-family: var(--font-heading);
}
[data-design-version="v2"] .visual-column img {
max-width: 160px;
max-height: 100px;
width: 160px;
height: 100px;
object-fit: cover;
border-radius: 4px;
}
[data-design-version="v2"] .country-link {
color: var(--color-accent-gold);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
[data-design-version="v2"] .appellation-group a:not(.country-link) {
padding-left: 12px;
font-size: 14px;
text-transform: none;
font-weight: 400;
}
[data-design-version="v2"] .popular-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
[data-design-version="v2"] .popular-list a {
min-width: 120px;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px 14px;
border: 1px solid rgba(22, 22, 22, 0.12);
border-radius: 999px;
text-transform: none;
letter-spacing: 0;
font-weight: 500;
}
/* Producer cards */
[data-design-version="v2"] .producer-card strong,
[data-design-version="v2"] .news-card h3 {
font-family: var(--font-heading);
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
[data-design-version="v2"] .news-section { background: var(--color-surface-warm); }
/* ── Page hero (about / contacts / guidelines) ────────────────────────── */
[data-design-version="v2"] .page-hero h1 {
font-family: var(--font-heading);
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
line-height: 1.08;
}
[data-design-version="v2"] .page-hero--about h1 {
font-size: clamp(40px, 4vw, 56px);
}
[data-design-version="v2"] .page-hero--contacts {
padding: 48px 0 24px;
background-image: none;
background: var(--color-background-base);
color: var(--color-neutral-black);
}
[data-design-version="v2"] .page-hero--contacts h1 {
font-size: clamp(32px, 3.4vw, 48px);
color: var(--color-neutral-black);
}
[data-design-version="v2"] .page-hero--contacts p {
color: var(--color-neutral-gray-700);
font-size: 16px;
}
[data-design-version="v2"] .page-hero--contacts .page-hero__meta {
box-shadow: none;
background: var(--color-surface-warm);
}
[data-design-version="v2"] .page-hero__meta strong {
font-family: var(--font-body);
font-size: 18px;
font-weight: 600;
text-transform: none !important;
letter-spacing: 0;
color: var(--color-primary-wine-100);
}
/* ── About / article ──────────────────────────────────────────────────── */
[data-design-version="v2"] .article-body { max-width: 640px; }
[data-design-version="v2"] .article-body h2 {
font-family: var(--font-heading);
font-size: clamp(22px, 2.4vw, 28px);
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
[data-design-version="v2"] .article-body .lead {
font-family: var(--font-heading);
font-weight: 500;
}
[data-design-version="v2"] .article-body blockquote {
border-left: 1px solid rgba(22, 22, 22, 0.2);
background: transparent;
font-family: var(--font-heading);
font-style: italic;
font-weight: 500;
color: var(--color-neutral-black);
}
[data-design-version="v2"] .feature-card {
min-height: 0;
align-content: start;
gap: 8px;
}
[data-design-version="v2"] .feature-card h3 {
font-family: var(--font-heading);
font-size: 20px;
text-transform: none !important;
letter-spacing: 0;
font-weight: 500;
}
[data-design-version="v2"] .muted-section,
[data-design-version="v2"] .info-card--accent {
background: var(--color-surface-warm);
}
/* ── Contacts ─────────────────────────────────────────────────────────── */
[data-design-version="v2"] .contact-panel a {
font-family: var(--font-body);
font-size: 22px;
font-weight: 600;
letter-spacing: 0;
color: var(--color-primary-wine-100);
}
/* Move legal block to bottom of stack */
[data-design-version="v2"] .contact-stack {
display: flex;
flex-direction: column;
}
[data-design-version="v2"] .contact-panel:has(> span:only-of-type) {
/* no-op fallback; target by text below if :has unsupported */
}
[data-design-version="v2"] .contact-stack .contact-panel:last-child {
order: 99;
font-size: 13px;
background: transparent;
box-shadow: none;
border-color: rgba(22, 22, 22, 0.08);
padding: 16px 20px;
}
[data-design-version="v2"] .contact-stack .contact-panel:last-child span {
color: var(--color-neutral-gray-600);
font-weight: 500;
}
/* Contact form */
[data-design-version="v2"] .contact-form h2 {
font-family: var(--font-heading);
font-size: 24px;
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
[data-design-version="v2"] .contact-form label {
font-weight: 400;
color: var(--color-neutral-gray-700);
}
/* ── Buttons (global) ─────────────────────────────────────────────────── */
[data-design-version="v2"] .button--primary {
background: var(--color-primary-wine-100);
border-color: var(--color-primary-wine-100);
text-transform: none;
letter-spacing: 0;
}
[data-design-version="v2"] .button--secondary {
background: var(--color-surface-warm);
color: var(--color-primary-wine-100);
border-color: transparent;
text-transform: none;
letter-spacing: 0;
}
/* ── Product detail page ──────────────────────────────────────────────── */
[data-design-version="v2"] .detail-copy h1 {
font-family: var(--font-heading);
font-size: clamp(36px, 4vw, 56px);
font-weight: 500;
text-transform: none !important;
letter-spacing: 0;
}
[data-design-version="v2"] .detail-copy > p:not(.eyebrow) {
max-width: 66ch;
}
[data-design-version="v2"] .detail-copy .hero-actions { margin-top: 16px; }
/* Remove cool background conflicts */
[data-design-version="v2"] .news-section,
[data-design-version="v2"] .section--muted {
background: var(--color-surface-warm);
}

120
src/js/design-switcher.js Normal file
View File

@ -0,0 +1,120 @@
(function () {
'use strict';
var KEY = 'dp-design-version';
var root = document.documentElement;
function getVersion() {
try {
return localStorage.getItem(KEY) || 'v1';
} catch (e) {
return 'v1';
}
}
function setVersion(v) {
try {
localStorage.setItem(KEY, v);
} catch (e) {}
}
function syncButtons(v) {
var buttons = document.querySelectorAll('.design-toggle__btn');
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var active = btn.dataset.version === v;
btn.classList.toggle('is-active', active);
btn.setAttribute('aria-pressed', active ? 'true' : 'false');
}
}
// ── V2 DOM enhancements ────────────────────────────────────────────────
// All insertions are idempotent and tagged data-v2-only so V1 CSS hides them.
function ensureBreadcrumbs() {
// Only on product page — detect by .product-detail.
var detail = document.querySelector('.product-detail');
if (!detail) return;
var main = detail.closest('main');
if (!main || main.querySelector('[data-v2-only="breadcrumbs"]')) return;
var nav = document.createElement('nav');
nav.className = 'breadcrumbs container';
nav.setAttribute('data-v2-only', 'breadcrumbs');
nav.setAttribute('aria-label', 'Хлебные крошки');
nav.innerHTML =
'<a href="index.html">Главная</a>' +
'<span aria-hidden="true">/</span>' +
'<a href="catalog.html">Каталог</a>' +
'<span aria-hidden="true">/</span>' +
'<a href="#">Bordeaux</a>' +
'<span aria-hidden="true">/</span>' +
'<span aria-current="page">Chateau Laroque Grand Cru</span>';
main.insertBefore(nav, detail.parentElement === main ? detail : main.firstChild);
}
function ensureSplitContactFields() {
var form = document.querySelector('.contact-form');
if (!form || form.dataset.v2Enhanced === '1') return;
var labels = form.querySelectorAll('label');
for (var i = 0; i < labels.length; i++) {
if (/Телефон или email/i.test(labels[i].textContent)) {
var comboLabel = labels[i];
comboLabel.setAttribute('data-v2-hide', '1');
var phoneLabel = document.createElement('label');
phoneLabel.setAttribute('data-v2-only', 'split');
phoneLabel.innerHTML = 'Телефон<input class="input" type="tel" name="phone-v2" placeholder="+7 (___) ___-__-__" />';
var emailLabel = document.createElement('label');
emailLabel.setAttribute('data-v2-only', 'split');
emailLabel.innerHTML = 'Email<input class="input" type="email" name="email-v2" placeholder="you@company.ru" />';
comboLabel.parentNode.insertBefore(phoneLabel, comboLabel.nextSibling);
comboLabel.parentNode.insertBefore(emailLabel, phoneLabel.nextSibling);
break;
}
}
form.dataset.v2Enhanced = '1';
form.addEventListener('submit', function (e) {
if (root.getAttribute('data-design-version') !== 'v2') return;
e.preventDefault();
if (form.querySelector('.form-success')) return;
var success = document.createElement('div');
success.className = 'form-success';
success.setAttribute('data-v2-only', 'success');
success.textContent = 'Заявка отправлена. Менеджер свяжется с вами в ближайшее время.';
form.appendChild(success);
var btn = form.querySelector('button[type="submit"]');
if (btn) {
btn.disabled = true;
btn.textContent = 'Заявка отправлена';
}
});
}
function enhanceDomV2() {
ensureBreadcrumbs();
ensureSplitContactFields();
}
function apply(v) {
root.setAttribute('data-design-version', v);
syncButtons(v);
if (v === 'v2') enhanceDomV2();
}
document.addEventListener('click', function (e) {
var btn = e.target.closest && e.target.closest('.design-toggle__btn');
if (!btn) return;
var v = btn.dataset.version;
if (!v) return;
setVersion(v);
apply(v);
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () { apply(getVersion()); });
} else {
apply(getVersion());
}
})();