3 Commits

Author SHA1 Message Date
Jules Neny
e90a7e12ef feat(v13-c): header 1-line cliquables + phrase intention Roboto Condensed 2026-05-11 19:58:07 +02:00
Jules Neny
e43ec60ecf merge(v13-p0): iframe AEP skeleton timeout fallback fix loading stuck 2026-05-11 19:53:54 +02:00
Jules Neny
1033099663 fix(v13-p0): iframe AEP skeleton timeout fallback (resolves loading stuck)
Le @load event ne fire pas (ou tardivement) sur l'iframe AEP enfermee
dans un wrapper avec transform scale(0.42) + viewport simulee 1440px.
Resultat : skeleton 'Chargement de la carte AEP...' reste affiche
indefiniment, masquant l'iframe meme si elle se charge.

Fix :
- setTimeout 2.5s dans onMounted qui force revealIframe() inconditionnellement
- onIframeLoad clear le timer si l'event fire dans les temps (cas nominal)
- retrait du z-10 sur le skeleton (defense en profondeur : si bug residuel,
  l'iframe sera quand meme visible derriere)
- factorisation revealIframe() partagee entre @load et fallback
- cleanup du timer dans onUnmounted

Build SSR : 5 pages, 0 warning, ~4s.
Tests browser manuels a faire par Jules pour confirmer disparition skeleton.
2026-05-11 19:52:57 +02:00
4 changed files with 94 additions and 36 deletions

10
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@astrojs/node": "^10.1.0", "@astrojs/node": "^10.1.0",
"@astrojs/vue": "^6.0.1", "@astrojs/vue": "^6.0.1",
"@fontsource-variable/roboto-condensed": "^5.2.8",
"@tailwindcss/vite": "^4.2.4", "@tailwindcss/vite": "^4.2.4",
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"astro": "^6.3.1", "astro": "^6.3.1",
@@ -1064,6 +1065,15 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/@fontsource-variable/roboto-condensed": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource-variable/roboto-condensed/-/roboto-condensed-5.2.8.tgz",
"integrity": "sha512-aIZ2kYSoJHkTI4z8x/PRgKX6Zb9TTtSE/u+fUYeiwL+5trP9rhYYEEeNjRttaMqRgoDHcSueArdRZ43wf/i2Kw==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@img/colour": { "node_modules/@img/colour": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",

View File

@@ -17,6 +17,7 @@
"dependencies": { "dependencies": {
"@astrojs/node": "^10.1.0", "@astrojs/node": "^10.1.0",
"@astrojs/vue": "^6.0.1", "@astrojs/vue": "^6.0.1",
"@fontsource-variable/roboto-condensed": "^5.2.8",
"@tailwindcss/vite": "^4.2.4", "@tailwindcss/vite": "^4.2.4",
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"astro": "^6.3.1", "astro": "^6.3.1",

View File

@@ -1,52 +1,80 @@
--- ---
// SiteHeader.astro - V1.2-M : bandeau header pleine largeur identite site // SiteHeader.astro - V1.3-C : header 1 ligne fine, liens cliquables, phrase intention Roboto Condensed
// Palette terre figee : papier #FAFAF7, encre #0F172A, encre douce #475569 // Palette V1.3 figee : papier #FAFAF7, encre #0F172A, encre douce #475569, border #E5E7EB
// Composition retenue : 2 lignes hierarchique // Composition :
// ligne 1 : "Trans-Former" wordmark dominant (semibold tracking serre) // Desktop (>= md) : 1 ligne ~44px - Trans-Former | Jules Neny | architecture d'ecologie politique [phrase intention right-aligned, Roboto Condensed]
// ligne 2 : "Jules Neny" + baseline italique cote a cote (separateur point median) // Mobile (< md) : 2 lignes compactes - ligne 1 Trans-Former / ligne 2 Jules Neny . AEP (cliquables) - phrase intention masquee
// Rationale : le wordmark domine sans ecraser ; la baseline reste lisible ; // Liens :
// composition adaptee a un manifeste (hierarchie typographique forte). // Trans-Former -> /
// Hauteur : ~64px desktop / ~48px mobile (compacte) // Jules Neny -> /a-propos
// Baseline raccourcie mobile : "architecture politique du vivant" // architecture d'ecologie politique -> https://aep.trans-former.fr (same-tab, site frere coherent)
// Typo phrase intention : Roboto Condensed Variable @fontsource (weight 400, font-stretch 75%)
import '@fontsource-variable/roboto-condensed/wght.css';
--- ---
<header <header
class="site-header w-full border-b border-[#E5E7EB] bg-[#FAFAF7] text-[#0F172A] px-4 md:px-6 flex items-center" class="site-header w-full border-b border-[#E5E7EB] bg-[#FAFAF7] text-[#0F172A] px-4 md:px-6 flex items-center"
role="banner" role="banner"
> >
<!-- Bloc identite (gauche) -->
<nav
class="flex flex-col md:flex-row md:items-baseline gap-x-2 gap-y-0.5 flex-shrink-0"
aria-label="identite site"
>
<!-- Ligne 1 desktop = wordmark inline ; mobile = ligne dediee -->
<a <a
href="/" href="/"
class="flex flex-col md:flex-row md:items-baseline gap-x-3 gap-y-0 no-underline text-[#0F172A] hover:text-[#0F172A]" class="font-semibold tracking-tight text-[#0F172A] hover:text-[#0F172A] text-[15px] md:text-[16px] leading-none no-underline hover:underline underline-offset-2 decoration-1"
aria-label="trans-former.fr - retour accueil" aria-label="trans-former.fr - accueil"
>
<!-- Ligne 1 : wordmark dominant -->
<span
class="font-semibold tracking-tight text-[#0F172A] text-[17px] md:text-[20px] leading-none"
> >
Trans-Former Trans-Former
</span>
<!-- Ligne 2 (desktop : inline ; mobile : sous wordmark) : Jules Neny + baseline -->
<span class="flex items-baseline gap-2 text-[#475569] leading-none">
<span class="text-[11px] md:text-[13px]">Jules Neny</span>
<span class="text-[#94A3B8]" aria-hidden="true">·</span>
<!-- Baseline longue : desktop / Baseline courte : mobile -->
<span class="italic text-[11px] md:text-[13px] hidden sm:inline">
architecture d'ecologie politique
</span>
<span class="italic text-[11px] inline sm:hidden">
architecture politique du vivant
</span>
</span>
</a> </a>
<!-- Bloc secondaire : Jules Neny . AEP (mobile : ligne 2 ; desktop : inline) -->
<span class="flex items-baseline gap-1.5 text-[#475569] leading-none">
<span class="text-[#94A3B8] hidden md:inline" aria-hidden="true">·</span>
<a
href="/a-propos"
class="text-[12px] md:text-[13px] text-[#475569] hover:text-[#0F172A] no-underline hover:underline underline-offset-2 decoration-1"
>
Jules Neny
</a>
<span class="text-[#94A3B8]" aria-hidden="true">·</span>
<a
href="https://aep.trans-former.fr"
class="text-[12px] md:text-[13px] text-[#475569] hover:text-[#0F172A] no-underline hover:underline underline-offset-2 decoration-1"
>
architecture d'écologie politique
</a>
</span>
</nav>
<!-- Phrase intention (droite, desktop only) - Roboto Condensed allongee -->
<p
class="intention hidden md:block ml-auto pl-6 text-right text-[#475569] text-[11px] leading-tight max-w-[55%]"
>
Comment créer une pratique systémique, créative et collective de transformation sociale pour répondre à l'effondrement et restaurer notre capacité à habiter la Terre dans l'Anthropocène&nbsp;?
</p>
</header> </header>
<style> <style>
.site-header { .site-header {
height: 48px; height: 44px;
} }
@media (min-width: 768px) { @media (max-width: 767px) {
.site-header { .site-header {
height: 64px; height: auto;
min-height: 48px;
padding-top: 6px;
padding-bottom: 6px;
} }
} }
/* Phrase intention : Roboto Condensed Variable, font-stretch 75% (allongement vertical condense)
Cantonnee a .intention pour eviter contagion stack Inter */
.intention {
font-family: 'Roboto Condensed Variable', 'Roboto Condensed', sans-serif;
font-weight: 400;
font-stretch: 75%;
font-style: italic;
letter-spacing: 0.01em;
}
</style> </style>

View File

@@ -23,24 +23,43 @@ const iframeStyle = computed(() => ({
transformOrigin: '0 0', transformOrigin: '0 0',
})) }))
let fallbackTimer: ReturnType<typeof setTimeout> | null = null
const revealIframe = () => {
if (iframeRef.value) {
iframeRef.value.classList.remove('opacity-0')
iframeRef.value.classList.add('opacity-100')
}
skeletonHidden.value = true
}
onMounted(() => { onMounted(() => {
if (wrapperRef.value && typeof ResizeObserver !== 'undefined') { if (wrapperRef.value && typeof ResizeObserver !== 'undefined') {
updateScale() updateScale()
resizeObs = new ResizeObserver(updateScale) resizeObs = new ResizeObserver(updateScale)
resizeObs.observe(wrapperRef.value) resizeObs.observe(wrapperRef.value)
} }
// Fallback : si @load ne fire pas dans 2.5s (transform/scale peut bloquer l'event),
// on revele quand meme l'iframe pour ne pas laisser le skeleton infini.
fallbackTimer = setTimeout(() => {
revealIframe()
}, 2500)
}) })
onUnmounted(() => { onUnmounted(() => {
resizeObs?.disconnect() resizeObs?.disconnect()
resizeObs = null resizeObs = null
if (fallbackTimer) {
clearTimeout(fallbackTimer)
fallbackTimer = null
}
}) })
const onIframeLoad = () => { const onIframeLoad = () => {
if (iframeRef.value) { if (fallbackTimer) {
iframeRef.value.classList.remove('opacity-0') clearTimeout(fallbackTimer)
iframeRef.value.classList.add('opacity-100') fallbackTimer = null
} }
skeletonHidden.value = true revealIframe()
} }
</script> </script>
@@ -51,7 +70,7 @@ const onIframeLoad = () => {
<div <div
v-if="!skeletonHidden" v-if="!skeletonHidden"
id="embed-skeleton" id="embed-skeleton"
class="absolute inset-0 flex items-center justify-center bg-neutral-50 animate-pulse z-10" class="absolute inset-0 flex items-center justify-center bg-neutral-50 animate-pulse"
> >
<span class="text-neutral-400 text-sm">Chargement de la carte AEP...</span> <span class="text-neutral-400 text-sm">Chargement de la carte AEP...</span>
</div> </div>