diff --git a/.github/workflows/test.yml-template b/.github/workflows/test.yml-template new file mode 100644 index 000000000..8b5743ecb --- /dev/null +++ b/.github/workflows/test.yml-template @@ -0,0 +1,29 @@ +name: Test + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test + - name: Upload HTML report(backstop data) + if: ${{ always() }} + uses: actions/upload-artifact@v2 + with: + name: report + path: backstop_data diff --git a/README.md b/README.md index 5f9e97051..9bf5c42f1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Museum landing page -Implement landing page according to [Figma design](https://www.figma.com/file/cRBCqE06cDrY3s4jX7h3iY/%D0%9D%D0%90%D0%9C%D0%A3-(Edit)?node-id=0%3A1) - Use BEM and SCSS + +Implement landing page according to [Figma design]() - Use BEM and SCSS Check font styles. Use [IBM Plex Sans](https://fonts.google.com/specimen/IBM+Plex+Sans?query=ibm), [Montserrat](https://fonts.google.com/specimen/Montserrat?query=mon) @@ -20,22 +21,22 @@ Check font styles. Use [IBM Plex Sans](https://fonts.google.com/specimen/IBM+Ple 1. Don’t forget to add a title for the whole web page (it could be the name of your landing) 2. A landing page is implemented strictly according to the design in Figma -4. Links in the header and footer menus should lead to the corresponding blocks of the landing page -5. The speed of animations is the same throughout the landing page (for example, increasing when hovering or moving blocks when scrolling) -6. Placeholders in the forms suggest what to enter, and if there is a validation of the form, then it is clear in what format to enter the phone number -7. Make sure everything looks neat on mobile and without horizontal scrolling -8. Add favicon -9. Add a smooth scroll for the whole page -10. When you try to send the form there is no 405 error and the form is automatically cleared after submit and is scrolled to the top of the page or the page is reloaded -11. The form shouldn’t submit empty -12. The buttons "exhibitions" and "tickets" should lead to the block with current events -13. The button "about us" should lead to the email digest -14. Facebook and Instagram icons in the footer should be clickable and open the museum's social networks in a new tab -15. Pictures in the gallery and exhibitions sections should increase on hover -16. OPTIONAL: After everything is done, you can add a slider for viewing pictures in the gallery (for mobile version) - +3. Links in the header and footer menus should lead to the corresponding blocks of the landing page +4. The speed of animations is the same throughout the landing page (for example, increasing when hovering or moving blocks when scrolling) +5. Placeholders in the forms suggest what to enter, and if there is a validation of the form, then it is clear in what format to enter the phone number +6. Make sure everything looks neat on mobile and without horizontal scrolling +7. Add favicon +8. Add a smooth scroll for the whole page +9. When you try to send the form there is no 405 error and the form is automatically cleared after submit and is scrolled to the top of the page or the page is reloaded +10. The form shouldn’t submit empty +11. The buttons "exhibitions" and "tickets" should lead to the block with current events +12. The button "about us" should lead to the email digest +13. Facebook and Instagram icons in the footer should be clickable and open the museum's social networks in a new tab +14. Pictures in the gallery and exhibitions sections should increase on hover +15. OPTIONAL: After everything is done, you can add a slider for viewing pictures in the gallery (for mobile version) ## Github flow + 1. **Fork** the repo. 2. **Clone** the forked one. (The project link should have your name but not `mate-academy`) 3. Run `npm install` (or just `npm i`). @@ -49,7 +50,7 @@ Check font styles. Use [IBM Plex Sans](https://fonts.google.com/specimen/IBM+Ple 11. `git push origin develop` - to send you code for PR. 12. Create a Pull Request (PR) from your branch `develop` to branch `master` of original repo. 13. Replace `` with your Github username in the - [DEMO LINK](https://.github.io/Museum/). + [DEMO LINK](https://AngelaStrechen.github.io/Museum/). 14. Copy `DEMO LINK` to the PR description. > To update you PR repeat steps 7-11. diff --git a/index.html b/index.html index d339e6856..cd41b4154 100644 --- a/index.html +++ b/index.html @@ -6,14 +6,525 @@ name="viewport" content="width=device-width, initial-scale=1.0" /> - Title + Museum + + + + + - -

Hello Mate Academy

- + +
+
+
+
+

ХУДОЖНІЙ МУЗЕЙ

+
+
+
+ події +
+ + +
+
+
+ +
+
+ + +
+
+ + +
+ +
+

ГРАФІК РОБОТИ

+ +
+
+
+ 12:00 – 20:00 + середа +
+
+ 12:00 – 20:00 + четвер +
+
+ 12:00 – 20:00 + п'ятниця +
+
+ 11:00 – 19:00 + субота +
+
+ 11:00 – 19:00 + неділя +
+
+ +
+

+ Вихідні: + понеділок, вівторок +

+

+ Каса припиняє роботу за 1 годину до закриття музею +

+
+
+
+
+
+
+ + +
+
+ +
+ +
+ +
+

Актуальні події

+ +
+
+ +
+ Йду і повертаюсь + +
+
+ Виставка + 10.10.2024 +
+ +
+

Йду і повертаюсь

+ +
+ +

+ Національний Художній Музей України презентує унікальну + частину колекції Градобанку - українське мистецтво другої + половини 1980-1995 років. +

+
+
+ + +
+ І спогади, і мрії + +
+
+ Виставка + 15.10.2024 +
+ +
+

+ І спогади, і мрії +

+ +
+ +

+ Національний художній музей України до 100 річчя від дня + народження видатної української художниці Тетяни Яблонської + відкриває ретроспективну ювілейну виставку «І спогади і мрії». +

+
+
+
+ +
+
+ Лекція +
+
+
+ Лекція + 20.10.2024 +
+ +

+ Від класицизму до романтизму +

+
+
+
+
+ + + + + + +
+ + + + + + diff --git a/package-lock.json b/package-lock.json index fbfeb1428..3bf64afbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@mate-academy/eslint-config": "latest", "@mate-academy/jest-mochawesome-reporter": "^1.0.0", "@mate-academy/linthtml-config": "latest", - "@mate-academy/scripts": "^2.1.1", + "@mate-academy/scripts": "^2.1.3", "@mate-academy/stylelint-config": "latest", "cypress": "^13.13.0", "eslint": "^8.57.0", @@ -1875,10 +1875,11 @@ "dev": true }, "node_modules/@mate-academy/scripts": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-2.1.2.tgz", - "integrity": "sha512-gUXFdqqOfYzF9R3RSx2pCa5GLdOkxB9bFbF+dpUpzucdgGAANqOGdqpmNnMj+e3xA9YHraUWq3xo9cwe5vD9pQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-2.1.3.tgz", + "integrity": "sha512-a07wHTj/1QUK2Aac5zHad+sGw4rIvcNl5lJmJpAD7OxeSbnCdyI6RXUHwXhjF5MaVo9YHrJ0xVahyERS2IIyBQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", diff --git a/package.json b/package.json index ab5f87b9b..d2c5a8560 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@mate-academy/eslint-config": "latest", "@mate-academy/jest-mochawesome-reporter": "^1.0.0", "@mate-academy/linthtml-config": "latest", - "@mate-academy/scripts": "^2.1.1", + "@mate-academy/scripts": "^2.1.3", "@mate-academy/stylelint-config": "latest", "cypress": "^13.13.0", "eslint": "^8.57.0", diff --git a/src/images/events/event-1.jpg b/src/images/events/event-1.jpg new file mode 100644 index 000000000..c4d8784d0 Binary files /dev/null and b/src/images/events/event-1.jpg differ diff --git a/src/images/events/event-2.jpg b/src/images/events/event-2.jpg new file mode 100644 index 000000000..228e46ef9 Binary files /dev/null and b/src/images/events/event-2.jpg differ diff --git a/src/images/events/event-3.jpg b/src/images/events/event-3.jpg new file mode 100644 index 000000000..092dc6887 Binary files /dev/null and b/src/images/events/event-3.jpg differ diff --git a/src/images/gallery/gallery-1.jpg b/src/images/gallery/gallery-1.jpg new file mode 100644 index 000000000..e8ca8ec95 Binary files /dev/null and b/src/images/gallery/gallery-1.jpg differ diff --git a/src/images/gallery/gallery-2.jpg b/src/images/gallery/gallery-2.jpg new file mode 100644 index 000000000..a2544222b Binary files /dev/null and b/src/images/gallery/gallery-2.jpg differ diff --git a/src/images/gallery/gallery-3.jpg b/src/images/gallery/gallery-3.jpg new file mode 100644 index 000000000..1090b8957 Binary files /dev/null and b/src/images/gallery/gallery-3.jpg differ diff --git a/src/images/gallery/gallery-4.jpg b/src/images/gallery/gallery-4.jpg new file mode 100644 index 000000000..39c6518f2 Binary files /dev/null and b/src/images/gallery/gallery-4.jpg differ diff --git a/src/images/gallery/gallery-5.jpg b/src/images/gallery/gallery-5.jpg new file mode 100644 index 000000000..cbc1c4388 Binary files /dev/null and b/src/images/gallery/gallery-5.jpg differ diff --git a/src/images/header_bg.jpg b/src/images/header_bg.jpg new file mode 100644 index 000000000..f14f2bc1f Binary files /dev/null and b/src/images/header_bg.jpg differ diff --git a/src/images/icons/favicon.png b/src/images/icons/favicon.png new file mode 100644 index 000000000..4b67cacc3 Binary files /dev/null and b/src/images/icons/favicon.png differ diff --git a/src/images/subscription_bg.jpg b/src/images/subscription_bg.jpg new file mode 100644 index 000000000..e3148c1e1 Binary files /dev/null and b/src/images/subscription_bg.jpg differ diff --git a/src/scripts/dots.js b/src/scripts/dots.js new file mode 100644 index 000000000..5d7313082 --- /dev/null +++ b/src/scripts/dots.js @@ -0,0 +1,61 @@ +'use strict'; + +const track = document.getElementById('galleryTrack'); +const dotsContainer = document.getElementById('galleryDots'); +const slides = document.querySelectorAll('.gallery__image'); + +let currentIndex = 0; + +// створення dots +slides.forEach((_, i) => { + const dot = document.createElement('div'); + dot.classList.add('dot'); + + if (i === 0) dot.classList.add('active'); + + dot.addEventListener('click', () => { + scrollToSlide(i); + }); + + dotsContainer.appendChild(dot); +}); + +const dots = document.querySelectorAll('.dot'); + +// scroll to slide +function scrollToSlide(index) { + const slide = slides[index]; + + track.scrollTo({ + left: slide.offsetLeft, + behavior: 'smooth', + }); + + setActiveDot(index); +} + +// active dot +function setActiveDot(index) { + dots.forEach((dot, i) => { + dot.classList.toggle('active', i === index); + }); + + currentIndex = index; +} + +// sync scroll → dots +track.addEventListener('scroll', () => { + let closestIndex = 0; + let minDistance = Infinity; + + slides.forEach((slide, i) => { + const distance = Math.abs(track.scrollLeft - slide.offsetLeft); + + if (distance < minDistance) { + minDistance = distance; + closestIndex = i; + } + }); + + setActiveDot(closestIndex); +}); diff --git a/src/scripts/main.js b/src/scripts/main.js index ad9a93a7c..c67ba5465 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -1 +1,109 @@ 'use strict'; + +const burger = document.querySelector('.burger'); +const menu = document.querySelector('.header-menu'); +const logo = document.querySelector('.header__logo'); +const topBar = document.querySelector('.header__top-bar'); +const menuLinks = document.querySelectorAll('.menu__link'); +const overlay = document.querySelector('.overlay'); + +// ====================== INIT ===================== +if (burger && menu && logo && topBar) { + burger.addEventListener('click', toggleMenu); +} + +// ====================== MENU ====================== +function toggleMenu() { + burger.classList.toggle('burger--active'); + menu.classList.toggle('header-menu--open'); + logo.classList.toggle('is-hidden'); + + document.body.classList.toggle('no-scroll'); + overlay.classList.toggle('is-active'); + + if (!menu.classList.contains('header-menu--open') && window.scrollY > 0) { + topBar.classList.add('is-hidden'); + } else { + topBar.classList.remove('is-hidden'); + } +} + +// ====================== OVERLAY CLOSE ====================== + +if (overlay) { + overlay.addEventListener('click', closeMenu); +} + +function closeMenu() { + burger.classList.remove('burger--active'); + menu.classList.remove('header-menu--open'); + logo.classList.remove('is-hidden'); + + document.body.classList.remove('no-scroll'); + overlay.classList.remove('is-active'); + + if (window.scrollY > 0) { + topBar.classList.add('is-hidden'); + } +} + +// ====================== SMOOTH SCROLL ====================== + +function smoothScroll(target, duration = 1800) { + const targetPosition = + target.getBoundingClientRect().top + window.pageYOffset; + + const startPosition = window.pageYOffset; + const distance = targetPosition - startPosition; + + let startTime = null; + + function animation(currentTime) { + if (startTime === null) { + startTime = currentTime; + } + + const timeElapsed = currentTime - startTime; + const progress = Math.min(timeElapsed / duration, 1); + + window.scrollTo(0, startPosition + distance * easeInOut(progress)); + + if (timeElapsed < duration) { + // eslint-disable-next-line no-undef + requestAnimationFrame(animation); + } + } + + function easeInOut(t) { + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; + } + + // eslint-disable-next-line no-undef + requestAnimationFrame(animation); +} + +// ====================== MENU LINKS ====================== + +menuLinks.forEach((link) => { + link.addEventListener('click', (event) => { + event.preventDefault(); + + const target = document.querySelector(link.getAttribute('href')); + + if (target) { + smoothScroll(target, 1800); + } + + closeMenu(); + }); +}); + +// ====================== HEADER SCROLL ====================== + +window.addEventListener('scroll', () => { + if (window.scrollY > 0 && !menu.classList.contains('header-menu--open')) { + topBar.classList.add('is-hidden'); + } else { + topBar.classList.remove('is-hidden'); + } +}); diff --git a/src/scripts/scroll.js b/src/scripts/scroll.js new file mode 100644 index 000000000..c8f44c18e --- /dev/null +++ b/src/scripts/scroll.js @@ -0,0 +1,10 @@ +'use strict'; + +const scrollTopButton = document.querySelector('.footer__scroll-top'); + +scrollTopButton.addEventListener('click', () => { + window.scrollTo({ + top: 0, + behavior: 'smooth', + }); +}); diff --git a/src/styles/_typography.scss b/src/styles/_typography.scss deleted file mode 100644 index 1837eb46e..000000000 --- a/src/styles/_typography.scss +++ /dev/null @@ -1,3 +0,0 @@ -h1 { - @extend %h1; -} diff --git a/src/styles/_utils.scss b/src/styles/_utils.scss deleted file mode 100644 index 3280c3fe1..000000000 --- a/src/styles/_utils.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'utils/vars'; -@import 'utils/mixins'; -@import 'utils/extends'; diff --git a/src/styles/_fonts.scss b/src/styles/base/_fonts.scss similarity index 70% rename from src/styles/_fonts.scss rename to src/styles/base/_fonts.scss index 45cdd5400..9ae7a8b54 100644 --- a/src/styles/_fonts.scss +++ b/src/styles/base/_fonts.scss @@ -1,6 +1,6 @@ @font-face { - font-family: Roboto, Arial, Helvetica, sans-serif; - src: url('../fonts/Roboto-Regular-webfont.woff') format('woff'); + font-family: Roboto, sans-serif; font-weight: normal; font-style: normal; + src: url('../fonts/Roboto-Regular-webfont.woff') format('woff'); } diff --git a/src/styles/base/_reset.scss b/src/styles/base/_reset.scss new file mode 100644 index 000000000..b0824ddd0 --- /dev/null +++ b/src/styles/base/_reset.scss @@ -0,0 +1,59 @@ +/* 1. Box sizing */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* 2. Скидання відступів */ +* { + margin: 0; + padding: 0; +} + +/* 3. Базові налаштування html/body */ +html, +body { + height: 100%; +} + +body { + -webkit-font-smoothing: antialiased; +} + +/* 5. Форми */ +input, +button, +textarea, +select { + font: inherit; +} + +/* 6. Кнопки */ +button { + cursor: pointer; + border: none; + background: none; +} + +/* 7. Списки */ +ul, +ol { + list-style: none; +} + +/* 8. Посилання */ +a { + color: inherit; + text-decoration: none; +} + +/* 9. Заголовки */ +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: inherit; +} diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss new file mode 100644 index 000000000..14aa841ee --- /dev/null +++ b/src/styles/base/_typography.scss @@ -0,0 +1,8 @@ +body { + font-family: 'IBM Plex Sans', sans-serif; + background: $c-gray; +} + +h1 { + @extend %h1; +} diff --git a/src/styles/components/_burger.scss b/src/styles/components/_burger.scss new file mode 100644 index 000000000..3808d41ee --- /dev/null +++ b/src/styles/components/_burger.scss @@ -0,0 +1,42 @@ +.burger { + cursor: pointer; + + z-index: 1001; + + display: flex; + flex-direction: column; + justify-content: space-between; + + width: 18px; + height: 12px; + border: none; + + background: none; + + &__line { + transform-origin: center; + + width: 100%; + height: 2px; + + background: $c-text-dark; + + transition: $effectDuration; + } + + &--active { + .burger__line { + background: $white; + } + .burger__line:nth-child(1) { + transform: translateY(5px) rotate(45deg); + } + .burger__line:nth-child(2) { + opacity: 0; + } + + .burger__line:nth-child(3) { + transform: translateY(-5px) rotate(-45deg); + } + } +} diff --git a/src/styles/components/_button.scss b/src/styles/components/_button.scss new file mode 100644 index 000000000..773b75780 --- /dev/null +++ b/src/styles/components/_button.scss @@ -0,0 +1,46 @@ +.button { + position: relative; + + align-items: center; + + padding: 18px 134px 18px 84px; + border: 1px solid $color-deep-green; + + font-weight: 500; + color: #0f0e08; + + background: transparent; + + transition: opacity 0.2s ease; + &::before { + content: '›'; + + position: absolute; + top: 0; + right: 0; + + display: flex; + align-items: center; + justify-content: center; + + width: 50px; + height: 100%; + + font-size: 24px; + color: #d7dde3; + + background: $color-deep-green; + + @include on-tablet { + width: 70px; + } + } + + &:hover { + opacity: 0.85; + } + + @include on-tablet { + padding: 27px 138px 27px 68px; + } +} diff --git a/src/styles/components/_container.scss b/src/styles/components/_container.scss new file mode 100644 index 000000000..55769c8ba --- /dev/null +++ b/src/styles/components/_container.scss @@ -0,0 +1,19 @@ +.container { + min-width: 280px; + margin: 0 auto; + padding-inline: 20px; + + @include on-tablet { + padding-inline: 34px; + } + + @include on-desktop { + padding-inline: 55px; + } +} + +.is-hidden { + pointer-events: none; + visibility: hidden; + opacity: 0; +} diff --git a/src/styles/components/_event-card.scss b/src/styles/components/_event-card.scss new file mode 100644 index 000000000..ccb34ec41 --- /dev/null +++ b/src/styles/components/_event-card.scss @@ -0,0 +1,185 @@ +.event-card { + display: flex; + flex-direction: column; + + @include on-desktop { + flex-direction: row; + gap: 30px; + align-items: center; + justify-content: space-between; + } + + &--lecture { + @include on-desktop { + display: flex; + flex-direction: column; + gap: 0; + align-items: stretch; + } + } + + &__image { + width: 100%; + height: auto; + margin-bottom: 20px; + + @include on-tablet { + margin-bottom: 40px; + } + + @include on-desktop { + margin-bottom: 104px; + } + + &--3 { + @include on-tablet { + width: 100%; + height: auto; + margin-bottom: -30px; + } + } + } + + &__type { + margin-right: 25px; + font-weight: 500; + color: #687480; + text-transform: uppercase; + + @include on-tablet { + font-size: 14px; + } + + &--exhibition { + margin-right: 0; + margin-bottom: 0; + + font-weight: 500; + color: #687480; + text-transform: uppercase; + } + } + + &__marker { + flex-shrink: 0; + width: 10px; + height: 10px; + background-color: #f14a27; + + @include on-tablet { + width: 15px; + height: 15px; + } + } + + &__date { + font-size: 16px; + font-weight: 700; + line-height: 150%; + color: $color-deep-green; + + @include on-tablet { + font-size: 14px; + } + } + + &__heading { + display: flex; + align-items: center; + margin-bottom: 10px; + + @include on-tablet { + margin-bottom: 15px; + } + } + + &__title { + margin-right: 10px; + margin-bottom: 10px; + + font-family: Montserrat, sans-serif; + font-size: 28px; + line-height: 100%; + + @include on-tablet { + font-size: 36px; + } + + &--center { + margin-right: 0; + margin-bottom: 0; + text-align: center; + } + } + + &__text { + max-width: 460px; + margin-bottom: 60px; + + font-family: 'IBM Plex Sans', sans-serif; + font-size: 16px; + font-weight: 300; + line-height: 150%; + color: #4e4e4e; + } + + &__overlay { + box-sizing: border-box; + width: 280px; + height: 140px; + margin: 0 auto; + margin-top: -70px; + margin-bottom: 100px; + padding: 20px 1px; + border-bottom: 2px solid rgba($color-deep-green, 0.25); + + background-color: $white; + box-shadow: 0 2px 4px rgba($color-deep-green, 0.1); + + @include on-tablet { + width: 570px; + height: 204px; + margin-bottom: 200px; + padding: 45px 0 50px; + } + } + + &__content { + display: flex; + flex-direction: column; + } + + &__meta { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 15px; + + @include on-tablet { + margin-bottom: 32px; + text-align: left; + } + + &-text { + display: flex; + align-items: center; + justify-content: space-between; + + margin-right: 21px; + margin-bottom: 20px; + margin-left: 21px; + + text-align: center; + + @include on-tablet { + margin-right: 100px; + margin-bottom: 23px; + margin-left: 100px; + } + } + } +} + +.events { + margin: 0 auto; +} diff --git a/src/styles/components/_footer.scss b/src/styles/components/_footer.scss new file mode 100644 index 000000000..1bd108724 --- /dev/null +++ b/src/styles/components/_footer.scss @@ -0,0 +1,273 @@ +.footer { + padding: 50px 20px; + + @include on-tablet { + @include page-grid; + + padding: 51px 34px 36px 36px; + } + + @include on-desktop { + padding: 48px 55px 35px; + } + + &__brand { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 40px; + + @include on-tablet { + grid-column: 1/2; + flex-direction: column; + gap: 62px; + justify-content: start; + + margin-bottom: 0; + } + + @include on-desktop { + grid-column: 1/2; + } + } + + &__logo { + font-size: 36px; + color: $c-text-dark; + } + + &__social { + display: flex; + gap: 20px; + + @include on-tablet { + gap: 32px; + } + + @include on-desktop { + gap: 22px; + } + + &-link { + display: flex; + align-items: center; + justify-content: center; + + width: 40px; + height: 40px; + + background: transparent; + + transition: + transform 0.25s ease, + filter 0.25s ease; + + &:hover { + transform: translateY(-2px); + filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25)); + } + + &--facebook { + width: 11px; + height: 24px; + } + + &--instagram { + width: 24px; + height: 24px; + } + } + } + + &__info { + display: flex; + flex-direction: row; + gap: 18px; + justify-content: start; + + @include on-tablet { + grid-column: 2/6; + } + + @include on-desktop { + grid-column: 3/7; + gap: 31px; + } + } + + &__schedule-title, + &__contacts-title { + margin-bottom: 20px; + font-size: 14px; + line-height: 141%; + color: $c-text-dark; + } + + &__schedule { + width: 50%; + + &-table { + margin-bottom: 30px; + } + + &-time { + font-size: 14px; + font-weight: 600; + line-height: 150%; + color: $c-text-dark; + } + + &-info { + width: 100%; + + @include on-tablet { + margin-bottom: 64px; + } + + @include on-desktop { + margin-bottom: 87px; + } + } + + &-day, + &-text { + font-size: 14px; + line-height: 150%; + color: $c-text-dark; + } + + &-label { + @include on-desktop { + font-weight: 600; + } + } + } + + &__contacts { + width: 50%; + + &-info { + font-style: normal; + } + + &-text, + &-link { + font-size: 14px; + line-height: 150%; + color: $c-text-dark; + } + } + + &__scroll-top { + display: block; + + width: 50px; + height: 50px; + margin-bottom: 20px; + margin-left: auto; + border: none; + + background: transparent; + + transition: transform 0.3s ease; + + @include on-tablet { + display: none; + } + + &:hover { + transform: translateY(-4px); + } + } + + &__divider { + width: 100%; + height: 1px; + margin-bottom: 40px; + background: #1a5a4c80; + + @include on-tablet { + grid-column: 2/6; + margin-bottom: 64px; + } + + @include on-desktop { + grid-column: 7/8; + + width: 1px; + height: auto; + margin: 0 auto; + margin-bottom: 98px; + } + } + + &__nav-wrapper { + @include on-tablet { + grid-column: 2/6; + } + + @include on-desktop { + grid-column: 8/11; + } + } + + &__nav { + display: flex; + flex-direction: column; + margin-bottom: 80px; + } + + &__list { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + + font-size: 14px; + font-weight: 400; + line-height: 100%; + color: $c-text-dark; + + @include on-desktop { + column-gap: 32px; + } + } + + &__item { + text-align: left; + } + + &__link { + position: relative; + color: $c-text-dark; + transition: text-shadow 0.2s ease; + + &:hover { + text-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + } + } + + &__bottom { + display: flex; + grid-column: 1/-1; + flex-direction: column; + gap: 10px; + align-items: center; + + @include on-tablet { + flex-direction: row; + justify-content: space-between; + } + } + + &__copyright, + &__designer { + font-size: 12px; + font-weight: 300; + line-height: 141%; + color: $c-text-dark; + } +} + +.footer__social .icon { + fill: #000; +} diff --git a/src/styles/components/_gallery.scss b/src/styles/components/_gallery.scss new file mode 100644 index 000000000..e99bfa1a6 --- /dev/null +++ b/src/styles/components/_gallery.scss @@ -0,0 +1,122 @@ +.gallery { + margin-bottom: 80px; + + @include on-desktop { + margin-bottom: 100px; + } + + &__slider { + position: relative; + overflow: hidden; + padding: 0 15px; + + @include on-tablet { + padding: 0 20px; + } + + @include on-desktop { + padding: 0 55px; + } + } + + &__track { + scroll-behavior: smooth; + scrollbar-width: none; + scroll-snap-type: x mandatory; + + overflow-x: auto; + display: flex; + gap: 20px; + + padding-bottom: 60px; + + transition: transform 0.5s ease; + + &::-webkit-scrollbar { + display: none; + } + + @include on-desktop { + @include page-grid; + + overflow: visible; + gap: 30px; + } + } + + &__image { + scroll-snap-align: start; + + flex: 0 0 auto; + + width: 280px; + min-height: 410px; + + object-fit: cover; + + @include on-tablet { + width: 340px; + min-height: 543px; + } + + @include on-desktop { + width: 100%; + min-height: 600px; + object-fit: cover; + } + + &:nth-child(1) { + @include on-desktop { + grid-column: 1 /5; + } + } + + &:nth-child(2) { + @include on-desktop { + grid-column: 5 /-1; + } + } + + &:nth-child(3) { + @include on-desktop { + grid-column: 1/9; + min-height: 360px; + } + } + + &:nth-child(4) { + @include on-desktop { + grid-column: 9 /-1; + min-height: 360px; + } + } + } + + &__dots { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + + display: flex; + gap: 12px; + + @include on-desktop { + display: none; + } + + .dot { + cursor: pointer; + + width: 9px; + height: 9px; + border-radius: 50%; + + background: $gray; + + &.active { + background: $color-deep-green; + } + } + } +} diff --git a/src/styles/components/_header-menu.scss b/src/styles/components/_header-menu.scss new file mode 100644 index 000000000..d3a2a8250 --- /dev/null +++ b/src/styles/components/_header-menu.scss @@ -0,0 +1,121 @@ +.header-menu { + pointer-events: none; + + position: fixed; + z-index: 900; + top: 0; + left: 0; + transform: translateY(-10px); + + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + width: 100%; + + opacity: 0; + background: $color-deep-green; + + transition: $effectDuration; + + @include on-tablet { + align-items: stretch; + justify-content: flex-start; + max-height: 225px; + } + + &__content { + display: flex; + flex-direction: column; + + @include on-tablet { + flex-direction: row; + align-items: stretch; + } + } + + &--open { + pointer-events: auto; + + transform: translateY(0); + + max-height: max-content; + padding: 27px 20px; + + color: #fff; + + opacity: 1; + + transition: $effectDuration; + + @include on-tablet { + padding: 40px 35px; + } + + @include on-desktop { + padding: 40px 155px 48px 56px; + } + } + + &__link { + position: relative; + color: $white; + text-decoration: none; + + &::after { + content: ''; + + position: absolute; + bottom: -3px; + left: 0; + + width: 0; + height: 1px; + + background: $white; + + transition: width 0.25s ease; + } + + &:hover::after { + width: 100%; + } + } + + &__nav { + display: flex; + flex-direction: column; + padding-left: 46px; + + @include on-tablet { + padding-left: 56px; + } + + @include on-desktop { + padding-left: 108px; + } + } + + &__list { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + + font-size: 16px; + font-weight: 500; + line-height: 100%; + + @include on-tablet { + row-gap: 24px; + } + + @include on-desktop { + gap: 30px 31px; + } + } + + &__item { + text-align: left; + } +} diff --git a/src/styles/components/_header.scss b/src/styles/components/_header.scss new file mode 100644 index 000000000..273077d21 --- /dev/null +++ b/src/styles/components/_header.scss @@ -0,0 +1,225 @@ +.header { + position: relative; + background-color: $white; + + @include on-tablet { + min-height: 384px; + } + + @include on-desktop { + min-height: 655px; + margin-bottom: 200px; + } + + &__hero { + position: relative; + z-index: 1; + + display: flex; + flex-direction: column; + + width: 100%; + height: max-content; + margin-bottom: 0; + + @include on-tablet { + flex-direction: row-reverse; + margin-bottom: 88px; + } + } + &__bg { + width: 100%; + min-height: 300px; + + background-image: url('../images/header_bg.jpg'); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + + @include on-tablet { + right: 0; + left: auto; + width: 50%; + } + + @include on-desktop { + width: 57%; + height: auto; + } + } + + &__content { + position: absolute; + z-index: 10; + top: 0; + right: 0; + left: 0; + + display: flex; + flex-direction: row; + align-items: center; + + padding: 27px 20px; + + @include on-tablet { + padding: 39px 34px; + } + + @include on-desktop { + padding-left: 55px; + } + } + + &__top-bar { + position: fixed; + z-index: 1000; + top: 0; + right: 0; + left: 0; + + display: flex; + align-items: center; + + padding: 27px 20px; + } + + &__burger { + margin-right: auto; + } + + &__logo { + position: absolute; + left: 50%; + transform: translateX(-50%); + margin-top: 9px; + + @include on-tablet { + left: 64%; + } + + @include on-desktop { + left: 50%; + } + + @include on-large-desktop { + font-size: 36px; + font-weight: 800; + } + } + + &__info { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px 20px 70px; + + @include on-tablet { + display: block; + + width: 50%; + height: 100%; + margin: 0 auto; + padding: 127px 0 19px 34px; + } + + @include on-laptop { + padding-left: 55px; + } + + @include on-small-desktop { + padding-bottom: 70px; + } + + @include on-desktop { + padding-bottom: 206px; + } + + @include on-large-desktop { + padding-top: 166px; + } + } + + &__title { + margin-bottom: 20px; + + font-size: 45px; + font-weight: 700; + line-height: 100%; + text-align: center; + + @include on-tablet { + margin-bottom: 38px; + font-size: 54px; + line-height: 120%; + text-align: left; + } + + @include on-laptop { + font-size: 60px; + } + + @include on-desktop { + margin-bottom: 89px; + } + + @include on-large-desktop { + margin-bottom: 137px; + } + } + + &__actions { + display: flex; + justify-content: space-between; + + @include on-desktop { + justify-content: flex-end; + } + } + + // ===== EVENTS (за замовчуванням сховано) ===== + &__events { + display: none; + align-items: center; + + @include on-desktop { + position: absolute; + bottom: 0; + left: 55px; + transform-origin: left bottom; + transform: rotate(-90deg); + + display: flex; + gap: 20px; + justify-content: space-between; + } + } + + // лінія + &__events-line { + width: 70px; + height: 1px; + background-color: $color-deep-green; + } + + // текст + &__events-text { + font-size: 14px; + font-weight: 700; + line-height: 100%; + color: #687480; + white-space: nowrap; + } + + // кнопка завжди справа від events + @include on-desktop { + &__button { + margin-right: 30px; + } + } + + @include on-large-desktop { + &__button { + margin-right: 50px; + } + } +} diff --git a/src/styles/components/_logo.scss b/src/styles/components/_logo.scss new file mode 100644 index 000000000..85e9aee18 --- /dev/null +++ b/src/styles/components/_logo.scss @@ -0,0 +1,22 @@ +.logo { + z-index: 2; + + display: flex; + flex-direction: column; + + font-family: Montserrat, sans-serif; + font-size: 30px; + font-weight: 700; + line-height: 100%; + color: $c-text-dark; + text-align: center; + text-decoration: none; + + @include on-small-tablet { + color: #fff; + } + + @include on-tablet { + color: $c-text-dark; + } +} diff --git a/src/styles/components/_overlay.scss b/src/styles/components/_overlay.scss new file mode 100644 index 000000000..815dfa679 --- /dev/null +++ b/src/styles/components/_overlay.scss @@ -0,0 +1,15 @@ +.overlay { + position: fixed; + z-index: 5; + inset: 0; + + visibility: hidden; + opacity: 0; + background: rgba(0, 0, 0, 0.5); + + transition: 0.3s; + &.is-active { + visibility: visible; + opacity: 1; + } +} diff --git a/src/styles/components/_page.scss b/src/styles/components/_page.scss new file mode 100644 index 000000000..8d698ec1f --- /dev/null +++ b/src/styles/components/_page.scss @@ -0,0 +1,11 @@ +.page { + scroll-behavior: smooth; + + &__body { + background: $white; + + &.no-scroll { + overflow: hidden; + } + } +} diff --git a/src/styles/components/_schedule.scss b/src/styles/components/_schedule.scss new file mode 100644 index 000000000..f4c8ab0c0 --- /dev/null +++ b/src/styles/components/_schedule.scss @@ -0,0 +1,82 @@ +.schedule { + display: flex; + flex-direction: column; + gap: 20px; + + &__divider { + width: 100%; + height: 1px; + margin: 20px 0; + background: #ffffff80; + + @include on-tablet { + width: 1px; + height: auto; + margin: 0 38px; + } + + @include on-desktop { + margin: 0 102px; + } + } + + &__title, + &__time, + &__day, + &__info-text, + &__label { + font-size: $font-size-small; + } + + &__title { + margin: 0; + font-weight: 400; + line-height: 141%; + } + + &__content { + display: flex; + flex-direction: column; + gap: 16px; + + @include on-tablet { + flex-direction: row; + gap: 26px; + align-items: flex-start; + } + + @include on-desktop { + gap: 30px; + } + } + + &__table { + @include on-tablet { + min-width: 170px; + } + } + + &__row { + display: flex; + gap: 20px; + justify-content: flex-start; + } + + &__time, + &__label { + font-weight: 600; + line-height: 150%; + } + + &__day, + &__info-text { + font-weight: 400; + line-height: 150%; + } + + &__info { + @include on-desktop { + max-width: 170px; + } + } +} diff --git a/src/styles/components/_section-title.scss b/src/styles/components/_section-title.scss new file mode 100644 index 000000000..aa8db3fd1 --- /dev/null +++ b/src/styles/components/_section-title.scss @@ -0,0 +1,22 @@ +.section-title { + margin-bottom: 40px; + padding-inline: 20px; + + font-family: Montserrat, sans-serif; + font-size: 36px; + font-weight: 700; + line-height: 100%; + text-align: center; + + @include on-tablet { + margin-bottom: 48px; + padding-inline: 34px; + font-size: 48px; + text-align: left; + } + + @include on-desktop { + margin-bottom: 56px; + padding-inline: 55px; + } +} diff --git a/src/styles/components/_subscribe.scss b/src/styles/components/_subscribe.scss new file mode 100644 index 000000000..317d12c1c --- /dev/null +++ b/src/styles/components/_subscribe.scss @@ -0,0 +1,149 @@ +.subscribe { + &__content { + width: 100%; + padding: 88px 20px; + + background-image: url('../images/subscription_bg.jpg'); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + + @include on-tablet { + padding: 82px 154px; + } + + @include on-desktop { + padding: 163px 355px; + } + } + + &__title { + margin-bottom: 20px; + + font-family: Montserrat, sans-serif; + font-size: 36px; + font-weight: 700; + line-height: 100%; + color: $white; + text-align: center; + + @include on-tablet { + margin-bottom: 16px; + font-size: 48px; + } + } + + &__text { + margin-bottom: 50px; + + font-size: 18px; + line-height: 150%; + color: $white; + text-align: center; + + @include on-tablet { + margin-bottom: 64px; + line-height: 100%; + } + + @include on-desktop { + margin-bottom: 72px; + } + } + + &__form { + display: flex; + align-items: center; + + width: 100%; + max-width: 280px; + height: 50px; + margin: 0 auto; + + @include on-tablet { + max-width: 460px; + } + + @include on-desktop { + max-width: 570px; + height: 70px; + } + } + + &__input { + flex: 1; + + height: 100%; + padding-left: 20px; + border: none; + + font-size: 14px; + color: $c-text-dark; + + background: $white; + outline: none; + + &::placeholder { + color: #687480; + } + + &:focus { + box-shadow: inset 0 0 0 2px rgba($red, 0.4); + } + } + + &__button { + cursor: pointer; + + display: flex; + align-items: center; + justify-content: center; + + width: 50px; + height: 100%; + border: none; + + background: $red; + + transition: filter 0.2s ease; + + @include on-tablet { + margin-left: 16px; + } + + @include on-desktop { + width: 70px; + margin-left: 30px; + } + + &__input { + font-family: Montserrat, sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 100%; + } + + &:hover { + filter: brightness(0.92); + } + + &-icon { + display: flex; + align-items: center; + justify-content: center; + + width: 50px; + height: 50px; + + font-size: 24px; + color: #000; + + background: $red; + + @include on-desktop { + width: 70px; + height: 70px; + } + } + } +} diff --git a/src/styles/main.scss b/src/styles/main.scss index fb9195d12..54ceabe75 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -1,7 +1,22 @@ -@import 'utils'; -@import 'fonts'; -@import 'typography'; +@import './utils/vars'; +@import './utils/mixins'; +@import './utils/extends'; -body { - background: $c-gray; -} +@import './base/reset'; +@import './base/fonts'; +@import './base/typography'; + +@import './components/page'; +@import './components/header'; +@import './components/header-menu'; +@import './components/schedule'; +@import './components/container'; +@import './components/logo'; +@import './components/burger'; +@import './components/button'; +@import './components/event-card'; +@import './components/gallery'; +@import './components/section-title'; +@import './components/subscribe'; +@import './components/overlay'; +@import './components/footer'; diff --git a/src/styles/utils/_extends.scss b/src/styles/utils/_extends.scss index d7201e7b3..beed94834 100644 --- a/src/styles/utils/_extends.scss +++ b/src/styles/utils/_extends.scss @@ -1,4 +1,4 @@ %h1 { - font-family: Roboto, sans-serif; - font-weight: 400; + font-family: Montserrat, sans-serif; + font-weight: 700; } diff --git a/src/styles/utils/_mixins.scss b/src/styles/utils/_mixins.scss index 80c79780d..f329a5468 100644 --- a/src/styles/utils/_mixins.scss +++ b/src/styles/utils/_mixins.scss @@ -4,3 +4,59 @@ #{$_property}: $_toValue; } } + +@mixin on-small-tablet() { + @media (min-width: $small-tablet-min-width) { + @content; + } +} + +@mixin on-tablet() { + @media (min-width: $tablet-min-width) { + @content; + } +} + +@mixin on-laptop() { + @media (min-width: $laptop-min-width) { + @content; + } +} + +@mixin on-small-desktop() { + @media (min-width: $small-desktop-min-width) { + @content; + } +} + +@mixin on-desktop() { + @media (min-width: $desktop-min-width) { + @content; + } +} + +@mixin on-large-desktop() { + @media (min-width: $large-desktop-min-width) { + @content; + } +} + +@mixin page-grid() { + --columns: 2; + + display: grid; + grid-template-columns: repeat(var(--columns), 1fr); + column-gap: 20px; + + @include on-tablet { + --columns: 6; + + column-gap: 30px; + } + + @include on-desktop { + --columns: 12; + + column-gap: 30px; + } +} diff --git a/src/styles/utils/_vars.scss b/src/styles/utils/_vars.scss index aeb006ffb..b608c22b3 100644 --- a/src/styles/utils/_vars.scss +++ b/src/styles/utils/_vars.scss @@ -1 +1,14 @@ $c-gray: #eee; +$c-text-dark: #0f0e08; +$white: #fff; +$red: #f14a27; +$gray: #e0e0e0; +$color-deep-green: #1a5a4c; +$font-size-small: 14px; +$small-tablet-min-width: 568px; +$tablet-min-width: 768px; +$laptop-min-width: 992px; +$small-desktop-min-width: 1024px; +$desktop-min-width: 1280px; +$large-desktop-min-width: 1440px; +$effectDuration: 0.3s;