Vendanges 2026 : un millésime prometteur
Trois semaines de récolte sous un ciel clément.
Starter T-IN · guide des composants
Les composants qui affichent les données du client : grilles de cartes, galeries photo, listes tarifaires, formulaire de contact. Les données viennent des content collections ou de site.ts — jamais du composant.
Cartes cliquables pour actualités, cuvées, chambres, événements. Card : href, image, meta, datetime, title, excerpt, revealDelay. CardGrid : columns (2, 3 ou 4).
<CardGrid columns={3}>
{news.map((post, i) => (
<Card
href={'/actualites/' + post.id + '/'}
image={{ src: post.data.image, alt: post.data.imageAlt }}
meta={formatDate(post.data.date)}
datetime={isoDate(post.data.date)}
title={post.data.title}
excerpt={post.data.excerpt}
revealDelay={i * 0.12}
/>
))}
</CardGrid> Grille photos + visionneuse native <dialog> : clic pour agrandir, flèches pour naviguer, Échap pour fermer. Sans JS, chaque vignette reste un lien vers le fichier. Props : photos [{ src, alt, width, height }], columns, caption.
<Gallery
columns={3}
caption="Le domaine au fil des saisons"
photos={[
{ src: '/images/gallery-1.svg', alt: '…' },
{ src: '/images/gallery-2.svg', alt: '…' },
]}
/> Pour présenter les gens de la maison — vigneron, chef, gérants — sans le rectangle de photo d'identité. Le sujet détouré se pose en premier plan sur une photo du lieu assombrie, légèrement de travers, et franchit le bord du cadre : c'est ce franchissement d'une arête droite qui rend lisibles le premier plan et l'inclinaison. L'image détourée doit être recadrée au plus près du sujet — toute marge transparente rouvrirait du vide autour de lui. La coupe basse se fond dans l'ombre du voile : inutile de fournir un sujet en pied. Props : scene, cutout, name, role, ratio, width, tilt, side, overhang, veil, desaturate, revealDelay.
<Portrait
scene={{ src: salle, alt: 'La salle et ses cerisiers en fleurs', width: 1120, height: 1400 }}
cutout={{ src: gerants, alt: 'Laure et Pierre, détourés', width: 839, height: 1061 }}
name="Laure & Pierre"
role="À la tête de la maison depuis 2024"
/>
// variantes : sujet ancré à gauche, plus incliné, décor moins voilé
<Portrait ... side="left" tilt={-6} width={78} veil={45} /> Récolté d'antonin_lacour. L'alternative éditoriale à CardGrid : même donnée, mais une page qui se lit au lieu d'une grille qui se parcourt. Manchette + filet épais, la parution la plus récente traitée comme une une (photo large + grand corps), les autres en brèves séparées d'un filet de colonne. La prop event ajoute une bande sombre de clôture — le rendez-vous annuel, qui ne se lit pas comme une actualité de plus. Le titre accepte `*entre astérisques*` → italique léger. À réserver aux clients qui publient vraiment : sous quatre parutions, CardGrid reste plus honnête.
Meursault · quatre parutions
À la une
Une récolte sans accroc, des acidités qui ont tenu, et pour une fois le temps de bien faire les choses.
Lire le récit
1er et 2 août 2026 — l’événement de l’été
Deux jours de marche gourmande entre les parcelles, six étapes, six cuvées. Gratuit pour les moins de douze ans.
<Journal
eyebrow="Meursault · quatre parutions"
title="Le journal *du domaine*"
issues={news.map((post) => ({
title: post.data.title,
href: '/actualites/' + post.id + '/',
date: post.data.date,
excerpt: post.data.excerpt,
image: { src: post.data.image, alt: post.data.imageAlt },
}))}
event={{
image: { src: '/images/fullbleed-1.svg', alt: '…' },
eyebrow: '1er et 2 août 2026 — l’événement de l’été',
title: 'La Ronde *des Trois Pierres*',
text: 'Deux jours de marche gourmande entre les parcelles.',
link: { href: '/contact', label: 'S’inscrire' },
}}
/> Récolté d'antonin_lacour (GammeVins + CarteCuveeSombre). Ouverture photographique sombre, barre de filtres, puis les produits en cartes : image détourée posée au sol d'un cadre 4/5, pastille de catégorie à gauche, statut à droite, nom et prix sur la même ligne de base. Le fond sombre n'est pas décoratif — c'est lui qui fait exister un packshot détouré (fit='contain', défaut ; fit='cover' pour une photo cadrée). Le filtrage est un îlot d'une vingtaine de lignes, sans framework : sans JavaScript toutes les cartes sont dans le HTML, seule la barre ne répond pas. Ne donner qu'une entrée de filtrage : dès qu'il faut en croiser plusieurs, c'est FiltresGrille (fiche suivante) qu'il faut prendre. Cuvées, greens fees, chambres, formules, prestations.
Six cuvées, trois appellations
Six parcellaires vendangés à la main, élevés en fût et en jarre, mis en bouteille sans collage ni filtration.
24,00 €
Chardonnay de haut de coteau, dix mois de fût, une pointe de salinité.
29,00 €
Pinot noir sur argile rouge, égrappé à moitié, dix-huit mois d’élevage.
32,00 €
La parcelle la plus froide du domaine : un vin qui met trois ans à s’ouvrir.
26,00 €
Vendange entière, macération longue, aucun soufre avant la mise.
34,00 €
Le sommet du domaine, sur un demi-hectare de vieilles vignes.
17,00 €
Pressurage direct, vinifié comme un blanc, bu comme un rosé.
<GrilleProduits
image={{ src: '/images/fullbleed-1.svg', alt: '…' }}
eyebrow="Six cuvées, trois appellations"
title="Nos *vins*"
lead="…"
categories={[{ key: 'blanc', label: 'Blancs' }, { key: 'rouge', label: 'Rouges' }]}
labels={{ singular: 'cuvée', plural: 'cuvées' }}
products={[
{
href: '/les-vins/les-trois-pierres/',
name: 'Les Trois Pierres',
category: 'blanc',
image: { src: '/images/packshot-1.svg', alt: '…' },
badge: 'Blanc', badgeColor: 'var(--detail)',
status: 'Disponible', statusHighlight: true,
price: '24,00 €',
description: 'Chardonnay de haut de coteau, dix mois de fût.',
meta: 'Millésime 2022 · 13 %',
},
// …
]}
/> Récolté de `recettes` (carnet + explorateur de diététique chinoise). Le grand frère de GrilleProduits : celui-là filtre sur un critère unique et suffit devant dix produits ; celui-ci sert dès qu'il faut croiser plusieurs entrées — couleur × millésime × accord × budget — avec une recherche plein texte. Les groupes sont déclaratifs (cle = paramètre d'URL, champ = attribut data- de la carte, mode = eq | in | lte) : le composant ne connaît aucune taxonomie, seulement ces trois informations. Les cartes sont rendues au build par l'appelant, dans le slot, avec leurs critères posés par attributsCarte() ; l'îlot ne fait que basculer hidden et aria-pressed — sans JavaScript, toutes les cartes sont dans le HTML et la page reste indexable. La recherche s'appuie sur un index JSON normalisé sans accent (taper « perriere » trouve « La Perrière ») plutôt que de recopier le texte dans chaque carte. L'état est reflété dans l'URL et relu au chargement : /les-vins/?couleur=blanc ouvre la page déjà réglée, et une sélection se partage par copier-coller. Critères secondaires rangés dans le tiroir « Affiner », compteur annoncé en role status, bouton « Tout effacer » qui n'apparaît qu'utile, état vide rédigé. Cuvées, chambres, greens fees, prestations, recettes.
Haut de coteau, dix mois de fût, une pointe de salinité.
Argile rouge, égrappé à moitié, dix-huit mois d’élevage.
La parcelle la plus froide du domaine : trois ans pour s’ouvrir.
Vendange entière, macération longue, aucun soufre avant la mise.
Le sommet du domaine, sur un demi-hectare de vieilles vignes.
Pressurage direct, vinifié comme un blanc, bu comme un rosé.
L’aligoté de la maison, vif, tendu, bu dans l’année.
Un demi-fût par an : la cuvée que le domaine garde pour la fin.
Aucune bouteille avec ces réglages. Élargissez un critère, ou effacez tout pour reparcourir la sélection entière.
<!-- Les groupes sont déclaratifs : le composant ne connaît aucune taxonomie. -->
const groupes: GroupeFiltre[] = [
{ cle: 'couleur', label: 'Couleur', champ: 'couleur', mode: 'eq', tout: 'Toutes',
options: optionsPresentes(COULEURS, cuvees, (c) => c.couleur) },
{ cle: 'accord', label: 'À table avec', champ: 'accords', mode: 'in', tout: 'Tout',
repli: true, options: optionsPresentes(ACCORDS, cuvees, (c) => c.accords) },
{ cle: 'prix', label: 'Budget', champ: 'prix', mode: 'lte', tout: 'Sans limite',
repli: true, options: [{ valeur: '25', label: 'moins de 25 €' }] },
];
<FiltresGrille
groupes={groupes}
total={cuvees.length}
colonnes={3}
labels={{ singulier: 'cuvée', pluriel: 'cuvées',
recherche: 'Chercher un vin, un cépage, une appellation',
placeholder: 'Chardonnay, Meursault, vieilles vignes…',
affiner: 'Affiner — accord, budget' }}
index={cuvees.map((c) => ({
cle: c.slug,
texte: [c.nom, c.appellation, c.cepage, c.note].join(' '),
}))}
vide={{ titre: 'Aucune bouteille avec ces réglages.' }}
>
{cuvees.map((c, i) => (
<li {...attributsCarte(
{ couleur: c.couleur, millesime: c.millesime, accords: c.accords, prix: c.prix },
c.slug,
)}>
<Card
href={'/les-vins/' + c.slug + '/'}
image={{ src: c.image, alt: 'Cuvée ' + c.nom }}
meta={c.appellation + ' · ' + c.millesime}
title={c.nom}
excerpt={c.note}
revealDelay={(i % 3) * 0.08}
/>
</li>
))}
</FiltresGrille> Récoltée d'antonin_lacour. Une feuille de papier : un losange par parcelle (sa taille dit le poids, sa couleur la catégorie), le nom des lieux, et un panneau qui détaille la parcelle désignée. Les positions ne sont pas dessinées à la main : elles sont projetées au build depuis les coordonnées réelles (lib/carte-parcelles.ts — aplatissement des longitudes, cadrage à échelle unique, écartement déterministe des losanges qui se recouvrent). Une parcelle ajoutée dans le contenu apparaît au bon endroit sans qu'on touche au dessin. Les losanges forment un radiogroup : flèches, Home/Fin, Entrée/Espace. Sans JavaScript, toutes les fiches sont dans le HTML (une visible), la page reste indexable. Parcelles d'un domaine, trous d'un parcours, sites d'un office de tourisme.
carte schématique — positions indicatives, parcelles réelles
<CarteParcelles
mention="carte schématique — positions indicatives"
categories={[
{ key: 'chardonnay', label: 'Chardonnay', color: 'var(--detail)' },
{ key: 'pinot', label: 'Pinot noir', color: 'var(--accent)' },
]}
places={[{ name: 'Meursault', lon: 4.7712, lat: 46.9782 }]}
plots={[
{
name: 'Les Trois Pierres',
place: 'Meursault',
category: 'chardonnay',
weight: 1.4,
lon: 4.7688, lat: 46.9801,
details: [
{ label: 'Cépage', value: 'Chardonnay' },
{ label: 'Âge des vignes', value: '48 ans (1978)' },
{ label: 'Surface', value: '1,4 ha' },
{ label: 'Exposition', value: 'Sud-est' },
],
note: 'La parcelle historique : les trois bornes de calcaire sont au bout du rang.',
},
// …
]}
/> Récoltée d'antonin_lacour (la carte des vins). Le complément de CarteParcelles : ici le fond est une œuvre fournie — une illustration importée, ou n'importe quel SVG passé en slot — et chaque repère est posé dessus en pourcentage du cadre, l'illustration faisant foi. Un repère est un lien ordinaire ; sa fiche minute (surtitre, titre, note, appel) s'ouvre au survol ou au focus en CSS seul — sans JavaScript, tout navigue. Au doigt, premier appui = fiche, second = navigation. Près d'un bord, la fiche s'aligne d'elle-même sur le repère ; sous 40 rem, elle s'ancre au bas de l'écran. Poser data-active-category sur la racine éteint les autres catégories : c'est le contrat qu'utilise un catalogue filtrant posé sur la même page. Cuvées sur leurs villages, trous d'un parcours sur la vue aérienne, chambres sur le plan de la maison.
<CarteReperes
image={carte}
imageAlt="Carte illustrée du domaine"
linkLabel="Voir la cuvée"
mention="carte illustrée — positions indicatives"
categories={[
{ key: 'rouge', label: 'Rouges', color: '#7a4038' },
{ key: 'blanc', label: 'Blancs', color: '#c8b273' },
]}
markers={[
{
title: 'Côtes-du-Couchois « Cuvée Amphore »',
kicker: 'Bourgogne Côtes-du-Couchois',
note: 'Rouge · Dracy-lès-Couches',
category: 'rouge',
href: '/nos-vins/cuvee-amphore/',
x: 27.2, y: 50.5,
},
// …
]}
/>
{/* Sans `image`, le slot porte le fond (SVG inline, <picture>…) : */}
<CarteReperes ratio="3 / 2" markers={…}>
<svg viewBox="0 0 1536 1024">…</svg>
</CarteReperes> Liste tarifaire <dl> : tarifs, cartes, menus, horaires. Fonctionne sur fond clair comme sombre. Props : items [{ label, value, note }]. Slot pour une note de bas de liste.
Dégustation offerte pour tout achat de six bouteilles.
<PriceList
items={[
{ label: 'Dégustation découverte', note: '4 vins · 45 minutes', value: '15 €' },
{ label: 'Verticale de millésimes', note: 'Sur rendez-vous', value: '60 €' },
]}
>
Dégustation offerte pour tout achat de six bouteilles.
</PriceList> Deux modes selon site.formEndpoint : endpoint renseigné → POST natif + fetch avec confirmation inline ; vide (mode actuel de la démo) → repli mailto: composé côté client. Pot-de-miel anti-spam intégré. Prop : subject (pré-remplit l'objet).
<ContactForm subject="Demande de visite" />
// src/data/site.ts
formEndpoint: 'https://formspree.io/f/xxxxxxx',