diff --git a/assets/js/contentNavigation.js b/assets/js/contentNavigation.js new file mode 100644 index 00000000..236c06c2 --- /dev/null +++ b/assets/js/contentNavigation.js @@ -0,0 +1,121 @@ +import * as mq from "./mediaqueries"; + +const isMobile = () => { + return window.matchMedia(mq.maxMD).matches; +}; + +const initAside = () => { + const aside = document.querySelector(".o-aside"); + const asideContent = document.querySelector(".o-aside__content"); + const handleBtn = document.querySelector(".o-aside__header"); + const overlay = document.getElementById("overlay"); + + if (!aside || !asideContent || !handleBtn || !overlay) return; + + let isClosed = true; + + const closeSheet = () => { + isClosed = true; + aside.classList.remove("o-aside--open"); + asideContent.removeAttribute("role"); + asideContent.setAttribute("aria-hidden", "true"); + handleBtn.setAttribute("aria-expanded", "false"); + overlay.classList.remove("overlay--show"); + }; + + const openSheet = () => { + isClosed = false; + aside.classList.add("o-aside--open"); + overlay.classList.add("overlay--show"); + asideContent.setAttribute("role", "dialog"); + asideContent.setAttribute("aria-hidden", "false"); + handleBtn.setAttribute("aria-expanded", "true"); + }; + + if (isMobile()) { + closeSheet(); + } + + const toggleSheet = () => { + if (isClosed) { + openSheet(); + } else { + closeSheet(); + } + }; + + handleBtn.addEventListener("click", toggleSheet); + overlay.addEventListener("click", closeSheet); + + // Drag support + let startY = 0; + let currentY = 0; + let isDragging = false; + + const dragStart = (e) => { + startY = e.clientY || e.touches?.[0].clientY; + isDragging = true; + }; + + const dragging = (e) => { + if (!isDragging) return; + e.preventDefault(); + currentY = e.clientY || e.touches?.[0].clientY; + const deltaY = startY - currentY; + + if (isClosed && deltaY > 50) { + openSheet(); + } + + if (!isClosed && deltaY < -50) { + closeSheet(); + } + }; + + const dragEnd = () => { + if (!isDragging) return; + isDragging = false; + }; + + handleBtn.addEventListener("mousedown", dragStart); + handleBtn.addEventListener("mousemove", dragging); + handleBtn.addEventListener("mouseup", dragEnd); + + handleBtn.addEventListener("touchstart", dragStart); + handleBtn.addEventListener("touchmove", dragging); + handleBtn.addEventListener("touchend", dragEnd); + + // close bottom-sheet if link is clicked + document.querySelectorAll(".o-aside__toc-link").forEach((link) => { + link.addEventListener("click", () => { + if (isMobile()) { + closeSheet(); + } + }); + }); + + let wasMobile = isMobile(); + + const handleResize = () => { + if (isMobile() && !wasMobile) { + wasMobile = true; + closeSheet(); + } + if (!isMobile()) { + wasMobile = false; + overlay.classList.remove("overlay--show"); + aside.classList.remove("o-aside--open"); + } + }; + + window.addEventListener("load", handleResize); + window.addEventListener("resize", handleResize); +}; + +if (document.readyState === "interactive") { + initAside(); +} else { + window.addEventListener("DOMContentLoaded", () => { + initAside(); + }); +} diff --git a/assets/js/highlightHeadline.js b/assets/js/highlightHeadline.js index 673488b9..75585680 100644 --- a/assets/js/highlightHeadline.js +++ b/assets/js/highlightHeadline.js @@ -1,9 +1,3 @@ -import * as mq from "./mediaqueries"; - -function isAsideActive() { - return window.matchMedia(mq.minLG).matches; -} - function initHighlightHeadline() { const headings = Array.from( document.querySelectorAll(".o-single__highlight :is(h1, h2, h3)"), @@ -28,10 +22,6 @@ function initHighlightHeadline() { let scrollDebounce; function updateActiveHeading() { - if (!isAsideActive()) { - return; - } - let currentHeading = null; for (let i = 0; i < headings.length; i++) { diff --git a/assets/js/main.js b/assets/js/main.js index aaff0e00..fba26f98 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -2,6 +2,7 @@ import "./mobileMenu.js"; import "./resizeObserver.js"; import "./mediaqueries.js"; import "./highlightHeadline.js"; +import "./contentNavigation.js"; import "./anchorlinks.js"; import "./dropdown.js"; import "./darkmode.js"; diff --git a/assets/js/mediaqueries.js b/assets/js/mediaqueries.js index 9b6bc813..74aebe24 100644 --- a/assets/js/mediaqueries.js +++ b/assets/js/mediaqueries.js @@ -3,14 +3,14 @@ * import * as mq from '../helpers/mediaqueries'; *================================================== */ -export const maxXS = "(max-width: 575px)"; -export const maxSM = "(max-width: 751px)"; -export const maxMD = "(max-width: 967px)"; -export const maxLG = "(max-width: 1182px)"; -export const maxXL = "(max-width: 1463px)"; +export const maxXS = "(max-width: 576px)"; +export const maxSM = "(max-width: 768px)"; +export const maxMD = "(max-width: 992px)"; +export const maxLG = "(max-width: 1200px)"; +export const maxXL = "(max-width: 1400px)"; -export const minSM = "(min-width: 576px)"; -export const minMD = "(min-width: 752px)"; -export const minLG = "(min-width: 968px)"; -export const minXL = "(min-width: 1183px)"; -export const minXXL = "(min-width: 1464px)"; +export const minSM = "(min-width: 577px)"; +export const minMD = "(min-width: 768px)"; +export const minLG = "(min-width: 993px)"; +export const minXL = "(min-width: 1201px)"; +export const minXXL = "(min-width: 1401px)"; diff --git a/assets/sass/contentNavigation.scss b/assets/sass/contentNavigation.scss new file mode 100644 index 00000000..62d7d973 --- /dev/null +++ b/assets/sass/contentNavigation.scss @@ -0,0 +1,194 @@ +.o-aside { + margin-bottom: 0; + position: sticky; + top: 8rem; + font-size: 1.4rem; + line-height: 1.5; + margin-right: 0; + z-index: 3; + display: flex; + flex-direction: column; + gap: 1.5rem; + flex-wrap: wrap; + height: fit-content; + + ul, + ol { + list-style-type: none; + list-style-position: outside; + padding-left: 0; + + ul, + ol { + margin-bottom: 0; + } + } +} + +.o-aside__header { + display: none; +} + +.o-aside a { + text-decoration-line: none; + display: flex; + align-items: flex-start; + gap: 0.4rem; +} + +.o-aside__backlink-text { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.o-aside__toc { + li { + list-style-type: none; + } + + ol { + padding: 0 0 0 1em; + } + + > ol { + padding-left: 0; + } +} + +.o-aside__toc-link { + display: flex; +} + +.o-aside__toc a[data-current] { + font-weight: bold; + + &:before { + content: ""; + width: 0.4rem; + height: 2.1rem; + position: absolute; + display: flex; + left: 0; + background-color: var(--link-default); + } +} + +.o-related { + &__list { + list-style-type: none; + list-style-position: outside; + padding-left: 0; + } + + &-date { + padding-left: 2.5rem; + } + + p { + margin-bottom: 0; + } +} + +.o-related__item img { + border-radius: var(--border-radius-s); +} + +@media (max-width: #{$breakpoint-lg}) { + .o-aside { + position: fixed; + top: unset; + bottom: 0; + left: 0; + width: 100%; + max-height: 100vh; + gap: 0; + overflow: hidden; + flex-flow: column; + transition: transform 0.5s ease-in-out; + border-radius: var(--border-radius-m) var(--border-radius-m) 0 0; + transform: translateY(calc(100% - 6.4rem - env(safe-area-inset-bottom))); + box-shadow: var(--box-shadow); + max-height: calc(100vh - 20rem - env(safe-area-inset-bottom)); + + &--open { + transform: translateY(0); + } + + &:not(.o-aside--open) { + > .o-aside__header { + border-bottom: none; + } + + > .o-aside__content { + pointer-events: none; + user-select: none; + + * { + opacity: 0; + } + } + } + + &__content { + background: var(--bg-default); + overflow-y: scroll; + overscroll-behavior-y: contain; + border-left: var(--border); + border-right: var(--border); + + .o-single__container { + border: none; + } + + * { + transition: opacity 0.3s ease-in-out; + } + } + + &__header { + display: flex; + align-items: center; + flex-direction: column; + gap: 1.5rem; + width: 100%; + border: var(--border); + border-bottom: 1px solid #3d444d; + padding: 1rem; + height: 6.4rem; + border-radius: 1rem 1rem 0 0; + background: var(--bg-default); + color: var(--color-body); + } + + &__drag { + height: 0.4rem; + width: 4rem; + display: block; + background: var(--color-body); + border-radius: 2rem; + } + + &__logo { + display: flex; + align-items: center; + gap: 1.5rem; + } + } + + main.o-newspage .o-aside { + display: none; + } +} + +.overlay--show { + backdrop-filter: blur(4px); + background-color: rgba(0, 0, 0, 0.6); + position: fixed; + inset: 0; + z-index: 3; +} + +body:has(.overlay--show) { + overflow: hidden; +} diff --git a/assets/sass/main.scss b/assets/sass/main.scss index 35954c0b..639c39d7 100644 --- a/assets/sass/main.scss +++ b/assets/sass/main.scss @@ -2,7 +2,7 @@ @import "styles.scss"; @import "fonts.scss"; @import "navigation.scss"; -@import "sidemenu.scss"; +@import "contentNavigation"; @import "search.scss"; @import "teaser.scss"; @import "footer.scss"; diff --git a/assets/sass/sidemenu.scss b/assets/sass/sidemenu.scss deleted file mode 100644 index 6b5fb068..00000000 --- a/assets/sass/sidemenu.scss +++ /dev/null @@ -1,113 +0,0 @@ -.o-aside { - position: sticky; - top: 8rem; - - font-size: 1.4rem; - line-height: 1.5; - - margin-right: 0; - margin-bottom: 1.2rem; - - z-index: 3; - - display: flex; - flex-direction: column; - gap: 1.5rem; - flex-wrap: wrap; - height: fit-content; - - @media (min-width: #{$breakpoint-lg}) { - margin-bottom: 0; - } - - @media (max-width: #{$breakpoint-lg}) { - display: none; - } - - .o-aside__mobile-container--open { - @media (max-width: #{$breakpoint-lg}) { - display: block; - } - } - - ul, - ol { - list-style-type: none; - list-style-position: outside; - padding-left: 0; - - ul, - ol { - margin-bottom: 0; - } - } - - @media print { - display: none; - } -} - -.o-aside a { - text-decoration-line: none; - display: flex; - align-items: flex-start; - gap: 0.4rem; -} - -.o-aside__backlink-text { - display: flex; - align-items: center; - gap: 0.4rem; -} - -.o-aside__toc { - li { - list-style-type: none; - } - - ol { - padding: 0 0 0 1em; - } - - > ol { - padding-left: 0; - } -} - -.o-aside__toc-link { - display: flex; -} - -.o-aside__toc a[data-current] { - font-weight: bold; - - &:before { - content: ""; - width: 0.4rem; - height: 2.1rem; - position: absolute; - display: flex; - left: 0; - background-color: var(--link-default); - } -} - -.o-related { - &__list { - list-style-type: none; - list-style-position: outside; - padding-left: 0; - } - - &-date { - padding-left: 2.5rem; - } - - p { - margin-bottom: 0; - } -} - -.o-related__item img { - border-radius: var(--border-radius-s); -} diff --git a/assets/sass/styles.scss b/assets/sass/styles.scss index 730ff6d0..018dae74 100644 --- a/assets/sass/styles.scss +++ b/assets/sass/styles.scss @@ -17,6 +17,11 @@ body { font-size: 1.4em; } } +@media (max-width: #{$breakpoint-lg}) { + body:has(article.o-single--with-sidemenu):not(:has(.o-newspage)) { + margin: 0 0 calc(7rem + env(safe-area-inset-bottom)) 0; + } +} p, ul, diff --git a/i18n/de.yaml b/i18n/de.yaml index 0bab356f..012760c2 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -14,6 +14,9 @@ booking: reservation-costs: Reservierungskosten visit-additional-information-website: Weitere Informationen visit-booking-website: Zur Buchungsseite +contentNavigation: + button: Inhaltsübersicht öffnen und schließen + title: Inhaltsübersicht countries: overview: Länder mit FIP Akzeptanz selection: diff --git a/i18n/en.yaml b/i18n/en.yaml index 5cc6077f..7af9b66f 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -13,6 +13,9 @@ booking: reservation-costs: Reservation Costs visit-additional-information-website: Additional Information visit-booking-website: To Booking Website +contentNavigation: + button: Open and close content overview + title: Content overview countries: overview: Countries with FIP acceptance selection: diff --git a/i18n/fr.yaml b/i18n/fr.yaml index 89ecc6e1..cc274e74 100644 --- a/i18n/fr.yaml +++ b/i18n/fr.yaml @@ -13,6 +13,9 @@ booking: reservation-costs: Frais de réservation visit-additional-information-website: Informations complémentaires visit-booking-website: Aller sur le site de réservation +contentNavigation: + button: Ouvrir et fermer l’aperçu du contenu + title: Aperçu du contenu countries: overview: Pays acceptation FIP selection: diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index c8179ffd..9cb621d9 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -7,8 +7,14 @@ {{ partial "head" . }}
+