Compare commits
4 commits
d42217fd20
...
34635f7982
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34635f7982 | ||
|
|
fdab621b48 | ||
|
|
90f155b679 | ||
|
|
66767f0136 |
12 changed files with 219 additions and 38 deletions
130
package-lock.json
generated
130
package-lock.json
generated
|
|
@ -16,7 +16,100 @@
|
|||
"vite": "7.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fs-extra": "^11.3.3"
|
||||
"fs-extra": "^11.3.3",
|
||||
"postcss": "^8.5.8",
|
||||
"postcss-custom-media": "^12.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/cascade-layer-name-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-/3iksyevwRfSJx5yH0RkcrcYXwuhMQx3Juqf40t97PeEy2/Mz2TItZ/z/216qpe4GgOyFBP8MKIwVvytzHmfIQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-parser-algorithms": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
|
||||
"integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-tokenizer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
|
||||
"integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/media-query-list-parser": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz",
|
||||
"integrity": "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
|
|
@ -1161,9 +1254,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"version": "8.5.8",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
||||
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -1188,6 +1281,35 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-custom-media": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-12.0.1.tgz",
|
||||
"integrity": "sha512-66syE14+VeqkUf0rRX0bvbTCbNRJF132jD+ceo8th1dap2YJEAqpdh5uG98CE3IbgHT7m9XM0GIlOazNWqQdeA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/cascade-layer-name-parser": "^3.0.0",
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0",
|
||||
"@csstools/media-query-list-parser": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/regexparam": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/regexparam/-/regexparam-1.3.0.tgz",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
"vite": "7.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fs-extra": "^11.3.3"
|
||||
"fs-extra": "^11.3.3",
|
||||
"postcss": "^8.5.8",
|
||||
"postcss-custom-media": "^12.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
postcss.config.js
Normal file
5
postcss.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import customMedia from 'postcss-custom-media'
|
||||
|
||||
export default {
|
||||
plugins: [customMedia()]
|
||||
}
|
||||
|
|
@ -55,9 +55,10 @@ columns:
|
|||
- strike
|
||||
- clear
|
||||
- link
|
||||
playLinks:
|
||||
playLink:
|
||||
label: Lien(s) pour jouer
|
||||
help: Laissez vide pour afficher "à venir / coming soon"
|
||||
translate: false
|
||||
type: url
|
||||
|
||||
- width: 3/10
|
||||
|
|
@ -71,6 +72,7 @@ columns:
|
|||
language: css
|
||||
size: custom-size
|
||||
default: radial-gradient(circle at 20% 80%, rgb(240, 154, 110) 0%, rgb(233, 101, 122) 100%)
|
||||
translate: false
|
||||
help: À remplir avec la valeur de la propriété CSS `background-color` souhaitée
|
||||
preview:
|
||||
label: Aperçu
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@
|
|||
}
|
||||
|
||||
/* Mobile — var(--breakpoint-mobile) = 700px */
|
||||
@media screen and (max-width: 700px) {
|
||||
@media (--mobile) {
|
||||
.navbar {
|
||||
min-height: 8vh;
|
||||
padding: 10vh 0 1vh;
|
||||
|
|
@ -179,7 +179,7 @@
|
|||
}
|
||||
|
||||
/* Tablet — var(--breakpoint-tablet) = 912px */
|
||||
@media screen and (min-width: 701px) and (max-width: 912px) {
|
||||
@media (--tablet-only) {
|
||||
.navbar-item {
|
||||
font-size: var(--font-size-paragraph-tablet);
|
||||
padding: 1vmax 1.8vmax;
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@
|
|||
}
|
||||
|
||||
/* Mobile — var(--breakpoint-mobile) = 700px */
|
||||
@media screen and (max-width: 700px) {
|
||||
@media (--mobile) {
|
||||
.menu-list {
|
||||
font-size: var(--font-size-subtitle-mobile);
|
||||
grid-area: 6/4 / span 8 / span 8;
|
||||
|
|
@ -269,7 +269,7 @@
|
|||
}
|
||||
|
||||
/* Tablet — var(--breakpoint-tablet) = 912px */
|
||||
@media screen and (min-width: 701px) and (max-width: 912px) {
|
||||
@media (--tablet-only) {
|
||||
.menu-list {
|
||||
font-size: var(--font-size-title-section-tablet);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
button {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.button {
|
||||
width: 14vmax;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/* Custom media queries — utilisables dans @media (--mobile) / @media (--tablet) */
|
||||
@custom-media --mobile (max-width: 700px);
|
||||
@custom-media --tablet (max-width: 912px);
|
||||
@custom-media --tablet-only (min-width: 701px) and (max-width: 912px);
|
||||
|
||||
/* CSS Variables */
|
||||
:root {
|
||||
/* Colors */
|
||||
|
|
@ -6,10 +11,6 @@
|
|||
--color-background: #000;
|
||||
--color-text: #fff;
|
||||
|
||||
/* BREAKPOINTS (référence — non utilisables directement dans @media)
|
||||
* --breakpoint-mobile : 700px
|
||||
* --breakpoint-tablet : 912px
|
||||
*/
|
||||
--breakpoint-mobile: 700px;
|
||||
--breakpoint-tablet: 912px;
|
||||
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@
|
|||
}
|
||||
|
||||
/* Mobile (≤ 700px) */
|
||||
@media screen and (max-width: 700px) {
|
||||
@media (--mobile) {
|
||||
.expertise-title {
|
||||
grid-area: 5/4 / span 1 / span 7;
|
||||
font-size: var(--font-size-title-main-mobile);
|
||||
|
|
@ -379,7 +379,7 @@
|
|||
}
|
||||
|
||||
/* Tablet (701–912px) */
|
||||
@media screen and (min-width: 701px) and (max-width: 912px) {
|
||||
@media (--tablet-only) {
|
||||
.expertise-title {
|
||||
grid-area: 5/6 / span 4 / span 12;
|
||||
font-size: var(--font-size-title-main-tablet);
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 701px) and (max-width: 912px) {
|
||||
@media (--tablet-only) {
|
||||
.home-subtitle {
|
||||
font-size: var(--font-size-subtitle-tablet);
|
||||
width: 70%;
|
||||
|
|
|
|||
|
|
@ -10,25 +10,45 @@
|
|||
const games = $derived(data?.games ?? [])
|
||||
|
||||
// --- State ---
|
||||
let currentIndex = $state(0) // index actif (carousel + bg)
|
||||
let displayedIndex = $state(0) // index du contenu affiché
|
||||
let currentIndex = $state(0)
|
||||
let displayedIndex = $state(0)
|
||||
let phase = $state('idle') // 'idle' | 'exiting' | 'entering'
|
||||
let slideDir = $state(1) // 1 = click droite (exit gauche), -1 = click gauche (exit droite)
|
||||
let slideDir = $state(1) // 1 = exit gauche, -1 = exit droite
|
||||
|
||||
// Bg crossfade
|
||||
let prevBgColor = $state(null)
|
||||
let bgFading = $state(false)
|
||||
let prevBgColor = $state(null)
|
||||
let bgFading = $state(false)
|
||||
|
||||
const displayedGame = $derived(games[displayedIndex] ?? null)
|
||||
|
||||
// Capture du hash synchrone avant que tout effect puisse le modifier
|
||||
const initialHash = window.location.hash.slice(1)
|
||||
|
||||
let t1 = null
|
||||
let t2 = null
|
||||
let t3 = null
|
||||
|
||||
// --- Ancres ---
|
||||
function setAnchor(index) {
|
||||
const slug = games[index]?.slug
|
||||
if (!slug) return
|
||||
history.replaceState(null, '', '#' + slug)
|
||||
}
|
||||
|
||||
function clearAnchor() {
|
||||
history.replaceState(null, '', window.location.pathname + window.location.search)
|
||||
}
|
||||
|
||||
// Restauration depuis l'ancre URL — une seule fois quand games est prêt
|
||||
$effect(() => {
|
||||
if (games.length === 0 || !initialHash) return
|
||||
const idx = games.findIndex(g => g.slug === initialHash)
|
||||
if (idx > 0) currentIndex = idx
|
||||
})
|
||||
|
||||
function selectGame(i) {
|
||||
if (i === currentIndex || phase !== 'idle' || !games.length) return
|
||||
|
||||
// slideDir = 1 → clic à droite → content sort par la gauche, entre par la droite
|
||||
slideDir = i > currentIndex ? 1 : -1
|
||||
phase = 'exiting'
|
||||
|
||||
|
|
@ -36,22 +56,36 @@
|
|||
clearTimeout(t2)
|
||||
clearTimeout(t3)
|
||||
|
||||
// Phase 2 (300ms) : swap contenu + carousel + bg simultanés
|
||||
t1 = setTimeout(() => {
|
||||
prevBgColor = displayedGame?.backgroundColor ?? null
|
||||
displayedIndex = i
|
||||
currentIndex = i
|
||||
phase = 'entering'
|
||||
bgFading = true
|
||||
setAnchor(i)
|
||||
|
||||
// Fin du bg fade
|
||||
t3 = setTimeout(() => { bgFading = false }, 500)
|
||||
|
||||
// Fin de l'entrée
|
||||
t2 = setTimeout(() => { phase = 'idle' }, 350)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// --- Clavier ---
|
||||
// Enregistré en capture pour s'exécuter avant le handler global de App.svelte.
|
||||
// Si la navigation reste dans Play (pas aux limites), on bloque App via
|
||||
// stopImmediatePropagation. Aux limites, on ne fait rien : App navigue normalement.
|
||||
function onKeyDown(e) {
|
||||
if (!isActive) return
|
||||
if (e.key === 'ArrowRight' && currentIndex < games.length - 1) {
|
||||
e.stopImmediatePropagation()
|
||||
e.preventDefault()
|
||||
selectGame(currentIndex + 1)
|
||||
} else if (e.key === 'ArrowLeft' && currentIndex > 0) {
|
||||
e.stopImmediatePropagation()
|
||||
e.preventDefault()
|
||||
selectGame(currentIndex - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Réinitialisation quand on quitte la slide
|
||||
$effect(() => {
|
||||
if (!isActive) {
|
||||
|
|
@ -62,13 +96,18 @@
|
|||
displayedIndex = 0
|
||||
phase = 'idle'
|
||||
bgFading = false
|
||||
clearAnchor()
|
||||
}
|
||||
})
|
||||
|
||||
onMount(() => () => {
|
||||
clearTimeout(t1)
|
||||
clearTimeout(t2)
|
||||
clearTimeout(t3)
|
||||
onMount(() => {
|
||||
window.addEventListener('keydown', onKeyDown, { capture: true })
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown, { capture: true })
|
||||
clearTimeout(t1)
|
||||
clearTimeout(t2)
|
||||
clearTimeout(t3)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -354,7 +393,6 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: clamp(140px, 15.09vw, 291px);
|
||||
gap: 0.5rem;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
|
|
@ -362,15 +400,22 @@
|
|||
transition: all 0.4s var(--ease-standard);
|
||||
}
|
||||
|
||||
.play-carousel-item.active button {
|
||||
opacity: 1;
|
||||
.play-carousel-item :global(img) {
|
||||
width: clamp(140px, 15.09vw, 291px);
|
||||
}
|
||||
|
||||
.play-carousel-item.active :global(img) {
|
||||
width: clamp(170px, 18.41vw, 355px);
|
||||
}
|
||||
|
||||
.play-carousel-item button img,
|
||||
.play-carousel-item.active button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.play-carousel-item button :global(img),
|
||||
.play-carousel-item button :global(picture img) {
|
||||
object-fit: cover;
|
||||
transition: border-color 0.4s var(--ease-standard);
|
||||
transition: all 0.4s var(--ease-standard);
|
||||
}
|
||||
|
||||
.play-carousel-title {
|
||||
|
|
@ -386,7 +431,7 @@
|
|||
}
|
||||
|
||||
/* --- Mobile (≤ 700px) --- */
|
||||
@media screen and (max-width: 700px) {
|
||||
@media (--mobile) {
|
||||
.game-preview {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@
|
|||
}
|
||||
|
||||
/* Mobile (≤ 700px) */
|
||||
@media screen and (max-width: 700px) {
|
||||
@media (--mobile) {
|
||||
.portfolio-gallery {
|
||||
grid-area: 1/1 / span 20 / span 20;
|
||||
opacity: 0.3;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue