Feat: footer reveal + refactor page About
All checks were successful
Deploy / Deploy to Production (push) Successful in 24s
All checks were successful
Deploy / Deploy to Production (push) Successful in 24s
- Footer global fixé en bas (App.svelte), masqué par le contenu des slides - margin-bottom sur .page-scrollable pour l'effet footer reveal (about, blog, article) - About : champ intro remplacé par heading (text) + subtitle (writer), blueprint + API + vue mis à jour - LanguageSwitcher : div → button avec hitbox élargie - i18n : clé our_team (fr: NOTRE ÉQUIPE / en: OUR TEAM) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
61607f8cd7
commit
b3b0580ab1
10 changed files with 148 additions and 130 deletions
|
|
@ -1,11 +1,13 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { slides } from '@state/slides.svelte'
|
||||
import Footer from '@components/layout/Footer.svelte'
|
||||
|
||||
import { t } from '@i18n'
|
||||
|
||||
let { data } = $props()
|
||||
|
||||
const intro = $derived(data?.intro ?? '')
|
||||
const heading = $derived(data?.heading ?? '')
|
||||
const subtitle = $derived(data?.subtitle ?? '')
|
||||
const body = $derived(data?.body ?? [])
|
||||
const members = $derived(data?.team ?? [])
|
||||
const isActive = $derived(slides.active?.id === 'about')
|
||||
|
|
@ -72,86 +74,88 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<div class="about page-scrollable" bind:this={sectionEl}>
|
||||
|
||||
<!-- Intro -->
|
||||
<section class="about-intro">
|
||||
<div class="about-intro-content">
|
||||
{@html intro}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Body blocks (Mission, Manifeste…) -->
|
||||
{#if body.length > 0}
|
||||
<section class="about-body">
|
||||
{#each body as block}
|
||||
{#if block.type === 'text'}
|
||||
<div class="about-body-block">
|
||||
{@html block.html}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="about golden-grid page-scrollable" bind:this={sectionEl}>
|
||||
<div class="page-container">
|
||||
<!-- Intro -->
|
||||
<section class="about-intro">
|
||||
<div class="about-intro-content">
|
||||
{#if heading}<h1 class="heading">{heading}</h1>{/if}
|
||||
<p class="subtitle">{subtitle}</p>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- Team carousel -->
|
||||
{#if members.length > 0}
|
||||
<section class="about-team">
|
||||
<h2 class="about-team-heading">OUR TEAM</h2>
|
||||
|
||||
<div class="team-carousel-container">
|
||||
<div
|
||||
class="team-grid"
|
||||
style="transform: translateX(-{carouselOffset}px)"
|
||||
ontouchstart={(e) => { touchStartX = e.touches[0].clientX }}
|
||||
ontouchend={(e) => {
|
||||
if (touchStartX === null) return
|
||||
const delta = touchStartX - e.changedTouches[0].clientX
|
||||
if (Math.abs(delta) > 50) delta > 0 ? nextSlide() : prevSlide()
|
||||
touchStartX = null
|
||||
}}
|
||||
>
|
||||
{#each members as member}
|
||||
<div class="team-member">
|
||||
{#if member.photo}
|
||||
<img src={member.photo} alt={member.name} class="team-member-image" draggable="false" />
|
||||
{/if}
|
||||
<h4 class="team-member-name">{member.name}</h4>
|
||||
<p class="team-member-title">{member.role}</p>
|
||||
|
||||
<!-- Body blocks (Mission, Manifeste…) -->
|
||||
{#if body.length > 0}
|
||||
<section class="about-body">
|
||||
{#each body as block}
|
||||
{#if block.type === 'text'}
|
||||
<div class="about-body-block">
|
||||
{@html block.html}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- Team carousel -->
|
||||
{#if members.length > 0}
|
||||
<section class="about-team">
|
||||
<h2 class="about-team-heading">{t('our_team')}</h2>
|
||||
|
||||
<div class="team-carousel-container">
|
||||
<div
|
||||
class="team-grid"
|
||||
style="transform: translateX(-{carouselOffset}px)"
|
||||
ontouchstart={(e) => { touchStartX = e.touches[0].clientX }}
|
||||
ontouchend={(e) => {
|
||||
if (touchStartX === null) return
|
||||
const delta = touchStartX - e.changedTouches[0].clientX
|
||||
if (Math.abs(delta) > 50) delta > 0 ? nextSlide() : prevSlide()
|
||||
touchStartX = null
|
||||
}}
|
||||
>
|
||||
{#each members as member}
|
||||
<div class="team-member">
|
||||
{#if member.photo}
|
||||
<img src={member.photo} alt={member.name} class="team-member-image" draggable="false" />
|
||||
{/if}
|
||||
<h4 class="team-member-name">{member.name}</h4>
|
||||
<p class="team-member-title">{member.role}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav-buttons">
|
||||
<button
|
||||
class="nav-button"
|
||||
disabled={currentSlide === 0}
|
||||
onclick={prevSlide}
|
||||
>← BEFORE</button>
|
||||
|
||||
<div class="pagination-indicator">
|
||||
{#each { length: totalSlides } as _, i}
|
||||
<button
|
||||
class="pagination-dot"
|
||||
class:active={i === currentSlide}
|
||||
aria-label="Slide {i + 1}"
|
||||
onclick={() => goToSlide(i)}
|
||||
></button>
|
||||
{/each}
|
||||
|
||||
<div class="nav-buttons">
|
||||
<button
|
||||
class="nav-button"
|
||||
disabled={currentSlide === 0}
|
||||
onclick={prevSlide}
|
||||
>← BEFORE</button>
|
||||
|
||||
<div class="pagination-indicator">
|
||||
{#each { length: totalSlides } as _, i}
|
||||
<button
|
||||
class="pagination-dot"
|
||||
class:active={i === currentSlide}
|
||||
aria-label="Slide {i + 1}"
|
||||
onclick={() => goToSlide(i)}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="nav-button"
|
||||
disabled={currentSlide === totalSlides - 1}
|
||||
onclick={nextSlide}
|
||||
>NEXT →</button>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="nav-button"
|
||||
disabled={currentSlide === totalSlides - 1}
|
||||
onclick={nextSlide}
|
||||
>NEXT →</button>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.about {
|
||||
|
|
@ -161,15 +165,22 @@
|
|||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
grid-area: 6/6/span 7/span 10;
|
||||
height: 100%;
|
||||
place-self: center;
|
||||
text-align: center;
|
||||
white-space: pre-line;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ── Intro ── */
|
||||
.about-intro {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8rem 3rem 2rem;
|
||||
}
|
||||
|
||||
.about-intro-content {
|
||||
max-width: 800px;
|
||||
text-align: center;
|
||||
font-family: "Danzza", sans-serif;
|
||||
font-size: var(--font-size-paragraph);
|
||||
|
|
@ -194,20 +205,17 @@
|
|||
color: #04fea0;
|
||||
}
|
||||
|
||||
.about-intro-content :global(p) {
|
||||
.about-intro-content .subtitle {
|
||||
font-size: var(--font-size-subtitle);
|
||||
font-weight: normal;
|
||||
margin-top: 1.5rem;
|
||||
text-align: center;
|
||||
|
||||
margin-top: 3.125rem;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
/* ── Body blocks ── */
|
||||
.about-body {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem 3rem 3rem;
|
||||
}
|
||||
|
||||
.about-body-block {
|
||||
border-left: 2px solid #04fea0;
|
||||
padding-left: 20px;
|
||||
|
|
@ -221,12 +229,22 @@
|
|||
}
|
||||
|
||||
.about-body-block :global(h3) {
|
||||
font-family: "Terminal", sans-serif;
|
||||
font-size: var(--font-size-subtitle);
|
||||
color: #04fea0;
|
||||
font-family: Danzza Medium,sans-serif;
|
||||
font-size: var(--font-size-paragraph);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.about-body-block :global(h3::before) {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: .6rem;
|
||||
height: .6rem;
|
||||
background-color: var(--color-primary);
|
||||
|
||||
margin-right: .6rem;
|
||||
margin-bottom: .1rem;
|
||||
}
|
||||
|
||||
.about-body-block :global(p) {
|
||||
opacity: 0.9;
|
||||
margin-bottom: 0.75rem;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Vue article — sous-composant de Blog.svelte.
|
||||
* Reçoit les données article via props, pas via le slide system.
|
||||
*/
|
||||
import Footer from '@components/layout/Footer.svelte'
|
||||
|
||||
import { t } from '@i18n'
|
||||
|
||||
let { data, onBack } = $props()
|
||||
|
|
@ -114,7 +114,6 @@
|
|||
{/if}
|
||||
</article>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { onMount } from 'svelte'
|
||||
import { slides } from '@state/slides.svelte'
|
||||
import { locale } from '@state/locale.svelte'
|
||||
import Footer from '@components/layout/Footer.svelte'
|
||||
|
||||
import Article from '@views/Article.svelte'
|
||||
import { t } from '@i18n'
|
||||
|
||||
|
|
@ -178,7 +178,6 @@
|
|||
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
|
|
@ -211,7 +210,6 @@
|
|||
|
||||
.page-container {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0 10%;
|
||||
}
|
||||
|
||||
|
|
@ -312,12 +310,6 @@
|
|||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.blog :global(.site-footer) {
|
||||
margin-left: -50px;
|
||||
margin-right: -50px;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* --- Mobile --- */
|
||||
@media (max-width: 700px) {
|
||||
.blog-header {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue