Guida completa alle ombre CSS: box-shadow, drop-shadow e text-shadow spiegati
Padroneggia tutte e tre le tecniche di ombra CSS — sintassi, parametri, differenze di rendering, pattern reali e implicazioni sulle performance.
Le ombre sono tra gli strumenti più semplici in CSS, eppure la maggior parte dei tutorial le tratta solo in superficie. Questa guida analizza in profondità tutti e tre i meccanismi — sintassi, parametri, differenze di rendering, casi d'uso reali e implicazioni sulle performance — così puoi scegliere sempre la tecnica giusta.
Perché CSS ha tre proprietà per le ombre?
Ogni meccanismo di ombra esiste per risolvere un problema diverso. Sono emersi in momenti diversi della storia di CSS e si rivolgono a target di rendering fondamentalmente differenti:
box-shadow— ombre per box rettangolari (il box model CSS). Disponibile da CSS 2.1, standardizzato in CSS3.filter: drop-shadow()— ombre che seguono la forma visibile reale dell'elemento, inclusa la trasparenza. Parte della specifica CSS Filter Effects Level 1.text-shadow— ombre applicate direttamente ai contorni dei glifi. Tecnicamente la più antica delle tre, introdotta in CSS 2, temporaneamente rimossa, poi ri-standardizzata in CSS3.
La confusione nasce perché tutte e tre producono ciò che l'occhio umano percepisce come "un'ombra", ma il motore di rendering le gestisce in fasi completamente diverse della pipeline di pittura.
Usa box-shadow per elementi UI con sfondi solidi. Usa drop-shadow per PNG, SVG o qualsiasi cosa con trasparenza. Usa text-shadow esclusivamente sul testo.
box-shadow: il cavallo di battaglia
Disegna una o più ombre dietro (o dentro) il border box di un elemento — la proprietà ombra più versatile e utilizzata.
Sintassi
box-shadow: [inset] offset-x offset-y [blur-radius] [spread-radius] color; Ogni valore svolge esattamente un compito:
| Parametro | Default | Cosa controlla |
|---|---|---|
inset | omesso (esterno) | Disegna l'ombra dentro l'elemento invece che dietro |
offset-x | obbligatorio | Distanza orizzontale. Positivo = destra, negativo = sinistra |
offset-y | obbligatorio | Distanza verticale. Positivo = basso, negativo = alto |
blur-radius | 0 | Quantità di sfocatura gaussiana. Più alto = più morbido e diffuso |
spread-radius | 0 | Espande (+) o contrae (–) l'ombra prima della sfocatura |
color | currentColor | Colore dell'ombra — usa sempre rgba() o hsla() per realismo |
Lo spread radius: il parametro sottoutilizzato
La maggior parte degli sviluppatori usa offset + blur e si ferma lì. Lo spread radius sblocca una classe di effetti completamente diversa. Con spread: 0 e blur: 0 diventa un bordo CSS perfetto e componibile che non influenza il layout:
/* Layered outline rings — no layout impact */
box-shadow:
0 0 0 4px rgba(201,184,154,.4),
0 0 0 8px rgba(201,184,154,.1); Con uno spread negativo puoi trasformare un'ombra sfocata in una direzionale che appare solo sotto l'elemento:
/* Shadow only at the bottom — more realistic */
box-shadow: 0 12px 24px -8px rgba(0,0,0,.6); Esempi live
4px 4px 8px rgba(0,0,0,.7) 0 0 0 4px accent inset 0 4px 12px 0 2px 4px + 0 8px 24px + ring 0 0 24px + 0 0 48px light + dark inset pair backdrop-blur + shadow Ombre multiple: l'ordine conta
Puoi impilare un numero illimitato di ombre con una lista separata da virgole. Il rendering avviene dall'avanti all'indietro — la prima ombra della lista è sopra. Questo permette di stratificare ombre ambientali e direzionali per una profondità naturale sia in modalità chiara che scura.
/* Three-layer system used in serious design systems */
box-shadow:
0 1px 2px rgba(0,0,0,.3), /* close shadow: sharp, small */
0 4px 16px rgba(0,0,0,.25), /* mid shadow: soft ambient */
0 0 0 1px rgba(255,255,255,.05); /* subtle highlight ring */ Ombre inset: profondità e stati premuti
La parola chiave inset sposta l'ombra all'interno del bordo dell'elemento. La direzione dell'offset si inverte intuitivamente: un offset Y positivo posiziona l'ombra lungo il bordo interno superiore, come se la luce venisse dall'alto. Essenziale per pulsanti premuti, input testuali in rilievo e neumorfismo:
/* Input focus state — depth without a visible border */
input:focus {
box-shadow:
inset 0 2px 6px rgba(0,0,0,.5),
0 0 0 2px rgba(201,184,154,.4);
} Tool gratuito Generatore Box Shadow Regola tutti i parametri e copia il CSS pronto per la produzione filter: drop-shadow() — ombre che seguono la forma
filter: drop-shadow() non è una proprietà — è una funzione CSS filter. I filtri vengono applicati dopo la pittura, operando sui pixel renderizzati finali di un elemento e dei suoi discendenti. Questa distinzione ha due conseguenze importanti:
- L'ombra segue il contorno visibile reale, rispettando la trasparenza alpha nei PNG e la forma dei percorsi SVG.
- Il filtro influenza l'intero sottoalbero, inclusi i figli. Questo significa che funziona automaticamente su gruppi di elementi.
Sintassi
filter: drop-shadow(offset-x offset-y blur-radius color); Nota: nessun spread-radius e nessuna parola chiave inset. La specifica li ha deliberatamente omessi. Se hai bisogno di spread su un'ombra che segue la forma, combina due chiamate drop-shadow() con raggi di sfocatura diversi.
Dove box-shadow fallisce e drop-shadow eccelle
Immagina un logo PNG trasparente su uno sfondo colorato. Applica box-shadow e otterrai un'ombra rettangolare attorno al bounding box invisibile — l'ombra sconfina nelle aree trasparenti. Applica drop-shadow() e l'ombra abbraccia la forma visibile reale del logo.
La stessa logica si applica a:
- Elementi SVG — percorsi, poligoni, forme composite
- CSS clip-path — l'ombra segue il confine del clip
- Elementi ruotati o trasformati — l'ombra segue correttamente la forma post-trasformazione
- Gruppi di componenti — un filtro sul parent ombreggia tutti i figli come unità
/* SVG icon with proper shadow */
.icon-wrapper {
filter: drop-shadow(0 4px 8px rgba(0,0,0,.6));
}
/* PNG logo — shadow follows logo shape, not its bounding box */
.logo {
filter: drop-shadow(2px 4px 12px rgba(0,0,0,.5));
}
/* Stacked for a glow effect on an irregular shape */
.neon-svg {
filter:
drop-shadow(0 0 6px rgba(201,184,154,.8))
drop-shadow(0 0 20px rgba(201,184,154,.4));
} Il compromesso chiave: il contesto di impilamento
Applicare filter crea un nuovo contesto di impilamento e un nuovo layer di compositing. Di solito va bene, ma significa che l'elemento non può più sovrapporsi correttamente ad altri layer di compositing — il che causa occasionalmente problemi di z-index. Se lo incontri, valuta se box-shadow può approssimare l'effetto.
text-shadow: profondità tipografica
text-shadow viene applicato esclusivamente ai contorni dei glifi del testo — non al box dell'elemento. Viene disegnato nella stessa fase del testo, dietro i glifi, rendendolo la scelta giusta quando si vuole profondità o leggibilità su elementi tipografici.
Sintassi
text-shadow: offset-x offset-y [blur-radius] color; Rispetto a box-shadow, non ci sono spread-radius né inset. Le ombre multiple sono separate da virgole, con lo stesso ordine dall'avanti all'indietro.
Esempi live
2px 2px 4px rgba(0,0,0,.8) 0 0 10px + 0 0 30px light + dark offset pair hard double offset Casi d'uso comuni
Leggibilità su immagini: Una sottile text-shadow: 0 1px 3px rgba(0,0,0,.6) rende il testo bianco leggibile su fotografie luminose senza necessità di un overlay scuro.
Tipografia neon/glow: Impila due ombre con offset zero e raggi di sfocatura crescenti. L'ombra interna crea il nucleo intenso, quella esterna crea il bagliore atmosferico:
.neon {
color: #c9b89a;
text-shadow:
0 0 8px rgba(201,184,154,.9),
0 0 24px rgba(201,184,154,.5),
0 0 60px rgba(201,184,154,.2);
} Letterpress / in rilievo: Combina un'ombra chiara in alto a sinistra con una scura in basso a destra. La direzione determina se il testo appare in rilievo o in incasso:
/* Raised (light = up-left, dark = down-right) */
text-shadow: -1px -1px 0 rgba(255,255,255,.15), 1px 1px 0 rgba(0,0,0,.6);
/* Recessed (reversed) */
text-shadow: 1px 1px 0 rgba(255,255,255,.1), -1px -1px 0 rgba(0,0,0,.5); Hard drop shadow (stile retro/fumetto): Zero blur, offset maggiore, ombre hard impilate:
.retro {
text-shadow: 3px 3px 0 #7a5c38, 6px 6px 0 rgba(0,0,0,.25);
} Tool gratuito Generatore Text Shadow Crea ed esporta effetti text-shadow con anteprima in tempo reale Confronto affiancato
| Caratteristica | box-shadow | drop-shadow() | text-shadow |
|---|---|---|---|
| Target | Box model elemento | Forma pixel renderizzata | Contorni glifi testo |
| Rispetta trasparenza | No — ignora alpha | Sì — segue la forma | N/A (solo testo) |
| spread-radius | Sì | No | No |
| Parola chiave inset | Sì | No | No |
| Layer multipli | Sì, separati da virgola | Sì, funzioni concatenate | Sì, separati da virgola |
| Contesto di impilamento | Nessun nuovo contesto | Crea nuovo contesto | Nessun nuovo contesto |
| Si applica ai figli | No | Sì (intero sottoalbero) | No |
| Funziona su forme SVG | Solo bounding box | Sì, per percorso | N/A |
| Animazione CSS | Animabile | Animabile | Animabile |
| Compositing GPU | Spesso, su layer promossi | Sì, sempre | Raramente |
La domanda che determina quale usare
Chiediti: l'elemento ha uno sfondo rettangolare e opaco? Se sì, usa box-shadow — è più semplice da ragionare e non ha effetti collaterali sul contesto di impilamento. Se no (PNG trasparente, SVG, forma irregolare, clip-path), usa drop-shadow(). Se il target è il testo, usa text-shadow.
Ricette e pattern per la produzione
Sistema di elevazione (stile Material)
Invece di hardcodare i valori delle ombre in tutto il codebase, definisci una scala di elevazione come proprietà CSS personalizzate. Questo rende la profondità globale coerente e facile da aggiornare:
:root {
--shadow-1: 0 1px 2px rgba(0,0,0,.4);
--shadow-2: 0 2px 6px rgba(0,0,0,.35), 0 1px 2px rgba(0,0,0,.3);
--shadow-3: 0 4px 16px rgba(0,0,0,.3), 0 2px 4px rgba(0,0,0,.3);
--shadow-4: 0 8px 32px rgba(0,0,0,.28), 0 4px 8px rgba(0,0,0,.25);
--shadow-5: 0 16px 48px rgba(0,0,0,.25), 0 8px 16px rgba(0,0,0,.2);
}
.card { box-shadow: var(--shadow-2); }
.card:hover { box-shadow: var(--shadow-4); transition: box-shadow .2s ease; }
.modal { box-shadow: var(--shadow-5); } Ombre colorate
Una delle tecniche più sottoutilizzate: invece di ombre nere, tinta l'ombra con il colore dominante dell'elemento. Si integra meglio nei temi chiari e scuri e crea una qualità simile al glow senza risultare aggressiva:
/* Button with colored shadow matching brand color */
.btn-primary {
background: #c9b89a;
box-shadow: 0 4px 20px rgba(201,184,154,.4), 0 2px 6px rgba(201,184,154,.3);
}
.btn-primary:hover {
box-shadow: 0 6px 28px rgba(201,184,154,.55), 0 3px 8px rgba(201,184,154,.4);
} Glassmorfismo con ombra
Il glassmorfismo richiede un interplay calibrato tra backdrop-filter, sfondo traslucido, bordo semi-trasparente e box-shadow per separare la card dallo sfondo:
.glass-card {
background: rgba(255, 255, 255, 0.06);
backdrop-filter: blur(16px) saturate(1.2);
-webkit-backdrop-filter: blur(16px) saturate(1.2);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 16px;
box-shadow:
0 8px 32px rgba(0,0,0,.4), /* ambient depth */
inset 0 1px 0 rgba(255,255,255,.08); /* top highlight */
} PixCode Glassmorphism Generator esporta il CSS completo inclusi i prefissi vendor.
Focus ring con box-shadow
Sostituire il outline predefinito del browser con box-shadow dà pieno controllo sullo stile mantenendo l'accessibilità. Il trucco è usare un'ombra solo di spread con blur a 0:
:focus-visible {
outline: none;
box-shadow:
0 0 0 2px var(--bg), /* gap between element and ring */
0 0 0 4px rgba(201,184,154,.7); /* visible ring */
} Neumorfismo
Il neumorfismo usa coppie di ombre inset e outer derivate dal colore di sfondo. Elemento, ombra e sfondo devono usare tonalità strettamente correlate — il contrasto eccessivo rompe l'illusione:
/* Element bg = slightly lighter than page bg */
.neumorphic {
background: #1c1c1c;
border-radius: 16px;
box-shadow:
8px 8px 16px #0a0a0a, /* dark shadow: down-right */
-8px -8px 16px #2e2e2e; /* light shadow: up-left */
}
.neumorphic.pressed {
box-shadow:
inset 4px 4px 10px #0a0a0a,
inset -4px -4px 10px #2e2e2e;
} Considerazioni sulle performance
Le ombre sono tra le operazioni di rendering di cui gli sviluppatori si preoccupano di più, spesso senza motivo. Ecco cosa accade realmente:
✓ Percorsi veloci
box-shadow su layer compositing GPU (position:fixed, will-change:transform). filter: drop-shadow() è sempre accelerato GPU. Animare box-shadow tramite transizioni CSS su elementi promossi.
✗ Percorsi lenti
Animare box-shadow su elementi non promossi innesca layout + paint ad ogni frame. Raggi di sfocatura grandi su filter: drop-shadow() sono costosi. text-shadow con sfocatura molto grande su molti elementi.
Quando si animano le ombre
Il pattern più sicuro per ombre animate (elevazioni hover, stati di caricamento) è promuovere prima l'elemento, poi animare l'ombra:
.card {
will-change: transform; /* promote to compositing layer */
transform: translateZ(0); /* trigger layer in older browsers */
transition: box-shadow .2s ease, transform .2s ease;
box-shadow: var(--shadow-2);
}
.card:hover {
transform: translateY(-2px); /* move on GPU — free */
box-shadow: var(--shadow-4); /* re-composite — also fast */
} Non applicare will-change indiscriminatamente — ogni layer promosso consuma memoria GPU. Usalo solo su elementi che si animano davvero, e rimuovilo al termine dell'animazione se aggiunto via JavaScript.
Performance: blur-radius vs spread-radius
Aumentare blur-radius è più costoso che aumentare spread-radius, perché il blur richiede una convoluzione gaussiana che scala con l'area in pixel. Un'ombra con spread: 10px, blur: 2px è meno costosa di spread: 0, blur: 12px per la stessa impronta visiva.
Generatori di ombre interattivi su PixCode.io
Tutte e tre le tecniche di ombra hanno generatori interattivi dedicati su PixCode — regola i parametri con slider, vedi il risultato live e copia il CSS pronto per la produzione con un clic.