Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
<script>
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
/**
|
|
|
|
|
* Vue article — sous-composant de Blog.svelte.
|
|
|
|
|
* Reçoit les données article via props, pas via le slide system.
|
|
|
|
|
*/
|
2026-03-17 10:00:53 +01:00
|
|
|
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
import { t } from '@i18n'
|
2026-03-20 11:57:21 +01:00
|
|
|
import { onMount } from 'svelte'
|
2026-03-18 12:14:59 +01:00
|
|
|
import Footer from '@components/layout/Footer.svelte'
|
2026-03-11 10:57:52 +01:00
|
|
|
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
let { data, onBack } = $props()
|
2026-03-10 19:19:05 +01:00
|
|
|
|
|
|
|
|
let copySuccess = $state(false)
|
|
|
|
|
let copyTimer = null
|
|
|
|
|
|
2026-03-20 13:51:37 +01:00
|
|
|
// Setup click-to-play iframes après le rendu du body HTML
|
|
|
|
|
$effect(() => {
|
|
|
|
|
if (!data.body) return
|
|
|
|
|
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
document.querySelectorAll('.iframe-game-container').forEach(container => {
|
|
|
|
|
if (container.dataset.initialized) return
|
|
|
|
|
container.dataset.initialized = 'true'
|
|
|
|
|
|
|
|
|
|
const overlay = container.querySelector('.iframe-click-overlay')
|
|
|
|
|
const iframe = container.querySelector('iframe')
|
|
|
|
|
const deactivateBtn = container.querySelector('.iframe-deactivate-btn')
|
|
|
|
|
|
|
|
|
|
overlay?.addEventListener('click', () => {
|
|
|
|
|
if (overlay.getAttribute('data-state') === 'ended') return
|
|
|
|
|
overlay.style.display = 'none'
|
|
|
|
|
iframe.style.pointerEvents = 'auto'
|
|
|
|
|
container.classList.add('game-active')
|
|
|
|
|
overlay.setAttribute('data-state', 'played')
|
2026-03-26 15:42:06 +01:00
|
|
|
container.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
2026-03-20 13:51:37 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
deactivateBtn?.addEventListener('click', (e) => {
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
overlay.style.display = 'flex'
|
|
|
|
|
overlay.setAttribute('data-state', 'initial')
|
|
|
|
|
iframe.style.pointerEvents = 'none'
|
|
|
|
|
container.classList.remove('game-active')
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}, 300)
|
|
|
|
|
|
|
|
|
|
return () => clearTimeout(timer)
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-20 11:57:21 +01:00
|
|
|
// Écoute les messages postMessage depuis l'iframe du jeu
|
|
|
|
|
onMount(() => {
|
|
|
|
|
const handleIframeMessage = (event) => {
|
|
|
|
|
if (!event.origin.includes('impact.games')) return
|
|
|
|
|
if (event.data.type !== 'GameReleaseFocus') return
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.iframe-click-overlay').forEach(overlay => {
|
|
|
|
|
if (overlay.getAttribute('data-state') === 'played') {
|
|
|
|
|
overlay.setAttribute('data-state', 'ended')
|
|
|
|
|
overlay.innerHTML = '<div class="overlay-content"></div>'
|
|
|
|
|
overlay.style.cursor = 'default'
|
|
|
|
|
overlay.style.pointerEvents = 'none'
|
|
|
|
|
}
|
|
|
|
|
overlay.style.display = 'flex'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.iframe-game-container').forEach(container => {
|
|
|
|
|
container.querySelector('iframe').style.pointerEvents = 'none'
|
|
|
|
|
container.classList.remove('game-active')
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.addEventListener('message', handleIframeMessage)
|
|
|
|
|
return () => window.removeEventListener('message', handleIframeMessage)
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-10 19:19:05 +01:00
|
|
|
function copyLink() {
|
|
|
|
|
navigator.clipboard.writeText(window.location.href)
|
|
|
|
|
copySuccess = true
|
|
|
|
|
clearTimeout(copyTimer)
|
|
|
|
|
copyTimer = setTimeout(() => { copySuccess = false }, 2000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const shareUrl = $derived(encodeURIComponent(window.location.href))
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
</script>
|
|
|
|
|
|
2026-03-12 11:38:01 +01:00
|
|
|
<div class="article-wrapper">
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
<article class="article">
|
|
|
|
|
|
2026-03-10 19:19:05 +01:00
|
|
|
<!-- Date + share buttons -->
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
<div class="article-topbar">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<time class="article-date">{t('published_on')} {data.published}</time>
|
2026-03-10 19:19:05 +01:00
|
|
|
<div class="article-share">
|
|
|
|
|
{#if copySuccess}
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<span class="copy-toast">{t('link_copied')}</span>
|
2026-03-10 19:19:05 +01:00
|
|
|
{/if}
|
|
|
|
|
<div class="share-buttons">
|
|
|
|
|
<!-- svelte-ignore a11y_invalid_attribute -->
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<a href="#" class="share-button" title={t('copy_link')} onclick={(e) => { e.preventDefault(); copyLink() }}>
|
|
|
|
|
<img src="/assets/icons/link-icon.svg" alt={t('copy_link')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://api.whatsapp.com/send?text={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/whatsapp-icon.svg" alt={t('share_whatsapp')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://twitter.com/intent/tweet?url={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/twitter-icon.svg" alt={t('share_x')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://www.facebook.com/sharer/sharer.php?u={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/facebook-icon.svg" alt={t('share_facebook')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://www.linkedin.com/sharing/share-offsite/?url={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/linkedin-icon.svg" alt={t('share_linkedin')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
</div>
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
|
|
|
|
|
<!-- Titre -->
|
|
|
|
|
<h1 class="article-title font-face-terminal">{data.title}</h1>
|
|
|
|
|
|
|
|
|
|
<!-- Intro -->
|
|
|
|
|
{#if data.intro}
|
|
|
|
|
<div class="article-intro">{@html data.intro}</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<!-- Cover -->
|
|
|
|
|
{#if data.cover}
|
|
|
|
|
<div class="article-cover">
|
|
|
|
|
<img src={data.cover} alt={data.title} />
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<!-- Body (Kirby blocks → HTML) -->
|
|
|
|
|
{#if data.body}
|
|
|
|
|
<div class="article-body">{@html data.body}</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
2026-03-10 19:19:05 +01:00
|
|
|
<!-- Share section (bas d'article) -->
|
|
|
|
|
<div class="article-share-section">
|
|
|
|
|
<hr class="share-divider" />
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<p class="share-label">{t('share_article')}</p>
|
2026-03-10 19:19:05 +01:00
|
|
|
<div class="share-buttons">
|
|
|
|
|
<!-- svelte-ignore a11y_invalid_attribute -->
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<a href="#" class="share-button" title={t('copy_link')} onclick={(e) => { e.preventDefault(); copyLink() }}>
|
|
|
|
|
<img src="/assets/icons/link-icon.svg" alt={t('copy_link')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://api.whatsapp.com/send?text={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/whatsapp-icon.svg" alt={t('share_whatsapp')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://twitter.com/intent/tweet?url={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/twitter-icon.svg" alt={t('share_x')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://www.facebook.com/sharer/sharer.php?u={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/facebook-icon.svg" alt={t('share_facebook')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
<a href="https://www.linkedin.com/sharing/share-offsite/?url={shareUrl}" target="_blank" rel="noopener noreferrer" class="share-button">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<img src="/assets/icons/linkedin-icon.svg" alt={t('share_linkedin')} width="20" height="20" />
|
2026-03-10 19:19:05 +01:00
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
<!-- Articles recommandés -->
|
|
|
|
|
{#if data.related?.length}
|
|
|
|
|
<section class="article-related">
|
Feat: intégration multilingue FR/EN (i18n)
- Ajout de src/i18n/index.js : dictionnaire centralisé + fonction t(key, vars)
- Ajout de LanguageSwitcher.svelte : toggle FR/EN avec persistance localStorage
- Router : normalizePath strip /en/, apiPrefix() pour les fetches, détection langue (URL > localStorage > navigator)
- Tous les composants (Header, Menu, Footer, Article, Blog, Play) migrent vers t() depuis @i18n
- Blog : navigation interne (fetch, history, getSlugFromUrl) locale-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:57:59 +01:00
|
|
|
<h2>{t('related')}</h2>
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
<div class="article-related-grid">
|
|
|
|
|
{#each data.related as rec}
|
|
|
|
|
<a href="/blog/{rec.slug}" class="article-related-card">
|
|
|
|
|
{#if rec.cover}
|
|
|
|
|
<img src={rec.cover} alt={rec.title} />
|
|
|
|
|
{/if}
|
|
|
|
|
<span class="article-related-title">{rec.title}</span>
|
|
|
|
|
</a>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
{/if}
|
|
|
|
|
</article>
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
|
2026-03-18 12:14:59 +01:00
|
|
|
<Footer />
|
2026-03-12 11:38:01 +01:00
|
|
|
</div>
|
2026-03-11 10:57:52 +01:00
|
|
|
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
<style>
|
2026-03-12 11:38:01 +01:00
|
|
|
.article-wrapper {
|
|
|
|
|
grid-area: 6 / 1 / span 15 / span 20;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
.article {
|
2026-03-12 11:38:01 +01:00
|
|
|
height: fit-content;
|
2026-03-10 19:19:05 +01:00
|
|
|
padding: 0 20%;
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
}
|
|
|
|
|
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
/* --- Topbar --- */
|
|
|
|
|
.article-topbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-date {
|
|
|
|
|
color: rgba(255, 255, 255, 0.7);
|
|
|
|
|
font-size: var(--font-size-paragraph);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 19:19:05 +01:00
|
|
|
/* --- Share (topbar) --- */
|
|
|
|
|
.article-share {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-buttons {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-button {
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
opacity: 0.8;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
transition: opacity 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 19:19:05 +01:00
|
|
|
.share-button:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.copy-toast {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: -36px;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
color: #1e1938;
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Share section (bas d'article) --- */
|
|
|
|
|
.article-share-section {
|
2026-03-20 14:08:37 +01:00
|
|
|
margin: auto;
|
2026-03-10 19:19:05 +01:00
|
|
|
text-align: center;
|
2026-03-20 14:08:37 +01:00
|
|
|
width: fit-content;
|
|
|
|
|
margin-top: 3rem;
|
|
|
|
|
margin-bottom: 3rem;
|
2026-03-10 19:19:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-divider {
|
|
|
|
|
border: none;
|
|
|
|
|
border-top: 1px solid white;
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-label {
|
|
|
|
|
font-family: "Danzza Bold", sans-serif;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-share-section .share-buttons {
|
|
|
|
|
justify-content: center;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Titre --- */
|
|
|
|
|
.article-title {
|
2026-03-10 19:19:05 +01:00
|
|
|
font-size: var(--font-size-title-section);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
font-family: "Danzza", sans-serif;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
text-align: center;
|
2026-03-10 19:19:05 +01:00
|
|
|
margin: 20px 0;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Intro --- */
|
|
|
|
|
.article-intro {
|
|
|
|
|
text-align: center;
|
2026-03-10 19:19:05 +01:00
|
|
|
font-size: var(--font-size-paragraph);
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
color: hsla(0,0%,100%,.9);
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
margin-bottom: 2.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Cover --- */
|
|
|
|
|
.article-cover {
|
|
|
|
|
margin-bottom: 3rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-cover img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Body (rich text from Kirby blocks) --- */
|
|
|
|
|
.article-body {
|
|
|
|
|
line-height: 1.8;
|
2026-03-10 19:31:56 +01:00
|
|
|
text-align: left;
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
padding: 0 15%;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(h2),
|
|
|
|
|
.article-body :global(h3) {
|
2026-03-10 19:31:56 +01:00
|
|
|
font-family: "Danzza", sans-serif;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
line-height: 1.3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(h2) {
|
|
|
|
|
font-size: 29px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(h3) {
|
|
|
|
|
font-size: 22px;
|
2026-03-10 19:31:56 +01:00
|
|
|
margin: 25px 0 15px;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(p) {
|
2026-03-10 19:31:56 +01:00
|
|
|
font-family: "Danzza", sans-serif;
|
|
|
|
|
font-size: 1rem;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(a) {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(ul),
|
|
|
|
|
.article-body :global(ol) {
|
|
|
|
|
margin: 1.25rem 0;
|
|
|
|
|
padding-left: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(li) {
|
|
|
|
|
margin-bottom: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(blockquote) {
|
2026-03-21 13:38:42 +01:00
|
|
|
font-family: "Danzza", sans-serif;
|
|
|
|
|
font-size: 19px;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
background-color: rgba(4,254,160,.05);
|
|
|
|
|
border-left: 4px solid #04fea0;
|
|
|
|
|
margin: 30px 0;
|
|
|
|
|
padding: 20px 20px 20px 30px;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(blockquote p) {
|
|
|
|
|
font-style: italic;
|
|
|
|
|
font-size: 19px;
|
|
|
|
|
margin-bottom: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(figure) {
|
|
|
|
|
margin: 2rem 0;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(figure img) {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(figcaption) {
|
|
|
|
|
margin-top: 0.5rem;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
color: rgba(255, 255, 255, 0.7);
|
|
|
|
|
font-size: var(--font-size-caption);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:03:18 +01:00
|
|
|
/* --- Bloc jeu iframe --- */
|
|
|
|
|
.article-body :global(.iframe-game-container) {
|
|
|
|
|
position: relative;
|
2026-03-20 12:08:33 +01:00
|
|
|
width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
margin: 2rem 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-game-container iframe) {
|
|
|
|
|
border: none;
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
transition: filter 0.3s ease;
|
2026-03-20 12:03:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-click-overlay) {
|
|
|
|
|
position: absolute;
|
2026-03-20 12:08:33 +01:00
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
2026-03-20 12:03:18 +01:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-03-20 12:08:33 +01:00
|
|
|
z-index: 10;
|
2026-03-20 12:03:18 +01:00
|
|
|
cursor: pointer;
|
2026-03-20 12:08:33 +01:00
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
background: rgba(13, 14, 34, 0.90);
|
|
|
|
|
backdrop-filter: blur(5px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-click-overlay[data-state="initial"]) {
|
|
|
|
|
background: rgba(13, 14, 34, 0.98);
|
|
|
|
|
backdrop-filter: blur(5px);
|
|
|
|
|
border: 1px solid rgba(77, 252, 161, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-click-overlay[data-state="initial"]:hover) {
|
|
|
|
|
border-color: rgba(77, 252, 161, 0.6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-click-overlay[data-state="ended"]) {
|
|
|
|
|
background: rgba(13, 14, 34, 0.10);
|
|
|
|
|
backdrop-filter: none;
|
|
|
|
|
border: 2px solid #4DFCA1;
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.overlay-content) {
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: white;
|
|
|
|
|
user-select: none;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.play-icon) {
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 60px;
|
|
|
|
|
background: linear-gradient(135deg, #4DFCA1 0%, #04fea0 100%);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
margin: 0 auto 16px auto;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
color: black;
|
|
|
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
|
|
|
box-shadow: 0 4px 15px rgba(77, 252, 161, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-click-overlay[data-state="initial"]:hover .play-icon) {
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
box-shadow: 0 6px 20px rgba(77, 252, 161, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.overlay-message) {
|
|
|
|
|
font-family: "Danzza", sans-serif;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin: 0;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-click-overlay[data-state="ended"] .overlay-message) {
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #4DFCA1;
|
2026-03-20 12:03:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-deactivate-btn) {
|
|
|
|
|
position: absolute;
|
2026-03-20 12:08:33 +01:00
|
|
|
top: 15px;
|
|
|
|
|
right: 15px;
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
|
|
color: white;
|
|
|
|
|
border: 2px solid #04fea0;
|
|
|
|
|
border-radius: 50%;
|
2026-03-20 12:03:18 +01:00
|
|
|
cursor: pointer;
|
2026-03-20 12:08:33 +01:00
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
z-index: 11;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity 0.3s ease;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-deactivate-btn:hover) {
|
|
|
|
|
background: #4DFCA1;
|
|
|
|
|
color: black;
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 15:44:25 +01:00
|
|
|
.article-body :global(.iframe-game-container.game-active .iframe-deactivate-btn) {
|
2026-03-20 12:08:33 +01:00
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(.iframe-game-container.game-active) {
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(77, 252, 161, 0.5);
|
2026-03-20 12:03:18 +01:00
|
|
|
}
|
|
|
|
|
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
/* --- Recommandations --- */
|
|
|
|
|
.article-related {
|
|
|
|
|
padding-top: 3rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related h2 {
|
|
|
|
|
font-family: "Danzza Bold", sans-serif;
|
|
|
|
|
font-size: var(--font-size-title-section);
|
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
gap: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-card {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
transition: transform 0.3s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-card:hover {
|
|
|
|
|
transform: translateY(-5px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-card img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 180px;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-title {
|
|
|
|
|
font-family: "Danzza Bold", sans-serif;
|
|
|
|
|
font-size: var(--font-size-paragraph);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Mobile --- */
|
2026-03-10 18:55:37 +01:00
|
|
|
@media (max-width: 700px) {
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
.article {
|
2026-03-21 13:38:42 +01:00
|
|
|
padding:0 0 4rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-date {
|
|
|
|
|
font-family: "Danzza Light", sans-serif;
|
|
|
|
|
opacity: .7;
|
|
|
|
|
font-size: var(--font-size-paragraph-mobile);
|
|
|
|
|
text-wrap: nowrap;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-title {
|
|
|
|
|
font-size: var(--font-size-title-section-mobile);
|
2026-03-21 13:38:42 +01:00
|
|
|
line-height: 1.4;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-intro {
|
2026-03-21 13:38:42 +01:00
|
|
|
font-family: "Danzza Light", sans-serif;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
font-size: var(--font-size-paragraph-mobile);
|
2026-03-21 13:38:42 +01:00
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body {
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-body :global(p),
|
|
|
|
|
.article-body :global(h3) {
|
|
|
|
|
font-family: "Danzza Light", sans-serif;
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-grid {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
2026-03-21 13:41:18 +01:00
|
|
|
|
|
|
|
|
.collection-card-title {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
}
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Tablet --- */
|
2026-03-10 18:55:37 +01:00
|
|
|
@media (min-width: 701px) and (max-width: 912px) {
|
Feat: pages Article + navigation blog/article interne
- Router: findSlideIndex() avec fallback parent path
(/blog/slug → /blog) pour sub-pages
- article.json.php: réécriture — date, intro, cover, body (blocks→HTML),
related articles (fallback siblings si vide)
- Article.svelte: sous-composant — topbar date+retour, titre Terminal,
intro, cover, body rich text (styles :global pour blocks Kirby),
related articles grid, responsive
- Blog.svelte: gère deux modes (liste + article) —
intercepte les clics article via stopPropagation (avant le router),
fetch article data, pushState pour URL, popstate pour back/forward,
direct navigation /blog/slug sur mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 16:55:34 +01:00
|
|
|
.article-title {
|
|
|
|
|
font-size: var(--font-size-title-main-tablet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.article-related-grid {
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
}
|
Migration vers architecture Svelte + Kirby inspirée de design-to-pack
- Mise en place de Svelte 4 avec Vite pour le frontend (SPA)
- Simplification des templates PHP (header/footer minimalistes)
- Création de templates JSON pour API (home, about, expertise, portfolio, jouer, game, blog, article, project)
- Ajout d'un controller de site pour définir genericData globalement
- Structure des stores Svelte (page, navigation, locale, site)
- Router avec navaid pour navigation SPA et interception des liens
- Composants layout (Header, Footer, Cursor) et vues de base
- Build Vite vers assets/dist/ (index.js/css)
- Header PHP détecte assets/dist pour basculer dev/prod
Architecture fonctionnelle de base établie, à améliorer et compléter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:30:15 +01:00
|
|
|
}
|
|
|
|
|
</style>
|