Componenti e isole
Catalogo
Sezione intitolata “Catalogo”| Componente | Tipo | Props principali |
|---|---|---|
BaseLayout.astro |
layout | title, description, image |
Navbar.astro |
struttura | legge site.nav e site.cta |
Footer.astro |
struttura | legge getSiteSettings() e site |
Hero.astro |
sezione | title, subtitle, eyebrow, image, cta, variant (home/page) |
SectionHeader.astro |
sezione | eyebrow, title, intro, action, align |
Faq.astro |
sezione | items: { q, a }[] |
PricingTable.astro |
sezione | rows: Tariffa[], testi opzionali |
PromoCard.astro |
card | promo: Promo |
Gallery.astro |
sezione | items: Spazio[] |
Team.astro |
sezione | members: Collaboratore[] |
Timeline.astro |
sezione | entries: TimelineEntry[] |
WhatsAppButton.astro |
utility | whatsapp, label (si nasconde se vuoto) |
AccessibilityBadge.astro |
utility | label |
PartnerCarousel.tsx |
isola | partners: { name, img, url? }[] |
EventFilters.tsx |
isola | events: Evento[], categories: string[] |
ContactForm.tsx |
isola | subjects, privacyUrl, heading |
CookieBanner.tsx |
isola | cookieUrl |
ConsentMap.tsx |
isola | src, title, mapClass |
Tutti gli stili sono token semantici: vedi Design system.
Astro o isola?
Sezione intitolata “Astro o isola?”Regola: se non c’è stato né evento, non è un’isola.
| Serve | Scelta |
|---|---|
Solo HTML/CSS, anche con <details> o anchor |
componente Astro |
| Stato locale, filtri, submit, localStorage | isola Preact |
| Serve subito al primo paint | client:load |
| Sta sotto la piega | client:visible |
Nel template:
CookieBanner→client:load(deve comparire subito).ContactForm,EventFilters,PartnerCarousel,ConsentMap→client:visible.- Tutto il resto è HTML statico.
Form contatti
Sezione intitolata “Form contatti”ContactForm fa:
- validazione client (nome, email, messaggio, consenso privacy);
- precompila l’oggetto da query string (
?servizio=,?evento=); - POST a
${PUBLIC_WORDPRESS_API_BASE_URL}/wp-json/headless/v1/contact; - mostra errori di campo restituiti dal server o un errore generico;
- invia un campo honeypot (
azienda) che gli utenti non vedono.
Il server (mu-plugin) rivalida tutto, applica un rate limit di 60 secondi per IP, salva
il messaggio nel CPT contatto e invia una mail. Il client non è mai la fonte di verità.
Cookie e mappa
Sezione intitolata “Cookie e mappa”lib/consent.tsgestisce una chiave inlocalStorage(site-cookie-consent) e due eventi:site:consentesite:open-preferences.CookieBannersi apre se non c’è scelta; il footer riapre le preferenze.ConsentMapnon carica nulla da Google finché non c’è consenso esplicito.- Nessuno script esterno, nessuna CMP di terze parti.
Filtri eventi
Sezione intitolata “Filtri eventi”EventFilters riceve eventi già filtrati dalla build e le categorie da
site.eventCategories. Il filtro è client-side: nessuna richiesta al server, nessun
caricamento progressivo. Mostra:
- il prossimo evento in evidenza;
- la griglia dei prossimi;
- l’archivio dei passati, se presente.
Le categorie devono combaciare tra WordPress (cat) e src/data/site.ts.
Carosello partner
Sezione intitolata “Carosello partner”Marquee continuo con pausa on hover. Con prefers-reduced-motion diventa una lista
scrollabile da tastiera. Il componente calcola quante copie servono per coprire il
contenitore, quindi non lascia buchi a destra.
Creare una sezione nuova
Sezione intitolata “Creare una sezione nuova”- Il componente riceve dati tipizzati (mai
any). - Non fa fetch: i dati arrivano dalla pagina via
getCollection. - Usa i token, non colori.
- Prevede lo stato vuoto: se non ci sono dati, la sezione non compare.
- Se serve interattività, valuta se basta HTML nativo (
<details>,<dialog>, form). - Aggiungi la sezione alla pagina e, se vuoi, alla home.
---import type { Testimonianza } from "../domain/Testimonianza";
interface Props { items: Testimonianza[];}
const { items } = Astro.props;---
{items.length > 0 && ( <section class="bg-panel py-24"> <div class="mx-auto max-w-7xl px-6"> <div class="grid gap-8 md:grid-cols-2"> {items.map((item) => ( <blockquote class="rounded-2xl border border-line bg-surface p-8 text-muted"> “{item.quote}” <footer class="mt-4 text-sm font-bold text-fg">{item.author}</footer> </blockquote> ))} </div> </div> </section>)}