Una guida ai moderni selettori di pseudo-classe CSS recentemente supportati
Pubblicato: 2022-03-10 I selettori di pseudo-classe sono quelli che iniziano con i due punti “ :
” e corrispondono in base a uno stato dell'elemento corrente. Lo stato può essere relativo all'albero del documento o in risposta a un cambiamento di stato come :hover
o :checked
.
:any-link
Sebbene definita in Selectors Level 4, questa pseudo-classe ha avuto il supporto cross-browser per un po' di tempo. La pseudo-classe any-link
corrisponderà a un collegamento ipertestuale di ancoraggio purché abbia un href
. Corrisponderà in un modo equivalente a far corrispondere entrambi :link
e :visited
contemporaneamente. In sostanza, questo può ridurre i tuoi stili di un selettore se stai aggiungendo proprietà di base come il color
che desideri applicare a tutti i collegamenti indipendentemente dal loro stato visitato.
:any-link { color: blue; text-underline-offset: 0.05em; }
Una nota importante sulla specificità è che :any-link
vincerà contro a
come selettore anche se a
è posizionato più in basso nella cascata poiché ha la specificità di una classe. Nell'esempio seguente, i collegamenti saranno viola:
:any-link { color: purple; } a { color: red; }
Quindi, se introduci :any-link
, tieni presente che dovrai includerlo nelle istanze di a
come selettore se saranno in diretta concorrenza per la specificità.
:focus-visible
Scommetto che una delle violazioni dell'accessibilità più comuni sul Web è la rimozione del outline
su elementi interattivi come collegamenti, pulsanti e input di moduli per il loro stato :focus
. Uno degli scopi principali di tale outline
è fungere da indicatore visivo per gli utenti che utilizzano principalmente le tastiere per navigare. Uno stato di messa a fuoco visibile è fondamentale come strumento di individuazione della strada poiché quegli utenti si spostano su un'interfaccia e per aiutare a rafforzare ciò che è un elemento interattivo. In particolare, il focus visibile è trattato nel criterio di successo WCAG 2.4.11: Focus Appearance (minimo).
La pseudo-classe :focus-visible
ha lo scopo di mostrare un anello di messa a fuoco solo quando l'interprete determina tramite euristica che dovrebbe essere visibile. In altre parole: i browser determineranno quando applicare :focus-visible
in base a cose come il metodo di input, il tipo di elemento e il contesto dell'interazione. A scopo di test tramite un computer desktop con input da tastiera e mouse, dovresti vedere gli stili :focus-visible
allegati quando inserisci una scheda in un elemento interattivo ma non quando fai clic su di esso, ad eccezione degli input di testo e delle aree di testo che dovrebbero mostrare :focus-visible
per tutti i tipi di input di messa a fuoco.
Nota : per maggiori dettagli, rivedere la bozza di lavoro delle specifiche :focus-visible
.
Le ultime versioni dei browser Firefox e Chromium sembrano ora gestire :focus-visible
sugli input dei moduli secondo la specifica che dice che l'UA dovrebbe rimuovere gli stili : :focus
quando :focus-visible
corrisponde. Safari non supporta ancora :focus-visible
, quindi dobbiamo assicurarci che uno stile :focus
sia incluso come ripiego per evitare di rimuovere il outline
per l'accessibilità.
Dato un pulsante e un input di testo con il seguente insieme di stili, vediamo cosa succede:
input:focus, button:focus { outline: 2px solid blue; outline-offset: 0.25em; } input:focus-visible { outline: 2px solid transparent; border-color: blue; } button:focus:not(:focus-visible) { outline: none; } button:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 2px #fff, 0 0 0 4px blue; }
Cromo e Firefox
-
input
Rimuovi correttamente gli stili:focus
quando gli elementi sono focalizzati tramite l'input del mouse a favore di:focus-visible
con conseguente modifica delborder-color
e nascondere iloutline
sull'input da tastiera -
button
Non usa solo:focus-visible
senza la regola aggiuntiva perbutton:focus:not(:focus-visible)
che rimuove il contorno su:focus
, ma consentirà la visibilità delbox-shadow
solo sull'input da tastiera
Safari
-
input
Continua a usare solo gli stili:focus
-
button
Questo sembra ora rispettare parzialmente l'intento di:focus-visible
sul pulsante nascondendo gli stili:focus
al clic, ma mostrando ancora gli stili:focus
sull'interazione della tastiera
Quindi, per ora, la raccomandazione sarebbe quella di continuare a includere :focus
stili e quindi migliorare progressivamente fino a utilizzare :focus-visible
che consente il codice demo. Ecco una CodePen con cui continuare a testare:
:focus-within
La pseudo-classe :focus-within
ha il supporto tra tutti i browser moderni e agisce quasi come un selettore genitore ma solo per una condizione molto specifica. Quando è collegato a un elemento contenitore e un elemento figlio corrisponde a :focus
, gli stili possono essere aggiunti all'elemento contenitore e/o a qualsiasi altro elemento all'interno del contenitore.
Un miglioramento pratico per utilizzare questo comportamento è lo stile di un'etichetta di modulo quando l'input associato ha lo stato attivo. Affinché ciò funzioni, avvolgiamo l'etichetta e l'input in un contenitore, quindi alleghiamo :focus-within
di quel contenitore e selezioniamo l'etichetta:
.form-group:focus-within label { color: blue; }
Ciò fa sì che l'etichetta diventi blu quando l'input è attivo.
Questa demo CodePen include anche l'aggiunta di una struttura direttamente al contenitore .form-group
:
:is()
Conosciuta anche come pseudo-classe "corrisponde a qualsiasi", :is()
può prendere un elenco di selettori con cui provare a confrontare. Ad esempio, invece di elencare individualmente gli stili di intestazione, puoi raggrupparli sotto il selettore di :is(h1, h2, h3)
.
Un paio di comportamenti unici sull'elenco di selezione :is()
:
- Se un selettore elencato non è valido, la regola continuerà a corrispondere ai selettori validi. Dato
:is(-ua-invalid, article, p)
la regola corrisponderà adarticle
ep
. - La specificità calcolata sarà uguale a quella del selettore passato con la specificità più alta. Ad esempio,
:is(#id, p)
avrà la specificità di#id
— 1.0.0 — mentre:is(p, a)
avrà una specificità di 0.0.1.
Il primo comportamento di ignorare i selettori non validi è un vantaggio chiave. Quando si utilizzano altri selettori in un gruppo in cui un selettore non è valido, il browser eliminerà l'intera regola. Ciò entra in gioco in alcuni casi in cui i prefissi del fornitore sono ancora necessari e il raggruppamento di selettori con prefisso e senza prefisso causa il fallimento della regola in tutti i browser. Con :is()
puoi raggruppare in sicurezza quegli stili e si applicheranno quando corrispondono e verranno ignorati quando non lo fanno.
Per me, raggruppare gli stili di intestazione come accennato in precedenza è già una grande vittoria con questo selettore. È anche il tipo di regola che mi sentirei a mio agio nell'usare senza un fallback quando applico stili non critici, come ad esempio:
:is(h1, h2, h3) { line-height: 1.2; } :is(h2, h3):not(:first-child) { margin-top: 2em; }
In questo esempio (che deriva dagli stili del documento nel mio progetto SmolCSS), avere l' line-height
maggiore ereditata dagli stili di base o la mancanza del margin-top
non è davvero un problema per i browser che non supportano. È semplicemente tutt'altro che ideale. Quello che non vorresti usare :is()
per ancora sarebbero stili di layout critici come Grid o Flex che controllano in modo significativo la tua interfaccia.
Inoltre, quando concatenato a un altro selettore, puoi verificare se il selettore di base corrisponde a un selettore discendente all'interno di :is()
. Ad esempio, la regola seguente seleziona solo i paragrafi che sono discendenti diretti degli articoli. Il selettore universale viene utilizzato come riferimento al selettore di base p
.
p:is(article > *)
Per il miglior supporto attuale, se desideri iniziare a usarlo, vorrai anche raddoppiare gli stili includendo regole duplicate usando :-webkit-any()
e :matches()
. Ricorda di stabilire queste regole individuali, o anche il browser di supporto lo eliminerà! In altre parole, includi tutto quanto segue:
:matches(h1, h2, h3) { } :-webkit-any(h1, h2, h3) { } :is(h1, h2, h3) { }
Vale la pena menzionare a questo punto che insieme ai selettori più recenti c'è una variazione aggiornata di @supports
che è @supports selector
. Questo è disponibile anche come @supports not selector
.
Nota : al momento (dei browser moderni), solo Safari non supporta questa regola.
Potresti verificare il supporto :is()
con qualcosa di simile al seguente, ma in realtà perderesti il supporto di Safari poiché Safari supporta :is()
ma non supporta @supports selector
.
@supports selector(:is(h1)) { :is(h1, h2, h3) { line-height: 1.1; } }
:where()
La pseudo-classe :where()
è quasi identica a :is()
tranne per una differenza fondamentale: avrà sempre specificità zero. Ciò ha incredibili implicazioni per le persone che stanno costruendo framework, temi e sistemi di progettazione . Utilizzando :where()
, un autore può impostare i valori predefiniti e gli sviluppatori a valle possono includere sostituzioni o estensioni senza conflitti di specificità.
Considera il seguente insieme di stili img
. Usando :where()
, anche con un selettore di specificità più elevato, la specificità rimane zero. Nell'esempio seguente, quale bordo di colore pensi che avrà l'immagine?
:where(article img:not(:first-child)) { border: 5px solid red; } :where(article) img { border: 5px solid green; } img { border: 5px solid orange; }
La prima regola non ha specificità poiché è interamente contenuta all'interno di :where()
. Quindi, direttamente contro la seconda regola, vince la seconda. Introducendo il selettore img
element-only come ultima regola, vincerà a causa della cascata. Questo perché verrà calcolato con la stessa specificità della regola :where(article) img
poiché la parte :where()
non aumenta la specificità.
L'uso di :where()
insieme ai fallback è un po' più difficile a causa della funzione di specificità zero poiché è probabile che quella funzione sia il motivo per cui vorresti usarla su :is()
. E se aggiungi regole di fallback, è probabile che queste battano :where()
per la sua stessa natura. Inoltre, ha un supporto complessivo migliore rispetto al @supports selector
, quindi è improbabile che provare a usarlo per creare un fallback fornisca molto (se presente) di guadagno. Fondamentalmente, sii consapevole dell'impossibilità di creare correttamente fallback per :where()
e controlla attentamente i tuoi dati per determinare se è sicuro iniziare a utilizzare per il tuo pubblico unico.
Puoi testare ulteriormente :where()
con il seguente CodePen che utilizza i selettori img
dall'alto:
Migliorato :not()
Il selettore di base :not()
è supportato da Internet Explorer 9. Ma i selettori di livello 4 migliorano :not()
consentendogli di prendere un elenco di selettori, proprio come :is()
e :where()
.
Le seguenti regole forniscono lo stesso risultato nel supporto dei browser:
article :not(h2):not(h3):not(h4) { margin-bottom: 1.5em; } article :not(h2, h3, h4) { margin-bottom: 1.5em; }
La capacità di :not()
di accettare un elenco di selezione ha un ottimo supporto per i browser moderni.
Come abbiamo visto con :is()
, :not()
avanzato può anche contenere un riferimento al selettore di base come discendente usando *
. Questo CodePen dimostra questa capacità selezionando collegamenti che non sono discendenti di nav
.
Bonus : la demo precedente include anche un esempio di concatenamento di :not()
e :is()
per selezionare immagini che non sono fratelli adiacenti di elementi h2
o h3
.
Proposta ma "a rischio" — :has()
La pseudo-classe finale che è una proposta molto eccitante ma non ha un browser che la implementa anche in modo sperimentale è :has()
. In effetti, è elencato nella bozza dell'editore di livello 4 del selettore come “a rischio”, il che significa che è riconosciuto che ha difficoltà nel completare la sua implementazione e quindi potrebbe essere escluso dalla raccomandazione.
Se implementato, :has()
sarebbe essenzialmente il "selettore genitore" che molte persone CSS hanno desiderato avere a disposizione. Funzionerebbe con una logica simile a una combinazione di :focus-within
e :is()
con selettori discendenti, dove stai cercando la presenza di discendenti ma lo stile applicato sarebbe all'elemento genitore.
Data la seguente regola, se la navigazione conteneva un pulsante, la navigazione avrebbe ridotto il riempimento superiore e inferiore:
nav { padding: 0.75rem 0.25rem; nav:has(button) { padding-top: 0.25rem; padding-bottom: 0.25rem; }
Ancora una volta, questo non è attualmente implementato in nessun browser nemmeno sperimentalmente, ma è divertente pensarci! Robin Rendle ha fornito ulteriori approfondimenti su questo futuro selettore su CSS-Tricks.
Menzione d'Onore dal Livello 3: :empty
Un'utile pseudo-classe che potresti aver perso da Selettori di livello 3 è :empty
che corrisponde a un elemento quando non ha elementi figlio, inclusi i nodi di testo.
La regola p:empty
corrisponderà a <p></p>
ma non a <p>Hello</p>
.
Un modo in cui puoi usare :empty
è nascondere gli elementi che forse sono segnaposto per il contenuto dinamico popolato con JavaScript. Forse hai un div che riceverà risultati di ricerca e quando sarà popolato avrà un bordo e un riempimento. Ma senza ancora risultati, non vuoi che occupi spazio sulla pagina. Usando :empty
puoi nasconderlo con:
.search-results:empty { display: none; }
Potresti pensare di aggiungere un messaggio nello stato vuoto ed essere tentato di aggiungerlo con uno pseudo-elemento e un content
. L'insidia qui è che i messaggi potrebbero non essere disponibili per gli utenti di tecnologie assistive che non sono coerenti sul fatto che possano accedere ai content
. In altre parole, per assicurarti che sia accessibile un tipo di messaggio "nessun risultato" , vorresti aggiungerlo come un elemento reale come un paragrafo ( aria-label
non sarebbe più accessibile per un div nascosto).
Risorse per l'apprendimento dei selettori
CSS ha molti più selettori comprensivi di pseudo-classi. Ecco alcuni altri posti per saperne di più su ciò che è disponibile:
- La documentazione dei selettori CSS di MDN include un elenco categorizzato completo;
- Ho scritto una guida in due parti ai selettori CSS avanzati, puoi iniziare con la prima parte;
- Divertiti a conoscere i selettori CSS con il gioco CSS Diner;
- Kitty Giraudel ha creato uno strumento di spiegazione del selettore che analizzerà e descriverà parti di un selettore fornito.