feat(nav): barre par échelles S / M / XL, tiroir < 1280, fil d'échelle mobile
- NavEchelles : trois groupes (accroche pastille + titre, <ul> étiqueté, aria-current) ≥ 1280 ; groupe courant seul 768-1279 ; lu depuis utils/echelles.ts - NavTiroir remplace le menu déroulant : tiroir à droite, voile, Échap, focus piégé et rendu, cibles 44 px ; bouton « Menu » masqué ≥ 1280 - FilEchelle < 768, dans le flux avant le conteneur de page - Soutenir et À propos dans la barre ≥ 1280 ; Signaler quitte la barre - barre de recherche d'en-tête retirée (/ garde sa recherche dans MobileSheet à 768 et 1000, vérifié au DOM) - html lang="fr" (Lighthouse html-has-lang) B10 M2-M4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBu7CSLy7PJgL4HJaV5oHP
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7762b31f1d
commit
79424d29cf
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-screen overflow-hidden" :class="{ dark: isDark }" style="background: var(--nav-bg);">
|
<div class="flex flex-col h-screen overflow-hidden" :class="{ dark: isDark }" style="background: var(--nav-bg);">
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════ TOP NAV GLOBAL -->
|
<!-- ═══════════════════════════════════════ TOP NAV GLOBAL (B10 : échelles S / M / XL) -->
|
||||||
<header
|
<header
|
||||||
class="flex items-center justify-between px-4 py-2.5 shrink-0 relative z-[9999] shadow-sm gap-3"
|
class="app-header flex items-center px-4 shrink-0 relative z-[9999] shadow-sm gap-3"
|
||||||
style="background: var(--nav-surface); border-bottom: 1px solid var(--nav-bg-alt);"
|
style="background: var(--nav-surface); border-bottom: 1px solid var(--nav-bg-alt);"
|
||||||
>
|
>
|
||||||
<!-- Logo -->
|
<!-- Logo : texte complet sauf 768-1023 et 1280-1439 (place des onglets) -->
|
||||||
<a href="/" class="logo-link flex items-center gap-2 hover:opacity-90 transition-opacity shrink-0" title="Architecture d'Écologie Politique">
|
<a href="/" class="logo-link flex items-center gap-2 hover:opacity-90 transition-opacity shrink-0" title="Architecture d'Écologie Politique">
|
||||||
<div
|
<div
|
||||||
class="h-8 px-2 rounded-lg flex items-center justify-center shrink-0"
|
class="h-8 px-2 rounded-lg flex items-center justify-center shrink-0"
|
||||||
@@ -20,112 +20,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- ── Onglets desktop (≥1024px) — remplace la barre de recherche ── -->
|
<!-- Échelles : trois groupes ≥ 1280, groupe courant 768-1279, rien < 768 (fil d'échelle) -->
|
||||||
<nav class="hidden lg:flex flex-1 justify-center items-end gap-0 mx-6" aria-label="Navigation projets">
|
<NavEchelles />
|
||||||
<NuxtLink
|
<div class="flex-1 md:hidden" aria-hidden="true" />
|
||||||
to="/outils"
|
|
||||||
class="nav-tab"
|
|
||||||
:class="{ 'nav-tab--active': route.path === '/outils' }"
|
|
||||||
>
|
|
||||||
Outils
|
|
||||||
</NuxtLink>
|
|
||||||
<NuxtLink
|
|
||||||
to="/"
|
|
||||||
class="nav-tab"
|
|
||||||
:class="{ 'nav-tab--active': route.path === '/' }"
|
|
||||||
>
|
|
||||||
Écosystème Entraide
|
|
||||||
</NuxtLink>
|
|
||||||
<NuxtLink
|
|
||||||
to="/agences"
|
|
||||||
class="nav-tab"
|
|
||||||
:class="{ 'nav-tab--active': route.path === '/agences' }"
|
|
||||||
>
|
|
||||||
Réseaux AEP
|
|
||||||
</NuxtLink>
|
|
||||||
<NuxtLink
|
|
||||||
to="/trouver-du-taf"
|
|
||||||
class="nav-tab"
|
|
||||||
:class="{ 'nav-tab--active': route.path === '/trouver-du-taf' }"
|
|
||||||
>
|
|
||||||
Jobs
|
|
||||||
</NuxtLink>
|
|
||||||
<NuxtLink
|
|
||||||
to="/codev"
|
|
||||||
class="nav-tab"
|
|
||||||
:class="{ 'nav-tab--active': route.path.startsWith('/codev') }"
|
|
||||||
>
|
|
||||||
Codev
|
|
||||||
</NuxtLink>
|
|
||||||
<NuxtLink
|
|
||||||
to="/media"
|
|
||||||
class="nav-tab"
|
|
||||||
:class="{ 'nav-tab--active': route.path.startsWith('/media') }"
|
|
||||||
>
|
|
||||||
recherche-média
|
|
||||||
</NuxtLink>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- ── Barre recherche mobile (640px–1024px) — masquée < 640px car accessible dans la sheet -->
|
|
||||||
<div class="hidden sm:flex lg:hidden flex-1 mx-2">
|
|
||||||
<label class="flex items-center gap-2 w-full px-3 py-1.5 rounded-xl border" style="background: var(--nav-bg); border-color: var(--nav-bg-alt);">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="color: var(--nav-text-muted); flex-shrink: 0;">
|
|
||||||
<circle cx="11" cy="11" r="8"/>
|
|
||||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
|
||||||
</svg>
|
|
||||||
<input
|
|
||||||
v-model="headerSearch"
|
|
||||||
type="search"
|
|
||||||
placeholder="Rechercher…"
|
|
||||||
class="flex-1 bg-transparent border-0 outline-none text-sm"
|
|
||||||
style="color: var(--nav-text); font-family: var(--nav-font);"
|
|
||||||
autocomplete="off"
|
|
||||||
@input="onHeaderSearch"
|
|
||||||
@keydown.enter="onHeaderSearch"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
v-if="headerSearch"
|
|
||||||
type="button"
|
|
||||||
@click.stop="clearHeaderSearch"
|
|
||||||
class="flex-shrink-0"
|
|
||||||
style="color: var(--nav-text-muted);"
|
|
||||||
aria-label="Effacer la recherche"
|
|
||||||
>
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
|
|
||||||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Actions droite -->
|
<!-- Actions droite -->
|
||||||
<div class="flex items-center gap-2 shrink-0">
|
<div class="flex items-center gap-2 shrink-0">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/manifeste"
|
to="/manifeste"
|
||||||
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all hover:opacity-80 hidden md:inline-flex items-center gap-1"
|
class="header-lien hidden md:inline-flex"
|
||||||
:style="route.path === '/manifeste' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text-muted);'"
|
:aria-current="route.path === '/manifeste' ? 'page' : undefined"
|
||||||
>
|
>
|
||||||
Manifeste
|
Manifeste
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/a-propos"
|
to="/a-propos"
|
||||||
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all hover:opacity-80 hidden md:inline-flex items-center gap-1"
|
class="header-lien header-xl"
|
||||||
style="color: var(--nav-text-muted);"
|
:aria-current="route.path === '/a-propos' ? 'page' : undefined"
|
||||||
>
|
>
|
||||||
À propos
|
À propos
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink
|
<a
|
||||||
to="/signaler"
|
:href="SOUTENIR_URL"
|
||||||
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all hover:opacity-80 hidden lg:inline-flex items-center gap-1"
|
target="_blank"
|
||||||
style="color: var(--nav-text-muted);"
|
rel="noopener noreferrer"
|
||||||
|
class="header-soutenir header-xl"
|
||||||
>
|
>
|
||||||
Signaler
|
Soutenir
|
||||||
</NuxtLink>
|
</a>
|
||||||
<!-- Proposer une ressource (5 onglets) -->
|
<!-- Proposer une ressource -->
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/proposer"
|
to="/proposer"
|
||||||
class="px-3 py-1.5 rounded-lg text-sm font-semibold transition-all hover:opacity-80 hidden sm:inline-flex items-center gap-1"
|
class="header-proposer hidden md:inline-flex"
|
||||||
style="background: var(--nav-accent); color: var(--nav-text);"
|
|
||||||
>
|
>
|
||||||
+ Proposer
|
+ Proposer
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
@@ -153,68 +79,51 @@
|
|||||||
<!-- Mobile : proposer icône -->
|
<!-- Mobile : proposer icône -->
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/proposer"
|
to="/proposer"
|
||||||
class="sm:hidden p-2 rounded-lg"
|
class="md:hidden p-2 rounded-lg"
|
||||||
style="background: var(--nav-accent); color: var(--nav-text);"
|
style="background: var(--accent); color: var(--on-accent);"
|
||||||
title="Proposer une ressource"
|
title="Proposer une ressource"
|
||||||
aria-label="Proposer"
|
aria-label="Proposer"
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||||||
</svg>
|
</svg>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<!-- Menu déroulant — toutes largeurs (les onglets n'y apparaissent qu'en dessous de 1024px) ; porte « Soutenir » -->
|
<!-- Menu (< 1280) : ouvre le tiroir ; le mot est écrit à partir de 768 -->
|
||||||
<div class="relative">
|
|
||||||
<button
|
<button
|
||||||
@click="hamburgerOpen = !hamburgerOpen"
|
type="button"
|
||||||
class="p-2 rounded-lg transition-all hover:opacity-80"
|
class="header-menu"
|
||||||
style="background: var(--nav-bg-alt); color: var(--nav-text-muted);"
|
aria-label="Ouvrir le menu"
|
||||||
:aria-label="hamburgerOpen ? 'Fermer le menu' : 'Menu'"
|
aria-haspopup="dialog"
|
||||||
:aria-expanded="hamburgerOpen"
|
:aria-expanded="menuOpen"
|
||||||
|
@click="menuOpen = true"
|
||||||
>
|
>
|
||||||
<svg v-if="!hamburgerOpen" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
||||||
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
|
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
|
||||||
</svg>
|
</svg>
|
||||||
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
<span class="header-menu__mot" aria-hidden="true">Menu</span>
|
||||||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
v-if="hamburgerOpen"
|
|
||||||
class="absolute right-0 top-full mt-1 rounded-lg shadow-lg min-w-[210px] py-1"
|
|
||||||
style="background: var(--nav-surface); border: 1px solid var(--nav-bg-alt); z-index: 9999;"
|
|
||||||
@click="hamburgerOpen = false"
|
|
||||||
>
|
|
||||||
<NuxtLink to="/outils" class="lg:hidden block px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70" :style="route.path === '/outils' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Outils</NuxtLink>
|
|
||||||
<NuxtLink to="/" class="lg:hidden block px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70" :style="route.path === '/' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Écosystème Entraide</NuxtLink>
|
|
||||||
<NuxtLink to="/agences" class="lg:hidden block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path === '/agences' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Réseaux AEP</NuxtLink>
|
|
||||||
<NuxtLink to="/trouver-du-taf" class="lg:hidden block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path === '/trouver-du-taf' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Jobs</NuxtLink>
|
|
||||||
<NuxtLink to="/media" class="lg:hidden block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path.startsWith('/media') ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">recherche-média</NuxtLink>
|
|
||||||
<NuxtLink to="/codev" class="lg:hidden block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path.startsWith('/codev') ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Codev</NuxtLink>
|
|
||||||
<div class="lg:hidden" style="height: 1px; background: var(--nav-bg-alt); margin: 4px 0;"></div>
|
|
||||||
<NuxtLink to="/manifeste" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path === '/manifeste' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text-muted);'">Manifeste</NuxtLink>
|
|
||||||
<NuxtLink to="/a-propos" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" style="color: var(--nav-text-muted);">À propos</NuxtLink>
|
|
||||||
<a href="https://liberapay.com/trans-former.fr/donate" target="_blank" rel="noopener noreferrer" class="block mx-3 my-2 px-4 py-2 rounded-lg text-sm font-semibold text-center transition-opacity hover:opacity-80" style="background: var(--nav-accent); color: var(--nav-text);">Soutenir</a>
|
|
||||||
<NuxtLink to="/signaler" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" style="color: var(--nav-text-muted);">Signaler</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- Fil d'échelle (< 768) : dans le flux de la colonne, hors du conteneur de page -->
|
||||||
|
<FilEchelle />
|
||||||
|
|
||||||
<!-- Contenu page (flex-1 pour remplir l'espace) -->
|
<!-- Contenu page (flex-1 pour remplir l'espace) -->
|
||||||
<div class="flex-1" :class="route.path === '/' ? 'overflow-hidden' : 'overflow-y-auto'">
|
<div class="flex-1 min-h-0" :class="route.path === '/' ? 'overflow-hidden' : 'overflow-y-auto'">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<NavTiroir :open="menuOpen" @close="menuOpen = false" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const router = useRouter()
|
import { SOUTENIR_URL } from '~/utils/liens'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const hamburgerOpen = ref(false)
|
const menuOpen = ref(false)
|
||||||
watch(() => route.path, () => { hamburgerOpen.value = false })
|
|
||||||
|
|
||||||
// ── Dark mode ─────────────────────────────────────────────────────────────
|
// ── Dark mode ─────────────────────────────────────────────────────────────
|
||||||
const isDark = ref(false)
|
const isDark = ref(false)
|
||||||
@@ -237,40 +146,14 @@ function toggleDark() {
|
|||||||
localStorage.setItem('aep_theme', 'light')
|
localStorage.setItem('aep_theme', 'light')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Barre de recherche header mobile ─────────────────────────────────────
|
|
||||||
const headerSearch = ref((route.query.q as string) ?? '')
|
|
||||||
|
|
||||||
// Sync depuis URL quand la route change
|
|
||||||
watch(() => route.query.q, (v) => {
|
|
||||||
headerSearch.value = (v as string) ?? ''
|
|
||||||
})
|
|
||||||
|
|
||||||
function onHeaderSearch() {
|
|
||||||
const q = headerSearch.value.trim()
|
|
||||||
if (route.path === '/') {
|
|
||||||
router.replace({ query: q ? { ...route.query, q } : { ...route.query, q: undefined } })
|
|
||||||
} else if (q) {
|
|
||||||
router.push({ path: '/', query: { q } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearHeaderSearch() {
|
|
||||||
headerSearch.value = ''
|
|
||||||
if (route.path === '/') {
|
|
||||||
const q = { ...route.query }
|
|
||||||
delete q.q
|
|
||||||
router.replace({ query: Object.keys(q).length ? q : undefined })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Fiche aléatoire ───────────────────────────────────────────────────────
|
|
||||||
function goRandom() {
|
|
||||||
router.push({ path: '/', query: { random: '1' } })
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* ── En-tête ──────────────────────────────────────────────────────────── */
|
||||||
|
.app-header {
|
||||||
|
min-height: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Logo header (texte 2 lignes) ─────────────────────────────────────── */
|
/* ── Logo header (texte 2 lignes) ─────────────────────────────────────── */
|
||||||
.logo-text {
|
.logo-text {
|
||||||
line-height: 1.05;
|
line-height: 1.05;
|
||||||
@@ -285,39 +168,96 @@ function goRandom() {
|
|||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.logo-line-1, .logo-line-2 { font-size: 0.85rem; }
|
.logo-line-1, .logo-line-2 { font-size: 0.85rem; }
|
||||||
}
|
}
|
||||||
|
/* Le groupe courant (768-1023) et les trois groupes (1280-1439) prennent la place du texte */
|
||||||
|
@media (min-width: 768px) and (max-width: 1023px), (min-width: 1280px) and (max-width: 1439px) {
|
||||||
|
.logo-text { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Onglets header desktop ───────────────────────────────────────────── */
|
/* ── Liens et boutons de droite ───────────────────────────────────────── */
|
||||||
.nav-tab {
|
.header-lien {
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 6px 16px 8px;
|
padding: 6px 8px;
|
||||||
font-size: 0.8rem;
|
border-radius: var(--radius-md);
|
||||||
font-weight: 500;
|
font-size: var(--fs-sm);
|
||||||
color: var(--nav-text-muted);
|
font-weight: var(--fw-medium);
|
||||||
|
white-space: nowrap;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.15s;
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
border-bottom: 2px solid transparent;
|
}
|
||||||
gap: 2px;
|
.header-lien:hover,
|
||||||
|
.header-lien[aria-current="page"] {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.header-lien[aria-current="page"] {
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tab:hover {
|
.header-soutenir {
|
||||||
color: var(--nav-text);
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1.5px solid var(--accent);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: var(--fs-sm);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tab--active {
|
.header-proposer {
|
||||||
color: var(--nav-text);
|
align-items: center;
|
||||||
border-bottom-color: var(--nav-primary-solid);
|
padding: 6px 11px;
|
||||||
font-weight: 600;
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--on-accent);
|
||||||
|
font-size: var(--fs-sm);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tab-badge {
|
.header-soutenir:hover,
|
||||||
font-size: 0.6rem;
|
.header-proposer:hover {
|
||||||
font-weight: 600;
|
opacity: 0.85;
|
||||||
text-transform: uppercase;
|
}
|
||||||
letter-spacing: 0.04em;
|
|
||||||
color: var(--nav-text-muted);
|
.header-lien:focus-visible,
|
||||||
opacity: 0.65;
|
.header-soutenir:focus-visible,
|
||||||
|
.header-proposer:focus-visible,
|
||||||
|
.header-menu:focus-visible {
|
||||||
|
outline: 2px solid var(--focus-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* À propos et Soutenir : barre complète seulement (≥ 1280), sinon dans le tiroir */
|
||||||
|
.header-xl { display: none; }
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.header-xl { display: inline-flex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-menu {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--bg-alt);
|
||||||
|
color: var(--text);
|
||||||
|
font-size: var(--fs-sm);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.header-menu__mot { display: none; }
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.header-menu {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-color: var(--border);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.header-menu__mot { display: inline; }
|
||||||
|
}
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.header-menu { display: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<!--
|
||||||
|
FilEchelle — B10-M4
|
||||||
|
< 768 px : une ligne de 28 px sous la barre, « S · Partir de soi · Outils · Jobs » :
|
||||||
|
l'échelle courante, l'onglet courant, puis ses voisins du groupe (liens).
|
||||||
|
Absente sur les pages hors groupe (Manifeste, À propos, Proposer, Signaler, fiche).
|
||||||
|
Montée dans le flux de la colonne d'app.vue, AVANT le conteneur de page : la carte
|
||||||
|
de / perd ces 28 px à vue, jamais en dessous de la ligne de flottaison.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<nav v-if="courant" class="fil-echelle" aria-label="Échelle courante">
|
||||||
|
<span class="fil-echelle__pastille">{{ courant.echelle }}</span>
|
||||||
|
<span class="fil-echelle__titre">{{ ECHELLES[courant.echelle].titre }}</span>
|
||||||
|
<span class="fil-echelle__sep" aria-hidden="true">·</span>
|
||||||
|
<span class="fil-echelle__courant" aria-current="page">{{ courant.onglet.libelle }}</span>
|
||||||
|
<template v-for="o in voisins" :key="o.to">
|
||||||
|
<span class="fil-echelle__sep" aria-hidden="true">·</span>
|
||||||
|
<NuxtLink :to="o.to" class="fil-echelle__lien">{{ o.libelle }}</NuxtLink>
|
||||||
|
</template>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ECHELLES, echelleDeRoute } from '~/utils/echelles'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const courant = computed(() => echelleDeRoute(route))
|
||||||
|
const voisins = computed(() =>
|
||||||
|
courant.value ? ECHELLES[courant.value.echelle].onglets.filter((o) => o !== courant.value!.onglet) : [],
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.fil-echelle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 var(--space-sm);
|
||||||
|
gap: 5px;
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
line-height: 1;
|
||||||
|
background: var(--surface);
|
||||||
|
border-bottom: 1px solid var(--bg-alt);
|
||||||
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fil-echelle::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fil-echelle__pastille {
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--on-accent);
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fil-echelle__courant {
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fil-echelle__lien {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--border);
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fil-echelle__lien:focus-visible {
|
||||||
|
outline: 2px solid var(--focus-ring);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.fil-echelle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
<!--
|
||||||
|
NavEchelles — B10-M2
|
||||||
|
Barre des trois échelles S / M / XL, lue depuis utils/echelles.ts (source unique).
|
||||||
|
≥ 1280 px : les trois groupes ; 768-1279 px : le seul groupe de la page courante ;
|
||||||
|
< 768 px : rien (FilEchelle + NavTiroir prennent le relais).
|
||||||
|
Chaque groupe = <ul> étiqueté par son accroche (pastille + titre de l'échelle).
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<nav class="nav-echelles" aria-label="Échelles">
|
||||||
|
<div
|
||||||
|
v-for="id in ORDRE_ECHELLES"
|
||||||
|
:key="id"
|
||||||
|
class="nav-echelles__grp"
|
||||||
|
:class="{ 'nav-echelles__grp--courant': courant?.echelle === id }"
|
||||||
|
>
|
||||||
|
<span :id="`nav-echelle-${id}`" class="nav-echelles__kick">
|
||||||
|
<span class="nav-echelles__pastille">{{ id }}</span>{{ ECHELLES[id].titre }}
|
||||||
|
</span>
|
||||||
|
<ul class="nav-echelles__tabs" :aria-labelledby="`nav-echelle-${id}`">
|
||||||
|
<li v-for="o in ECHELLES[id].onglets" :key="o.to">
|
||||||
|
<NuxtLink
|
||||||
|
:to="o.to"
|
||||||
|
class="nav-echelles__tab"
|
||||||
|
:class="{ 'nav-echelles__tab--actif': o.actif(route) }"
|
||||||
|
:aria-current="o.actif(route) ? 'page' : undefined"
|
||||||
|
>{{ o.libelle }}</NuxtLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ECHELLES, ORDRE_ECHELLES, echelleDeRoute } from '~/utils/echelles'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const courant = computed(() => echelleDeRoute(route))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.nav-echelles {
|
||||||
|
display: none;
|
||||||
|
align-self: stretch;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
gap: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__grp {
|
||||||
|
position: relative;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__kick {
|
||||||
|
padding: 0 var(--space-xs);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
line-height: 1.3;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__grp--courant .nav-echelles__kick {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__pastille {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--on-accent);
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__tabs {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__tab {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 9px 9px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: var(--fw-medium);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
transition: color var(--dur-fast) var(--ease-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__tab:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__tab:focus-visible {
|
||||||
|
outline: 2px solid var(--focus-ring);
|
||||||
|
outline-offset: -2px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-echelles__tab--actif {
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
border-bottom-color: var(--nav-primary-solid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 768-1279 : le seul groupe courant, aligné à gauche après le logo */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.nav-echelles {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.nav-echelles__grp--courant {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ≥ 1280 : les trois groupes, centrés, séparés par un filet */
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.nav-echelles {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.nav-echelles__grp {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.nav-echelles__grp + .nav-echelles__grp::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: calc(var(--space-lg) / -2);
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.nav-echelles__tab { transition: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
<!--
|
||||||
|
NavTiroir — B10-M3
|
||||||
|
Menu < 1280 px : tiroir pleine hauteur à droite, voile, Échap, focus piégé,
|
||||||
|
focus rendu à l'élément qui l'a ouvert. Les trois échelles dans l'ordre de la
|
||||||
|
barre (utils/echelles.ts), puis Manifeste, À propos, Signaler, et Soutenir.
|
||||||
|
Se ferme seul au changement de route et si la fenêtre passe à ≥ 1280 px.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<Teleport to="body">
|
||||||
|
<Transition name="nav-tiroir">
|
||||||
|
<div v-if="open" class="nav-tiroir" @keydown="onKeydown">
|
||||||
|
<div class="nav-tiroir__voile" aria-hidden="true" @click="fermer" />
|
||||||
|
<div
|
||||||
|
ref="panneau"
|
||||||
|
class="nav-tiroir__panneau"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="nav-tiroir-titre"
|
||||||
|
>
|
||||||
|
<div class="nav-tiroir__tete">
|
||||||
|
<h2 id="nav-tiroir-titre" class="nav-tiroir__titre">Menu</h2>
|
||||||
|
<button type="button" class="nav-tiroir__fermer" aria-label="Fermer le menu" @click="fermer">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-tiroir__corps" aria-label="Échelles et pages">
|
||||||
|
<p class="nav-tiroir__cadre">{{ ECHELLES_CADRE_COURT }}</p>
|
||||||
|
|
||||||
|
<div v-for="id in ORDRE_ECHELLES" :key="id" class="nav-tiroir__grp">
|
||||||
|
<span :id="`nav-tiroir-echelle-${id}`" class="nav-tiroir__kick">
|
||||||
|
<span class="nav-tiroir__pastille">{{ id }}</span>{{ ECHELLES[id].titre }}
|
||||||
|
</span>
|
||||||
|
<ul class="nav-tiroir__liste" :aria-labelledby="`nav-tiroir-echelle-${id}`">
|
||||||
|
<li v-for="o in ECHELLES[id].onglets" :key="o.to">
|
||||||
|
<NuxtLink
|
||||||
|
:to="o.to"
|
||||||
|
class="nav-tiroir__lien"
|
||||||
|
:class="{ 'nav-tiroir__lien--actif': o.actif(route) }"
|
||||||
|
:aria-current="o.actif(route) ? 'page' : undefined"
|
||||||
|
@click="fermer"
|
||||||
|
>{{ o.libelle }}</NuxtLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="nav-tiroir__sep" />
|
||||||
|
|
||||||
|
<ul class="nav-tiroir__liste">
|
||||||
|
<li v-for="p in PAGES" :key="p.to">
|
||||||
|
<NuxtLink
|
||||||
|
:to="p.to"
|
||||||
|
class="nav-tiroir__second"
|
||||||
|
:aria-current="route.path === p.to ? 'page' : undefined"
|
||||||
|
@click="fermer"
|
||||||
|
>{{ p.libelle }}</NuxtLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a :href="SOUTENIR_URL" target="_blank" rel="noopener noreferrer" class="nav-tiroir__soutenir">Soutenir</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ECHELLES, ECHELLES_CADRE_COURT, ORDRE_ECHELLES } from '~/utils/echelles'
|
||||||
|
import { SOUTENIR_URL } from '~/utils/liens'
|
||||||
|
|
||||||
|
const props = defineProps<{ open: boolean }>()
|
||||||
|
const emit = defineEmits<{ close: [] }>()
|
||||||
|
|
||||||
|
const PAGES = [
|
||||||
|
{ libelle: 'Manifeste', to: '/manifeste' },
|
||||||
|
{ libelle: 'À propos', to: '/a-propos' },
|
||||||
|
{ libelle: 'Signaler', to: '/signaler' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const panneau = ref<HTMLElement | null>(null)
|
||||||
|
let retourFocus: HTMLElement | null = null
|
||||||
|
|
||||||
|
function fermer() {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusables(): HTMLElement[] {
|
||||||
|
if (!panneau.value) return []
|
||||||
|
return [...panneau.value.querySelectorAll<HTMLElement>('a[href], button:not([disabled])')]
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault()
|
||||||
|
fermer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.key !== 'Tab') return
|
||||||
|
const els = focusables()
|
||||||
|
if (!els.length) return
|
||||||
|
const premier = els[0]
|
||||||
|
const dernier = els[els.length - 1]
|
||||||
|
if (e.shiftKey && document.activeElement === premier) {
|
||||||
|
e.preventDefault()
|
||||||
|
dernier.focus()
|
||||||
|
} else if (!e.shiftKey && document.activeElement === dernier) {
|
||||||
|
e.preventDefault()
|
||||||
|
premier.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.open, async (ouvert) => {
|
||||||
|
if (ouvert) {
|
||||||
|
retourFocus = document.activeElement as HTMLElement | null
|
||||||
|
await nextTick()
|
||||||
|
focusables()[0]?.focus()
|
||||||
|
} else if (retourFocus) {
|
||||||
|
// Le bouton d'ouverture peut ne plus être visible (fenêtre élargie) : focus() est alors sans effet.
|
||||||
|
retourFocus.focus()
|
||||||
|
retourFocus = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => route.fullPath, () => { if (props.open) fermer() })
|
||||||
|
|
||||||
|
let mq: MediaQueryList | null = null
|
||||||
|
function onLarge(e: MediaQueryListEvent) { if (e.matches && props.open) fermer() }
|
||||||
|
onMounted(() => {
|
||||||
|
mq = window.matchMedia('(min-width: 1280px)')
|
||||||
|
mq.addEventListener('change', onLarge)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => mq?.removeEventListener('change', onLarge))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.nav-tiroir {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__voile {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(var(--nav-primary-raw), 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__panneau {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: min(320px, 88vw);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--surface);
|
||||||
|
color: var(--text);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__tete {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: var(--space-xs) var(--space-sm) var(--space-xs) var(--space-md);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__titre {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--fs-sm);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__fermer {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__corps {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--space-sm) var(--space-md) var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__cadre {
|
||||||
|
margin: 0 0 var(--space-xs);
|
||||||
|
font-size: var(--fs-xs);
|
||||||
|
line-height: var(--lh-base);
|
||||||
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__grp {
|
||||||
|
margin-top: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__kick {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: var(--space-2xs);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__pastille {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 6px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--on-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__liste {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__lien,
|
||||||
|
.nav-tiroir__second {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 0 var(--space-xs);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__lien {
|
||||||
|
font-size: var(--fs-base);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__lien--actif {
|
||||||
|
background: var(--bg-alt);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__second {
|
||||||
|
font-size: var(--fs-sm);
|
||||||
|
color: color-mix(in srgb, var(--text) 78%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__lien:focus-visible,
|
||||||
|
.nav-tiroir__second:focus-visible,
|
||||||
|
.nav-tiroir__soutenir:focus-visible,
|
||||||
|
.nav-tiroir__fermer:focus-visible {
|
||||||
|
outline: 2px solid var(--focus-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__sep {
|
||||||
|
margin: var(--space-sm) 0;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir__soutenir {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 44px;
|
||||||
|
margin-top: var(--space-sm);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--on-accent);
|
||||||
|
font-size: var(--fs-sm);
|
||||||
|
font-weight: var(--fw-semibold);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tiroir-enter-active,
|
||||||
|
.nav-tiroir-leave-active {
|
||||||
|
transition: opacity var(--dur-base) var(--ease-out);
|
||||||
|
}
|
||||||
|
.nav-tiroir-enter-active .nav-tiroir__panneau,
|
||||||
|
.nav-tiroir-leave-active .nav-tiroir__panneau {
|
||||||
|
transition: transform var(--dur-base) var(--ease-out);
|
||||||
|
}
|
||||||
|
.nav-tiroir-enter-from,
|
||||||
|
.nav-tiroir-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.nav-tiroir-enter-from .nav-tiroir__panneau,
|
||||||
|
.nav-tiroir-leave-to .nav-tiroir__panneau {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.nav-tiroir-enter-active,
|
||||||
|
.nav-tiroir-leave-active,
|
||||||
|
.nav-tiroir-enter-active .nav-tiroir__panneau,
|
||||||
|
.nav-tiroir-leave-active .nav-tiroir__panneau {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,6 +3,7 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
app: {
|
app: {
|
||||||
head: {
|
head: {
|
||||||
|
htmlAttrs: { lang: 'fr' },
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
|
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
|
||||||
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32.png' },
|
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32.png' },
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/** Lien de don (Liberapay), partagé par la barre, le tiroir et À propos. */
|
||||||
|
export const SOUTENIR_URL = 'https://liberapay.com/trans-former.fr/donate'
|
||||||
Reference in New Issue
Block a user