Vai al contenuto principale

Diario di bordo · Fase 0 · 6/7

Verre, il design system di Aubia

Verre sostituisce il vecchio design di aubia.dev e servirà anche per aubia.cloud e l'applicazione desktop: due temi in grigi neutri, un vetro grigio, Geist e componenti presi da un registry.

Pubblicato il Aggiornato il 10 min di lettura

  • design-system
  • verre
  • tailwind
  • motion

Lo sfondo scuro di aubia.dev è un grigio quasi nero, #0E0F12, senza la sfumatura blu che aveva alla prima pubblicazione di questo articolo. Il tema chiaro, "alba", si basa su un grigio molto pallido, #EDEFF2. Entrambi vengono da Verre, la nuova identità visiva di Aubia, di cui ho approvato il mockup il 5 ottobre 2026.

Ad agosto, questo articolo descriveva un altro sito: due temi Catppuccin completi, un vetro attraversato dalle macchie di colore dello sfondo, e Chakra Petch, un font angoloso riservato ai titoli di aubia.dev. Ogni app di Aubia definiva i propri colori e i propri componenti, e l'applicazione desktop prevedeva quattro varianti di tema mentre il sito ne proponeva due. Verre dà alle tre app un'unica identità, e il sito è il primo ad applicarla.

Da Luma, lo stile di componenti di shadcn/ui, ho ripreso i raggi dei bordi, la spaziatura, l'elevazione e le animazioni di stato. Il materiale, i colori, la tipografia e le icone sono propri di Verre.

Un registry shadcn fornisce i componenti

I componenti di Verre si trovano in un registry shadcn privato, servito da GitHub. Un'app non li installa come dipendenza: il comando shadcn add ne copia il codice sorgente nel repository dell'app, accanto al resto del suo codice. Ho scartato un pacchetto npm versionato: avrebbe fornito un codice compilato al posto di questi sorgenti, e avrei dovuto pubblicare e proteggere un ulteriore registry npm privato.

Ho installato nel sito 91 elementi del registry. Un manifest riporta l'hash SHA-256 di ogni file copiato, e un test verifica che la copia resti identica alla sorgente. Perché questo controllo regga, una copia non viene mai modificata direttamente: un adattamento di stile passa da un foglio di stile a parte, importato dopo quelli del registry.

Questi componenti si basano su Base UI 1.9.0, che shadcn ora raccomanda al posto di Radix. Base UI gestisce il focus, la navigazione da tastiera e i ruoli ARIA. Un link con l'aspetto di un pulsante passa da un componente ButtonLink separato, perché la primitiva Button imporrebbe a un link il ruolo di pulsante.

Le icone provengono da Hugeicons, in stile Stroke Rounded, con licenza MIT. Le ho preferite a Font Awesome Pro: la sua licenza è a pagamento, e il mockup approvato è disegnato con Hugeicons.

Due temi, con il sistema come scelta predefinita

Verre definisce un tema chiaro, "alba", e un tema scuro, "Notte". Quello scuro usa grigi neutri per le superfici, le linee e i testi. Da Catppuccin, la palette del vecchio sito, Verre conserva gli accenti: il pesca di Latte nel tema chiaro, #FE640B, quello di Mocha nel tema scuro, #FAB387, e poi, sulla home page, il verde acqua e il lavanda dei sottotitoli e il gradiente del titolo principale. Il pesca serve per i link, i punti elenco e il contorno del focus da tastiera.

I colori non formano più una palette completa esposta come classi. Prendono il nome dal loro ruolo, come sfondo, vetro, testo o accento, e ogni ruolo riceve un valore in ciascuno dei due temi. Un blocco @theme inline collega questi nomi a quelli di shadcn e alle utility di Tailwind: un componente usa bg-background o text-muted-foreground, e il valore segue il tema visualizzato.

Ho mantenuto i colori del mockup così com'erano, anche dove alcuni testi scendono sotto le soglie di contrasto WCAG AA, in entrambi i temi: il design system segnala questi contrasti deboli senza correggerli. Gli altri requisiti di accessibilità restano validi: navigazione da tastiera, focus visibile, aree di tocco di almeno 44 px sugli schermi larghi meno di 768 px, skip link.

Con il vecchio design, il sito si apriva nel tema scuro, qualunque fosse la preferenza del visitatore. Ho scelto come predefinito il tema del sistema: la sua preferenza è già impostata nel sistema operativo, e la stessa scelta varrà nelle tre app. Il selettore continua a proporre "Chiaro", "Scuro" e "Sistema".

Il tema è noto prima del primo rendering

Uno script inserito nel <head>, prima dei fogli di stile, legge la scelta salvata sotto la chiave aubia-theme e la preferenza del sistema. Imposta data-theme per una scelta esplicita e data-scheme per la preferenza: il CSS applica il tema giusto senza attendere React. Senza JavaScript, la media query prefers-color-scheme attiva il tema scuro quando il sistema lo richiede. Se lo storage locale è bloccato, la sua scelta vale solo per la visita in corso.

Il passaggio da un tema all'altro è animato. Dopo un clic sul selettore, la pagina attende 200 ms, il tempo che l'indicatore del selettore scivoli sulla nuova scelta, poi il nuovo tema si rivela in 650 ms dentro un cerchio che si allarga dal pulsante, tramite l'API View Transition. In modalità "Sistema", un cambiamento della preferenza produce una dissolvenza dell'intera pagina. Quando il sistema richiede il movimento ridotto, il tema cambia di colpo.

Il CSS della modalità "Sistema" legge data-scheme anziché la media query. Chromium mostra già i colori della nuova preferenza quando emette l'evento change: se la pagina seguisse la media query, sarebbe cambiata prima che lo script catturasse lo stato iniziale della transizione.

Le card riprendono il vetro grigio della barra

L'effetto vetro viene dalla proprietà backdrop-filter: sfoca ciò che passa dietro un elemento, e uno sfondo semiopaco tinge il risultato. Nel vecchio design, questo filtro saturava anche i colori, e quelli delle macchie dello sfondo attraversavano le card. Il vetro ordinario del design system, quello dei menu di selezione e delle notifiche, satura ancora, al 160%, con una sfocatura di 24 px. Quello della barra di navigazione sfoca altrettanto ma toglie ogni colore:

css
@utility surface-bar {
  background: var(--bar-glass);
  border: 1px solid var(--glass-line);
  backdrop-filter: var(--bar-blur);
}

--bar-blur vale blur(24px) grayscale(100%), e --bar-glass un bianco all'80% nel tema chiaro, un grigio al 70% nel tema scuro: gli aloni di luce dello sfondo non tingono più questo vetro. Sul sito lo riprendono le card, le domande della FAQ e, negli articoli, la copertina e le tabelle, senza riflessi al passaggio del puntatore.

Il menu delle lingue compare fuori dalla barra

Quando un antenato ha già un backdrop-filter, definisce una backdrop root: il filtro di un discendente non vede più ciò che si trova dietro quell'antenato. Il menu delle lingue della barra, un menu di Base UI, viene renderizzato in un portal: la barra non è più il suo antenato, e il menu applica una sfocatura propria. Il vetro della barra è definito sulla radice del documento perché anche questo menu possa ritrovarlo.

Questo menu si monta solo sul client: i suoi link sono assenti dall'HTML iniziale. Il piè di pagina riprende quindi questi link in un elemento <details> nativo: compaiono nell'HTML prodotto dal rendering lato server, e l'apertura funziona senza JavaScript. React vi aggiunge la navigazione con i tasti freccia, la chiusura con Esc o con un clic all'esterno, e all'apertura sposta il focus sulla lingua corrente.

Aloni e una trama di stelle sotto il contenuto

Lo sfondo delle pagine si compone di due livelli fissi. Il primo disegna in ogni angolo un'ellisse di luce del colore che il simbolo del logo ha nello stesso angolo: viola in alto a sinistra, rosa in alto a destra, corallo in basso a sinistra, pesca chiaro in basso a destra, più tenui nel tema scuro che in quello chiaro. Il secondo è una trama di forme di 7 px, soli-stella nel tema chiaro e stelle nel tema scuro, che la luce degli aloni rivela. Il suo passo, vicino ai 32 px, è un divisore esatto della larghezza del contenuto: una colonna di forme coincide con ciascuno dei due bordi del contenuto.

In tutto il sito, a intervalli da 0,3 a 0,9 secondi, una forma cresce e prende una tinta del tema, al massimo sei alla volta, e una lente d'ingrandimento illumina le forme vicine al puntatore. Il ciclo di animazione si ferma quando la scheda è nascosta.

Geist per il testo come per il codice

Ho tolto Chakra Petch. Ora tutto il testo è in Geist, titolo principale della home page compreso, e il codice, i percorsi e i comandi sono in Geist Mono. I due font sono variabili: un solo file WOFF2 latino per font copre i pesi da 100 a 900.

Sono ospitati sul sito. Il plugin dei font di laravel-vite-plugin legge i file dei pacchetti Fontsource installati e genera le dichiarazioni @font-face durante la build; la direttiva Blade @fonts le inserisce in tutte le pagine, insieme al loro precaricamento. Nessuna richiesta parte verso un fornitore di font.

Con display: 'swap', il testo compare prima in un font di fallback. Per Geist, il plugin adatta le metriche di questo fallback, e all'arrivo del font il testo quasi non si ricompone. Geist Mono mantiene un normale fallback monospace: il plugin cerca nel file la categoria del font, monospace o sans serif, non ne trova nessuna e prenderebbe Arial come modello.

Il movimento e una sola regola per ridurlo

Le durate e le curve di easing vengono da variabili CSS, --d-* e --ease-*, lette sia dai fogli di stile sia dagli script. La curva delle microinterazioni, --ease-out, vale cubic-bezier(.2, .8, .2, 1).

Al caricamento, nessun testo visibile parte da opacità zero, perché Chromium esclude dall'LCP un elemento invisibile. Il titolo principale della home page entra con uno slittamento di 14 px. La dissolvenza resta riservata ai testi che compaiono allo scorrimento, più in basso nella pagina, e alle decorazioni.

Prima di Verre, le regole per il movimento ridotto riguardavano alcune animazioni e lasciavano attive le altre transizioni. Verre le sostituisce con una sola, per tutto il sito:

css
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
    animation: none !important;
  }
}

Ogni stato compare allora subito. I colori cambiano ancora, ma all'istante; gli spostamenti all'hover e alla pressione passano dalla variante motion-safe: di Tailwind, che non si applica più.

Le animazioni gestite da script sfuggono a questa regola, come quella del testo introduttivo della home page, le cui frasi compaiono carattere per carattere. Leggono la preferenza tramite l'hook useReducedMotion del registry, costruito su useSyncExternalStore: si sottoscrive all'evento change di matchMedia, e un cambio d'impostazione durante la visita ferma subito l'animazione in corso. Il server non conosce questa preferenza. L'hook restituisce quindi false durante il rendering lato server e l'idratazione, poi legge il valore effettivo, come descrive la documentazione di React per useSyncExternalStore. L'HTML del server contiene già l'ultima frase, e con il movimento ridotto il testo introduttivo la mantiene.

Il codice degli articoli conserva i colori di Catppuccin

I blocchi di codice di questo blog vengono colorati lato server da Phiki 2.2, durante la build, con Catppuccin Latte per il tema chiaro e Mocha per quello scuro. Una classe PHP copia questi due temi e ne sostituisce i colori neutri, cioè sfondo, testo, commenti e punteggiatura, con variabili CSS di Verre; se un aggiornamento di Phiki elimina una delle regole di colore che sostituisce, la classe fallisce invece di distribuire un tema incompleto. Un blocco di codice prende quindi lo sfondo del tema visualizzato, e solo i suoi colori di sintassi vengono ancora da Catppuccin.

La beta di Aubia è gratuita e senza limiti di tempo. Per ricevere il suo invito basta iscriversi.

Si iscriva alla lista d'attesa

Torni al blog