Garantisci la leggibilità del testo con colori di sfondo dinamici

Funzionalità web: contrast-color()

Quando crei componenti UI riutilizzabili (come badge o pulsanti) o supporti temi dinamici, garantire che il testo sia leggibile su un colore di sfondo imprevedibile può essere difficile. La funzione CSS contrast-color() viene risolta in black o white, massimizzando il rapporto di contrasto rispetto al colore di sfondo specificato.

Determina il colore ottimale del testo

Quando un elemento ha un colore di sfondo variabile o inserito dinamicamente, puoi fare affidamento sul browser per calcolare il colore del testo più sicuro.

OBBLIGATORIO: imposta la proprietà color utilizzando la funzione contrast-color(), passando il colore di sfondo (o la relativa proprietà personalizzata) come argomento.

.badge {
  /* Define the background color as a custom property */
  --badge-bg: #e02424;
  background-color: var(--badge-bg);
  /* MANDATORY: Dynamic contrasting color */
  color: contrast-color(var(--badge-bg));
}

DA EVITARE: utilizzare contrast-color() per background-color. È progettata specificamente per i colori di primo piano (come testo o bordi) in contrasto con un determinato colore di sfondo.

Integrazione con i temi

Poiché contrast-color() accetta qualsiasi valore <color> valido, incluse le proprietà personalizzate CSS, è particolarmente efficace nei sistemi di progettazione. Centralizzando i temi di sfondo come variabili, ti assicuri che il contrasto dei contenuti in primo piano venga aggiornato automaticamente ogni volta che il tema cambia, anche con i colori specificati dall'utente.

COSA FARE: fai riferimento alle variabili di background condivise all'interno di contrast-color() per garantire la sincronizzazione nell'applicazione.

.theme-card {
  background-color: var(--theme-surface-color);
  color: contrast-color(var(--theme-surface-color));
}

.theme-container[data-theme="light"] {
  --theme-surface-color: #f4f4f4;
}
.theme-container[data-theme="dark"] {
  --theme-surface-color: #1a1a1a;
}
.theme-container[data-theme="custom"] {
  --theme-surface-color: var(--user-custom-theme);
}

DA EVITARE: impostare un colore del testo hardcoded separato per ogni stato di interazione quando lo sfondo è dinamico. In questo modo, si reintroduce la mancata corrispondenza che contrast-color() dovrebbe evitare.

Strategie di riserva

Per supportare i browser senza contrast-color(), devi fornire un'alternativa. Se il colore di sfondo è noto e fisso, utilizza un colore di contrasto hardcoded. Per gli sfondi dinamici in cui il colore è sconosciuto, scegli una strategia dalla tabella seguente in base ai requisiti della UI e ai target di supporto del browser.

Strategia Ideale per… Considerazioni
Sintassi dei colori relativi Calcolo automatico del contrasto di alta qualità solo per CSS. Fallback solo CSS di altissima qualità.
Ombra del testo Miglioramento rapido della leggibilità su qualsiasi sfondo. Può sembrare "sporco" o "luminoso"; potrebbe non essere adatto a tutti i design.
Tratto del testo Preservare lo spessore del carattere garantendo al contempo il contrasto dei bordi. Se disponibile, utilizza paint-order per evitare di assottigliare le forme delle lettere.
Overlay traslucido Garantire un'area di contrasto minima dietro il testo. Modifica l'aspetto del colore di sfondo sotto il testo, richiede un elemento di testo separato.
Filtri SVG Contrasto reattivo che si aggiorna in base alle modifiche dello sfondo. Richiede un elemento di testo separato; implementazione complessa.

Per i browser che lo supportano, RCS fornisce il fallback automatico più efficace per i colori dinamici.

@supports (color: oklch(from red l c h)) {
  .badge {
    /* Highest threshold that passes WCAG. Higher values may be more legible. */
    --threshold: 0.623;
    --l: max(0, sign(var(--threshold) - l));
    color: oklch(from var(--badge-bg) var(--l) 0 h);
  }
}

@supports (color: contrast-color(red)) {
  .badge {
    color: contrast-color(var(--badge-bg));
  }
}

Alternative CSS Fallbacks

COSA FARE: seleziona il fallback che meglio si adatta ai tuoi vincoli di progettazione se RCS non è sufficiente o supportato.

Opzione 1: tratto del testo

.badge--stroke {
  -webkit-text-stroke: 4px black;
}

Opzione 2: tratto del testo con paint-order per preservare le forme delle lettere

.badge--stroke {
  -webkit-text-stroke: 4px black;
  paint-order: stroke fill;
}

Opzione 3: overlay di sfondo traslucido

.badge--overlay {
  position: relative;
  color: #fff;
  overflow: hidden;
}
.badge--overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgb(0 0 0 / 0.4);
  z-index: 0;
}
.badge--overlay span {
  position: relative;
  z-index: 1;
}

Opzione 4: Ombreggiatura testo

.badge--shadow {
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
}

Opzione 5: filtri SVG

  1. Carica questa definizione di filtro SVG. Deve essere aggiunto direttamente al documento HTML se devi supportare le versioni di Safari interessate da questo bug di WebKit, che impedisce i riferimenti a URL esterni e di dati per i filtri. In caso contrario, deve essere caricato da un file esterno, un file SVG o incorporato nel CSS.

    <!-- Technique from https://miunau.com/posts/dynamic-text-contrast-in-css/ -->
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0" style="display: none;">
      <defs>
        <filter id="contrast-filter" color-interpolation-filters="sRGB">
          <feColorMatrix type="matrix"
            values="0.2126 0.7152 0.0722 0 0
              0.2126 0.7152 0.0722 0 0
              0.2126 0.7152 0.0722 0 0
              0 0 0 1 0"/>
          <feMorphology operator="dilate" radius="2"/>
          <feComponentTransfer>
            <feFuncR type="linear" slope="-255" intercept="128"/>
            <feFuncG type="linear" slope="-255" intercept="128"/>
            <feFuncB type="linear" slope="-255" intercept="128"/>
          </feComponentTransfer>
          <feComposite operator="in" in2="SourceGraphic"/>
        </filter>
      </defs>
    </svg>
    
  2. Inserisci il testo in un tag <span>.

    <div class="badge--svg">
      <span>Badge content</span>
    </div>
    
  3. Applica il filtro a <span>.

.badge--svg span{
  color: var(--badge-bg);
  filter: url(#contrast-filter);
}