Combinazioni di selettori e regole @

Funzionalità web: Query di stile del container

Il problema

Spesso dobbiamo combinare criteri di filtro di tipi diversi, ad esempio selettori + query supporti, query supporti + query contenitore e così via.

Ad esempio, combinando (prefers-color-scheme: dark) con un selettore che ha come target gli override della combinazione di colori (ad es. :root:has(> head > meta[name="color-scheme"][content="dark"]) o :root.dark) per applicare determinati stili in modalità Luce/Buio, indipendentemente da come è stata impostata la combinazione di colori.

L'approccio ingenuo porta a un codice duplicato:

/* Light mode styles */
.card.featured {
  font-weight: 400;
}

#logo-dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  /* Dark mode styles */
  :root:not(.light) {
    .card.featured {
      font-weight: 300;
    }

    #logo-dark {
      display: inline;
    }

    #logo {
      display: none;
    }
  }
}

/* Dark mode styles, again */
:root.dark {
  .card.featured {
    font-weight: 300;
  }

  #logo-dark {
    display: inline;
  }

  #logo {
    display: none;
  }
}

Che cosa fare invece

Memorizza il risultato del filtro combinato in una proprietà personalizzata, quindi utilizza le query di stile del contenitore per impostare regole basate su questa proprietà.

IMPORTANTE: a causa del funzionamento delle query container, questa tecnica funziona solo se possiamo impostare la proprietà personalizzata su un predecessore di qualsiasi regola che deve essere abbinata.

1. Disaccoppia la logica di selezione dalle regole applicate

:root {
  --color-scheme: light;

  @media (prefers-color-scheme: dark) {
    --color-scheme: dark;
  }

  &:has(> head > meta[name="color-scheme"][content="dark"]) {
    --color-scheme: dark;
  }

  &:has(> head > meta[name="color-scheme"][content="light"]) {
    --color-scheme: light;
  }
}

2. Chiave della proprietà personalizzata per le regole applicate

@container not style(--color-scheme: dark) {
  .card.featured {
    font-weight: 400;
  }

  #logo-dark {
    display: none;
  }
}

@container style(--color-scheme: dark) {
  .card.featured {
    font-weight: 300;
  }

  #logo {
    display: none;
  }
}

3. FACOLTATIVO: classi di utilità per attivare/disattivare la visibilità

Un caso molto comune è l'attivazione/disattivazione della visibilità in base alla corrispondenza dei criteri. Le classi di utilità devono avere la precedenza sugli stili dei componenti arbitrari (quindi !important) e il loro display mostrato è sconosciuto, quindi non possono utilizzare gli override. Controllale con not style(...):

@container not style(--color-scheme: dark) {
  .if-dark {
    display: none !important;
  }
}

@container not style(--color-scheme: light) {
  .if-light {
    display: none !important;
  }
}

Fallback

Esistono due tipi di fallback, a seconda della criticità della ramificazione e di ciò che costituisce un fallback accettabile.

  1. Per la ramificazione critica, in cui tutti gli stati devono funzionare correttamente anche se le query di stile non sono supportate, non puoi utilizzare le query di stile. Utilizza invece l'approccio ingenuo descritto sopra, duplicando le regole per ogni stato. Le classi di utilità possono comunque essere utilizzate, purché siano definite in questo modo.
  2. Per le diramazioni non critiche, in cui è accettabile uno stato predefinito, puoi definire gli stili di base al di fuori di qualsiasi regola @container e utilizzare le query sugli stili solo come override. In questo modo, si preserva la maggior parte dello spirito di questo approccio, il cui principale svantaggio è l'accoppiamento dei valori introdotto dagli override.
#logo-dark {
  display: none;
}

@container style(--color-scheme: dark) {
  #logo-dark {
    display: inline; /* override */
  }

  #logo {
    display: none;
  }
}