diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..1782ac3 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(git show:*)" + ] + } +} diff --git a/assets/css/src/article.css b/assets/css/src/article.css index 4b3d735..c94b0bb 100644 --- a/assets/css/src/article.css +++ b/assets/css/src/article.css @@ -30,11 +30,16 @@ article #main-content #chapo::after { article #main-content li:not(.text) { list-style-type: inherit; } - article h3 { + scroll-margin-top: calc(var(--unit--vertical) * 1); margin-top: calc(3 * var(--unit--vertical)); margin-bottom: calc(1 * var(--unit--vertical)); } +@media screen and (max-width: 640px) { + article h3 { + scroll-margin-top: calc(var(--unit--vertical) * 5); + } +} article li, article ol{ margin-left: var(--unit--horizontal); diff --git a/assets/css/src/footer.css b/assets/css/src/footer.css index a50de8a..d575fb6 100644 --- a/assets/css/src/footer.css +++ b/assets/css/src/footer.css @@ -12,12 +12,39 @@ bottom: 0; } +@media screen and (max-width: 640px) { + #main-footer ul { + bottom: 0; + display: flex; + justify-content: space-around; + border-top: 1px solid var(--color-primary); + background-color: var(--color-background); + } + #main-footer .open-nav-wrapper:has([data-open-panel="toc"]) { + margin-right: 50px; + } +} + +@media screen and (min-width: 1100px) { + /* On mobile > 1100px, le bouton sommaire n'est pas nécessaire car la TOC est visible */ + #main-footer .open-nav-wrapper:has([data-open-panel="toc"]) { + display: none !important; + } +} + #main-footer li:not(.open-nav-wrapper) { display: none; } +#main-footer li{ + flex: 1; +} +#main-footer li > *{ + width:calc(100% - var(--unit--vertical) * 2); +} + #main-footer button.open-nav { - transform: translateY(-1px); + transform: translateY(-2px); } [data-template="home"] .title-wrapper button.open-nav { @@ -26,19 +53,14 @@ @media screen and (max-width: 640px) { #main-footer .open-nav { - box-sizing: border-box; - bottom: 0; display: flex; justify-content: center; - width: 100%; outline: none; - border-top: 1px solid var(--color-primary); font-size: var(--font-size-m); background-color: var(--color-background); - padding: calc(var(--unit--vertical) / 2) var(--unit--horizontal); - margin-bottom: env(safe-area-inset-bottom); color: var(--color-primary); line-height: 1; + padding: calc(var(--unit--vertical) / 2) var(--unit--horizontal); } } diff --git a/assets/css/src/header.css b/assets/css/src/header.css index 783e683..a2cce66 100644 --- a/assets/css/src/header.css +++ b/assets/css/src/header.css @@ -82,6 +82,7 @@ article > h1 { display: flex; flex-direction: column; + gap: var(--unit--vertical); } [data-template="home"] .page-cover { diff --git a/assets/css/src/toc.css b/assets/css/src/toc.css index 9239140..dfae1ba 100644 --- a/assets/css/src/toc.css +++ b/assets/css/src/toc.css @@ -1,15 +1,39 @@ +.toc { + display: flex; + flex-direction: column; + justify-content: center; +} -.page-cover .toc{ +.page-cover .toc { + flex: 1; +} + +@media (min-width: 1100px) { + .page-cover .toc { position: fixed; - display: block; - width: calc(var(--body-padding) - var(--unit--horizontal) * 2) ; + width: calc(var(--body-padding) - var(--unit--horizontal) * 2); left: 0; top: 15vw; padding-inline: var(--unit--horizontal); + padding-top: calc(var(--unit--vertical) / 2); + } } -.toc_label{ - font-size: var(--font-size-m); + +.panel-toc .toc { + padding: var(--unit--vertical) var(--unit--horizontal); +} + +.toc_label { + font-size: var(--font-size-m); + margin-bottom: calc(var(--unit--vertical) / 4); +} + +.toc ul { + display: flex; + flex-direction: column; + gap: calc(var(--unit--vertical) / 4); +} + +.toc li { + margin-left: 0; } -.toc li{ - margin-left: 0; -} \ No newline at end of file diff --git a/assets/js/script.js b/assets/js/script.js index f734272..99c2327 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -69,9 +69,13 @@ function toggleLogoState() { } function toggleFooterState() { if (scrollY > 90) { - document.querySelector(".open-nav-wrapper").classList.remove("hidden"); + document.querySelectorAll(".open-nav-wrapper").forEach(element => { + element.classList.remove("hidden"); + }); } else { - document.querySelector(".open-nav-wrapper").classList.add("hidden"); + document.querySelectorAll(".open-nav-wrapper").forEach(element => { + element.classList.add("hidden"); + }); } } @@ -125,16 +129,24 @@ function subscribe(event) { } } -const panelNav = document.querySelector(".panel"); +const panels = document.querySelectorAll(".panel[data-panel]"); const navOverlay = document.querySelector("#nav-overlay"); -const openNavBtns = document.querySelectorAll("button.open-nav"); -const closeNavBtn = document.querySelector(".panel-close"); -function closeNav() { - panelNav.classList.remove("panel--visible"); + +function closeAllPanels() { + panels.forEach(panel => panel.classList.remove("panel--visible")); navOverlay.classList.remove("nav-overlay--visible"); document.body.classList.remove("no-scroll"); } +function openPanel(name) { + const panel = document.querySelector(`.panel[data-panel="${name}"]`); + if (panel) { + panel.classList.add("panel--visible"); + navOverlay.classList.add("nav-overlay--visible"); + document.body.classList.add("no-scroll"); + } +} + document.addEventListener("DOMContentLoaded", () => { ragadjust("h1, h2, h4, h5", ["all"]); window.window.scrollTo({ @@ -158,10 +170,10 @@ document.addEventListener("DOMContentLoaded", () => { window.addEventListener("keyup", (event) => { if (event.key === "Escape") { - closeNav(); + closeAllPanels(); } }); - document.querySelectorAll(".panel").forEach((panel) => { + panels.forEach((panel) => { panel.addEventListener("click", (event) => { event.stopPropagation(); }); @@ -193,18 +205,20 @@ document.addEventListener("DOMContentLoaded", () => { }); }); - openNavBtns.forEach((openNavBtn) => { - openNavBtn.addEventListener("click", () => { - panelNav.classList.add("panel--visible"); - navOverlay.classList.add("nav-overlay--visible"); - document.body.classList.add("no-scroll"); + document.querySelectorAll("[data-open-panel]").forEach((btn) => { + btn.addEventListener("click", () => { + openPanel(btn.dataset.openPanel); }); }); - closeNavBtn.addEventListener("click", () => { - closeNav(); + document.querySelectorAll(".panel-close").forEach((btn) => { + btn.addEventListener("click", closeAllPanels); }); - navOverlay.addEventListener("click", () => { - closeNav(); + + navOverlay.addEventListener("click", closeAllPanels); + + // Fermer le panel TOC quand on clique sur un lien + document.querySelectorAll(".panel-toc .toc a").forEach((link) => { + link.addEventListener("click", closeAllPanels); }); }); diff --git a/site/plugins/helpers/index.php b/site/plugins/helpers/index.php index 8726f56..3a4d755 100644 --- a/site/plugins/helpers/index.php +++ b/site/plugins/helpers/index.php @@ -12,14 +12,6 @@ function allYears ($article) { return $years; } -function addAnchors($content) { - $content = preg_replace_callback('/

(.*?)<\/h3>/', function($matches) { - $slug = Str::slug($matches[1]); - return '

' . $matches[1] . '

'; - }, $content); - return $content; -} - function setTitleFontSizeClass($title, $level = 'h1') { $length = strlen($title); diff --git a/site/plugins/toc/index.php b/site/plugins/toc/index.php new file mode 100644 index 0000000..5a6e97d --- /dev/null +++ b/site/plugins/toc/index.php @@ -0,0 +1,47 @@ + [ + /** + * Vérifie si la page doit afficher une TOC + */ + 'hasToc' => function(): bool { + if (!$this->parent()?->parent()?->is('textes')) { + return false; + } + if (!$this->bodyBlocks()?->isNotEmpty()) { + return false; + } + return (bool) preg_match('/

/', $this->bodyBlocks()->toBlocks()); + }, + + /** + * Retourne les items de la TOC + */ + 'tocItems' => function(): array { + if (!$this->bodyBlocks()?->isNotEmpty()) { + return []; + } + preg_match_all('/

(.*?)<\/h3>/', $this->bodyBlocks()->toBlocks(), $matches); + + return array_map(fn($title) => [ + 'title' => $title, + 'slug' => Str::slug($title) + ], $matches[1]); + }, + + /** + * Retourne le contenu avec les ancres ajoutées aux h3 + */ + 'bodyWithAnchors' => function(): string { + if (!$this->bodyBlocks()?->isNotEmpty()) { + return ''; + } + return preg_replace_callback( + '/

(.*?)<\/h3>/', + fn($m) => '

' . $m[1] . '

', + $this->bodyBlocks()->toBlocks() + ); + } + ] +]); diff --git a/site/snippets/cover.php b/site/snippets/cover.php index 1b2f77a..fc22ac3 100644 --- a/site/snippets/cover.php +++ b/site/snippets/cover.php @@ -8,9 +8,9 @@ $isOpen ??= false; title() ?> - parent()->parent()->is('textes')){ - snippet('toc', ["content" => $page->bodyBlocks()->toBlocks()]); - } ?> + hasToc()): ?> + + text()): ?>
text() ?> diff --git a/site/snippets/footer.php b/site/snippets/footer.php index 87457ce..61b668b 100644 --- a/site/snippets/footer.php +++ b/site/snippets/footer.php @@ -3,8 +3,13 @@