commit a69da34e3c7df0e35d4bf716ae89e38d9b70d039 Author: AB Date: Sun Apr 19 15:52:49 2026 +0300 Initial commit: wedding website template with configurable content Zola-based single-page wedding site with all content (schedule, details, colors, images, RSVP link) managed via config.toml. Multi-day schedule layout, Blue & Green color palette, CSS custom properties for theming. Co-Authored-By: Claude diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d26766 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Zola build output +public/ + +# Editor swap files +*.swp +*.swo + +# Windows artifacts +nul + +# Claude Code +.claude/ diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..ba1b01a --- /dev/null +++ b/config.toml @@ -0,0 +1,130 @@ +title = "Свадьба Марии и Александра" +description = "Приглашаем вас на нашу свадьбу!" +base_url = "https://wedding.hexor.cy" +default_language = "ru" +compile_sass = true +minify_html = true + +[extra] +bride = "Мария" +groom = "Александр" +wedding_date = "21 августа 2026" +wedding_time = "10:00" +wedding_datetime_iso = "2026-08-21T10:00:00" +venue_name = "Московское Небо" +venue_address = "Москва, Проспект Мира, 119 с422" +venue_map_url = "https://yandex.com/maps/?um=constructor%3Ab306a9bb2e7f5aebbb90dc22d61211eef19e7f1cbd55be8a5d871d4460d4d51e&source=constructorLink" +venue_map_embed = "https://yandex.com/map-widget/v1/?um=constructor%3Ab306a9bb2e7f5aebbb90dc22d61211eef19e7f1cbd55be8a5d871d4460d4d51e&source=constructor" + +# Изображения (положите файлы в static/images/) +hero_image = "/images/hero.jpg" +bride_photo = "/images/bride.jpg" +groom_photo = "/images/groom.jpg" + +# Hero +hero_invite = "Приглашаем на свадьбу" + +# About +about_title = "Наша история" +about_text = [ + "Мы встретились в один прекрасный день и поняли, что созданы друг для друга. Спустя время, проведённое вместе, мы решили объединить наши жизни навсегда.", + "Мы будем счастливы разделить этот важный день с самыми близкими и дорогими людьми.", +] + +# Location +dress_code = "Коктейльный / нарядный" + +# RSVP +rsvp_title = "Подтвердите участие" +rsvp_deadline = "1 августа 2026" +rsvp_url = "" +rsvp_button = "Заполнить форму" + +# Цвета (CSS-значения) +[extra.colors] +primary = "#2b4c7e" +primary_light = "#7da0c4" +accent = "#7e8e6c" +text = "#2c3e50" +text_light = "#5a6d7e" +background = "#f9fbfd" +background_alt = "#eef3f7" +white = "#fffff8" +border = "#c9d6e0" +hero_gradient = "linear-gradient(135deg, #1a3a5c 0%, #2b4c7e 50%, #3a6b5a 100%)" + +# Карточки деталей +[[extra.details]] +icon = "🎁" +title = "Подарки" +description = "Лучший подарок для нас — ваше присутствие. Но если вы хотите сделать подарок, мы будем рады вкладу в наше свадебное путешествие." + +[[extra.details]] +icon = "📷" +title = "Фотографии" +description = "На свадьбе будет профессиональный фотограф. Мы поделимся фотографиями после мероприятия." + +[[extra.details]] +icon = "🚖" +title = "Трансфер" +description = "Мы организуем трансфер от станции метро до площадки и обратно. Подробности сообщим ближе к дате." + +# День 1 +[[extra.schedule]] +day = "19 августа" +label = "Встреча гостей" + +[[extra.schedule.items]] +time = "14:00" +title = "Заезд" +description = "Размещение гостей в отеле" + +[[extra.schedule.items]] +time = "18:00" +title = "Приветственный ужин" +description = "Знакомство и лёгкий фуршет на террасе" + +# День 2 +[[extra.schedule]] +day = "20 августа" +label = "День свадьбы" + +[[extra.schedule.items]] +time = "15:00" +title = "Церемония" +description = "Торжественная церемония бракосочетания в кругу самых близких" + +[[extra.schedule.items]] +time = "16:00" +title = "Фуршет" +description = "Лёгкие закуски и шампанское на открытой террасе" + +[[extra.schedule.items]] +time = "17:00" +title = "Банкет" +description = "Праздничный ужин, тосты и поздравления" + +[[extra.schedule.items]] +time = "19:00" +title = "Первый танец" +description = "Наш первый танец как мужа и жены" + +[[extra.schedule.items]] +time = "19:30" +title = "Вечерняя программа" +description = "Танцы, музыка и веселье до поздней ночи" + +# День 3 +[[extra.schedule]] +day = "21 августа" +label = "Прощальный бранч" + +[[extra.schedule.items]] +time = "11:00" +title = "Бранч" +description = "Совместный завтрак и тёплые воспоминания о вчерашнем дне" + +[[extra.schedule.items]] +time = "14:00" +title = "Отъезд" +description = "Прощание и отъезд гостей" diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..b8e6671 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,4 @@ ++++ +title = "Свадьба Анны и Михаила" +description = "Приглашаем вас на нашу свадьбу!" ++++ diff --git a/sass/style.scss b/sass/style.scss new file mode 100644 index 0000000..8b9ec42 --- /dev/null +++ b/sass/style.scss @@ -0,0 +1,765 @@ +// ========================== +// Variables (fallback defaults, overridden by config.toml via CSS custom properties) +// ========================== +$font-serif: 'Cormorant Garamond', 'Georgia', serif; +$font-sans: 'Montserrat', 'Helvetica Neue', sans-serif; +$max-width: 1100px; +$transition: 0.3s ease; + +:root { + --color-primary: #2b4c7e; + --color-primary-light: #7da0c4; + --color-accent: #7e8e6c; + --color-text: #2c3e50; + --color-text-light: #5a6d7e; + --color-bg: #f9fbfd; + --color-bg-alt: #eef3f7; + --color-white: #fffff8; + --color-border: #c9d6e0; + --hero-gradient: linear-gradient(135deg, #1a3a5c 0%, #2b4c7e 50%, #3a6b5a 100%); +} + +// ========================== +// Reset & Base +// ========================== +*, *::before, *::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; + font-size: 16px; +} + +body { + font-family: $font-sans; + font-weight: 300; + color: var(--color-text); + background: var(--color-bg); + line-height: 1.7; + -webkit-font-smoothing: antialiased; +} + +.container { + max-width: $max-width; + margin: 0 auto; + padding: 0 24px; +} + +img { + max-width: 100%; + height: auto; +} + +a { + color: var(--color-primary); + text-decoration: none; + transition: color $transition; + + &:hover { + color: var(--color-primary-light); + } +} + +// ========================== +// Navigation +// ========================== +.nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + padding: 20px 0; + transition: all $transition; + background: rgba(0, 0, 0, 0.4); + + &--scrolled { + background: rgba(255, 255, 255, 0.97); + padding: 12px 0; + box-shadow: 0 1px 20px rgba(0, 0, 0, 0.06); + } + + &__inner { + max-width: $max-width; + margin: 0 auto; + padding: 0 24px; + display: flex; + align-items: center; + justify-content: space-between; + } + + &__logo { + font-family: $font-serif; + font-size: 1.4rem; + font-weight: 600; + color: var(--color-white); + text-decoration: none; + + .nav--scrolled & { + color: var(--color-primary); + } + } + + &__toggle { + display: none; + flex-direction: column; + gap: 5px; + background: none; + border: none; + cursor: pointer; + padding: 4px; + + span { + display: block; + width: 24px; + height: 2px; + background: var(--color-white); + transition: all $transition; + + .nav--scrolled & { + background: var(--color-text); + } + } + } + + &__links { + display: flex; + list-style: none; + gap: 32px; + + a { + color: rgba(255, 255, 255, 0.9); + text-decoration: none; + font-size: 0.85rem; + font-weight: 400; + letter-spacing: 0.05em; + text-transform: uppercase; + transition: color $transition; + + &:hover { + color: var(--color-white); + } + + .nav--scrolled & { + color: var(--color-text-light); + + &:hover { + color: var(--color-primary); + } + } + } + } +} + +// ========================== +// Hero +// ========================== +.hero { + position: relative; + height: 100vh; + min-height: 600px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + background-size: cover; + background-position: center; + background-color: var(--color-primary); + color: var(--color-white); + + &__overlay { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.35); + } + + &__content { + position: relative; + z-index: 1; + padding: 40px 48px; + background: rgba(0, 0, 0, 0.45); + border-radius: 4px; + } + + &__invite { + font-family: $font-sans; + font-size: 0.9rem; + font-weight: 300; + letter-spacing: 0.3em; + text-transform: uppercase; + margin-bottom: 24px; + opacity: 0.9; + } + + &__names { + font-family: $font-serif; + font-size: clamp(2.5rem, 8vw, 5rem); + font-weight: 400; + line-height: 1.2; + margin-bottom: 16px; + + span { + font-style: italic; + display: inline-block; + margin: 0 12px; + opacity: 0.8; + } + } + + &__divider { + width: 60px; + height: 1px; + background: rgba(255, 255, 255, 0.5); + margin: 24px auto; + } + + &__date { + font-family: $font-serif; + font-size: 1.4rem; + font-weight: 400; + letter-spacing: 0.05em; + margin-bottom: 8px; + } + + &__venue { + font-size: 0.9rem; + font-weight: 300; + opacity: 0.85; + letter-spacing: 0.05em; + } + + &__scroll { + position: absolute; + bottom: 40px; + left: 50%; + transform: translateX(-50%); + z-index: 1; + + a { + color: rgba(255, 255, 255, 0.6); + font-size: 1.6rem; + animation: bounce 2s infinite; + display: block; + } + } +} + +@keyframes bounce { + 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } + 40% { transform: translateY(-10px); } + 60% { transform: translateY(-5px); } +} + +// ========================== +// Sections +// ========================== +.section { + padding: 100px 0; + + &--countdown { + background: var(--color-bg-alt); + } + + &--schedule { + background: var(--color-white); + } + + &--location { + background: var(--color-bg-alt); + } + + &--details { + background: var(--color-white); + } + + &--rsvp { + background: var(--color-bg-alt); + } + + &__title { + font-family: $font-serif; + font-size: clamp(2rem, 4vw, 2.8rem); + font-weight: 400; + text-align: center; + color: var(--color-text); + } + + &__divider { + width: 50px; + height: 1px; + background: var(--color-primary); + margin: 20px auto 50px; + } + + &__subtitle { + text-align: center; + color: var(--color-text-light); + font-size: 0.95rem; + margin-bottom: 40px; + } +} + +// ========================== +// About +// ========================== +.about { + display: grid; + grid-template-columns: 1fr 2fr 1fr; + gap: 50px; + align-items: center; + + &__photo { + text-align: center; + } + + &__photo-placeholder { + width: 100%; + aspect-ratio: 3 / 4; + background: var(--color-bg-alt); + border: 1px solid var(--color-border); + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-light); + font-size: 0.85rem; + margin-bottom: 16px; + } + + &__name { + font-family: $font-serif; + font-size: 1.3rem; + color: var(--color-primary); + } + + &__story { + text-align: center; + color: var(--color-text-light); + font-size: 1rem; + line-height: 1.9; + + p + p { + margin-top: 16px; + } + } +} + +// ========================== +// Countdown +// ========================== +.countdown { + display: flex; + justify-content: center; + gap: 40px; + flex-wrap: wrap; + + &__item { + text-align: center; + } + + &__number { + display: block; + font-family: $font-serif; + font-size: 3.5rem; + font-weight: 400; + color: var(--color-primary); + line-height: 1; + } + + &__label { + display: block; + font-size: 0.8rem; + color: var(--color-text-light); + text-transform: uppercase; + letter-spacing: 0.1em; + margin-top: 8px; + } +} + +// ========================== +// Schedule Days +// ========================== +.schedule-days { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 40px; + align-items: start; +} + +.schedule-day { + &__header { + text-align: center; + margin-bottom: 32px; + } + + &__date { + display: block; + font-family: $font-serif; + font-size: 1.5rem; + font-weight: 600; + color: var(--color-text); + margin-bottom: 4px; + } + + &__label { + display: block; + font-size: 0.9rem; + color: var(--color-text-light); + letter-spacing: 0.05em; + text-transform: uppercase; + } +} + +// ========================== +// Timeline +// ========================== +.timeline { + max-width: 650px; + margin: 0 auto; + position: relative; + + &::before { + content: ''; + position: absolute; + left: 80px; + top: 0; + bottom: 0; + width: 1px; + background: var(--color-border); + } + + &__item { + display: flex; + gap: 32px; + padding: 24px 0; + position: relative; + + &::after { + content: ''; + position: absolute; + left: 74px; + top: 32px; + width: 13px; + height: 13px; + border-radius: 50%; + border: 2px solid var(--color-accent); + background: var(--color-white); + } + } + + &__time { + flex-shrink: 0; + width: 55px; + font-family: $font-serif; + font-size: 1.2rem; + color: var(--color-primary); + text-align: right; + padding-top: 2px; + } + + &__content { + padding-left: 24px; + + h3 { + font-family: $font-serif; + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 4px; + } + + p { + color: var(--color-text-light); + font-size: 0.9rem; + } + } +} + +// ========================== +// Location +// ========================== +.location { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 60px; + align-items: center; + + &__info { + h3 { + font-family: $font-serif; + font-size: 1.8rem; + font-weight: 400; + margin-bottom: 8px; + } + } + + &__address { + color: var(--color-text-light); + margin-bottom: 24px; + } + + &__details { + margin-bottom: 30px; + + p { + padding: 6px 0; + font-size: 0.95rem; + + strong { + font-weight: 500; + } + } + } + + &__map-placeholder { + width: 100%; + aspect-ratio: 4 / 3; + background: var(--color-border); + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-light); + font-size: 0.9rem; + border-radius: 4px; + } +} + +// ========================== +// Details +// ========================== +.details { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 40px; + + &__card { + text-align: center; + padding: 40px 30px; + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: 4px; + } + + &__icon { + font-size: 2.5rem; + margin-bottom: 20px; + } + + h3 { + font-family: $font-serif; + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 12px; + } + + p { + color: var(--color-text-light); + font-size: 0.9rem; + line-height: 1.7; + } +} + +// ========================== +// RSVP Form +// ========================== +.rsvp-form { + max-width: 500px; + margin: 0 auto; + + &__group { + margin-bottom: 20px; + + &--radio { + display: flex; + gap: 24px; + justify-content: center; + } + } + + &__input { + width: 100%; + padding: 14px 18px; + font-family: $font-sans; + font-size: 0.9rem; + font-weight: 300; + color: var(--color-text); + background: var(--color-white); + border: 1px solid var(--color-border); + border-radius: 4px; + outline: none; + transition: border-color $transition; + appearance: none; + -webkit-appearance: none; + + &:focus { + border-color: var(--color-primary); + } + + &::placeholder { + color: #bbb; + } + } + + &__textarea { + min-height: 120px; + resize: vertical; + } + + &__radio { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + font-size: 0.9rem; + + input[type="radio"] { + accent-color: var(--color-primary); + } + } + + &__success { + text-align: center; + font-family: $font-serif; + font-size: 1.4rem; + color: var(--color-primary); + padding: 40px; + } +} + +// ========================== +// Buttons +// ========================== +.btn { + display: inline-block; + padding: 14px 40px; + font-family: $font-sans; + font-size: 0.85rem; + font-weight: 400; + letter-spacing: 0.1em; + text-transform: uppercase; + border: none; + border-radius: 4px; + cursor: pointer; + transition: all $transition; + text-decoration: none; + + &--primary { + display: block; + width: 100%; + background: var(--color-primary); + color: var(--color-white); + + &:hover { + background: var(--color-primary-light); + color: var(--color-white); + } + } + + &--outline { + border: 1px solid var(--color-primary); + color: var(--color-primary); + background: transparent; + + &:hover { + background: var(--color-primary); + color: var(--color-white); + } + } +} + +// ========================== +// Footer +// ========================== +.footer { + background: var(--color-text); + color: rgba(255, 255, 255, 0.7); + text-align: center; + padding: 50px 0; + + &__names { + font-family: $font-serif; + font-size: 1.6rem; + color: var(--color-white); + margin-bottom: 8px; + } + + &__date { + font-size: 0.85rem; + letter-spacing: 0.1em; + } +} + +// ========================== +// Responsive +// ========================== +@media (max-width: 768px) { + .nav { + &__toggle { + display: flex; + } + + &__links { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--color-white); + flex-direction: column; + padding: 20px 24px; + gap: 16px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); + + a { + color: var(--color-text) !important; + + &:hover { + color: var(--color-primary) !important; + } + } + } + + &--open .nav__links { + display: flex; + } + } + + .about { + grid-template-columns: 1fr; + gap: 30px; + + &__photo--groom { + order: -1; + } + } + + .countdown { + gap: 24px; + } + + .schedule-days { + grid-template-columns: 1fr; + gap: 48px; + } + + .timeline { + &::before { left: 60px; } + &__item::after { left: 54px; } + &__time { width: 40px; font-size: 1rem; } + } + + .location { + grid-template-columns: 1fr; + gap: 30px; + } + + .details { + grid-template-columns: 1fr; + gap: 20px; + } + + .section { + padding: 70px 0; + } +} diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/static/images/.gitkeep b/static/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/static/images/hero.jpg b/static/images/hero.jpg new file mode 100644 index 0000000..2eb264d Binary files /dev/null and b/static/images/hero.jpg differ diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..14a9ae8 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,77 @@ + + + + + + {% block title %}{{ config.title }}{% endblock %} + + + + + + {% if config.extra.colors %} + + {% endif %} + + + + + {% block content %}{% endblock %} + +
+
+ + +
+
+ + + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..1983ddf --- /dev/null +++ b/templates/index.html @@ -0,0 +1,181 @@ +{% extends "base.html" %} + +{% block content %} + +
+
+
+

{{ config.extra.hero_invite }}

+

{{ config.extra.bride }} & {{ config.extra.groom }}

+
+

{{ config.extra.wedding_date }}

+

{{ config.extra.venue_name }}

+
+
+ +
+
+ + +
+
+

{{ config.extra.about_title }}

+
+
+
+ {% if config.extra.bride_photo %} + {{ config.extra.bride }} + {% else %} +
Фото невесты
+ {% endif %} +

{{ config.extra.bride }}

+
+
+ {% for paragraph in config.extra.about_text %} +

{{ paragraph }}

+ {% endfor %} +
+
+ {% if config.extra.groom_photo %} + {{ config.extra.groom }} + {% else %} +
Фото жениха
+ {% endif %} +

{{ config.extra.groom }}

+
+
+
+
+ + +
+
+

До нашей свадьбы

+
+
+
+ 0 + дней +
+
+ 0 + часов +
+
+ 0 + минут +
+
+ 0 + секунд +
+
+
+
+ + +
+
+

Программа

+
+
+ {% for day in config.extra.schedule %} +
+
+ {{ day.day }} + {{ day.label }} +
+
+ {% for item in day.items %} +
+
{{ item.time }}
+
+

{{ item.title }}

+

{{ item.description }}

+
+
+ {% endfor %} +
+
+ {% endfor %} +
+
+
+ + +
+
+

Место проведения

+
+
+
+

{{ config.extra.venue_name }}

+

{{ config.extra.venue_address }}

+
+

Дата: {{ config.extra.wedding_date }}

+

Начало: {{ config.extra.wedding_time }}

+

Дресс-код: {{ config.extra.dress_code }}

+
+ Открыть карту +
+
+ {% if config.extra.venue_map_embed %} + + {% else %} +
+

Карта

+
+ {% endif %} +
+
+
+
+ + +
+
+

Важные детали

+
+
+ {% for card in config.extra.details %} +
+
{{ card.icon | safe }}
+

{{ card.title }}

+

{{ card.description }}

+
+ {% endfor %} +
+
+
+ + +
+
+

{{ config.extra.rsvp_title }}

+
+

Пожалуйста, сообщите о вашем присутствии до {{ config.extra.rsvp_deadline }}

+
+ {{ config.extra.rsvp_button }} +
+
+
+ + +{% endblock %}