side-panel : refactor nav + toc into single side-panel with view switching

Rename nav.php to side-panel.php to host both navigation and table of contents
views. The panel uses data-view attributes to switch between nav and toc content.
Footer buttons updated to target the unified panel. TOC button now visible on desktop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-03-28 09:28:47 +01:00
parent 798123a60a
commit 6c1a8c8850
16 changed files with 103 additions and 115 deletions

View file

@ -2,8 +2,8 @@
position: fixed;
bottom: 0;
box-sizing: border-box;
border-bottom: 0;
z-index: 2;
}
[data-template="home"] #main-footer {
@ -20,19 +20,9 @@
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 table des matières 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) {
#main-footer li:not(.footer-btn-wrapper) {
display: none;
}
@ -43,16 +33,16 @@
width: calc(100% - var(--unit--vertical) * 2);
}
#main-footer button.open-nav {
#main-footer button.plus {
transform: translateY(-2px);
}
[data-template="home"] .title-wrapper button.open-nav {
[data-template="home"] .title-wrapper button.plus[data-open-panel] {
display: inline-block !important;
}
@media screen and (max-width: 640px) {
#main-footer .open-nav {
#main-footer .footer-btn-wrapper button {
display: flex;
justify-content: center;
outline: none;
@ -80,15 +70,15 @@
display: block;
}
#main-footer button.open-nav {
#main-footer button.plus {
margin-bottom: var(--unit--vertical);
}
[data-template="home"] #main-footer .open-nav-wrapper {
[data-template="home"] #main-footer .footer-btn-wrapper {
display: none !important;
}
.open-nav-wrapper {
.footer-btn-wrapper {
padding: 0;
border: none;
background-color: transparent;

View file

@ -16,7 +16,7 @@
overflow: auto;
width: 100vw;
height: 100dvh;
top: 0;
top: 0;
background-color: var(--color-background);
outline: 1px solid var(--color-primary);
transition: all 0.5s var(--curve-sine);
@ -199,7 +199,7 @@ button.see-more {
}
@media screen and (min-width: 640px) {
nav.panel {
.side-panel {
width: 40rem;
}

View file

@ -19,7 +19,7 @@
}
}
.panel-toc .toc {
.side-panel__view[data-view="toc"] .toc {
padding: var(--unit--vertical) var(--unit--horizontal);
}

View file

@ -1,31 +1,34 @@
.theme-toggler{
position: fixed;
right: 0;
bottom: 0;
padding: calc((var(--unit--vertical) / 2) / 2) calc(var(--unit--horizontal) / 2);
margin: calc((var(--unit--vertical) / 2) / 2) calc(var(--unit--horizontal) / 2);
margin-bottom: calc(var(--unit--vertical) - ((var(--unit--vertical) / 2) / 2));
z-index: 100;
.theme-toggler {
position: fixed;
right: 0;
bottom: 0;
padding: calc((var(--unit--vertical) / 2) / 2)
calc(var(--unit--horizontal) / 2);
margin: calc((var(--unit--vertical) / 2) / 2)
calc(var(--unit--horizontal) / 2);
margin-bottom: calc(
var(--unit--vertical) - ((var(--unit--vertical) / 2) / 2)
);
z-index: 1;
}
.theme-toggler-icon {
width: 1.2rem;
height: 1.2rem;
width: 1.2rem;
height: 1.2rem;
background-color: var(--color-primary);
mask-size: cover;
-webkit-mask-size: cover;
background-color: var(--color-primary);
mask: var(--icon-theme-toggler) no-repeat center;
-webkit-mask: var(--icon-theme-toggler) no-repeat center;
mask-size: cover;
-webkit-mask-size: cover;
mask: var(--icon-theme-toggler) no-repeat center;
-webkit-mask: var(--icon-theme-toggler) no-repeat center;
}
@media screen and (max-width: 640px) {
.theme-toggler{
margin-bottom: calc((var(--unit--vertical) / 2) / 2);
}
.theme-toggler-icon {
width: 1.1rem;
height: 1.1rem;
}
}
.theme-toggler {
margin-bottom: calc((var(--unit--vertical) / 2) / 2);
}
.theme-toggler-icon {
width: 1.1rem;
height: 1.1rem;
}
}

View file

@ -865,7 +865,7 @@ button.see-more {
}
@media screen and (min-width: 640px) {
nav.panel {
.side-panel {
width: 40rem;
}
.panel {
@ -1078,6 +1078,7 @@ body.full-width #main-content {
bottom: 0;
box-sizing: border-box;
border-bottom: 0;
z-index: 2;
}
[data-template=home] #main-footer {
@ -1094,17 +1095,8 @@ body.full-width #main-content {
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 table des matières 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) {
#main-footer li:not(.footer-btn-wrapper) {
display: none;
}
@ -1116,16 +1108,16 @@ body.full-width #main-content {
width: calc(100% - var(--unit--vertical) * 2);
}
#main-footer button.open-nav {
#main-footer button.plus {
transform: translateY(-2px);
}
[data-template=home] .title-wrapper button.open-nav {
[data-template=home] .title-wrapper button.plus[data-open-panel] {
display: inline-block !important;
}
@media screen and (max-width: 640px) {
#main-footer .open-nav {
#main-footer .footer-btn-wrapper button {
display: flex;
justify-content: center;
outline: none;
@ -1151,13 +1143,13 @@ body.full-width #main-content {
#main-footer ul {
display: block;
}
#main-footer button.open-nav {
#main-footer button.plus {
margin-bottom: var(--unit--vertical);
}
[data-template=home] #main-footer .open-nav-wrapper {
[data-template=home] #main-footer .footer-btn-wrapper {
display: none !important;
}
.open-nav-wrapper {
.footer-btn-wrapper {
padding: 0;
border: none;
background-color: transparent;
@ -1170,7 +1162,7 @@ body.full-width #main-content {
padding: calc(var(--unit--vertical) / 2 / 2) calc(var(--unit--horizontal) / 2);
margin: calc(var(--unit--vertical) / 2 / 2) calc(var(--unit--horizontal) / 2);
margin-bottom: calc(var(--unit--vertical) - var(--unit--vertical) / 2 / 2);
z-index: 100;
z-index: 1;
}
.theme-toggler-icon {
@ -1291,7 +1283,7 @@ body.full-width #main-content {
padding-top: calc(var(--unit--vertical) / 2);
}
}
.panel-toc .toc {
.side-panel__view[data-view=toc] .toc {
padding: var(--unit--vertical) var(--unit--horizontal);
}

File diff suppressed because one or more lines are too long

View file

@ -3,7 +3,7 @@
@import "src/generic";
@import "src/texts";
@import "src/header";
@import "src/nav";
@import "src/side-panel";
@import "src/article";
@import "src/virtual";
@import "src/home";

View file

@ -69,11 +69,11 @@ function toggleLogoState() {
}
function toggleFooterState() {
if (scrollY > 90) {
document.querySelectorAll(".open-nav-wrapper").forEach(element => {
document.querySelectorAll(".footer-btn-wrapper").forEach(element => {
element.classList.remove("hidden");
});
} else {
document.querySelectorAll(".open-nav-wrapper").forEach(element => {
document.querySelectorAll(".footer-btn-wrapper").forEach(element => {
element.classList.add("hidden");
});
}
@ -138,9 +138,14 @@ function closeAllPanels() {
document.body.classList.remove("no-scroll");
}
function openPanel(name) {
function openPanel(name, view) {
const panel = document.querySelector(`.panel[data-panel="${name}"]`);
if (panel) {
if (view) {
panel.querySelectorAll('[data-view]').forEach(v => v.classList.add('hidden'));
const target = panel.querySelector(`[data-view="${view}"]`);
if (target) target.classList.remove('hidden');
}
panel.classList.add("panel--visible");
navOverlay.classList.add("nav-overlay--visible");
document.body.classList.add("no-scroll");
@ -179,7 +184,7 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
const navSortBtns = document.querySelectorAll("nav .sort-btn");
const navSortBtns = document.querySelectorAll(".side-panel .sort-btn");
const navSections = document.querySelectorAll(
".panel__all-texts, .panel__collection"
);
@ -207,7 +212,7 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("[data-open-panel]").forEach((btn) => {
btn.addEventListener("click", () => {
openPanel(btn.dataset.openPanel);
openPanel(btn.dataset.openPanel, btn.dataset.view);
});
});
@ -218,7 +223,7 @@ document.addEventListener("DOMContentLoaded", () => {
navOverlay.addEventListener("click", closeAllPanels);
// Fermer le panel TOC quand on clique sur un lien
document.querySelectorAll(".panel-toc .toc a").forEach((link) => {
document.querySelectorAll('[data-view="toc"] .toc a').forEach((link) => {
link.addEventListener("click", closeAllPanels);
});
});

View file

@ -8,9 +8,6 @@ $isOpen ??= false;
<?= $slots->title() ?>
</a>
</div>
<?php if ($page->hasToc()): ?>
<?php snippet('toc') ?>
<?php endif ?>
<?php if ($slots->text()): ?>
<div class="text-wrapper">
<?= $slots->text() ?>

View file

@ -1,15 +1,14 @@
<?php if (!$page->is(page('lettre')) && !$page->is(page('a-propos'))): ?>
<footer id="main-footer">
<ul id="links">
<li class="open-nav-wrapper hidden">
<button class="plus open-nav" data-open-panel="text" title="chercher parmi les textes">textes</button>
</li>
<?php if ($page->hasToc()): ?>
<li class="open-nav-wrapper hidden">
<button class="plus open-nav" data-open-panel="toc" title="ouvrir le table des matières">table des
matières</button>
</li>
<li class="footer-btn-wrapper hidden">
<button class="plus" data-open-panel="side-panel" data-view="toc" title="ouvrir la table des matières">sommaire</button>
</li>
<?php endif ?>
<li class="footer-btn-wrapper hidden">
<button class="plus" data-open-panel="side-panel" data-view="nav" title="chercher parmi les textes">textes</button>
</li>
<li>
<a href="/lettre" id="subscribe-btn" class="plus">
s'inscrire

View file

@ -100,8 +100,5 @@ $entryTopPos ??= 20;
</h1>
</a>
</header>
<?php snippet('nav') ?>
<?php if ($page->hasToc()): ?>
<?php snippet('panel-toc') ?>
<?php endif ?>
<?php snippet('side-panel') ?>
<div id="nav-overlay"></div>

View file

@ -1,22 +0,0 @@
<nav class="panel panel-text" data-panel="text" x-data="{search: ''}">
<header>
<p class="sort-btns">
<button class="sort-btn sort-btn--years active">années</span></button>
<button class="sort-btn sort-btn--categories">catégories</button>
<button class="sort-btn sort-btn--all">voir tout</button>
</p>
<div class="search">
<input class="search__input" type="text" placeholder="Chercher" x-model="search">
<div class="search__icon"></div>
<button x-show="search.length > 0" @click="search = ''" class="search__icon">
<img
src="<?= url('assets/images/icons/close.svg') ?>"
alt="">
</button>
</div>
</header>
<?php snippet('nav__texts--all') ?>
<?php snippet('nav__texts--collection', ['collection' => 'categories']) ?>
<?php snippet('nav__texts--collection', ['collection' => 'years']) ?>
<button class="less panel-close">textes</button>
</nav>

View file

@ -1,4 +0,0 @@
<div class="panel panel-toc" data-panel="toc">
<?php snippet('toc') ?>
<button class="less panel-close">table des matières</button>
</div>

View file

@ -0,0 +1,31 @@
<div class="side-panel panel" data-panel="side-panel">
<div class="side-panel__view" data-view="nav" x-data="{search: ''}">
<header>
<p class="sort-btns">
<button class="sort-btn sort-btn--years active">années</span></button>
<button class="sort-btn sort-btn--categories">catégories</button>
<button class="sort-btn sort-btn--all">voir tout</button>
</p>
<div class="search">
<input class="search__input" type="text" placeholder="Chercher" x-model="search">
<div class="search__icon"></div>
<button x-show="search.length > 0" @click="search = ''" class="search__icon">
<img
src="<?= url('assets/images/icons/close.svg') ?>"
alt="">
</button>
</div>
</header>
<?php snippet('nav__texts--all') ?>
<?php snippet('nav__texts--collection', ['collection' => 'categories']) ?>
<?php snippet('nav__texts--collection', ['collection' => 'years']) ?>
</div>
<?php if ($page->hasToc()): ?>
<div class="side-panel__view hidden" data-view="toc">
<?php snippet('toc') ?>
</div>
<?php endif ?>
<button class="less panel-close">fermer</button>
</div>

View file

@ -1,5 +1,5 @@
<nav class="toc">
<div class="light toc_label">table des matières</div>
<div class="light toc_label">sommaire</div>
<ul>
<?php foreach ($page->tocItems() as $item): ?>
<li>

View file

@ -16,7 +16,7 @@
class="oggle-btn toggle-btn--left"
>éditorial</p>
</a>
<button class="plus open-nav" data-open-panel="text" title="ouvrir la navigation">textes</button>
<button class="plus" data-open-panel="side-panel" data-view="nav" title="ouvrir la navigation">textes</button>
<?php endslot() ?>
<?php endsnippet() ?>
<div id="main-edito" id="main-content">