CSS Zibaldone

Uso e abuso del contenuto generato

A Marcello Cerruti, amico e maestro.

Introduzione

Tradizionalmente si è portati a ritenere che tra struttura S (codice HTML) e presentazione P (codice CSS) esista una corrispondenza biunivoca tale che ad ogni elemento di un insieme corrisponderà un unico elemento nell'altro insieme. Nello specifico, si consideri il seguente esempio:

.warning {
color: #c00;
background: #ffc;
}

In questo caso un elemento class in P corrisponde a più elementi in S. L'attributo class ha solo valore presentazionale. D'altro canto l'attributo id ha sia valore presentazionale che strutturale, in quanto può non solo identificare uno stile unico nella pagina, ma anche, ad esempio, un'ancora. Inoltre mentre class può essere applicato a diversi elementi, id conserva una sua peculiare univocità.

Inoltre è altrettanto sbagliato ritenere che i due modelli (o insiemi) siano reciprocamente interdipendenti: la struttura sopravvive ed è fruibile senza la presentazione, mentre la presentazione non ha senso senza la struttura.

In questo contesto occorre stabilire il ruolo del contenuto generato ed il suo rapporto tra struttura e presentazione. Indicheremo il contenuto generato con G, e vedremo quando un abuso di quest'ultimo è dannoso alla fruibilità delle pagine web.

Il contenuto generato

Per definizione il contenuto generato viene prodotto dal CSS attraverso l'uso combinato degli pseudo-elementi :before (b) e :after (a) e della proprietà 'content'. Quindi non è azzardato scrivere che:

G ∈ P

Tuttavia l'uso degli pseudo-elementi pone un problema in tale rapporto, in quanto essi sono interpretati dal browser come:

<p>
<before>G</before>
Contenuto del paragrafo
<after>G</after>
</p>

Il risultato non è dissimile da quello che otteniamo con JavaScript tramite document.write, ed infatti il contenuto generato può essere considerato come una forma di scripting CSS, di cui conserva una traccia nell'uso di certe funzioni (come 'attr(valore)' o 'counters(contatore)').

Posto che gli pseudo-elementi, all'atto dell'inserimento del contenuto, vengono considerati come elementi non presenti nel sorgente e come unità di memorizzazione del contenuto, si potrebbe giungere alla conclusione secondo cui:

a ∈ S
b ∈ S

Quindi, in via ipotetica, anche:

G ∈ S

Questo azzardo è smentito dal fatto che non vi è traccia di tali pseudo-elementi nel sorgente, ma è pur vero che i browser considerino vero tale rapporto all'atto della formattazione della pagina.

Un diagramma ipotetico potrebbe essere il seguente:

Intersezione di S e P tramite G

Come si evince dalle precedenti considerazioni, l'uso del contenuto generato pone dei problemi a livello di accessibilità alle informazioni della pagina, in quanto:

  1. A differenza di JavaScript, il contenuto generato non è supportato da Internet Explorer per Windows. Di conseguenza, tutte le informazioni veicolate dal contenuto generato risulteranno inaccessibili agli utenti di questo browser.
  2. Con i CSS disabilitati, le informazioni veicolate dal contenuto generato sono inaccessibili (simile in questo a JavaScript quando viene disabilitato).

Queste due considerazioni ci spingono ad approfondire ulteriormente l'argomento, esaminando da vicino alcuni casi di uso e abuso del contenuto generato.

Sic et non: uso e abuso

Cominciamo da un semplice esempio:

p.nota:before {
content: "Nota: ";
font-weight: bold;
padding-right: 5px;
}

Questo esempio è stato a lungo citato come un buon uso del contenuto generato. In realtà, se i CSS sono disabilitati o il contenuto generato non è supportato, l'utente non vedrà (e neppure sentirà nel caso dei lettori di schermo) la parola "Nota" prima del paragrafo. Molto meglio adottare una soluzione strutturale in questo caso:

<p class="nota">
<strong>Nota:</strong>
...
</p>

Un altro caso è la gestione degli URL nella pagina. Per esempio:

a[href^="http://"]:after {
content: " (URL: " attr(href) " )";
color: #000;
}

Oppure:

li > a[href^="#"]:hover:after {
content: attr(href);
padding-left: 5px;
}

Nel primo caso inseriamo l'URL assoluto dell'elemento A dopo il contenuto dell'elemento, mentre nel secondo creiamo un effetto dinamico sull':hover inserendo l'URL relativo di un'ancora interna dopo il contenuto dell'elemento A (il caso tipico è quello di un sommario di pagina). Come abbiamo visto in precedenza, queste due soluzioni vengono vanificate dallo scarso supporto al contenuto generato e dal fatto che l'informazione veicolata non è più visibile con i CSS disabilitati. Possiamo risolvere il primo caso inserendo strutturalmente l'URL nella pagina.

Un ulteriore caso si verifica con i contatori. Consideriamo il seguente esempio:

body {counter-reset: capitolo;}

h2:before {
counter-increment: capitolo;
content: "Capitolo " counter(capitolo)":";
}

L'effetto previsto "Capitolo 1:, Capitolo 2:,..." viene vanificato per i motivi visti poc'anzi. Una situazione ancor peggiore si verifica per le liste annidate, in cui tutta la numerazione progressiva viene perduta. Ancora una volta la soluzione strutturale è da preferire.

Un caso estremo lo abbiamo con:

p.image:before {
content: url("img/img.jpg");
float: left;
padding: 3px;
border: 1px solid #000;
margin: 0 5px 5px 0;
}

Qui viene creata un'immagine flottata all'inizio di un paragrafo. Oltre ai problemi già visti, in questo caso non è possibile utilizzare l'attributo altlongdesc per rendere accessibile l'immagine. Inoltre solo Opera supporta la proprietà 'float' per il contenuto generato.

Passiamo ora alla "pars aedificans", ossia agli esempi di buon uso del contenuto generato. Il primo esempio riguarda la gestione delle virgolette sull'elemento Q:

q:before {
content: no-open-quote;
}
q:after {
content: no-close-quote;
}

È risaputo che Internet Explorer non aggiunge di default le virgolette di apertura e di chiusura all'elemento Q, mentre Firefox ad esempio lo fa. Questa diversa interpretazione pone un problema all'atto della formattazione, in quanto se aggiungiamo le virgolette come entità HTML queste si sovrapporranno a quelle già inserite dal browser. Tramite contenuto generato possiamo sopprimere l'inserirmento delle virgolette e risolvere il problema. Tuttavia, inserire le virgolette tramite la proprietà 'quotes' può essere controproducente per i motivi visti in precedenza.

Da questo primo esempio possiamo dedurre che un uso sano del contenuto generato si ha quando inseriamo elementi presentazionali la cui omissione non arreca danno alla fruizione delle informazioni della pagina. Un altro esempio:

h2:before, h3:before, h4:before {
content: '\221E';
font-weight: normal;
padding-right: 5px;
color: #000;
background: transparent;
}

body > h2:after,
body > h3:after,
body > h4:after {
content: '\221E';
font-weight: normal;
padding-left: 5px;
color: #000;
background: transparent;
}

In questo esempio inseriamo il simbolo matematico di infinito prima e dopo il contenuto delle intestazioni di secondo, terzo e quarto livello. La sua omissione non toglie nulla all'accessibilità del contenuto di questi elementi. Ancora:

p[id="scarica"]:before {
font-weight: bold;
content: '\2192';
padding-right: 4px;
}

#content > #scarica:hover:before {
content: '\2190';
}

Qui creiamo un effetto di rollover cambiando il simbolo della freccia verso sinistra con quella verso destra quando l'utente passa col mouse sopra il paragrafo. L'effetto si può anche ottenere senza contenuto generato con la proprietà 'background'. Anche in questo caso l'omissione del contenuto generato non toglie nulla alla fruizione del contenuto del paragrafo. Un ultimo esempio:

a[title="Collegamento esterno"]:before {
content: '\20AA';
color: #000;
background: transparent;
padding-right: 3px;
}

In quest'ultimo esempio inseriamo un simbolo prima di un collegamento esterno. In questo caso l'effetto si può ottenere anche con la proprietà 'background'. Da notare che anche qui l'omissione del contenuto generato è ininfluente e che l'informazione chiave è veicolata dall'attributo title.

Conclusione

Da quanto abbiamo visto sinora, è lecito concludere affermando che il contenuto generato si rivela utile solo quando le informazioni fornite con esso non sono essenziali alla fruizione della pagina. Un uso diverso è da considerarsi abuso e quindi dannoso.

Inoltre vanno tenute presenti le seguenti considerazioni:

  1. Il contenuto generato aumenta l'usabilità della pagina ma non la sua accessibilità.
  2. Il contenuto generato è veicolato ad una tecnologia lato client che non garantisce una distribuzione sicura del contenuto.
  3. Il contenuto generato non garantisce un'interoperabilità attuale con i browser che non lo supportano.
  4. Il contenuto generato può solo avere una forward-compatibility ma non una backward-compatibility.

1. L'accessibilità della pagina ha la sua ragion d'essere in una struttura solida e semantica. Fare affidamento su caratteristiche presentazionali può rivelarsi controproducente quando queste caratteristiche non sono disponibili o non sono supportate. Il mito dell'aumento dell'accessibilità tramite contenuto generato è infondato e fuorviante. Nello specifico, il punto 4 delle CSS Techniques for Web Content Accessibility Guidelines 1.0 è completamente errato.

2. Il contenuto generato si basa sulle potenzialità dei CSS che dipendono interamente dal supporto da parte del programma utente o dal fatto che tale supporto sia abilitato o meno. Per una distribuzione sicura del contenuto occorre fare affidamento sulla struttura codificata della pagina e non su tecnologie accessorie.

3. Il contenuto generato è supportato solo da alcuni browser. Nello specifico, Internet Explorer per Windows non lo supporta. Ciò significa che gli utenti di questo browser (o gli utenti con disabilità che si appoggiano ad esso per navigare con lettori di schermo) non avranno accesso alle informazioni veicolate dal contenuto generato.

4. Il contenuto generato potrà avere maggior impatto sui contenuti Web solo nel futuro, quando il suo supporto sarà esteso ad altri browser. In via ipotetica una futura versione di Internet Explorer (forward-compatibility) potrebbe estendere il supporto al contenuto generato, ma tale supporto non sarebbe compatibile con le versioni precedenti (backward-compatibility).