Logo
  • Works
  • About
  • Contact
  • Yoyo
  • eShop

LANDING 02

BODY

/* === SWWS — Featured gallery (B&W default, color on hover) ============ / / Cible le nouveau bloc / #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card__cover { / N&B propre, un peu d’intensité / filter: grayscale(1) contrast(1.08) brightness(0.96); transition: filter .45s ease, transform .35s ease; will-change: filter, transform; } / Couleur + léger zoom au hover/focus (desktop & clavier) / @media (hover:hover) { #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card.gallery:hover .notion-collection-card__cover, #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card.gallery:focus-within .notion-collection-card__cover { filter: grayscale(0) contrast(1.02) brightness(1.0); transform: scale(1.03); } } / Mobile/tactile : pas de zoom (évite les sauts), couleur au tap (active) / @media (hover:none) { #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card.gallery:active .notion-collection-card__cover { filter: grayscale(0) contrast(1.02) brightness(1.0); } } / Option: ajout d'un grain très léger pour un rendu éditorial (facultatif) / #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card__cover::after { content: ""; position: absolute; inset: 0; pointer-events: none; background-image: radial-gradient(rgba(255,255,255,0.04) 1px, transparent 1px); background-size: 2px 2px; / grain fin / mix-blend-mode: overlay; opacity: .25; transition: opacity .35s ease; } @media (hover:hover) { #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card.gallery:hover .notion-collection-card__cover::after { opacity: .12; / un peu moins de grain en couleur / } } / Accessibilité: si l’utilisateur préfère moins d’animations / @media (prefers-reduced-motion: reduce) { #block-29a95fdcc23e80c9abecc3777b9f2655 .notion-collection-card__cover { transition: none; } } / === SWWS — Titres des cartes Notion : fond blanc + hover jaune ============ / .notion-property.notion-property__title.notion-collection-card__property.title.notion-semantic-string { background: #fff; / fond blanc / color: #000; / texte noir / padding: 0px 012px; display: inline-block; border-radius: 0px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.25; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: background 0.3s ease, color 0.3s ease, box-shadow 0.3s ease; } / === SWWS — YOYO Horizontal Gallery (aspect ratio edition) === / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection__header-wrapper { margin-bottom: 12px; } / Rail horizontal / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-gallery { display: grid !important; grid-auto-flow: column; grid-auto-columns: minmax(180px, 36vw); gap: 18px; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; padding: 8px 24px 24px; scroll-behavior: smooth; -webkit-overflow-scrolling: touch; scrollbar-width: none; position: relative; } #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-gallery::-webkit-scrollbar { display: none; } / Cartes / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card.gallery { scroll-snap-align: center; border-radius: 12px; overflow: hidden; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease; } #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card.gallery:hover { transform: translateY(-2px); border-color: rgba(255,255,255,0.18); box-shadow: 0 10px 30px rgba(0,0,0,.25); } / Couverture image — proportion fixe / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card__cover { width: 100%; aspect-ratio: 4 / 5; / 👈 proportion stable (4:5 = vertical élégant) / object-fit: cover !important; object-position: center 45% !important; display: block; } / Contenu sous l’image / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card__content { padding: 12px 12px 14px; color: #5B5B5B; font-family: "Space Grotesk", ui-sans-serif, system-ui, sans-serif; font-size: 18px; line-height: 1.45; background: transparent; } / Effet “edges fade” pour indiquer le scroll / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-gallery { -webkit-mask-image: linear-gradient(to right, transparent 0, black 24px, black calc(100% - 24px), transparent 100%); mask-image: linear-gradient(to right, transparent 0, black 24px, black calc(100% - 24px), transparent 100%); } / ---- Mobile adjustments ---- / @media (max-width: 720px) { #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-gallery { grid-auto-columns: 30vw; / carte presque plein écran / gap: 16px; padding: 8px 12px 24px; } #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card__cover { aspect-ratio: 5 / 4; / plus proche du carré, mieux sur téléphone / } } / YOYOLIGHT — ne cacher le header vide qu'en version téléphone / @media (max-width: 768px) { #block-29b95fdcc23e8076ae45cee0bceb4255 > .notion-collection__header-wrapper { display: none !important; margin: 0 !important; padding: 0 !important; } / petit espace fluide entre le titre H2 et la galerie / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-gallery { margin-top: 8px !important; } } / Fix mobile : supprimer l’espace laissé par le header-wrapper intermédiaire / @media (max-width: 768px) { / Masque le header vide + retire son espace / #block-29b95fdcc23e801a95bfcfe99896e893 > .notion-collection__header-wrapper { display: none !important; margin: 0 !important; padding: 0 !important; height: 0 !important; } / Si Notion insère un paragraphe vide, on le neutralise aussi / #block-29b95fdcc23e801a95bfcfe99896e893 .notion-text:empty, #block-29b95fdcc23e801a95bfcfe99896e893 .notion-semantic-string:empty { display: none !important; } / Ajoute un mini-espacement au-dessus du rail pour respirer (optionnel) / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-gallery { margin-top: 8px !important; / ajuste à 0 si tu veux collé / } } / Overlay uses --hover-img set by JS / #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card.gallery { position: relative; overflow: hidden; } #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card.gallery::after { content: ""; position: absolute; inset: 0; background-image: var(--hover-img); background-size: cover; background-position: center 45%; opacity: 0; transition: opacity .35s ease; pointer-events: none; } / Desktop hover / @media (hover:hover) { #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card.gallery.swws-lamp-ready:hover::after { opacity: 1; } } / Mobile tap */ @media (hover:none) { #block-29b95fdcc23e8076ae45cee0bceb4255 .notion-collection-card.gallery.swws-lamp-ready:active::after { opacity: 1; } }

CSS

HEAD

<!-- === SWWS Landing Hero — ASCII Canvas Background (Quality MAX) ===== --> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Roboto+Mono:wght@400&display=swap" rel="stylesheet"> <style> .swws-hero, .swws-hero * { box-sizing: border-box; } .swws-hero { position: relative; height: 100dvh; width: 100%; isolation: isolate; overflow: clip; display: grid; place-items: center; background: #000; color: #fff; text-align: center; font-family: "Space Grotesk", ui-sans-serif, system-ui, sans-serif; } /* Canvas ASCII en fond / .swws-ascii { position:absolute; inset:0; width:100%; height:100%; display:block; z-index:1; } / Vignettage doux pour lisibilité du texte / .swws-hero::after{ content:""; position:absolute; inset:0; pointer-events:none; z-index:2; background: radial-gradient(120% 85% at 50% 12%, transparent 0 58%, rgba(0,0,0,.45) 100%); } .swws-hero__content { position:relative; z-index:5; padding:2rem 1rem; max-width:min(900px, 90vw); } .swws-eyebrow{ display:inline-flex; align-items:center; gap:.6rem; padding:.4rem .7rem; border-radius:999px; background:rgba(255,255,255,.12); backdrop-filter:blur(8px); font-weight:600; font-size:18px; letter-spacing:.08em; text-transform:uppercase; } .swws-dot{ width:.8rem; aspect-ratio:1; border-radius:50%; background:#FFFF00; box-shadow:0 0 .5rem #FFFF00; } / H2 dynamique par lignes (déjà validé) / .swws-h2{ display:flex; flex-direction:column; align-items:center; overflow:hidden; font-weight:700; font-size:clamp(32px,6vw,72px); line-height:1.1; letter-spacing:-0.015em; color:#fff; } .swws-h2 span{ display:block; transform:translateY(120%); opacity:0; animation:swwsLineReveal .9s ease-out forwards; } @keyframes swwsLineReveal{ to{ transform:translateY(0); opacity:1; } } .swws-h2 span:nth-child(1){animation-delay:.2s} .swws-h2 span:nth-child(2){animation-delay:.45s} .swws-h2 span:nth-child(3){animation-delay:.7s} .swws-h2 span:nth-child(4){animation-delay:.95s} .swws-contact{ font-weight:400; font-size:16px; line-height:1.7; color:#fff; opacity:.9; margin-bottom:2rem; } .swws-contact a{ color:#fff; text-decoration:underline; text-underline-offset:3px; transition:color .2s ease; } .swws-contact a:hover{ color:#feedcf; } .swws-cta{ display:inline-flex; align-items:center; gap:.6rem; padding:.9rem 1.4rem; border-radius:0px; color:#111; background:rgba(254,237,207,0.9); border:1px solid rgba(255,255,0,.35); text-decoration:none; font-weight:600; font-size:15px; box-shadow:0 10px 30px rgba(0,0,0,.25); transition:transform .2s ease, box-shadow .2s ease, background .2s ease, color .2s ease; } .swws-cta:hover{ transform:translateY(-2px); box-shadow:0 14px 40px rgba(0,0,0,.35); background:rgba(255,255,0,1); color:#111; } .swws-cta svg{ width:18px; height:18px; } .swws-hero__bottom-fade{ position:absolute; inset:auto 0 0 0; height 160px; background:linear-gradient(to bottom, transparent, #fff); pointer-events:none; z-index:6; } @media (prefers-color-scheme: dark){ .swws-hero__bottom-fade{ background:linear-gradient(to bottom, transparent, #0b0d16); } } @media (prefers-reduced-motion: reduce){ .swws-h2 span{ animation:none !important; opacity:1 !important; transform:none !important; } } </style> <section id="swws-hero" class="swws-hero" aria-label="Sebastien Wierinck WorkShop — Hero"> <canvas id="swws-ascii" class="swws-ascii" aria-hidden="true"></canvas> <div class="swws-hero__content"> <span class="swws-eyebrow"><span class="swws-dot"></span> Sebastien Wierinck WorkShop</span> <h2 class="swws-h2"> <span>A design practice</span> <span>at the intersection of</span> <span>furniture, public art,</span> <span>and digital craft.</span> </h2> <p class="swws-contact"> Get in touch → <a href="mailto:sw@swws.net">sw@swws.net</a><br> Always open to collaborations, commissions, and professional opportunities. </p> <a class="swws-cta" href="https://swws.net/works"> Explore our Portfolio <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"> <path d="M5 12h12M13 6l6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </a> </div> <div class="swws-hero__bottom-fade"></div> </section> <script> / === ASCII Canvas — Quality MAX with auto density & perf guards ===== / (() => { const canvas = document.getElementById('swws-ascii'); if (!canvas) return; const ctx = canvas.getContext('2d', { alpha: true, willReadFrequently: true }); // Images (ordre validé) const IMAGES = [ 'https://images.spr.so/cdn-cgi/imagedelivery/j42No7y-dcokJuNgXeA0ig/775095d7-97ff-423c-8787-04648236bdb6/005/w=3840,quality=90,fit=scale-down', 'https://images.spr.so/cdn-cgi/imagedelivery/j42No7y-dcokJuNgXeA0ig/e00f1546-30d8-444d-a663-cb1d9946d1b6/Buffet-Enfilade-SebastienWierinckSebastienNormand061/w=1920,quality=90,fit=scale-down', 'https://images.spr.so/cdn-cgi/imagedelivery/j42No7y-dcokJuNgXeA0ig/dd8fc821-2964-4b81-ba44-aa04cee3f03a/BodyFold-Septeme-SWierinckSebastienNormand013/w=3840,quality=90,fit=scale-down', 'https://images.spr.so/cdn-cgi/imagedelivery/j42No7y-dcokJuNgXeA0ig/f6a0da9d-d7c7-41c3-b17a-c9ea5bf66fec/CD104-02/w=1920,quality=90,fit=scale-down', 'https://images.spr.so/cdn-cgi/imagedelivery/j42No7y-dcokJuNgXeA0ig/527cd0ef-b6eb-4a9d-bb9d-ac0ffa7d7faa/WORMS-SWWSSbastienNormand008_WEB/w=3840,quality=90,fit=scale-down', 'https://images.spr.so/cdn-cgi/imagedelivery/j42No7y-dcokJuNgXeA0ig/29cd6995-4e66-4d1b-8201-cd78b7abdfdf/J1-OS015-001c/w=1920,quality=90,fit=scale-down' ]; // Paramètres qualité const CHARSET = '@%#WS$9876543210?!abc;:+=-,._ '; // sombre → clair (long = finesse) const COLOR_MODE = 'mono'; // 'mono' ou 'color' const BASE_CELL_DESKTOP = 5; // plus petit = plus de détails (5 recommandé pour Retina) const BASE_CELL_MOBILE = 4; // densité plus légère pour mobile const MAX_CELLS = 180000; // garde-fou perf (colonnes * lignes) const CONTRAST = 1.1; const BRIGHTNESS = 1.05; const SLIDE_MS = 6000; let imgIndex = 0, currentImg = null, slideTimer = null; // Offscreen canvas pour downsample const off = ('OffscreenCanvas' in window) ? new OffscreenCanvas(1,1) : document.createElement('canvas'); const octx = off.getContext('2d', { willReadFrequently: true }); // Chargement d'image function loadImage(src){ return new Promise((resolve, reject)=>{ const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); } // DPR & resize function fitCanvasToParent(){ const parent = canvas.parentElement; const dpr = Math.min(window.devicePixelRatio || 1, 2); // cap à 2 const w = parent.clientWidth; const h = parent.clientHeight; canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; canvas.width = Math.floor(w * dpr); canvas.height = Math.floor(h * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } function calcGrid(){ const W = canvas.clientWidth, H = canvas.clientHeight; const isMobile = Math.min(W, H) < 820; // heuristique téléphone/tablette portrait // Base cell size selon device const baseCell = isMobile ? BASE_CELL_MOBILE : BASE_CELL_DESKTOP; // Ajustement par DPR (plus fin sur Retina) const dpr = Math.min(window.devicePixelRatio || 1, 2); const dprBoost = dpr >= 1.5 ? 0.85 : 1.0; // un peu plus dense si Retina // Densité adaptative selon surface (évite 4K trop lourd) const area = W * H; let densityFactor = 1.4; if (area > 2000000) densityFactor = 1.15; // 2MP if (area > 3500000) densityFactor = 1.3; // 3.5MP const CELL = baseCell * dprBoost * densityFactor; // Estimation lignes/colonnes (ratio lignes ~ 1.9 pour caractères) let cols = Math.ceil(W / CELL); let rows = Math.ceil(H / (CELL * 1.0)); // Garde-fou : limite le nb total de “cellules” const total = cols * rows; if (total > MAX_CELLS){ const scale = Math.sqrt(total / MAX_CELLS); cols = Math.floor(cols / scale); rows = Math.floor(rows / scale); } return { cols, rows, cellPx: CELL }; } function drawASCII(){ if (!currentImg) return; const W = canvas.clientWidth, H = canvas.clientHeight; const { cols, rows } = calcGrid(); // 1) Dessin “cover” de l’image source dans l’offscreen à la résolution du grid const sW = currentImg.width, sH = currentImg.height; const scale = Math.max(cols / sW, rows / sH); const dW = sW * scale, dH = sH * scale; const dx = (cols - dW) * 0.5; const dy = (rows - dH) * 0.55; // focus légèrement plus bas if (off.width) { off.width = cols; off.height = rows; } else { off.width = cols; off.height = rows; } // pour OffscreenCanvas octx.imageSmoothingEnabled = true; octx.clearRect(0, 0, cols, rows); octx.drawImage(currentImg, dx, dy, dW, dH); const imgData = octx.getImageData(0, 0, cols, rows).data; // 2) Peindre le fond ctx.clearRect(0, 0, W, H); ctx.fillStyle = '#000'; ctx.fillRect(0, 0, W, H); // 3) Rendu caractères const charW = W / cols; const charH = H / rows; // Police optimisée (Roboto Mono = très propre) ctx.textBaseline = 'top'; ctx.font = ${Math.ceil(charH * 1.0)}px 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; // Coloriage for (let y = 0; y < rows; y++){ for (let x = 0; x < cols; x++){ const i = (y * cols + x) * 4; let r = imgData[i] / 255; let g = imgData[i+1] / 255; let b = imgData[i+2] / 255; // Luminance perceptuelle let l = (0.2126r + 0.7152g + 0.0722b); l = Math.min(1, Math.max(0, (l - 0.5) * CONTRAST + 0.5)) * BRIGHTNESS; const ci = Math.min(CHARSET.length - 1, Math.floor((1 - l) * (CHARSET.length - 1))); const ch = CHARSET[ci]; if (COLOR_MODE === 'color'){ ctx.fillStyle = rgb(${Math.round(r*255)},${Math.round(g*255)},${Math.round(b*255)}); } else { const c = Math.round(l * 255); ctx.fillStyle = rgb(${c},${c},${c}); } ctx.fillText(ch, x * charW, y * charH); } } } async function showImage(i){ try { currentImg = await loadImage(IMAGES[i]); drawASCII(); } catch(e){ console.warn('ASCII load error', e); } } function onResize(){ fitCanvasToParent(); // debounce (évite de redessiner trop souvent pendant un resize) clearTimeout(onResize._t); onResize._t = setTimeout(drawASCII, 60); } function startSlides(){ clearInterval(slideTimer); slideTimer = setInterval(() => { imgIndex = (imgIndex + 1) % IMAGES.length; showImage(imgIndex); }, SLIDE_MS); } // Pause quand onglet caché (perf) document.addEventListener('visibilitychange', () => { if (document.hidden) { clearInterval(slideTimer); } else { startSlides(); drawASCII(); } }); // Boot fitCanvasToParent(); showImage(imgIndex); startSlides(); window.addEventListener('resize', onResize, { passive:true }); })(); </script> <script> (function () { const GALLERY_ID = "#block-29b95fdcc23e8076ae45cee0bceb4255"; // ta galerie YOYO const LAMP_PROP_SELECTOR = ".property-7c5b5265"; // propriété "Lampe on" function getFileURL(el) { // <img> const img = el.querySelector('img'); if (img?.src) return img.src; // <source srcset> (dans <picture>) const source = el.querySelector('source[srcset]'); if (source?.srcset) return source.srcset.split(' ')[0]; // <a href> const a = el.querySelector('a[href]'); if (a?.href) return a.href; return null; } function hydrateGallery() { const root = document.querySelector(GALLERY_ID); if (!root) return; root.querySelectorAll(".notion-collection-card.gallery").forEach(card => { // Cherche la propriété "Lampe on" par sa classe unique const lampRow = card.querySelector(LAMP_PROP_SELECTOR); if (!lampRow) return; const url = getFileURL(lampRow); if (!url) return; // 1) Injecte l'URL dans une variable CSS sur la carte card.style.setProperty("--hover-img", url('${url}')); card.classList.add("swws-lamp-ready"); // 2) Cache visuellement la propriété (reste dans le DOM) Object.assign(lampRow.style, { display: "none", visibility: "hidden", height: "0", margin: "0", padding: "0", border: "none" }); }); } const run = () => requestAnimationFrame(hydrateGallery); document.addEventListener("DOMContentLoaded", run); window.addEventListener("load", run); const mo = new MutationObserver(run); mo.observe(document.documentElement, { childList: true, subtree: true }); })(); </script>

URL
Logo

Works

eShop

Contact

copyright : Sebastien Wierinck - OnSite Studio

Instagram