Migrate to 11ty and add prototype inspector
- Set up 11ty with Nunjucks templates (src/ → _site/) - Extract shared header and footer into partials — single source of truth - Convert all 11 pages to .njk with front matter (layout, title, permalink) - Add base/default/ui-kit layout chain - Add custom JS element inspector (Alt+I) with Typography, Tokens and Styles panels - Add CLAUDE.md with architecture overview and dev commands - Add .claude/launch.json with dev server configs - Add docs/elementor-token-handoff.md - Add _site/ to .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
17
.claude/launch.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "11ty dev server (live reload)",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "dev"],
|
||||
"port": 8080
|
||||
},
|
||||
{
|
||||
"name": "Python HTTP Server (_site)",
|
||||
"runtimeExecutable": "python3",
|
||||
"runtimeArgs": ["-m", "http.server", "8081", "--directory", "/Users/lexx/Projects/2026/dp-trade-UI-kit/_site"],
|
||||
"port": 8081
|
||||
}
|
||||
]
|
||||
}
|
||||
15
.eleventy.js
Normal file
@ -0,0 +1,15 @@
|
||||
export default function (eleventyConfig) {
|
||||
eleventyConfig.addPassthroughCopy("src/css");
|
||||
eleventyConfig.addPassthroughCopy("src/assets");
|
||||
eleventyConfig.addPassthroughCopy("src/js");
|
||||
|
||||
return {
|
||||
dir: {
|
||||
input: "src",
|
||||
output: "_site",
|
||||
includes: "_includes",
|
||||
},
|
||||
templateFormats: ["njk", "html"],
|
||||
htmlTemplateEngine: "njk",
|
||||
};
|
||||
}
|
||||
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
_site/
|
||||
.env
|
||||
|
||||
100
CLAUDE.md
Normal file
@ -0,0 +1,100 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
Перед началом работы над любой задачей сложнее одного шага
|
||||
составь todo-список и показывай его прогресс по ходу выполнения.
|
||||
Для задач с неочевидным подходом сначала предложи план и дождись подтверждения.
|
||||
|
||||
## Project
|
||||
|
||||
Static HTML/CSS UI-kit and page prototypes for **DP Trade** — a B2B wine wholesale catalog (wine-dp-trade.ru). The output of this project is handed off to **WordPress/Elementor** for production deployment. See `docs/elementor-token-handoff.md` for the full Elementor migration guide.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Dev server (11ty with live reload)
|
||||
npm run dev # http://localhost:8080
|
||||
|
||||
# Production build → _site/
|
||||
npm run build
|
||||
```
|
||||
|
||||
Dev server config is also saved in `.claude/launch.json` for use with `preview_start`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### CSS layer (single dependency direction)
|
||||
|
||||
```
|
||||
css/tokens.css ← design tokens only (colors, fonts, spacing, radius, shadows)
|
||||
↓
|
||||
css/site.css ← all page styles, imports tokens.css
|
||||
css/ui-kit.css ← design system showcase styles, standalone
|
||||
```
|
||||
|
||||
Never put visual styles in `tokens.css`. Never import `site.css` into `ui-kit.css`.
|
||||
|
||||
### 11ty template structure (`src/`)
|
||||
|
||||
```
|
||||
src/
|
||||
_includes/
|
||||
layouts/
|
||||
base.njk ← HTML shell: <head>, fonts, CSS link, <body class="{{ bodyClass }}">
|
||||
default.njk ← base + header partial + footer partial (used by all site pages)
|
||||
ui-kit.njk ← base only, no header/footer (used by ui-kit page)
|
||||
partials/
|
||||
header.njk ← full site header with mega-menu nav
|
||||
footer.njk ← full site footer
|
||||
css/ ← passthrough copy → _site/css/
|
||||
assets/ ← passthrough copy → _site/assets/
|
||||
*.njk ← one file per page
|
||||
```
|
||||
|
||||
### Page front matter
|
||||
|
||||
Every page declares layout, title, optional bodyClass, and permalink:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "DP Trade — Catalog"
|
||||
layout: default # or "ui-kit" for the design system page
|
||||
bodyClass: compact-type # omit if not needed
|
||||
permalink: /catalog.html # keeps flat output, preserves relative CSS paths
|
||||
---
|
||||
```
|
||||
|
||||
Pages with `bodyClass: compact-type`: catalog, about, contacts, article-guidelines.
|
||||
The `ui-kit` page uses `layout: ui-kit` and `css: ui-kit` (links `ui-kit.css` instead of `site.css`).
|
||||
|
||||
### CSS paths
|
||||
|
||||
All CSS paths in `base.njk` are **absolute** (`/css/site.css`), which requires the 11ty dev server — they will not work when opening `_site/` files directly in a browser via `file://`.
|
||||
|
||||
### Design tokens
|
||||
|
||||
Key CSS custom properties defined in `css/tokens.css`:
|
||||
|
||||
- Colors: `--color-primary-wine-100` (#4b0f24), `--color-accent-gold` (#b9965b), `--color-accent-blue` (#1f3476)
|
||||
- Fonts: `--font-heading` (Montserrat 800), `--font-body` (Inter), `--font-heading-classic` (Playfair Display)
|
||||
- Container: `--container: 1240px`
|
||||
|
||||
When adding new tokens, add to `tokens.css` and mirror to the Elementor CSS layer in `docs/elementor-token-handoff.md` (Section 6).
|
||||
|
||||
### Responsive breakpoints
|
||||
|
||||
Defined at the bottom of `css/site.css`:
|
||||
|
||||
- `@media (max-width: 1080px)` — tablet
|
||||
- `@media (max-width: 720px)` — mobile
|
||||
|
||||
## Key files
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------- | ------------------------------------------------------------ |
|
||||
| `css/tokens.css` | Single source of truth for all design values |
|
||||
| `docs/elementor-token-handoff.md` | Guide for migrating tokens/components to WordPress Elementor |
|
||||
| `.claude/launch.json` | Dev server configurations |
|
||||
| `src/_includes/partials/header.njk` | Mega-menu navigation — edit here, propagates to all pages |
|
||||
| `src/_includes/partials/footer.njk` | Footer — edit here, propagates to all pages |
|
||||
666
docs/elementor-token-handoff.md
Normal file
@ -0,0 +1,666 @@
|
||||
# Перенос UI-kit токенов и элементов в Elementor
|
||||
|
||||
Документ описывает безопасный перенос дизайн-токенов и базовых элементов DP Trade UI-kit в Elementor WordPress. Главная цель: внедрить новую дизайн-систему постепенно, не ломая текущие страницы и глобальные настройки сайта.
|
||||
|
||||
## 1. Базовый принцип переноса
|
||||
|
||||
Не заменяйте существующие настройки Elementor сразу. Сначала добавьте новые токены рядом со старыми, соберите тестовую страницу, проверьте визуально, затем постепенно переводите реальные шаблоны и страницы на новые значения.
|
||||
|
||||
Рекомендуемый порядок:
|
||||
|
||||
1. Сделать backup сайта и базы данных.
|
||||
2. Работать на staging-копии или тестовой странице.
|
||||
3. Добавить новые Global Colors.
|
||||
4. Добавить новые Global Fonts.
|
||||
5. Настроить базовые элементы: кнопки, поля, карточки, теги.
|
||||
6. Создать страницу `UI Kit Preview` в WordPress.
|
||||
7. Проверить desktop, tablet, mobile.
|
||||
8. Только после проверки применять к Header, Footer, каталогу и страницам.
|
||||
|
||||
## 2. Источники токенов в проекте
|
||||
|
||||
Основные файлы:
|
||||
|
||||
- `css/tokens.css` — базовые дизайн-токены сайта.
|
||||
- `css/site.css` — стили реальных страниц, header, footer, карточек, каталога, форм.
|
||||
- `css/ui-kit.css` — витрина UI-kit, состояния и примеры компонентов.
|
||||
- `ui-kit.html` — живая HTML-витрина дизайн-системы.
|
||||
|
||||
В Elementor переносите не весь CSS целиком, а роли и значения: цвета, шрифты, отступы, радиусы, тени и повторяемые компоненты.
|
||||
|
||||
## 3. Карта цветов для Elementor Global Colors
|
||||
|
||||
Перейдите в:
|
||||
|
||||
`Elementor -> Site Settings -> Global Colors`
|
||||
|
||||
Добавьте новые цвета. Старые цвета пока не удаляйте.
|
||||
|
||||
| Elementor name | CSS token | Value | Role |
|
||||
|---|---:|---:|---|
|
||||
| DP Wine 100 | `--color-primary-wine-100` | `#4b0f24` | Основной брендовый цвет, primary buttons, hover menu, акценты |
|
||||
| DP Wine 80 | `--color-primary-wine-80` | `#6d1c36` | Hover для primary, вторичный винный оттенок |
|
||||
| DP Black | `--color-neutral-black` | `#161616` | Основной текст |
|
||||
| DP Gray 700 | `--color-neutral-gray-700` | `#30343a` | Навигация, плотный вторичный текст |
|
||||
| DP Gray 600 | `--color-neutral-gray-600` | `#66605f` | Описания, muted text, подписи |
|
||||
| DP Gray 300 | `--color-neutral-gray-300` | `#d9dee6` | Бордеры, разделители |
|
||||
| DP Background | `--color-background-base` | `#f4f6f9` | Основной фон страниц |
|
||||
| DP Surface | `--color-surface` | `#ffffff` | Карточки, поля, панели |
|
||||
| DP Gold | `--color-accent-gold` | `#b9965b` | Eyebrow, статусы, премиальные акценты |
|
||||
| DP Blue | `--color-accent-blue` | `#1f3476` | Логотип, телефон, информационные акценты |
|
||||
| DP Error | `--color-error` | `#a33a2f` | Ошибки форм |
|
||||
|
||||
Важно: если в текущем Elementor уже есть цвета `Primary`, `Secondary`, `Text`, `Accent`, не заменяйте их сразу. Добавьте цвета с префиксом `DP`, затем перепривязывайте элементы вручную.
|
||||
|
||||
## 4. Карта шрифтов для Elementor Global Fonts
|
||||
|
||||
Перейдите в:
|
||||
|
||||
`Elementor -> Site Settings -> Global Fonts`
|
||||
|
||||
Подключение шрифтов:
|
||||
|
||||
- Headings: `Montserrat`
|
||||
- Body: `Inter`
|
||||
- Classic heading, если понадобится для редакционных блоков: `Playfair Display`
|
||||
|
||||
| Elementor style | Font family | Weight | Size desktop | Line height | Transform |
|
||||
|---|---|---:|---:|---:|---|
|
||||
| DP H1 | `Montserrat` | `800` | `48-72px` | `1.1` | Uppercase |
|
||||
| DP H2 | `Montserrat` | `800` | `36-40px` | `1.1-1.22` | Uppercase |
|
||||
| DP H3 | `Montserrat` | `800` | `24-28px` | `1.15-1.25` | Uppercase |
|
||||
| DP Body | `Inter` | `400` | `16px` | `1.5` | None |
|
||||
| DP Body Large | `Inter` | `400` | `18px` | `28px` | None |
|
||||
| DP Caption | `Inter` | `600-700` | `12-13px` | `16px` | Optional uppercase |
|
||||
| DP Button | `Inter` | `700` | `14-16px` | `1.2` | None |
|
||||
| DP Nav | `Inter` | `800` | `12px` | `1.2` | Uppercase |
|
||||
|
||||
Рекомендация по responsive:
|
||||
|
||||
| Style | Tablet | Mobile |
|
||||
|---|---:|---:|
|
||||
| DP H1 | `40-48px` | `34-40px` |
|
||||
| DP H2 | `32-36px` | `28-32px` |
|
||||
| DP H3 | `22-24px` | `20-22px` |
|
||||
| DP Body | `16px` | `16px` |
|
||||
| DP Button | `14-16px` | `14px` |
|
||||
|
||||
Не используйте viewport-based font size в Elementor. Лучше задавать отдельные значения для desktop, tablet и mobile.
|
||||
|
||||
## 5. Spacing, radius, shadows
|
||||
|
||||
В Elementor нет полноценной системы design tokens для spacing, поэтому переносите значения как правила для секций, контейнеров и классов.
|
||||
|
||||
### Spacing scale
|
||||
|
||||
| Token | Value | Где использовать |
|
||||
|---|---:|---|
|
||||
| `--spacing-4` | `4px` | Мелкие зазоры, иконки |
|
||||
| `--spacing-8` | `8px` | Gap внутри кнопок, compact controls |
|
||||
| `--spacing-16` | `16px` | Карточки, поля, grid gap |
|
||||
| `--spacing-24` | `24px` | Padding карточек, блоки форм |
|
||||
| `--spacing-32` | `32px` | Gap между колонками |
|
||||
| `--spacing-48` | `48px` | Крупные отступы секций |
|
||||
| `--spacing-64` | `64px` | Hero, большие секции |
|
||||
|
||||
### Layout
|
||||
|
||||
| Token | Value | Elementor setting |
|
||||
|---|---:|---|
|
||||
| `--container` | `1240px` | Site Settings -> Layout -> Content Width |
|
||||
| Section desktop padding | `72px 24-72px` | Section/container padding |
|
||||
| Section mobile padding | `48px 20px` | Responsive padding |
|
||||
| Container side gap | `20px` each side | Container width: `min(100% - 40px, 1240px)` в CSS |
|
||||
|
||||
### Radius
|
||||
|
||||
| Token | Value | Где использовать |
|
||||
|---|---:|---|
|
||||
| `--radius-sm` | `8px` | Кнопки, поля, карточки, теги |
|
||||
| `--radius-md` | `12px` | Крупные панели, preview blocks |
|
||||
| `--radius-lg` | `24px` | Редкие крупные акцентные блоки |
|
||||
|
||||
### Shadows
|
||||
|
||||
Elementor поддерживает box-shadow в Advanced/Style, но удобнее завести CSS-классы.
|
||||
|
||||
| Token | Value | Где использовать |
|
||||
|---|---|---|
|
||||
| `--shadow-soft` | `0 12px 32px rgba(22, 22, 22, 0.08)` | Обычные карточки, формы, footer panel |
|
||||
| `--shadow-lift` | `0 20px 52px rgba(75, 15, 36, 0.14)` | Hover карточек, mega menu, primary hover |
|
||||
| Header shadow | `0 10px 28px rgba(22, 22, 22, 0.05)` | Sticky header |
|
||||
| Focus shadow | `0 0 0 4px rgba(75, 15, 36, 0.08)` | Search/input focus |
|
||||
|
||||
## 6. Безопасный CSS-слой для Elementor
|
||||
|
||||
Добавляйте CSS не через глобальные теги `body`, `h1`, `button`, `input`, а через классы с префиксом `dp-`.
|
||||
|
||||
Где добавлять:
|
||||
|
||||
1. Сначала на тестовой странице: `Page Settings -> Advanced -> Custom CSS`.
|
||||
2. После проверки: `Elementor -> Site Settings -> Custom CSS`.
|
||||
3. Если есть child theme, финальную версию можно перенести в `style.css`.
|
||||
|
||||
Базовый слой:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--dp-wine-100: #4b0f24;
|
||||
--dp-wine-80: #6d1c36;
|
||||
--dp-black: #161616;
|
||||
--dp-gray-700: #30343a;
|
||||
--dp-gray-600: #66605f;
|
||||
--dp-gray-300: #d9dee6;
|
||||
--dp-bg: #f4f6f9;
|
||||
--dp-surface: #ffffff;
|
||||
--dp-gold: #b9965b;
|
||||
--dp-blue: #1f3476;
|
||||
--dp-error: #a33a2f;
|
||||
--dp-radius-sm: 8px;
|
||||
--dp-shadow-soft: 0 12px 32px rgba(22, 22, 22, 0.08);
|
||||
--dp-shadow-lift: 0 20px 52px rgba(75, 15, 36, 0.14);
|
||||
}
|
||||
|
||||
.dp-section {
|
||||
padding-top: 72px;
|
||||
padding-bottom: 72px;
|
||||
}
|
||||
|
||||
.dp-surface {
|
||||
background: var(--dp-surface);
|
||||
border: 1px solid rgba(102, 96, 95, 0.16);
|
||||
border-radius: var(--dp-radius-sm);
|
||||
box-shadow: var(--dp-shadow-soft);
|
||||
}
|
||||
|
||||
.dp-eyebrow {
|
||||
color: var(--dp-gold);
|
||||
font-family: Inter, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
```
|
||||
|
||||
Mobile override:
|
||||
|
||||
```css
|
||||
@media (max-width: 767px) {
|
||||
.dp-section {
|
||||
padding-top: 48px;
|
||||
padding-bottom: 48px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Перенос кнопок
|
||||
|
||||
В Elementor настройте:
|
||||
|
||||
`Site Settings -> Buttons`
|
||||
|
||||
Базовая кнопка:
|
||||
|
||||
| Property | Value |
|
||||
|---|---:|
|
||||
| Min height | `48px` |
|
||||
| Padding | `12px 22px` |
|
||||
| Border radius | `8px` |
|
||||
| Font | `Inter`, `700` |
|
||||
| Transition | `0.2s` |
|
||||
|
||||
Primary:
|
||||
|
||||
| State | Text | Background | Border | Shadow |
|
||||
|---|---:|---:|---:|---|
|
||||
| Default | `#ffffff` | `#4b0f24` | transparent | `0 10px 22px rgba(75, 15, 36, 0.16)` |
|
||||
| Hover | `#ffffff` | `#6d1c36` | transparent | `0 20px 52px rgba(75, 15, 36, 0.14)` |
|
||||
|
||||
Secondary:
|
||||
|
||||
| State | Text | Background | Border |
|
||||
|---|---:|---:|---:|
|
||||
| Default | `#4b0f24` | `rgba(255,255,255,0.55)` | `rgba(75,15,36,0.24)` |
|
||||
| Hover | `#4b0f24` | `rgba(75,15,36,0.06)` | `rgba(75,15,36,0.36)` |
|
||||
|
||||
CSS-классы:
|
||||
|
||||
```css
|
||||
.dp-btn-primary .elementor-button {
|
||||
min-height: 48px;
|
||||
padding: 12px 22px;
|
||||
border-radius: 8px;
|
||||
background: var(--dp-wine-100);
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 10px 22px rgba(75, 15, 36, 0.16);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.dp-btn-primary .elementor-button:hover {
|
||||
background: var(--dp-wine-80);
|
||||
box-shadow: var(--dp-shadow-lift);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.dp-btn-secondary .elementor-button {
|
||||
min-height: 48px;
|
||||
padding: 12px 22px;
|
||||
border: 1px solid rgba(75, 15, 36, 0.24);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
color: var(--dp-wine-100);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.dp-btn-sm .elementor-button {
|
||||
min-height: 38px;
|
||||
padding: 8px 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
```
|
||||
|
||||
В Elementor у виджета Button добавляйте классы:
|
||||
|
||||
- `dp-btn-primary`
|
||||
- `dp-btn-secondary`
|
||||
- `dp-btn-sm`
|
||||
|
||||
## 8. Перенос форм и input fields
|
||||
|
||||
Для Elementor Forms:
|
||||
|
||||
`Site Settings -> Form Fields`
|
||||
|
||||
| Property | Value |
|
||||
|---|---:|
|
||||
| Background | `#ffffff` |
|
||||
| Text | `#161616` |
|
||||
| Placeholder | `#66605f` |
|
||||
| Border | `1px solid rgba(102, 96, 95, 0.24)` |
|
||||
| Border radius | `8px` |
|
||||
| Padding | `12-14px` |
|
||||
| Focus border | `#4b0f24` |
|
||||
| Focus shadow | `0 0 0 3-4px rgba(75, 15, 36, 0.08-0.10)` |
|
||||
| Error color | `#a33a2f` |
|
||||
|
||||
CSS-класс для формы:
|
||||
|
||||
```css
|
||||
.dp-form .elementor-field {
|
||||
border: 1px solid rgba(102, 96, 95, 0.24);
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
color: var(--dp-black);
|
||||
font-family: Inter, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dp-form .elementor-field:focus {
|
||||
border-color: var(--dp-wine-100);
|
||||
box-shadow: 0 0 0 4px rgba(75, 15, 36, 0.08);
|
||||
}
|
||||
|
||||
.dp-form .elementor-field-label {
|
||||
color: var(--dp-gray-700);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.dp-form .elementor-message-danger {
|
||||
color: var(--dp-error);
|
||||
}
|
||||
```
|
||||
|
||||
Проверить после переноса:
|
||||
|
||||
- default state;
|
||||
- focus state;
|
||||
- required fields;
|
||||
- error message;
|
||||
- textarea;
|
||||
- disabled field;
|
||||
- submit button;
|
||||
- mobile keyboard behavior.
|
||||
|
||||
## 9. Карточки товаров и контентные карточки
|
||||
|
||||
Карточки UI-kit используют:
|
||||
|
||||
- белую поверхность;
|
||||
- radius `8px`;
|
||||
- border `rgba(102, 96, 95, 0.16)`;
|
||||
- shadow soft;
|
||||
- hover lift;
|
||||
- внутренний padding `16-24px`;
|
||||
- muted text `#66605f`;
|
||||
- uppercase heading на `Montserrat 800`.
|
||||
|
||||
CSS-класс:
|
||||
|
||||
```css
|
||||
.dp-card {
|
||||
min-width: 0;
|
||||
border: 1px solid rgba(102, 96, 95, 0.16);
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: var(--dp-shadow-soft);
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.dp-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: var(--dp-shadow-lift);
|
||||
}
|
||||
|
||||
.dp-card-title {
|
||||
color: var(--dp-black);
|
||||
font-family: Montserrat, Inter, Arial, sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 1.15;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dp-card-text {
|
||||
color: var(--dp-gray-600);
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
```
|
||||
|
||||
Для Elementor:
|
||||
|
||||
1. Создайте контейнер карточки.
|
||||
2. В Advanced -> CSS Classes добавьте `dp-card`.
|
||||
3. Заголовку добавьте `dp-card-title`.
|
||||
4. Описанию добавьте `dp-card-text`.
|
||||
5. Кнопке добавьте `dp-btn-primary` или `dp-btn-secondary`.
|
||||
|
||||
## 10. Теги, chips, статусы
|
||||
|
||||
Используются для стран, категорий, фильтров, статусов.
|
||||
|
||||
```css
|
||||
.dp-tag,
|
||||
.dp-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dp-tag-filled,
|
||||
.dp-chip-active {
|
||||
background: var(--dp-wine-100);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.dp-tag-outline {
|
||||
border: 1px solid rgba(75, 15, 36, 0.24);
|
||||
color: var(--dp-wine-100);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dp-tag-gold {
|
||||
background: rgba(185, 150, 91, 0.18);
|
||||
color: #7c5f26;
|
||||
}
|
||||
```
|
||||
|
||||
В Elementor это можно делать через:
|
||||
|
||||
- Heading widget с HTML tag `span`;
|
||||
- Text Editor;
|
||||
- Button widget без ссылки;
|
||||
- Loop item meta fields, если это каталог.
|
||||
|
||||
## 11. Header и навигация
|
||||
|
||||
Header в UI-kit использует:
|
||||
|
||||
- sticky position;
|
||||
- background `rgba(248, 250, 252, 0.94)`;
|
||||
- border-bottom `rgba(102, 96, 95, 0.18)`;
|
||||
- shadow `0 10px 28px rgba(22, 22, 22, 0.05)`;
|
||||
- blur `backdrop-filter: blur(18px)`;
|
||||
- nav font `Inter 800 12px uppercase`;
|
||||
- active/hover color `#4b0f24`.
|
||||
|
||||
Рекомендация:
|
||||
|
||||
1. Не меняйте текущий Header template сразу.
|
||||
2. Дублируйте шаблон Header в Elementor.
|
||||
3. Назовите копию `Header DP UI Kit Draft`.
|
||||
4. Примените новые стили на копии.
|
||||
5. Проверьте меню, dropdown, search, phone, cart/favorites icons.
|
||||
6. Только после проверки назначайте новый шаблон на сайт.
|
||||
|
||||
CSS-класс для контейнера:
|
||||
|
||||
```css
|
||||
.dp-header {
|
||||
background: rgba(248, 250, 252, 0.94);
|
||||
border-bottom: 1px solid rgba(102, 96, 95, 0.18);
|
||||
box-shadow: 0 10px 28px rgba(22, 22, 22, 0.05);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.dp-nav a,
|
||||
.dp-nav .elementor-item {
|
||||
color: var(--dp-gray-700);
|
||||
font-family: Inter, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dp-nav a:hover,
|
||||
.dp-nav .elementor-item:hover,
|
||||
.dp-nav .elementor-item-active {
|
||||
color: var(--dp-wine-100);
|
||||
}
|
||||
```
|
||||
|
||||
## 12. Footer
|
||||
|
||||
Footer использует карточную панель:
|
||||
|
||||
- surface `#ffffff`;
|
||||
- radius `8px`;
|
||||
- shadow soft;
|
||||
- структурированную сетку ссылок;
|
||||
- muted legal text;
|
||||
- акцентные ссылки на hover.
|
||||
|
||||
Безопасный перенос:
|
||||
|
||||
1. Дублируйте текущий Footer template.
|
||||
2. Добавьте классы `dp-footer`, `dp-surface`, `dp-nav`.
|
||||
3. Проверьте desktop/mobile сетку.
|
||||
4. Не удаляйте старый footer до полной проверки.
|
||||
|
||||
## 13. Каталог и product cards
|
||||
|
||||
Для каталога важны следующие элементы:
|
||||
|
||||
- toolbar;
|
||||
- sort select;
|
||||
- filters;
|
||||
- grid/list cards;
|
||||
- compact cards;
|
||||
- price block;
|
||||
- tags;
|
||||
- CTA button;
|
||||
- hover state.
|
||||
|
||||
Порядок переноса:
|
||||
|
||||
1. Сначала перенесите визуальный стиль карточки товара.
|
||||
2. Потом фильтры и chips.
|
||||
3. Потом toolbar/sort.
|
||||
4. Потом list/compact variants.
|
||||
5. Последним этапом подключайте hover и lift effects.
|
||||
|
||||
Минимальный набор CSS-классов:
|
||||
|
||||
```txt
|
||||
dp-card
|
||||
dp-product-card
|
||||
dp-card-title
|
||||
dp-card-text
|
||||
dp-tag
|
||||
dp-tag-filled
|
||||
dp-btn-primary
|
||||
dp-btn-sm
|
||||
```
|
||||
|
||||
## 14. Страница UI Kit Preview в WordPress
|
||||
|
||||
Создайте отдельную страницу:
|
||||
|
||||
`UI Kit Preview`
|
||||
|
||||
Разместите на ней:
|
||||
|
||||
1. Цветовые swatches.
|
||||
2. H1, H2, H3, body, caption.
|
||||
3. Primary, secondary, small buttons.
|
||||
4. Form fields: default, focus, error.
|
||||
5. Product card.
|
||||
6. Tags/chips.
|
||||
7. Header preview.
|
||||
8. Footer preview.
|
||||
9. Catalog toolbar preview.
|
||||
10. Mobile preview section.
|
||||
|
||||
Страница нужна как контрольная витрина. Любое изменение токенов сначала проверяйте на ней.
|
||||
|
||||
## 15. Что нельзя делать при переносе
|
||||
|
||||
Не добавляйте глобально такие правила без полной проверки:
|
||||
|
||||
```css
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
```
|
||||
|
||||
Они могут сломать тему, плагины, WooCommerce, формы, меню и старые страницы.
|
||||
|
||||
Также не рекомендуется:
|
||||
|
||||
- удалять старые Elementor Global Colors;
|
||||
- массово заменять цвета по всему сайту;
|
||||
- менять breakpoints без полного responsive QA;
|
||||
- вставлять весь `site.css` в Elementor;
|
||||
- переносить demo-стили UI-kit sidebar;
|
||||
- менять Header/Footer на production без дубликата и предпросмотра.
|
||||
|
||||
## 16. QA checklist перед публикацией
|
||||
|
||||
Проверьте:
|
||||
|
||||
- Главная страница.
|
||||
- Каталог.
|
||||
- Карточка товара.
|
||||
- Страница контактов.
|
||||
- Страница статьи/новости.
|
||||
- Header desktop.
|
||||
- Header mobile.
|
||||
- Mega menu/dropdown.
|
||||
- Footer desktop.
|
||||
- Footer mobile.
|
||||
- Все формы.
|
||||
- Search.
|
||||
- Cart/favorites/account icons, если есть.
|
||||
- 404 page.
|
||||
- WooCommerce cart/checkout/account, если сайт использует WooCommerce.
|
||||
|
||||
Для каждого экрана:
|
||||
|
||||
- нет горизонтального скролла;
|
||||
- текст не обрезается;
|
||||
- кнопки не ломаются;
|
||||
- hover/focus состояния видны;
|
||||
- карточки не прыгают по высоте;
|
||||
- изображения не растягиваются;
|
||||
- меню открывается и закрывается корректно;
|
||||
- формы отправляются;
|
||||
- ошибки форм читаемы;
|
||||
- контраст текста достаточный.
|
||||
|
||||
## 17. Публикация на production
|
||||
|
||||
Финальный порядок:
|
||||
|
||||
1. Сделать свежий backup production.
|
||||
2. Экспортировать Elementor templates.
|
||||
3. Перенести Global Colors.
|
||||
4. Перенести Global Fonts.
|
||||
5. Добавить проверенный CSS-слой.
|
||||
6. Импортировать или обновить шаблоны.
|
||||
7. Назначить Header/Footer только после проверки preview.
|
||||
8. Elementor -> Tools -> Regenerate CSS & Data.
|
||||
9. Очистить кэш плагина оптимизации.
|
||||
10. Очистить server/CDN cache, если есть.
|
||||
11. Проверить сайт в incognito.
|
||||
|
||||
## 18. План отката
|
||||
|
||||
Перед публикацией должны быть готовы:
|
||||
|
||||
- backup production;
|
||||
- экспорт старых Elementor templates;
|
||||
- список измененных Global Colors/Fonts;
|
||||
- копия добавленного Custom CSS;
|
||||
- скриншоты ключевых страниц до переноса.
|
||||
|
||||
Если что-то сломалось:
|
||||
|
||||
1. Отключите новый Custom CSS.
|
||||
2. Верните старый Header/Footer template.
|
||||
3. Regenerate CSS & Data.
|
||||
4. Очистите кэш.
|
||||
5. Проверьте сайт.
|
||||
6. Если проблема осталась, восстановите backup.
|
||||
|
||||
## 19. Минимальный набор для первого этапа
|
||||
|
||||
Для первого безопасного внедрения достаточно перенести:
|
||||
|
||||
1. Global Colors с префиксом `DP`.
|
||||
2. Global Fonts `DP H1`, `DP H2`, `DP H3`, `DP Body`, `DP Button`.
|
||||
3. CSS-переменные `--dp-*`.
|
||||
4. Классы кнопок `dp-btn-primary`, `dp-btn-secondary`, `dp-btn-sm`.
|
||||
5. Класс карточки `dp-card`.
|
||||
6. Класс формы `dp-form`.
|
||||
7. Классы тегов `dp-tag`, `dp-tag-filled`, `dp-tag-outline`.
|
||||
8. Тестовую страницу `UI Kit Preview`.
|
||||
|
||||
После этого можно спокойно двигаться к Header, Footer, каталогу и product templates.
|
||||
1652
package-lock.json
generated
Normal file
13
package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "dp-trade-ui-kit",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "eleventy --serve",
|
||||
"build": "eleventy"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^3.0.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
16
src/_includes/layouts/base.njk
Normal file
@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{ title }}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&family=Inter:wght@400;500;600;700;800&family=Manrope:wght@400;500;600;700;800&family=Montserrat:wght@500;600;700;800&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="/css/{{ css | default('site') }}.css" />
|
||||
</head>
|
||||
<body{% if bodyClass %} class="{{ bodyClass }}"{% endif %}>
|
||||
{{ content | safe }}
|
||||
<script src="/js/inspector.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
src/_includes/layouts/default.njk
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
layout: layouts/base
|
||||
---
|
||||
<div class="site-shell">
|
||||
{% include "partials/header.njk" %}
|
||||
{{ content | safe }}
|
||||
{% include "partials/footer.njk" %}
|
||||
</div>
|
||||
5
src/_includes/layouts/ui-kit.njk
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
layout: layouts/base
|
||||
css: ui-kit
|
||||
---
|
||||
{{ content | safe }}
|
||||
73
src/_includes/partials/footer.njk
Normal file
@ -0,0 +1,73 @@
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-container">
|
||||
<section class="footer-main">
|
||||
<div class="footer-brand-block">
|
||||
<a class="footer-brand" href="/" aria-label="DP Trade">
|
||||
<img src="https://wine-dp-trade.ru/wp-content/uploads/2024/11/logo-500x107.png" alt="DP Trade" width="156" height="33" />
|
||||
</a>
|
||||
<p class="footer-brand-subtitle">Территория качественного вина</p>
|
||||
</div>
|
||||
|
||||
<section class="footer-nav" aria-label="Навигация по разделам">
|
||||
<article class="nav-group">
|
||||
<h2>Каталог</h2>
|
||||
<ul>
|
||||
<li><a href="/catalog.html">Вина по регионам</a></li>
|
||||
<li><a href="#">Франция</a></li>
|
||||
<li><a href="#">Италия</a></li>
|
||||
<li><a href="#">Испания</a></li>
|
||||
<li><a href="#">Новинки</a></li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="nav-group">
|
||||
<h2>Клиентам</h2>
|
||||
<ul>
|
||||
<li><a href="#">Доставка</a></li>
|
||||
<li><a href="#">Оплата</a></li>
|
||||
<li><a href="#">Как купить</a></li>
|
||||
<li><a href="#">Скачать каталог</a></li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="nav-group">
|
||||
<h2>Компания</h2>
|
||||
<ul>
|
||||
<li><a href="/about.html">О компании</a></li>
|
||||
<li><a href="/contacts.html">Контакты</a></li>
|
||||
<li><a href="/contacts-auth.html">Контакты auth</a></li>
|
||||
<li><a href="#">Склады</a></li>
|
||||
<li><a href="#">Реквизиты</a></li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<article class="contacts-card">
|
||||
<h2>Контакты</h2>
|
||||
<a class="contact-link contact-phone" href="tel:+74959379460">+7 (495) 937-94-60</a>
|
||||
<a class="contact-link" href="mailto:dptr@dp-trade.ru">dptr@dp-trade.ru</a>
|
||||
<div class="social-links" aria-label="Социальные сети">
|
||||
<a href="#" aria-label="Telegram">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M21 4 3 11.2l6.3 2.2L18 7.7l-6.8 7.1.2 5.2 3-3.3 4.7 3.5L21 4Z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" aria-label="VK">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M4 7h3l2 4.2c.5 1.1 1 1.6 1.3 1.6.2 0 .4-.3.4-1V7h3v3.7c0 .4.2.6.5.6.6 0 1.8-1.6 2.6-4.3H20c-.7 3-1.8 4.8-3 5.7 1.2.8 2.4 2.2 3.2 4.3h-3.4c-.8-1.5-1.8-2.7-2.5-2.7-.4 0-.6.3-.6.8V17h-1.5C8.8 17 6.2 13.9 4 7Z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<section class="footer-legal" aria-label="Правовая информация">
|
||||
<p>
|
||||
Интернет-витрина размещает информацию об алкогольной продукции исключительно в целях ознакомления.
|
||||
Дистанционная продажа алкогольной продукции не осуществляется.
|
||||
<a href="#">Подробнее о правовой информации</a>
|
||||
</p>
|
||||
<p>© 2026 DP-Trade</p>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</footer>
|
||||
253
src/_includes/partials/header.njk
Normal file
@ -0,0 +1,253 @@
|
||||
<header class="site-header">
|
||||
<div class="container header-top">
|
||||
<a class="brand-logo" href="/" aria-label="DP Trade">
|
||||
<img src="https://wine-dp-trade.ru/wp-content/uploads/2024/11/logo-500x107.png" alt="DP Trade" width="156" height="33">
|
||||
</a>
|
||||
|
||||
<form class="search-bar" action="#" role="search">
|
||||
<button type="submit" aria-label="Поиск">⌕</button>
|
||||
<input type="search" placeholder="Поиск по вину, региону, производителю" aria-label="Поиск">
|
||||
</form>
|
||||
|
||||
<div class="header-actions">
|
||||
<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">
|
||||
<path d="M12 12.2a4.1 4.1 0 1 0 0-8.2 4.1 4.1 0 0 0 0 8.2Z" />
|
||||
<path d="M4.8 20.2c1.1-3.3 3.6-5 7.2-5s6.1 1.7 7.2 5" />
|
||||
</svg>
|
||||
</a>
|
||||
<a class="header-icon header-icon--air" href="#" aria-label="Корзина">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M5 6h2l1.4 9.2h8.5L19 8H8" />
|
||||
<path d="M9.4 19.2h.1M16.8 19.2h.1" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="container main-nav" aria-label="Основная навигация">
|
||||
<a href="/catalog.html">Каталог</a>
|
||||
|
||||
<div class="nav-item">
|
||||
<button class="nav-trigger" type="button" aria-expanded="false">Регион <span class="nav-chevron" aria-hidden="true"><svg viewBox="0 0 10 6"><path d="M1 1l4 4 4-4" /></svg></span></button>
|
||||
|
||||
<section class="mega-menu" aria-label="Регион">
|
||||
<div class="mega-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Регион</p>
|
||||
<h2>Вина по географии</h2>
|
||||
</div>
|
||||
<p>Быстрый вход в каталог через страну, апелласьон или популярный регион.</p>
|
||||
</div>
|
||||
|
||||
<div class="menu-grid">
|
||||
<article class="menu-column visual-column">
|
||||
<img src="https://images.unsplash.com/photo-1506377247377-2a5b3b417ebb?auto=format&fit=crop&w=900&q=80" alt="Виноградник на холмах">
|
||||
<p>Выбирайте через знакомую страну или сразу через регион, если точно знаете, что ищете.</p>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">Страны</p>
|
||||
<a href="#">Старый Свет</a>
|
||||
<a href="#">Франция</a>
|
||||
<a href="#">Италия</a>
|
||||
<a href="#">Испания</a>
|
||||
<a href="#">Германия</a>
|
||||
<a href="#">Новый Свет</a>
|
||||
<a href="#">Чили</a>
|
||||
<a href="#">Аргентина</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column appellations">
|
||||
<p class="section-title">Апелласьоны</p>
|
||||
<div class="appellation-group">
|
||||
<a class="country-link" href="#">Франция</a>
|
||||
<a href="#">Bordeaux</a>
|
||||
<a href="#">Medoc</a>
|
||||
<a href="#">Margaux</a>
|
||||
<a href="#">Pauillac</a>
|
||||
<a href="#">Saint-Emilion</a>
|
||||
</div>
|
||||
<div class="appellation-group">
|
||||
<a class="country-link" href="#">Италия</a>
|
||||
<a href="#">Toscana</a>
|
||||
<a href="#">Chianti</a>
|
||||
<a href="#">Brunello di Montalcino</a>
|
||||
<a href="#">Piemonte</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="menu-column popular-column">
|
||||
<p class="section-title">Популярные регионы</p>
|
||||
<div class="popular-list">
|
||||
<a href="#">Бордо</a>
|
||||
<a href="#">Тоскана</a>
|
||||
<a href="#">Пьемонт</a>
|
||||
<a href="#">Риоха</a>
|
||||
<a href="#">Напа Вэлли</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="nav-item">
|
||||
<button class="nav-trigger" type="button" aria-expanded="false">Вино <span class="nav-chevron" aria-hidden="true"><svg viewBox="0 0 10 6"><path d="M1 1l4 4 4-4" /></svg></span></button>
|
||||
|
||||
<section class="mega-menu" aria-label="Вино">
|
||||
<div class="mega-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Вино</p>
|
||||
<h2>Каталог по типу и стилю</h2>
|
||||
</div>
|
||||
<p>Быстрый выбор вина по цвету, категории и уровню сладости.</p>
|
||||
</div>
|
||||
|
||||
<div class="menu-grid">
|
||||
<article class="menu-column visual-column">
|
||||
<img src="https://images.unsplash.com/photo-1510812431401-41d2bd2722f3?auto=format&fit=crop&w=900&q=80" alt="Бокалы вина">
|
||||
<p>Начните с типа вина или сразу переходите к стилю, если знаете нужный профиль.</p>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">По типу</p>
|
||||
<a href="#">Красное</a>
|
||||
<a href="#">Белое</a>
|
||||
<a href="#">Розовое</a>
|
||||
<a href="#">Игристое</a>
|
||||
<a href="#">Шампанское</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">По стилю</p>
|
||||
<a href="#">Сухие</a>
|
||||
<a href="#">Полусухие</a>
|
||||
<a href="#">Полусладкие</a>
|
||||
<a href="#">Сладкие</a>
|
||||
<a href="#">Брют</a>
|
||||
<a href="#">Экстра брют</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column popular-column">
|
||||
<p class="section-title">Популярное</p>
|
||||
<div class="popular-list">
|
||||
<a href="#">Новинки</a>
|
||||
<a href="#">Хиты продаж</a>
|
||||
<a href="#">Для ресторанов</a>
|
||||
<a href="#">Премиум подборка</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="nav-item">
|
||||
<button class="nav-trigger" type="button" aria-expanded="false">Крепкий алкоголь <span class="nav-chevron" aria-hidden="true"><svg viewBox="0 0 10 6"><path d="M1 1l4 4 4-4" /></svg></span></button>
|
||||
|
||||
<section class="mega-menu" aria-label="Крепкий алкоголь">
|
||||
<div class="mega-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Spirits</p>
|
||||
<h2>Крепкий алкоголь</h2>
|
||||
</div>
|
||||
<p>Подборка крепких напитков для баров, ресторанов и специализированной розницы.</p>
|
||||
</div>
|
||||
|
||||
<div class="menu-grid">
|
||||
<article class="menu-column visual-column">
|
||||
<img src="https://images.unsplash.com/photo-1527281400683-1aae777175f8?auto=format&fit=crop&w=900&q=80" alt="Крепкий алкоголь в бокале">
|
||||
<p>Категории, выдержка и происхождение для быстрой навигации по ассортименту.</p>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">Категории</p>
|
||||
<a href="#">Виски</a>
|
||||
<a href="#">Коньяк</a>
|
||||
<a href="#">Арманьяк</a>
|
||||
<a href="#">Ром</a>
|
||||
<a href="#">Джин</a>
|
||||
<a href="#">Водка</a>
|
||||
<a href="#">Текила</a>
|
||||
<a href="#">Ликеры</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">По стилю</p>
|
||||
<a href="#">Односолодовый</a>
|
||||
<a href="#">Купажированный</a>
|
||||
<a href="#">Выдержанный</a>
|
||||
<a href="#">Пряный</a>
|
||||
<a href="#">Дижестив</a>
|
||||
<a href="#">Для коктейлей</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column popular-column">
|
||||
<p class="section-title">Популярное</p>
|
||||
<div class="popular-list">
|
||||
<a href="#">Single Malt</a>
|
||||
<a href="#">Cognac VSOP</a>
|
||||
<a href="#">Premium Gin</a>
|
||||
<a href="#">Bar Selection</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="nav-item">
|
||||
<button class="nav-trigger" type="button" aria-expanded="false">Производители <span class="nav-chevron" aria-hidden="true"><svg viewBox="0 0 10 6"><path d="M1 1l4 4 4-4" /></svg></span></button>
|
||||
|
||||
<section class="mega-menu" aria-label="Производители">
|
||||
<div class="mega-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Производители</p>
|
||||
<h2>Дома, хозяйства и бренды</h2>
|
||||
</div>
|
||||
<p>Навигация по ключевым производителям, регионам и партнерским брендам DP Trade.</p>
|
||||
</div>
|
||||
|
||||
<div class="menu-grid">
|
||||
<article class="menu-column visual-column">
|
||||
<img src="https://images.unsplash.com/photo-1568213816046-0ee1c42bd559?auto=format&fit=crop&w=900&q=80" alt="Винодельня и бочки">
|
||||
<p>Откройте ассортимент через знакомые хозяйства, винные дома и регионы производства.</p>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">По странам</p>
|
||||
<a href="#">Франция</a>
|
||||
<a href="#">Италия</a>
|
||||
<a href="#">Испания</a>
|
||||
<a href="#">Германия</a>
|
||||
<a href="#">Португалия</a>
|
||||
<a href="#">Чили</a>
|
||||
<a href="#">Аргентина</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column">
|
||||
<p class="section-title">Избранные</p>
|
||||
<a href="#">Chateau Laroque</a>
|
||||
<a href="#">Marchesi Antinori</a>
|
||||
<a href="#">Torres</a>
|
||||
<a href="#">Dr. Loosen</a>
|
||||
<a href="#">Catena Zapata</a>
|
||||
<a href="#">Penfolds</a>
|
||||
</article>
|
||||
|
||||
<article class="menu-column popular-column">
|
||||
<p class="section-title">Форматы</p>
|
||||
<div class="popular-list">
|
||||
<a href="#">Семейные хозяйства</a>
|
||||
<a href="#">Grand Cru</a>
|
||||
<a href="#">Органика</a>
|
||||
<a href="#">Эксклюзив DP Trade</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<a href="#">Поставки</a>
|
||||
<a href="#">Новости</a>
|
||||
<a href="/ui-kit.html">UI-kit</a>
|
||||
</nav>
|
||||
</header>
|
||||
46
src/about.njk
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "DP Trade — О компании"
|
||||
layout: layouts/default
|
||||
permalink: /about.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="content-page">
|
||||
<section class="page-hero page-hero--about">
|
||||
<div class="container page-hero__inner">
|
||||
<div>
|
||||
<p class="eyebrow">О компании</p>
|
||||
<h1>DP Trade — территория качественного вина</h1>
|
||||
<p>Импорт, каталог, персональная работа с клиентами, собственные склады в регионах и доставка собственным транспортом.</p>
|
||||
</div>
|
||||
<aside class="page-hero__meta" aria-label="Кратко о компании">
|
||||
<span>Since 1991</span>
|
||||
<strong>Premium wine trade</strong>
|
||||
<p>Портфель винных домов для ресторанов, розницы и профессиональных закупок.</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container about-grid">
|
||||
<article class="article-body">
|
||||
<p class="lead">DP Trade развивает профессиональный винный каталог, где регионы, производители, партии и коммерческие условия собраны в едином интерфейсе для быстрых закупочных решений.</p>
|
||||
<p>Главная ценность сервиса — соединить сильный ассортимент с понятной навигацией: от страны и апелласьона до конкретного производителя, винтажа и карточки товара.</p>
|
||||
<h2>Как устроен подход</h2>
|
||||
<p>Компания делает акцент на удобном каталоге, персональном отношении, собственных складских возможностях и доставке собственным транспортом. Для B2B-клиентов это значит меньше ручных уточнений и быстрее путь от выбора до заказа.</p>
|
||||
</article>
|
||||
<div class="feature-grid feature-grid--about">
|
||||
<article class="feature-card"><span>01</span><h3>Удобный каталог</h3><p>Фильтры по типу, региону, производителю, сорту, году, объему и стилю.</p></article>
|
||||
<article class="feature-card"><span>02</span><h3>Персональный подход</h3><p>Быстрая коммуникация с менеджером и подборки под формат клиента.</p></article>
|
||||
<article class="feature-card"><span>03</span><h3>Склады в регионах</h3><p>Инфраструктура для регулярных поставок и управления доступностью.</p></article>
|
||||
<article class="feature-card"><span>04</span><h3>Собственная доставка</h3><p>Контроль логистики и аккуратная работа с профессиональными заказами.</p></article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section muted-section">
|
||||
<div class="container section-heading section-heading--split">
|
||||
<div><p class="eyebrow">Portfolio</p><h2>Винные дома и регионы</h2></div>
|
||||
<p>Страница показывает, как может выглядеть корпоративный раздел: спокойная подача, крупные тезисы и блок преимуществ без перегруза.</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
112
src/article-guidelines.njk
Normal file
@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "DP Trade — Article Guidelines"
|
||||
layout: layouts/default
|
||||
permalink: /article-guidelines.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="content-page guidelines-page">
|
||||
<section class="page-hero page-hero--guidelines">
|
||||
<div class="container page-hero__inner">
|
||||
<div>
|
||||
<p class="eyebrow">Editorial guidelines</p>
|
||||
<h1>Рекомендации по статьям DP Trade</h1>
|
||||
<p>Требования к структуре материала, заголовкам, тексту и фотографиям для новостей, историй производителей и экспертных заметок.</p>
|
||||
</div>
|
||||
<aside class="page-hero__meta">
|
||||
<span>Для редакции</span>
|
||||
<strong>Article system</strong>
|
||||
<p>Единые правила помогают статьям выглядеть как часть продукта, а не как случайные публикации.</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container guideline-grid">
|
||||
<article class="guideline-card guideline-card--lead">
|
||||
<span>01</span>
|
||||
<h2>Структура статьи</h2>
|
||||
<p>Каждый материал должен быстро отвечать на три вопроса: о ком/о чём статья, почему это важно для аудитории DP Trade и какое действие читатель может сделать дальше.</p>
|
||||
<ul>
|
||||
<li>Заголовок: конкретный, с именем производителя, региона или события.</li>
|
||||
<li>Лид: 1-2 предложения, раскрывает главный повод.</li>
|
||||
<li>Основной текст: 3-5 смысловых блоков с подзаголовками или визуальными паузами.</li>
|
||||
<li>Финал: вывод, рекомендация, ссылка в каталог или повод обратиться к менеджеру.</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="guideline-card">
|
||||
<span>02</span>
|
||||
<h2>Заголовки</h2>
|
||||
<p>Заголовок должен быть редакционным, но полезным: не только красивым, а ещё и объясняющим тему.</p>
|
||||
<ul>
|
||||
<li>Оптимально: 55-90 символов.</li>
|
||||
<li>Использовать имена: Villa Raiano, Gaja, Bordeaux, Fiano.</li>
|
||||
<li>Избегать пустых формул: «уникальная история», «легендарное событие» без факта.</li>
|
||||
<li>Подзаголовки должны вести читателя по смыслу, а не повторять заголовок.</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="guideline-card">
|
||||
<span>03</span>
|
||||
<h2>Текст</h2>
|
||||
<p>Стиль: профессиональный, спокойный, без рекламного нажима. Пишем для закупщиков, сомелье, ресторанов и розницы.</p>
|
||||
<ul>
|
||||
<li>Абзац: 350-650 знаков.</li>
|
||||
<li>Лид: до 280 знаков.</li>
|
||||
<li>Новость: 3 000-5 500 знаков.</li>
|
||||
<li>История производителя: 5 000-8 000 знаков.</li>
|
||||
<li>Цифры, годы, награды и сорта проверять отдельно.</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="guideline-card">
|
||||
<span>04</span>
|
||||
<h2>Фотографии</h2>
|
||||
<p>Фотографии должны показывать реальный продукт, место, людей или процесс. Лучше меньше декоративности и больше конкретики.</p>
|
||||
<ul>
|
||||
<li>Hero: горизонтальное фото от 1600px по ширине.</li>
|
||||
<li>Внутренние фото: от 1200px, без сильной компрессии.</li>
|
||||
<li>Для карточек: предмет или бутылка должны быть читаемы на мобильном.</li>
|
||||
<li>Не использовать тёмные, размытые, случайно обрезанные изображения.</li>
|
||||
<li>Обязательно писать alt: кто/что изображено и где.</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="guideline-card">
|
||||
<span>05</span>
|
||||
<h2>Композиция</h2>
|
||||
<p>Для длинной статьи нужны визуальные паузы: крупное фото после лида, цитата или факт-блок в середине и финальный акцент.</p>
|
||||
<ul>
|
||||
<li>Не ставить подряд больше 4 текстовых абзацев без паузы.</li>
|
||||
<li>Цитаты использовать только если они усиливают материал.</li>
|
||||
<li>Сайдбар может содержать факты: страна, регион, сорт, год, награда.</li>
|
||||
<li>Финальный CTA должен быть связан с каталогом или менеджером.</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="guideline-card">
|
||||
<span>06</span>
|
||||
<h2>SEO и handoff</h2>
|
||||
<p>Материал должен быть понятен поиску и разработке: один H1, последовательные H2, корректные alt и прозрачная структура блоков.</p>
|
||||
<ul>
|
||||
<li>Title: до 60 символов, включает бренд или тему.</li>
|
||||
<li>Description: 140-160 символов.</li>
|
||||
<li>URL: латиница, короткий slug.</li>
|
||||
<li>Изображения называть по смыслу, а не `photo-final-2`.</li>
|
||||
<li>В CMS хранить дату, автора/раздел, теги и связанные товары.</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section muted-section">
|
||||
<div class="container guideline-checklist">
|
||||
<div>
|
||||
<p class="eyebrow">Pre-publish checklist</p>
|
||||
<h2>Проверка перед публикацией</h2>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Заголовок отражает конкретный повод и не звучит как реклама.</li>
|
||||
<li>В лиде понятна ценность материала для профессиональной аудитории.</li>
|
||||
<li>Все годы, имена, апелласьоны и награды проверены.</li>
|
||||
<li>Есть минимум 2-4 качественные фотографии с alt-текстами.</li>
|
||||
<li>Финал ведёт к каталогу, производителю, товару или контакту с менеджером.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
BIN
src/assets/images/00073820_1.png
Normal file
|
After Width: | Height: | Size: 4.9 MiB |
BIN
src/assets/images/00080768_1.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
src/assets/images/00081538_1.png
Normal file
|
After Width: | Height: | Size: 5.4 MiB |
BIN
src/assets/images/00081726_1.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
src/assets/images/photo_2026-04-06_16-53-23-2.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/images/photo_2026-04-06_16-53-23.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
src/assets/images/photo_2026-04-06_16-53-24.jpg
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
src/assets/images/photo_2026-04-06_16-53-26.jpg
Normal file
|
After Width: | Height: | Size: 188 KiB |
232
src/bottle-cards.njk
Normal file
@ -0,0 +1,232 @@
|
||||
---
|
||||
title: "DP Trade — Bottle Card Variants"
|
||||
layout: layouts/default
|
||||
permalink: /bottle-cards.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="section card-lab-section">
|
||||
<div class="container">
|
||||
<div class="card-lab-head">
|
||||
<div>
|
||||
<p class="eyebrow">UI-kit / Product cards</p>
|
||||
<h1>Варианты карточек бутылок</h1>
|
||||
</div>
|
||||
<p>Отдельная витрина для выбора направления карточки: крупные плитки, компактные версии и горизонтальный формат для B2B-каталога.</p>
|
||||
</div>
|
||||
|
||||
<section class="card-variant-block" aria-labelledby="cards-grid-title">
|
||||
<div class="card-variant-title">
|
||||
<p class="eyebrow">Variant 01</p>
|
||||
<h2 id="cards-grid-title">Акцентные карточки каталога</h2>
|
||||
</div>
|
||||
<div class="bottle-card-grid">
|
||||
<article class="bottle-card bottle-card--bordeaux">
|
||||
<div class="bottle-card__media">
|
||||
<img class="bottle-card__photo" src="assets/images/00081538_1.png" alt="Chateau Laroque Grand Cru" />
|
||||
</div>
|
||||
<div class="bottle-card__body">
|
||||
<p class="bottle-card__region">Bordeaux</p>
|
||||
<h3>Chateau Laroque Grand Cru</h3>
|
||||
<p>France · Red dry · 2019 · 0.75 L</p>
|
||||
</div>
|
||||
<div class="bottle-card__footer">
|
||||
<strong>3 890 ₽</strong>
|
||||
<a class="button button--primary button--sm" href="product.html">Подробнее</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="bottle-card bottle-card--tuscany">
|
||||
<div class="bottle-card__media">
|
||||
<img class="bottle-card__photo" src="assets/images/00080768_1.png" alt="Brunello di Montalcino" />
|
||||
</div>
|
||||
<div class="bottle-card__body">
|
||||
<p class="bottle-card__region">Tuscany</p>
|
||||
<h3>Brunello di Montalcino</h3>
|
||||
<p>Italy · Red dry · 2018 · 0.75 L</p>
|
||||
</div>
|
||||
<div class="bottle-card__footer">
|
||||
<strong>4 240 ₽</strong>
|
||||
<a class="button button--primary button--sm" href="product.html">Подробнее</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="bottle-card bottle-card--mosel">
|
||||
<div class="bottle-card__media">
|
||||
<img class="bottle-card__photo" src="assets/images/00081726_1.png" alt="Riesling Kabinett" />
|
||||
</div>
|
||||
<div class="bottle-card__body">
|
||||
<p class="bottle-card__region">Mosel</p>
|
||||
<h3>Riesling Kabinett</h3>
|
||||
<p>Germany · White · 2021 · 0.75 L</p>
|
||||
</div>
|
||||
<div class="bottle-card__footer">
|
||||
<strong>2 150 ₽</strong>
|
||||
<a class="button button--primary button--sm" href="product.html">Подробнее</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-variant-block" aria-labelledby="cards-alt-title">
|
||||
<div class="card-variant-title">
|
||||
<p class="eyebrow">Variant 02-04</p>
|
||||
<h2 id="cards-alt-title">Альтернативные форматы</h2>
|
||||
</div>
|
||||
<div class="bottle-card-mix">
|
||||
<article class="bottle-card bottle-card--clean">
|
||||
<div class="bottle-card__media">
|
||||
<img class="bottle-card__photo" src="assets/images/00081538_1.png" alt="Oremus Case of Aszu" />
|
||||
</div>
|
||||
<div class="bottle-card__body">
|
||||
<p class="bottle-card__region">Clean retail</p>
|
||||
<h3>Oremus Case of Aszu 5 Puttonyos</h3>
|
||||
<p>Hungary · White sweet · 2000 · 0.5 L</p>
|
||||
</div>
|
||||
<div class="bottle-card__footer">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary button--sm" href="product.html">В избранное</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="bottle-card bottle-card--compact">
|
||||
<div class="bottle-card__media">
|
||||
<img class="bottle-card__photo" src="assets/images/00080768_1.png" alt="Oremus Tokaji Aszu" />
|
||||
</div>
|
||||
<div class="bottle-card__body">
|
||||
<p class="bottle-card__region">Compact</p>
|
||||
<h3>Tokaji Aszu 5 Puttonyos</h3>
|
||||
<p>Hungary · 12% · Furmint, Harslevelu</p>
|
||||
</div>
|
||||
<div class="bottle-card__footer">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--primary button--sm" href="product.html">Подробнее</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="bottle-card bottle-card--horizontal">
|
||||
<div class="bottle-card__media">
|
||||
<img class="bottle-card__photo" src="assets/images/00081726_1.png" alt="Oremus Tokaji Late Harvest" />
|
||||
</div>
|
||||
<div class="bottle-card__content">
|
||||
<div class="bottle-card__body">
|
||||
<p class="bottle-card__region">B2B row card</p>
|
||||
<h3>Oremus Tokaji Late Harvest</h3>
|
||||
<p>Вино белое полусладкое · Венгрия · 11.5%</p>
|
||||
<p class="bottle-card__details">Сорт: Фурминт, Зета, Харшлевелю</p>
|
||||
</div>
|
||||
<div class="bottle-card__footer">
|
||||
<strong>4 980 ₽</strong>
|
||||
<a class="button button--secondary button--sm" href="product.html">В избранное</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-variant-block" aria-labelledby="catalog-card-title">
|
||||
<div class="card-variant-title">
|
||||
<p class="eyebrow">Catalog current</p>
|
||||
<h2 id="catalog-card-title">Варианты текущей карточки каталога</h2>
|
||||
</div>
|
||||
<div class="catalog-card-variants">
|
||||
<article class="catalog-card-sample">
|
||||
<p class="catalog-card-sample__label">Default / как в каталоге</p>
|
||||
<div class="product-card product-card--list">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Case of Aszu 5 Puttonyos, 0.5 л. 2000">
|
||||
<img src="assets/images/00081538_1.png" alt="Вино белое Oremus, Case of Aszu 5 Puttonyos, 0.5 л. 2000" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Case of Aszu 5 Puttonyos, 0.5 л. 2000</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Вино Белое Сладкое<br />12 %</p>
|
||||
<p>Сорт: Фурминт (70%), Харшлевелю (28%), Мускат (2%)</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="catalog-card-sample">
|
||||
<p class="catalog-card-sample__label">Compact / меньше высота</p>
|
||||
<div class="product-card product-card--list product-card--list-compact">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000">
|
||||
<img src="assets/images/00080768_1.png" alt="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Сладкое · 12% · Фурминт, Харшлевелю, Мускат</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="catalog-card-sample">
|
||||
<p class="catalog-card-sample__label">Hover / активная строка</p>
|
||||
<div class="product-card product-card--list product-card--list-hover">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Tokaji Late Harvest, 0.5 л. 2021">
|
||||
<img src="assets/images/00081726_1.png" alt="Вино белое Oremus, Tokaji Late Harvest, 0.5 л. 2021" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Tokaji Late Harvest, 0.5 л. 2021</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Вино Белое Полусладкое<br />11.5 %</p>
|
||||
<p>Сорт: Фурминт, Зета, Харшлевелю</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>4 980 ₽</strong>
|
||||
<a class="button button--primary" href="#">Подробнее</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="catalog-card-sample">
|
||||
<p class="catalog-card-sample__label">Color image / цветной фон фото</p>
|
||||
<div class="product-card product-card--list product-card--list-color">
|
||||
<a class="product-image product-image--gold" href="product.html" aria-label="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000">
|
||||
<img src="assets/images/00080768_1.png" alt="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Вино Белое Сладкое<br />12 %</p>
|
||||
<p>Сорт: Фурминт (70%), Харшлевелю (28%), Мускат (2%)</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="catalog-card-sample">
|
||||
<p class="catalog-card-sample__label">B2B dense / больше данных</p>
|
||||
<div class="product-card product-card--list product-card--list-b2b">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000">
|
||||
<img src="assets/images/00081538_1.png" alt="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000</h3>
|
||||
<p class="product-origin">Венгрия · Tokaj · Oremus · 0.5 л · 12%</p>
|
||||
<div class="product-params">
|
||||
<span>Тип: белое сладкое</span>
|
||||
<span>Сорт: Фурминт 70%</span>
|
||||
<span>Упаковка: 6 шт.</span>
|
||||
<span>Артикул: 00073820</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
80
src/catalog.njk
Normal file
@ -0,0 +1,80 @@
|
||||
---
|
||||
title: "DP Trade — Catalog"
|
||||
layout: layouts/default
|
||||
permalink: /catalog.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="section catalog-section">
|
||||
<div class="container">
|
||||
<div class="catalog-head">
|
||||
<div>
|
||||
<p class="eyebrow">Catalog / Country</p>
|
||||
<h1>Венгрия</h1>
|
||||
</div>
|
||||
<div class="catalog-actions" aria-label="Действия каталога">
|
||||
<span>Всего найдено: 22</span>
|
||||
<a class="button button--secondary" href="#">Все в избранное</a>
|
||||
<a class="button button--secondary" href="#">Очистить избранное</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="catalog-toolbar">
|
||||
<span>Всего найдено: 22</span>
|
||||
<select class="input catalog-sort" aria-label="Сортировка">
|
||||
<option>Исходная сортировка</option>
|
||||
<option>По цене</option>
|
||||
<option>По названию</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<section class="product-list" aria-label="Список товаров">
|
||||
<article class="product-card product-card--list">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Case of Aszu 5 Puttonyos, 0.5 л. 2000">
|
||||
<img src="assets/images/00081538_1.png" alt="Вино белое Oremus, Case of Aszu 5 Puttonyos, 0.5 л. 2000" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Case of Aszu 5 Puttonyos, 0.5 л. 2000</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Вино Белое Сладкое<br />12 %</p>
|
||||
<p>Сорт: Фурминт (70%), Харшлевелю (28%), Мускат (2%)</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="product-card product-card--list">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000">
|
||||
<img src="assets/images/00080768_1.png" alt="Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Tokaji Aszu 5 Puttonyos, 0.5 л. 2000</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Вино Белое Сладкое<br />12 %</p>
|
||||
<p>Сорт: Фурминт (70%), Харшлевелю (28%), Мускат (2%)</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>19 240 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="product-card product-card--list">
|
||||
<a class="product-image" href="product.html" aria-label="Вино белое Oremus, Tokaji Late Harvest, 0.5 л. 2021">
|
||||
<img src="assets/images/00081726_1.png" alt="Вино белое Oremus, Tokaji Late Harvest, 0.5 л. 2021" />
|
||||
</a>
|
||||
<div class="product-info">
|
||||
<h3>Вино белое Oremus, Tokaji Late Harvest, 0.5 л. 2021</h3>
|
||||
<p class="product-origin">Вино белое, Oremus, Венгрия</p>
|
||||
<p>Вино Белое Полусладкое<br />11.5 %</p>
|
||||
<p>Сорт: Фурминт, Зета, Харшлевелю</p>
|
||||
</div>
|
||||
<div class="product-buy">
|
||||
<strong>4 980 ₽</strong>
|
||||
<a class="button button--secondary" href="#">В избранное</a>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
61
src/contacts-auth.njk
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
title: "DP Trade — Контакты / Auth Gate"
|
||||
layout: layouts/default
|
||||
permalink: /contacts-auth.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="content-page contacts-auth-page">
|
||||
<section class="page-hero page-hero--contacts">
|
||||
<div class="container page-hero__inner">
|
||||
<div>
|
||||
<p class="eyebrow">Контакты</p>
|
||||
<h1>Связаться с DP Trade</h1>
|
||||
<p>Контакты открыты для всех, но заявка менеджеру доступна только зарегистрированным или залогиненным пользователям.</p>
|
||||
</div>
|
||||
<aside class="page-hero__meta" aria-label="Основные контакты">
|
||||
<span>Sales office</span>
|
||||
<strong>+7 (495) 937-94-60</strong>
|
||||
<p>dptr@dp-trade.ru</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container contacts-layout">
|
||||
<div class="contact-stack">
|
||||
<article class="contact-panel">
|
||||
<span>Телефон</span>
|
||||
<a href="tel:+74959379460">+7 (495) 937-94-60</a>
|
||||
<p>Для консультаций по ассортименту, поставкам и условиям сотрудничества.</p>
|
||||
</article>
|
||||
<article class="contact-panel">
|
||||
<span>Email</span>
|
||||
<a href="mailto:dptr@dp-trade.ru">dptr@dp-trade.ru</a>
|
||||
<p>Удобно для запросов прайса, карточек производителей и B2B-документов.</p>
|
||||
</article>
|
||||
<article class="contact-panel">
|
||||
<span>Юридическая информация</span>
|
||||
<p>Интернет-витрина размещает информацию об алкогольной продукции исключительно в ознакомительных целях. Дистанционная продажа алкогольной продукции не осуществляется.</p>
|
||||
</article>
|
||||
</div>
|
||||
<section class="contact-form contact-auth-gate" aria-labelledby="auth-gate-title">
|
||||
<div class="auth-gate__badge">Требуется аккаунт</div>
|
||||
<h2 id="auth-gate-title">Заявка доступна после входа</h2>
|
||||
<p>Мы показываем телефон и email открыто, но форму заявки менеджеру оставляем внутри личного кабинета: так менеджер сразу видит компанию, историю запросов и коммерческий статус клиента.</p>
|
||||
<div class="auth-gate__actions">
|
||||
<a class="button button--primary" href="#">Войти</a>
|
||||
<a class="button button--secondary" href="#">Зарегистрироваться</a>
|
||||
</div>
|
||||
<div class="auth-gate__note">
|
||||
<strong>После входа откроется:</strong>
|
||||
<span>форма заявки, автозаполнение контактов, привязка к компании и история обращений.</span>
|
||||
</div>
|
||||
<div class="locked-form-preview" aria-label="Предпросмотр заблокированной формы">
|
||||
<label>Имя<input class="input" type="text" placeholder="Как к вам обращаться" disabled /></label>
|
||||
<label>Телефон или email<input class="input" type="text" placeholder="Контакт для ответа" disabled /></label>
|
||||
<label>Сообщение<textarea class="input" rows="4" placeholder="Напишите, что нужно подобрать" disabled></textarea></label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
50
src/contacts.njk
Normal file
@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "DP Trade — Контакты"
|
||||
layout: layouts/default
|
||||
permalink: /contacts.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="content-page">
|
||||
<section class="page-hero page-hero--contacts">
|
||||
<div class="container page-hero__inner">
|
||||
<div>
|
||||
<p class="eyebrow">Контакты</p>
|
||||
<h1>Связаться с DP Trade</h1>
|
||||
<p>Для вопросов по каталогу, ассортименту, поставкам и работе с менеджером используйте телефон, почту или форму заявки.</p>
|
||||
</div>
|
||||
<aside class="page-hero__meta" aria-label="Основные контакты">
|
||||
<span>Sales office</span>
|
||||
<strong>+7 (495) 937-94-60</strong>
|
||||
<p>dptr@dp-trade.ru</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container contacts-layout">
|
||||
<div class="contact-stack">
|
||||
<article class="contact-panel">
|
||||
<span>Телефон</span>
|
||||
<a href="tel:+74959379460">+7 (495) 937-94-60</a>
|
||||
<p>Для консультаций по ассортименту, поставкам и условиям сотрудничества.</p>
|
||||
</article>
|
||||
<article class="contact-panel">
|
||||
<span>Email</span>
|
||||
<a href="mailto:dptr@dp-trade.ru">dptr@dp-trade.ru</a>
|
||||
<p>Удобно для запросов прайса, карточек производителей и B2B-документов.</p>
|
||||
</article>
|
||||
<article class="contact-panel">
|
||||
<span>Юридическая информация</span>
|
||||
<p>Интернет-витрина размещает информацию об алкогольной продукции исключительно в ознакомительных целях. Дистанционная продажа алкогольной продукции не осуществляется.</p>
|
||||
</article>
|
||||
</div>
|
||||
<form class="contact-form" action="#">
|
||||
<h2>Заявка менеджеру</h2>
|
||||
<label>Имя<input class="input" type="text" placeholder="Как к вам обращаться" /></label>
|
||||
<label>Телефон или email<input class="input" type="text" placeholder="Контакт для ответа" /></label>
|
||||
<label>Сообщение<textarea class="input" rows="5" placeholder="Напишите, что нужно подобрать"></textarea></label>
|
||||
<button class="button button--primary" type="submit">Отправить заявку</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
2541
src/css/site.css
Normal file
34
src/css/tokens.css
Normal file
@ -0,0 +1,34 @@
|
||||
:root {
|
||||
--color-primary-wine-100: #4b0f24;
|
||||
--color-primary-wine-80: #6d1c36;
|
||||
--color-neutral-black: #161616;
|
||||
--color-neutral-gray-700: #30343a;
|
||||
--color-neutral-gray-600: #66605f;
|
||||
--color-neutral-gray-300: #d9dee6;
|
||||
--color-background-base: #f4f6f9;
|
||||
--color-surface: #ffffff;
|
||||
--color-accent-gold: #b9965b;
|
||||
--color-accent-blue: #1f3476;
|
||||
|
||||
--font-heading: "Montserrat", "Inter", Arial, sans-serif;
|
||||
--font-body: "Inter", Arial, sans-serif;
|
||||
--font-heading-classic: "Playfair Display", Georgia, serif;
|
||||
--font-body-classic: "Inter", Arial, sans-serif;
|
||||
--font-heading-retail: "Montserrat", "Inter", Arial, sans-serif;
|
||||
|
||||
--spacing-4: 4px;
|
||||
--spacing-8: 8px;
|
||||
--spacing-16: 16px;
|
||||
--spacing-24: 24px;
|
||||
--spacing-32: 32px;
|
||||
--spacing-48: 48px;
|
||||
--spacing-64: 64px;
|
||||
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 24px;
|
||||
|
||||
--shadow-soft: 0 12px 32px rgba(22, 22, 22, 0.08);
|
||||
--shadow-lift: 0 20px 52px rgba(75, 15, 36, 0.14);
|
||||
--container: 1240px;
|
||||
}
|
||||
1390
src/css/ui-kit.css
Normal file
90
src/index.njk
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
title: "DP Trade — Home"
|
||||
layout: layouts/default
|
||||
permalink: /
|
||||
---
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="container hero-copy">
|
||||
<p class="eyebrow">DP Trade</p>
|
||||
<h1>Премиальный каталог вина для торговли</h1>
|
||||
<p>Регионы, производители, партии и коммерческие условия собраны в одном интерфейсе для быстрых закупочных решений.</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button--primary" href="catalog.html">Смотреть каталог</a>
|
||||
<a class="button button--secondary" href="ui-kit.html">Открыть UI-kit</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section producers-section">
|
||||
<div class="container">
|
||||
<div class="section-heading section-heading--split">
|
||||
<div>
|
||||
<p class="eyebrow">Producers</p>
|
||||
<h2>Производители</h2>
|
||||
</div>
|
||||
<p>Ключевые винные дома и хозяйства из портфеля DP Trade: от классических брендов до эксклюзивных партнеров.</p>
|
||||
</div>
|
||||
<div class="producer-grid">
|
||||
<a class="producer-card" href="#"><span>Argentina</span><strong>Catena Zapata</strong></a>
|
||||
<a class="producer-card" href="#"><span>Champagne</span><strong>Bollinger</strong></a>
|
||||
<a class="producer-card" href="#"><span>Burgundy</span><strong>Domaine De Villaine</strong></a>
|
||||
<a class="producer-card" href="#"><span>Italy</span><strong>Gaja</strong></a>
|
||||
<a class="producer-card" href="#"><span>France</span><strong>Pascal Jolivet</strong></a>
|
||||
<a class="producer-card" href="#"><span>Rhone</span><strong>Paul Jaboulet Aine</strong></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section news-section">
|
||||
<div class="container">
|
||||
<div class="section-heading section-heading--split">
|
||||
<div>
|
||||
<p class="eyebrow">Latest news</p>
|
||||
<h2>Последние новости</h2>
|
||||
</div>
|
||||
<a class="button button--secondary" href="#">Все новости</a>
|
||||
</div>
|
||||
<div class="news-grid">
|
||||
<article class="news-card">
|
||||
<div class="news-card__image news-card__image--catena"></div>
|
||||
<div><span>Интервью</span><h3>Интервью с Лаурой Катеной</h3></div>
|
||||
</article>
|
||||
<article class="news-card">
|
||||
<div class="news-card__image news-card__image--brand"></div>
|
||||
<div><span>Бренды</span><h3>Catena Zapata вновь признана самым почитаемым винным брендом мира</h3></div>
|
||||
</article>
|
||||
<article class="news-card">
|
||||
<div class="news-card__image news-card__image--italy"></div>
|
||||
<div><span>Италия</span><h3>Villa Raiano: от оливкового масла к одному из лучших фиано Италии</h3></div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Recommendations</p>
|
||||
<h2>Рекомендации недели</h2>
|
||||
</div>
|
||||
<div class="product-grid">
|
||||
<article class="product-card">
|
||||
<div class="product-media"><img class="product-photo" src="assets/images/00081538_1.png" alt="Chateau Laroque Grand Cru" /></div>
|
||||
<div><span class="muted-caps">Bordeaux</span><h3>Chateau Laroque Grand Cru</h3><p>France · Red dry · 2019 · 0.75 L</p></div>
|
||||
<div class="product-footer"><strong>3 890 ₽</strong><a class="button button--primary button--sm" href="product.html">Подробнее</a></div>
|
||||
</article>
|
||||
<article class="product-card">
|
||||
<div class="product-media product-media--amber"><img class="product-photo" src="assets/images/00080768_1.png" alt="Brunello di Montalcino" /></div>
|
||||
<div><span class="muted-caps">Tuscany</span><h3>Brunello di Montalcino</h3><p>Italy · Red dry · 2018 · 0.75 L</p></div>
|
||||
<div class="product-footer"><strong>4 240 ₽</strong><a class="button button--primary button--sm" href="product.html">Подробнее</a></div>
|
||||
</article>
|
||||
<article class="product-card">
|
||||
<div class="product-media product-media--green"><img class="product-photo" src="assets/images/00081726_1.png" alt="Riesling Kabinett" /></div>
|
||||
<div><span class="muted-caps">Mosel</span><h3>Riesling Kabinett</h3><p>Germany · White · 2021 · 0.75 L</p></div>
|
||||
<div class="product-footer"><strong>2 150 ₽</strong><a class="button button--primary button--sm" href="product.html">Подробнее</a></div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
624
src/js/inspector.js
Normal file
@ -0,0 +1,624 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// ── Конфиг ─────────────────────────────────────────────────────────────────
|
||||
|
||||
const TOKEN_PREFIXES = ['--color-', '--font-', '--spacing-', '--radius-', '--shadow-', '--container'];
|
||||
|
||||
// layout-свойства для секции «Стили» (типографика — отдельная секция)
|
||||
const LAYOUT_PROPS = [
|
||||
'display', 'position', 'flexDirection', 'flexWrap',
|
||||
'gridTemplateColumns', 'gridTemplateRows', 'gap', 'alignItems', 'justifyContent',
|
||||
'width', 'maxWidth', 'minHeight', 'height',
|
||||
'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
|
||||
'margin', 'marginTop', 'marginBottom',
|
||||
'backgroundColor', 'borderRadius', 'boxShadow', 'opacity',
|
||||
];
|
||||
|
||||
const SKIP_VALUES = new Set([
|
||||
'none', 'normal', 'auto', '0px', 'rgba(0, 0, 0, 0)', 'transparent',
|
||||
'start', 'static', 'visible', 'nowrap', 'row', 'inline',
|
||||
'0px 0px 0px 0px rgba(0, 0, 0, 0)',
|
||||
]);
|
||||
|
||||
const TOKEN_PROP_MAP = {
|
||||
'--color-': ['color', 'background-color', 'border-top-color',
|
||||
'border-bottom-color', 'border-left-color', 'border-right-color'],
|
||||
'--font-': ['font-family'],
|
||||
'--spacing-': ['padding-top', 'padding-right', 'padding-bottom', 'padding-left',
|
||||
'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
|
||||
'row-gap', 'column-gap'],
|
||||
'--radius-': ['border-top-left-radius'],
|
||||
'--shadow-': ['box-shadow'],
|
||||
'--container': ['max-width'],
|
||||
};
|
||||
|
||||
// ── Состояние ───────────────────────────────────────────────────────────────
|
||||
|
||||
let enabled = false;
|
||||
let pinned = null;
|
||||
|
||||
// ── Утилиты: цвет ───────────────────────────────────────────────────────────
|
||||
|
||||
function rgbToHex(rgb) {
|
||||
const m = rgb.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
|
||||
if (!m) return null;
|
||||
return '#' + [m[1], m[2], m[3]]
|
||||
.map(n => parseInt(n).toString(16).padStart(2, '0'))
|
||||
.join('').toUpperCase();
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
hex = hex.replace(/^#/, '');
|
||||
if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
|
||||
const r = parseInt(hex.slice(0, 2), 16);
|
||||
const g = parseInt(hex.slice(2, 4), 16);
|
||||
const b = parseInt(hex.slice(4, 6), 16);
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
|
||||
function normalizeColor(val) {
|
||||
val = val.trim().toLowerCase();
|
||||
if (/^#[0-9a-f]{3,8}$/.test(val)) return hexToRgb(val);
|
||||
return val.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
function firstFont(val) {
|
||||
return val.split(',')[0].trim().replace(/["']/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
function formatColor(val) {
|
||||
if (/^#/.test(val.trim())) return val.trim().toUpperCase();
|
||||
if (/^rgba?\(/.test(val.trim())) return rgbToHex(val) || val;
|
||||
return val;
|
||||
}
|
||||
|
||||
// ── Токены из :root ──────────────────────────────────────────────────────────
|
||||
|
||||
let _rootTokens = null;
|
||||
function getRootTokens() {
|
||||
if (_rootTokens) return _rootTokens;
|
||||
_rootTokens = {};
|
||||
for (const sheet of document.styleSheets) {
|
||||
try {
|
||||
for (const rule of sheet.cssRules) {
|
||||
if (rule.selectorText === ':root') {
|
||||
for (const prop of rule.style) {
|
||||
if (TOKEN_PREFIXES.some(p => prop.startsWith(p))) {
|
||||
_rootTokens[prop] = rule.style.getPropertyValue(prop).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
return _rootTokens;
|
||||
}
|
||||
|
||||
// ── Обратный маппинг: значение → имя токена ─────────────────────────────────
|
||||
// Используется в секции Типографика, чтобы показать
|
||||
// «Inter → --font-body» и «#30343A → --color-neutral-gray-700».
|
||||
|
||||
let _colorMap = null;
|
||||
let _fontMap = null;
|
||||
|
||||
function getColorTokenMap() {
|
||||
if (_colorMap) return _colorMap;
|
||||
_colorMap = {};
|
||||
for (const [name, val] of Object.entries(getRootTokens())) {
|
||||
if (!name.startsWith('--color-')) continue;
|
||||
try { _colorMap[normalizeColor(val)] = name; } catch (_) {}
|
||||
}
|
||||
return _colorMap;
|
||||
}
|
||||
|
||||
function getFontTokenMap() {
|
||||
if (_fontMap) return _fontMap;
|
||||
_fontMap = {};
|
||||
for (const [name, val] of Object.entries(getRootTokens())) {
|
||||
if (!name.startsWith('--font-')) continue;
|
||||
const key = firstFont(val);
|
||||
// Приоритет: --font-heading > --font-heading-* > --font-body > etc.
|
||||
// Первый встреченный побеждает, поэтому не перезаписываем.
|
||||
if (!_fontMap[key]) _fontMap[key] = { name, fullValue: val };
|
||||
}
|
||||
return _fontMap;
|
||||
}
|
||||
|
||||
// ── Токены, активные на элементе (для секции «Токены») ──────────────────────
|
||||
|
||||
function getElementTokens(el) {
|
||||
const tokens = getRootTokens();
|
||||
const cs = getComputedStyle(el);
|
||||
const found = new Map();
|
||||
|
||||
for (const [tokenName, tokenRaw] of Object.entries(tokens)) {
|
||||
const prefix = TOKEN_PREFIXES.find(p => tokenName.startsWith(p));
|
||||
if (!prefix) continue;
|
||||
const propsToCheck = TOKEN_PROP_MAP[prefix] || [];
|
||||
|
||||
for (const cssProp of propsToCheck) {
|
||||
const computed = cs.getPropertyValue(cssProp).trim();
|
||||
if (!computed) continue;
|
||||
let match = false;
|
||||
|
||||
if (prefix === '--color-') {
|
||||
try { match = normalizeColor(computed) === normalizeColor(tokenRaw); } catch (_) {}
|
||||
} else if (prefix === '--font-') {
|
||||
match = firstFont(computed) === firstFont(tokenRaw);
|
||||
} else if (prefix === '--spacing-' || prefix === '--radius-' || prefix === '--container') {
|
||||
match = computed === tokenRaw.trim();
|
||||
} else if (prefix === '--shadow-') {
|
||||
match = computed.includes(tokenRaw.split(' ').slice(0, 3).join(' '));
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (!found.has(tokenName)) {
|
||||
found.set(tokenName, { raw: tokenRaw, usedIn: [] });
|
||||
}
|
||||
found.get(tokenName).usedIn.push(cssProp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
// ── Типографика элемента ─────────────────────────────────────────────────────
|
||||
|
||||
function getTypography(el) {
|
||||
const cs = getComputedStyle(el);
|
||||
const colorMap = getColorTokenMap();
|
||||
const fontMap = getFontTokenMap();
|
||||
|
||||
const rawFont = cs.fontFamily;
|
||||
const rawColor = cs.color;
|
||||
const fontKey = firstFont(rawFont);
|
||||
const fontEntry = fontMap[fontKey];
|
||||
|
||||
// Название шрифта — первый, без кавычек
|
||||
const fontName = fontKey.charAt(0).toUpperCase() + fontKey.slice(1);
|
||||
|
||||
// Токен шрифта — ищем точное совпадение первого шрифта
|
||||
const fontToken = fontEntry ? fontEntry.name : null;
|
||||
|
||||
// Цвет текста
|
||||
const colorHex = formatColor(rawColor);
|
||||
const colorToken = colorMap[normalizeColor(rawColor)] || null;
|
||||
|
||||
// Линейная высота — упрощаем до числа если возможно
|
||||
const lhRaw = cs.lineHeight;
|
||||
const lhNum = parseFloat(lhRaw);
|
||||
const fsNum = parseFloat(cs.fontSize);
|
||||
const lh = (fsNum && lhNum)
|
||||
? (lhNum / fsNum).toFixed(2).replace(/\.?0+$/, '')
|
||||
: lhRaw;
|
||||
|
||||
// Letter-spacing
|
||||
const ls = cs.letterSpacing !== '0px' ? cs.letterSpacing : null;
|
||||
|
||||
// Text-transform
|
||||
const tt = cs.textTransform !== 'none' ? cs.textTransform : null;
|
||||
|
||||
return {
|
||||
fontName,
|
||||
fontToken,
|
||||
fontFull: rawFont,
|
||||
fontSize: cs.fontSize,
|
||||
fontWeight: cs.fontWeight,
|
||||
lineHeight: lh,
|
||||
letterSpacing: ls,
|
||||
textTransform: tt,
|
||||
colorHex,
|
||||
colorToken,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Layout-стили (без типографики) ──────────────────────────────────────────
|
||||
|
||||
function getLayoutStyles(el) {
|
||||
const cs = getComputedStyle(el);
|
||||
const result = [];
|
||||
for (const prop of LAYOUT_PROPS) {
|
||||
let val = cs[prop];
|
||||
if (!val || SKIP_VALUES.has(val)) continue;
|
||||
if (/^rgba?\(/.test(val)) val = formatColor(val);
|
||||
result.push({ prop: camelToKebab(prop), value: val });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function camelToKebab(s) {
|
||||
return s.replace(/[A-Z]/g, c => '-' + c.toLowerCase());
|
||||
}
|
||||
|
||||
function formatTokenValue(raw) {
|
||||
const t = raw.trim();
|
||||
if (/^#[0-9a-fA-F]{3,6}$/.test(t)) return t.toUpperCase();
|
||||
if (/^rgba?\(/.test(t)) return rgbToHex(t) || t;
|
||||
return t;
|
||||
}
|
||||
|
||||
// ── DOM ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'dp-inspector-panel';
|
||||
panel.innerHTML = `
|
||||
<div class="dpi-header">
|
||||
<span class="dpi-title">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
|
||||
</svg>
|
||||
Инспектор
|
||||
</span>
|
||||
<button class="dpi-close" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="dpi-body">
|
||||
<p class="dpi-empty">Кликните на элемент</p>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(panel);
|
||||
|
||||
const toggle = document.createElement('button');
|
||||
toggle.id = 'dp-inspector-toggle';
|
||||
toggle.title = 'Инспектор элементов (Alt+I)';
|
||||
toggle.innerHTML = `
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
|
||||
</svg>`;
|
||||
document.body.appendChild(toggle);
|
||||
|
||||
const highlight = document.createElement('div');
|
||||
highlight.id = 'dp-inspector-highlight';
|
||||
document.body.appendChild(highlight);
|
||||
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.id = 'dp-inspector-tooltip';
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
// ── Стили ───────────────────────────────────────────────────────────────────
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
#dp-inspector-toggle {
|
||||
position: fixed; bottom: 24px; right: 24px; z-index: 99998;
|
||||
width: 44px; height: 44px; border-radius: 50%; border: none;
|
||||
background: #4b0f24; color: #fff; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
box-shadow: 0 4px 16px rgba(75,15,36,.35);
|
||||
transition: background .15s, transform .15s;
|
||||
}
|
||||
#dp-inspector-toggle:hover { background: #6d1c36; transform: scale(1.08); }
|
||||
#dp-inspector-toggle.active { background: #b9965b; }
|
||||
|
||||
#dp-inspector-panel {
|
||||
position: fixed; top: 0; right: -360px; width: 340px; height: 100vh;
|
||||
z-index: 99999; background: #fff;
|
||||
border-left: 1px solid rgba(102,96,95,.18);
|
||||
box-shadow: -12px 0 40px rgba(22,22,22,.10);
|
||||
display: flex; flex-direction: column;
|
||||
font-family: 'Inter', system-ui, sans-serif; font-size: 13px;
|
||||
transition: right .25s cubic-bezier(.4,0,.2,1); overflow: hidden;
|
||||
}
|
||||
#dp-inspector-panel.open { right: 0; }
|
||||
|
||||
.dpi-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 14px 16px; background: #4b0f24; color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.dpi-title {
|
||||
display: flex; align-items: center; gap: 7px;
|
||||
font-weight: 700; font-size: 13px;
|
||||
letter-spacing: .04em; text-transform: uppercase;
|
||||
}
|
||||
.dpi-close {
|
||||
background: none; border: none; color: rgba(255,255,255,.7);
|
||||
cursor: pointer; font-size: 15px; padding: 2px 4px; line-height: 1;
|
||||
}
|
||||
.dpi-close:hover { color: #fff; }
|
||||
|
||||
.dpi-body { flex: 1; overflow-y: auto; padding: 0 0 80px; }
|
||||
.dpi-empty { color: #66605f; text-align: center; padding: 40px 16px; margin: 0; }
|
||||
|
||||
.dpi-section { padding: 12px 16px; border-bottom: 1px solid rgba(102,96,95,.12); }
|
||||
.dpi-section:last-child { border-bottom: none; }
|
||||
|
||||
.dpi-section-title {
|
||||
font-size: 10px; font-weight: 800; letter-spacing: .08em;
|
||||
text-transform: uppercase; color: #b9965b; margin: 0 0 8px;
|
||||
}
|
||||
|
||||
/* Элемент */
|
||||
.dpi-element-tag {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 12px;
|
||||
color: #4b0f24; font-weight: 600; margin: 0 0 6px; word-break: break-all;
|
||||
}
|
||||
.dpi-classes { display: flex; flex-wrap: wrap; gap: 5px; }
|
||||
.dpi-class {
|
||||
padding: 2px 8px; border-radius: 4px;
|
||||
background: rgba(75,15,36,.08); color: #4b0f24;
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
font-weight: 600; cursor: pointer; user-select: all;
|
||||
}
|
||||
.dpi-class:hover { background: rgba(75,15,36,.16); }
|
||||
|
||||
/* Типографика */
|
||||
.dpi-type-row {
|
||||
display: grid; grid-template-columns: 76px 1fr;
|
||||
align-items: baseline; gap: 6px;
|
||||
padding: 3px 0; border-bottom: 1px solid rgba(102,96,95,.07);
|
||||
}
|
||||
.dpi-type-row:last-child { border-bottom: none; }
|
||||
.dpi-type-label {
|
||||
font-size: 10px; font-weight: 700; text-transform: uppercase;
|
||||
letter-spacing: .06em; color: #66605f;
|
||||
}
|
||||
.dpi-type-val {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
color: #161616; font-weight: 600;
|
||||
}
|
||||
.dpi-type-token {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 10px;
|
||||
color: #b9965b; margin-left: 6px; font-weight: 400;
|
||||
}
|
||||
.dpi-color-dot {
|
||||
display: inline-block; width: 10px; height: 10px;
|
||||
border-radius: 2px; border: 1px solid rgba(0,0,0,.12);
|
||||
margin-right: 5px; vertical-align: middle; flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Токены */
|
||||
.dpi-token {
|
||||
display: grid; grid-template-columns: 16px 1fr auto;
|
||||
align-items: start; gap: 7px;
|
||||
padding: 5px 0; border-bottom: 1px solid rgba(102,96,95,.07);
|
||||
}
|
||||
.dpi-token:last-child { border-bottom: none; }
|
||||
.dpi-token-swatch {
|
||||
width: 14px; height: 14px; border-radius: 3px;
|
||||
border: 1px solid rgba(0,0,0,.12); margin-top: 1px;
|
||||
}
|
||||
.dpi-token-swatch.no-color {
|
||||
background: repeating-linear-gradient(45deg,#ddd 0 3px,#fff 3px 6px);
|
||||
}
|
||||
.dpi-token-info { min-width: 0; }
|
||||
.dpi-token-name {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
color: #30343a; display: block;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.dpi-token-uses {
|
||||
font-size: 10px; color: #b9965b; margin-top: 1px;
|
||||
display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.dpi-token-value {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
color: #4b0f24; font-weight: 600; white-space: nowrap; margin-top: 1px;
|
||||
}
|
||||
|
||||
/* Стили */
|
||||
.dpi-style {
|
||||
display: flex; justify-content: space-between; gap: 8px;
|
||||
padding: 3px 0; border-bottom: 1px solid rgba(102,96,95,.07);
|
||||
}
|
||||
.dpi-style:last-child { border-bottom: none; }
|
||||
.dpi-style-prop {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
color: #30343a; flex-shrink: 0;
|
||||
}
|
||||
.dpi-style-val {
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
color: #4b0f24; font-weight: 600;
|
||||
text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Highlight */
|
||||
#dp-inspector-highlight {
|
||||
position: fixed; pointer-events: none; z-index: 99990;
|
||||
border: 2px solid #4b0f24; background: rgba(75,15,36,.06);
|
||||
border-radius: 3px; display: none;
|
||||
transition: top .05s, left .05s, width .05s, height .05s;
|
||||
}
|
||||
body.dp-inspect-mode #dp-inspector-highlight { display: block; }
|
||||
|
||||
#dp-inspector-tooltip {
|
||||
position: fixed; pointer-events: none; z-index: 99995;
|
||||
background: #4b0f24; color: #fff;
|
||||
font-family: 'Menlo','Monaco',monospace; font-size: 11px;
|
||||
padding: 3px 8px; border-radius: 4px; white-space: nowrap;
|
||||
display: none; max-width: 300px;
|
||||
overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
body.dp-inspect-mode #dp-inspector-tooltip { display: block; }
|
||||
|
||||
body.dp-inspect-mode * { cursor: crosshair !important; }
|
||||
body.dp-inspect-mode #dp-inspector-toggle,
|
||||
body.dp-inspect-mode #dp-inspector-panel,
|
||||
body.dp-inspect-mode #dp-inspector-panel * { cursor: default !important; }
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// ── Рендер панели ───────────────────────────────────────────────────────────
|
||||
|
||||
function buildPanel(el) {
|
||||
const tag = el.tagName.toLowerCase();
|
||||
const id = el.id ? `#${el.id}` : '';
|
||||
const classes = [...el.classList];
|
||||
const typo = getTypography(el);
|
||||
const tokens = getElementTokens(el);
|
||||
const layout = getLayoutStyles(el);
|
||||
|
||||
let html = '';
|
||||
|
||||
// ── Элемент ──────────────────────────────────────────────────────────────
|
||||
html += `<div class="dpi-section">
|
||||
<p class="dpi-section-title">Элемент</p>
|
||||
<p class="dpi-element-tag"><${tag}${id}></p>`;
|
||||
html += classes.length
|
||||
? `<div class="dpi-classes">${classes.map(c => `<span class="dpi-class">.${c}</span>`).join('')}</div>`
|
||||
: `<p style="color:#66605f;margin:0;font-size:12px">Нет классов</p>`;
|
||||
html += `</div>`;
|
||||
|
||||
// ── Типографика ──────────────────────────────────────────────────────────
|
||||
html += `<div class="dpi-section">
|
||||
<p class="dpi-section-title">Типографика</p>`;
|
||||
|
||||
// Шрифт
|
||||
const fontToken = typo.fontToken
|
||||
? `<span class="dpi-type-token">${typo.fontToken}</span>` : '';
|
||||
html += `<div class="dpi-type-row" title="${typo.fontFull}">
|
||||
<span class="dpi-type-label">Шрифт</span>
|
||||
<span class="dpi-type-val">${typo.fontName}${fontToken}</span>
|
||||
</div>`;
|
||||
|
||||
// Размер
|
||||
html += `<div class="dpi-type-row">
|
||||
<span class="dpi-type-label">Размер</span>
|
||||
<span class="dpi-type-val">${typo.fontSize}</span>
|
||||
</div>`;
|
||||
|
||||
// Насыщенность
|
||||
html += `<div class="dpi-type-row">
|
||||
<span class="dpi-type-label">Насыщ.</span>
|
||||
<span class="dpi-type-val">${typo.fontWeight}</span>
|
||||
</div>`;
|
||||
|
||||
// Межстрочный
|
||||
html += `<div class="dpi-type-row">
|
||||
<span class="dpi-type-label">Line-height</span>
|
||||
<span class="dpi-type-val">${typo.lineHeight}</span>
|
||||
</div>`;
|
||||
|
||||
// Letter-spacing
|
||||
if (typo.letterSpacing) {
|
||||
html += `<div class="dpi-type-row">
|
||||
<span class="dpi-type-label">Tracking</span>
|
||||
<span class="dpi-type-val">${typo.letterSpacing}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Text-transform
|
||||
if (typo.textTransform) {
|
||||
html += `<div class="dpi-type-row">
|
||||
<span class="dpi-type-label">Transform</span>
|
||||
<span class="dpi-type-val">${typo.textTransform}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Цвет текста
|
||||
const colorDot = `<span class="dpi-color-dot" style="background:${typo.colorHex}"></span>`;
|
||||
const colorToken = typo.colorToken
|
||||
? `<span class="dpi-type-token">${typo.colorToken}</span>` : '';
|
||||
html += `<div class="dpi-type-row">
|
||||
<span class="dpi-type-label">Цвет</span>
|
||||
<span class="dpi-type-val">${colorDot}${typo.colorHex}${colorToken}</span>
|
||||
</div>`;
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
// ── Токены ───────────────────────────────────────────────────────────────
|
||||
html += `<div class="dpi-section">
|
||||
<p class="dpi-section-title">Токены${tokens.size ? ` (${tokens.size})` : ''}</p>`;
|
||||
|
||||
if (tokens.size) {
|
||||
for (const [name, { raw, usedIn }] of tokens) {
|
||||
const display = formatTokenValue(raw);
|
||||
const isColor = name.startsWith('--color-');
|
||||
const swatch = isColor
|
||||
? `<span class="dpi-token-swatch" style="background:${raw}"></span>`
|
||||
: `<span class="dpi-token-swatch no-color"></span>`;
|
||||
html += `<div class="dpi-token">
|
||||
${swatch}
|
||||
<span class="dpi-token-info">
|
||||
<span class="dpi-token-name" title="${name}">${name}</span>
|
||||
<span class="dpi-token-uses">${usedIn.join(', ')}</span>
|
||||
</span>
|
||||
<span class="dpi-token-value">${display}</span>
|
||||
</div>`;
|
||||
}
|
||||
} else {
|
||||
html += `<p style="color:#66605f;margin:0;font-size:12px">Нет прямых совпадений с tokens.css</p>`;
|
||||
}
|
||||
html += `</div>`;
|
||||
|
||||
// ── Стили (layout) ───────────────────────────────────────────────────────
|
||||
if (layout.length) {
|
||||
html += `<div class="dpi-section">
|
||||
<p class="dpi-section-title">Стили</p>`;
|
||||
for (const { prop, value } of layout) {
|
||||
html += `<div class="dpi-style">
|
||||
<span class="dpi-style-prop">${prop}</span>
|
||||
<span class="dpi-style-val" title="${value}">${value}</span>
|
||||
</div>`;
|
||||
}
|
||||
html += `</div>`;
|
||||
}
|
||||
|
||||
panel.querySelector('.dpi-body').innerHTML = html;
|
||||
}
|
||||
|
||||
// ── Управление ──────────────────────────────────────────────────────────────
|
||||
|
||||
function enable() {
|
||||
enabled = true;
|
||||
document.body.classList.add('dp-inspect-mode');
|
||||
toggle.classList.add('active');
|
||||
panel.classList.add('open');
|
||||
panel.querySelector('.dpi-body').innerHTML = '<p class="dpi-empty">Кликните на элемент</p>';
|
||||
}
|
||||
|
||||
function disable() {
|
||||
enabled = false; pinned = null;
|
||||
document.body.classList.remove('dp-inspect-mode');
|
||||
toggle.classList.remove('active');
|
||||
panel.classList.remove('open');
|
||||
highlight.style.display = 'none';
|
||||
tooltip.style.display = 'none';
|
||||
}
|
||||
|
||||
function isInspectable(el) {
|
||||
if (!el || el === document.body || el === document.documentElement) return false;
|
||||
if (['dp-inspector-panel','dp-inspector-toggle',
|
||||
'dp-inspector-highlight','dp-inspector-tooltip'].includes(el.id)) return false;
|
||||
if (el.closest('#dp-inspector-panel, #dp-inspector-toggle')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── События ─────────────────────────────────────────────────────────────────
|
||||
|
||||
toggle.addEventListener('click', () => enabled ? disable() : enable());
|
||||
panel.querySelector('.dpi-close').addEventListener('click', disable);
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.altKey && e.key === 'i') { e.preventDefault(); enabled ? disable() : enable(); }
|
||||
if (e.key === 'Escape' && enabled) disable();
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', e => {
|
||||
if (!enabled) return;
|
||||
const el = document.elementFromPoint(e.clientX, e.clientY);
|
||||
if (!isInspectable(el) || el === pinned) return;
|
||||
|
||||
const r = el.getBoundingClientRect();
|
||||
Object.assign(highlight.style, {
|
||||
display: 'block', top: r.top + 'px', left: r.left + 'px',
|
||||
width: r.width + 'px', height: r.height + 'px',
|
||||
});
|
||||
|
||||
const cls = [...el.classList].map(c => '.' + c).join('');
|
||||
tooltip.textContent = el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') + cls;
|
||||
tooltip.style.display = 'block';
|
||||
tooltip.style.left = Math.min(e.clientX + 12, window.innerWidth - tooltip.offsetWidth - 8) + 'px';
|
||||
tooltip.style.top = (e.clientY + 20) + 'px';
|
||||
});
|
||||
|
||||
document.addEventListener('click', e => {
|
||||
if (!enabled) return;
|
||||
const el = document.elementFromPoint(e.clientX, e.clientY);
|
||||
if (!isInspectable(el)) return;
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
pinned = el;
|
||||
buildPanel(el);
|
||||
}, true);
|
||||
|
||||
})();
|
||||
78
src/news-villa-raiano-v2.njk
Normal file
@ -0,0 +1,78 @@
|
||||
---
|
||||
title: "DP Trade — Villa Raiano editorial"
|
||||
layout: layouts/default
|
||||
permalink: /news-villa-raiano-v2.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="news-editorial">
|
||||
<article>
|
||||
<header class="editorial-hero">
|
||||
<div class="container editorial-hero__inner">
|
||||
<div class="editorial-hero__copy">
|
||||
<p class="eyebrow">Новости / Villa Raiano</p>
|
||||
<h1>Villa Raiano: от оливкового масла к одному из лучших фиано Италии</h1>
|
||||
<div class="editorial-meta"><span>6 апреля, 2026</span><span>Ирпиния, Кампания</span></div>
|
||||
</div>
|
||||
<figure class="editorial-hero__image">
|
||||
<img src="assets/images/photo_2026-04-06_16-53-26.jpg" alt="Винодельня Villa Raiano среди холмов Ирпинии" />
|
||||
</figure>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="section editorial-section">
|
||||
<div class="container editorial-layout">
|
||||
<aside class="editorial-side">
|
||||
<span>Article focus</span>
|
||||
<p>Семья Бассо, новая винодельня, ставка на белые вина и признание Fiano di Avellino.</p>
|
||||
</aside>
|
||||
<div class="editorial-body">
|
||||
<p class="lead">Рассказываем о семье Бассо и винодельне Villa Raiano, совершившей небольшую революцию в Ирпинии, сменив курс с красных вин на белые. Судя по нашим последним дегустациям, у них это получилось.</p>
|
||||
<p>На холмах Сан-Микеле-ди-Серино, в провинции Авеллино, расположена винодельня семьи Бассо. Название связано с корнями: Villa Raiano — историческая местность коммуны Серино, где находилась старая оливковая плантация семьи.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<figure class="container editorial-image editorial-image--wide">
|
||||
<img src="assets/images/photo_2026-04-06_16-53-24.jpg" alt="Сбор белого винограда Villa Raiano" />
|
||||
</figure>
|
||||
|
||||
<section class="section editorial-section">
|
||||
<div class="container editorial-layout">
|
||||
<aside class="editorial-side editorial-side--timeline">
|
||||
<span>Timeline</span>
|
||||
<ol>
|
||||
<li><strong>1990-е</strong><small>семейная маслобойня</small></li>
|
||||
<li><strong>2008</strong><small>новое оборудование и команда</small></li>
|
||||
<li><strong>2009</strong><small>винодельня на холме</small></li>
|
||||
<li><strong>2024</strong><small>подземная винодельня</small></li>
|
||||
</ol>
|
||||
</aside>
|
||||
<div class="editorial-body">
|
||||
<p>В середине 1990-х с основанием винодельни в Ирпинии начался винодельческий бум. Первые годы для семьи Бассо были скорее хобби: они делали и разливали вино на семейной маслобойне. С 1999 по 2008 год они стали сотрудничать с энологом Луиджи Мойо, который учился вместе с Сабино Бассо в аграрном институте Авеллино.</p>
|
||||
<blockquote>«В 2008 году мы купили новое оборудование, начали сотрудничество с Фортунато Себастьяно и построили винодельню на вершине холма в Сан-Микеле-ди-Серино», — рассказывает сын Сабино Бассо, Федерико.</blockquote>
|
||||
<p>Открытие в 2009 году совпало с новым курсом: появились четыре белых вина с отдельных виноградников — три Fiano di Avellino и один Greco di Tufo. Через десять лет к работе подключилось новое поколение: «В компанию пришли я, мой брат Фабрицио и наша кузина Брунелла, дочь Симоне».</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container editorial-photo-grid" aria-label="Фото Villa Raiano">
|
||||
<figure><img src="assets/images/photo_2026-04-06_16-53-23.jpg" alt="Ящики с белым виноградом Villa Raiano" /></figure>
|
||||
<figure><img src="assets/images/photo_2026-04-06_16-53-23-2.jpg" alt="Работа в погребе Villa Raiano" /></figure>
|
||||
</section>
|
||||
|
||||
<section class="section editorial-section editorial-section--final">
|
||||
<div class="container editorial-layout">
|
||||
<aside class="editorial-side">
|
||||
<span>Recognition</span>
|
||||
<p>Gambero Rosso отметил Fiano di Avellino 2024 за лучшее соотношение цены и качества в регионе.</p>
|
||||
</aside>
|
||||
<div class="editorial-body">
|
||||
<p>В 2024 году открылась новая подземная винодельня, полностью интегрированная в ландшафт. Сегодня Villa Raiano владеет 30 гектарами виноградников: хозяйство работает по биологическим принципам и сертифицировано с 2011 года.</p>
|
||||
<blockquote>«Мы уверены, что наш регион — земля великих белых вин», — говорит Федерико. «Сила наших DOC — в огромных различиях, которые один и тот же сорт может проявлять в разных условиях».</blockquote>
|
||||
<p>Континентальный климат позволяет ягодам созревать медленно, что делает вина особенными — как красные, так и белые. На последних дегустациях Gambero Rosso Fiano di Avellino 2024 года особенно впечатлил итальянских экспертов, получив премию Miglior Qualità Prezzo Regionale гида BereBene 2026.</p>
|
||||
<p class="editorial-outro">Чем не повод самому проверить мнение Gambero Rosso?</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
53
src/news-villa-raiano.njk
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "DP Trade — Villa Raiano"
|
||||
layout: layouts/default
|
||||
permalink: /news-villa-raiano.html
|
||||
bodyClass: compact-type
|
||||
---
|
||||
<main class="content-page">
|
||||
<section class="page-hero page-hero--news">
|
||||
<div class="container page-hero__inner">
|
||||
<div>
|
||||
<p class="eyebrow">Новости / Италия</p>
|
||||
<h1>Villa Raiano: от оливкового масла к одному из лучших Fiano Италии</h1>
|
||||
<p>История семейного хозяйства из Кампании, где любовь к Ирпинии выросла из маслобойного производства в современную винодельню.</p>
|
||||
</div>
|
||||
<aside class="page-hero__meta" aria-label="Параметры новости">
|
||||
<span>Producer spotlight</span>
|
||||
<strong>Villa Raiano</strong>
|
||||
<p>Campania · Irpinia · Fiano di Avellino</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container article-layout">
|
||||
<article class="article-body">
|
||||
<p class="lead">Villa Raiano появилась в 1996 году по инициативе семьи Бассо, известной производством оливкового масла. Первые вина делались в помещениях старого маслобойного завода, а в 2009 году хозяйство переехало в новую винодельню в Ирпинии.</p>
|
||||
<p>Для DP Trade эта история важна не только как биография производителя. Villa Raiano показывает, как локальная ремесленная культура Кампании может стать точной, современной и очень узнаваемой винной стилистикой.</p>
|
||||
<h2>Почему Fiano di Avellino</h2>
|
||||
<p>Фьяно из Авеллино ценят за минеральность, плотную фактуру и способность к развитию в бутылке. В молодых винах часто появляются цитрусовые, груша, персик, травы и медовые оттенки; с возрастом они становятся глубже, прянее и сложнее.</p>
|
||||
<p>Villa Raiano работает с традиционными сортами региона: Fiano, Greco, Falanghina и Aglianico. Такой фокус помогает хозяйству говорить не универсальным языком международного вина, а языком конкретного места.</p>
|
||||
<blockquote>Фокус карточки новости: семейная история, локальный сорт и ценность производителя для профессионального каталога.</blockquote>
|
||||
<h2>Что показать в каталоге</h2>
|
||||
<p>Для товарной страницы и подборок можно вынести происхождение, сорт, стиль, потенциал выдержки и гастрономические пары. В B2B-сценарии особенно полезны быстрые маркеры: регион, апелласьон, тип вина, крепость, объем и доступность партии.</p>
|
||||
</article>
|
||||
<aside class="article-aside">
|
||||
<div class="info-card">
|
||||
<span>Ключевые факты</span>
|
||||
<dl>
|
||||
<div><dt>Страна</dt><dd>Италия</dd></div>
|
||||
<div><dt>Регион</dt><dd>Кампания, Ирпиния</dd></div>
|
||||
<div><dt>Основание</dt><dd>1996</dd></div>
|
||||
<div><dt>Сорта</dt><dd>Fiano, Greco, Aglianico</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="info-card info-card--accent">
|
||||
<span>Для UI-kit</span>
|
||||
<p>Эта страница проверяет длинный заголовок новости, текстовую статью, боковую карточку фактов и CTA обратно в каталог.</p>
|
||||
<a class="button button--primary button--sm" href="catalog.html">В каталог</a>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
26
src/product.njk
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "DP Trade — Product"
|
||||
layout: layouts/default
|
||||
permalink: /product.html
|
||||
---
|
||||
<main class="section">
|
||||
<div class="container product-detail">
|
||||
<div class="product-media"><img class="product-photo" src="assets/images/00081538_1.png" alt="Chateau Laroque Grand Cru" /></div>
|
||||
<section class="detail-copy">
|
||||
<p class="eyebrow">Product / Default</p>
|
||||
<h1>Chateau Laroque Grand Cru</h1>
|
||||
<p>Премиальная позиция из Bordeaux для ресторанных карт и специализированной розницы.</p>
|
||||
<div class="meta-list">
|
||||
<div><span>Регион</span><strong>Bordeaux</strong></div>
|
||||
<div><span>Тип</span><strong>Red dry</strong></div>
|
||||
<div><span>Год</span><strong>2019</strong></div>
|
||||
<div><span>Объем</span><strong>0.75 L</strong></div>
|
||||
<div><span>Цена</span><strong>3 890 ₽</strong></div>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a class="button button--primary" href="#">Запросить прайс</a>
|
||||
<a class="button button--secondary" href="catalog.html">Вернуться в каталог</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||