Reactiviteit van designtokens

Webfuncties: Containerstijlquery's

Achtergrond en overzicht

Vaak moet een auteur contextuele wijzigingen aanbrengen in het ontwerp van een component. In het verleden moesten auteurs selectors gebruiken om dergelijke wijzigingen toe te passen. Dit betekende vaak dat hoewel veel van hun designtokens konden bestaan als aangepaste eigenschappen, designtokens van een hogere orde alleen konden worden gecodeerd als een selectiepatroon (bijvoorbeeld met een klassenaam of kenmerkconventie) of als props/context in een JavaScript-framework.

Met stijlquery's voor containers kunnen auteurs elementen opmaken op basis van de berekende waarde van de aangepaste eigenschap van een bovenliggend element. Dit betekent dat auteurs betekenisvolle waarden voor designtokens kunnen schrijven in hun stylesheets in plaats van te vertrouwen op opmaak of JavaScript voor de presentatie.

Hoe wordt dit geïmplementeerd

Een reactieve ontwerptoken implementeren met een containerstijlquery is vrij eenvoudig:

  1. Stel de ontwerptoken van een hogere orde in als een aangepaste eigenschap voor een container. Dit hoeft geen geregistreerde aangepaste eigenschap te zijn.
  2. Gebruik de regel @container style() om de waarde van die aangepaste eigenschap op te vragen.
  3. Pas de juiste stijlen toe op een onderliggend element in de container.

Een paar dingen om aan te denken:

  • De container die wordt opgevraagd met een stijlquery, hoeft geen container-type of container-name te hebben, maar een container-name kan specifiekere query's mogelijk maken.
  • De container zelf kan niet worden opgemaakt met de containerstijlquery.

Hieronder vindt u een basisvoorbeeld van de bovenstaande implementatiestappen.

<div class="features">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
.features {
  --density: spacious;
}

.bugs {
  --density: compact;
}

@container style(--density: compact) {
  .card {
    padding: 8px;
  }
}

@container style(--density: spacious) {
  .card {
    padding: 24px;
  }
}

Reservestrategieën

Totdat er Baseline-ondersteuning is voor containerstijlquery's, wordt het AFGERADEN om ze te gebruiken voor kernfuncties die beschikbaar moeten zijn in alle browsers. Het is namelijk niet eenvoudig om een fallback voor ze te maken die de voordelen niet wegneemt of die eigen beperkingen heeft. Als een gebruikersvoorkeur voor UI-dichtheid bijvoorbeeld niet wordt beschouwd als een kernfunctie die beschikbaar moet zijn voor alle functionaliteit, kunnen containerstijlquery's worden gebruikt om de functie te implementeren zonder een fallback.

In plaats daarvan selectors gebruiken

Voor kernfuncties moet een alternatieve aanpak met kiezers worden gebruikt. In dit voorbeeld wordt een data-density-kenmerk gebruikt om de ontwerptoken voor dichtheid te coderen in de opmaak in plaats van als een aangepaste eigenschap:

<div class="features" data-density="spacious">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs" data-density="compact">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
/* This example uses `:where()` to avoid increasing specificity */
:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

Een belangrijke beperking van deze fallback-benadering is dat deze geen ondersteuning biedt voor geneste elementen met het kenmerk data-density, omdat de selectorspecificiteit hetzelfde is. De volgorde van weergave wordt gebruikt om de stijlen te bepalen ([data-density="spacious"] heeft altijd voorrang op [data-density="compact"]).

Stijlquery's gebruiken als progressive enhancement

Hoewel het NIET WORDT AANBEVOLEN, kun je, als je stijlquery's wilt gebruiken als progressive enhancement voor een kernfunctie, een aantal aangepaste eigenschappen maken en daarna de stijlquery's opnemen om duplicatie te voorkomen. Zorg dat de alternatieve aanpak :where() gebruikt als u de containerelementen selecteert om te voorkomen dat de specificiteit toeneemt.

.card {
  --card-padding-compact: 8px;
  --card-padding-spacious: 24px;
}

:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

/* Use style queries as a progressive enhancement: same specificity, so order of appearance is used */

@container style(--density: compact) {
  .card {
    padding: var(--card-padding-compact);
  }
}

@container style(--density: spacious) {
  .card {
    padding: var(--card-padding-spacious);
  }
}

Functies controleren met alleen CSS

Als je de containerstijlquery's wilt controleren om bepaalde UI die ervan afhankelijk is voorwaardelijk weer te geven, kun je een stijlquery gebruiken:

:root {
  --style-queries-supported: check;
}

.density-toggle {
  display: none;
}

@container style(--style-queries-supported) {
  .density-toggle {
    display: revert;
  }
}

Functiecontrole met JavaScript

Functiecontrole met JavaScript is iets ingewikkelder, omdat de CSSContainerRule-interface al bestond voordat containerstijlquery's werden toegevoegd. De interface is daarom onbetrouwbaar voor dit doel. In plaats daarvan moet je de berekende waarde van een bekende property controleren die wordt ingesteld met een stijlquery.

In dit voorbeeld wordt een aangepaste eigenschap gebruikt, omdat deze geen visueel effect heeft:

:root {
  --style-queries-supported: check;
}

@container style(--style-queries-supported: check) {
  body {
    --style-queries-supported: yes;
  }
}

Controleer daarna de berekende waarde in JavaScript, zoals hier:

if (getComputedStyle(document.body).getPropertyValue("--style-queries-supported") === "yes") {
  // Use container style queries
} else {
  // Use fallback strategy
}