Gräver djupare i containerstilsfrågor

Källnod: 1765194

Jag skrev en del tidiga tankar om containerstilsfrågor en liten stund sedan. Det är fortfarande tidiga dagar. De är redan definierade i CSS Containment Module Level 1-specifikation (för närvarande i redaktörens utkaststatus) men det pågår fortfarande ett par enastående diskussioner.

Grundidén är att vi kan definiera en behållare och sedan tillämpa stilar villkorligt på dess avkomlingar baserat på dess beräknade stil.

@container ?  {
  /* conditional styles */
}

Det bästa exemplet jag har sett hittills är att ta bort kursiv stil från något liknande , och när de används i ett sammanhang där innehållet redan är kursivt:

em, i, q {
  font-style: italic; /* default UA behavior */
}

/* When the container's font-style is italic, remove italics from these elements. */
@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Det är den allmänna tanken. Men om du inte visste det, håller Miriam Suzanne, som är redaktör för specen, en pågående och grundlig uppsättning av personliga anteckningar om förfrågningar i containerstil som är allmänt tillgänglig. Det uppdaterades häromdagen och jag tillbringade lite tid där och försökte linda huvudet runt mer nyanserade aspekter av stilfrågor. Det är inofficiella grejer, men jag tänkte skriva ner några saker som stack ut för mig. Vem vet? Kanske är det grejer vi så småningom kan se fram emot!

Varje element är en stilbehållare

Vi behöver inte ens explicit tilldela en container-name or container-type för att definiera en stilbehållare eftersom allt är en stilbehållare som standard.

Så du ser det där exemplet ovan som tar bort kursiv stil? Observera att det inte identifierar en behållare. Den hoppar direkt till frågan med hjälp av style() fungera. Så vilken behållare frågas efter? Det kommer att bli direkt förälder till elementen ta emot de tillämpade stilarna. Och om inte det, så är det det nästa närmaste relativa behållare som har företräde.

Jag gillar det. Det är väldigt CSS-y för frågan att söka efter en matchning och sedan fortsätta att bubbla upp tills den hittar ett matchande villkor.

Det var svårt för min lilla hjärna att förstå varför vi kan komma undan med en implicit behållare baserad på stilar men inte så mycket när vi har att göra med dimensionsfrågor, som size och inline-size. Miriam förklarar det bra:

Dimensionsfrågor kräver css inneslutning på behållarens storlek, layout och stil för att förhindra layoutslingor. Inneslutning är en invasiv sak att tillämpa brett, så det var viktigt att författarna har noggrann kontroll över vilka element som är (eller inte är) storleksbehållare.

Stilbaserade frågor har inte samma begränsning. Det finns redan inget sätt i CSS för efterkommande stilar att påverka en förfaders beräknade stilar. Så ingen inneslutning krävs, och det finns inga invasiva eller oväntade biverkningar i att etablera ett element som en stil frågebehållare.

(Betonar min)

Allt beror på konsekvenser - av vilka det inte finns några så långt som att allt är en stilfrågebehållare direkt ur lådan.

  • Om en behållare hittas: villkoren löses mot den behållaren.
  • Om flera behållare matchar: den närmaste relativa behållaren har företräde.
  • Om inga matchningar hittas: unknown returnerad.

Det är samma sak "förlåtande" anda som resten av CSS.

En behållare kan stödja både dimensions- och stilfrågor

Låt oss säga att vi vill definiera en stilfråga utan en explicit container-name:

@container style(font-style: italic) {
  em {
    font-style: normal;
  }
}

Detta fungerar för alla element är stilbehållare, oavsett container-type. Det är det som gör att vi implicit kan fråga efter stilar och lita på den närmaste matchningen. Och detta är helt okej eftersom det återigen inte finns några negativa biverkningar när man skapar stilbehållare.

Vi måste använda en explicit container-type för dimensionsfrågor, men inte så mycket för stilfrågor eftersom varje element är en stilfråga. Det betyder också att den här behållaren är både en stil och dimensionell fråga:

.card-container {
  container: card / inline-size; /* implictly a style query container as well */
}

Exkluderar en behållare från att frågas

Vi kanske inte vill att en container ska delta i matchningsprocessen. Det är där det kanske går att ställa in container-type: none på ett element.

.some-element {
  container-type: none;
}

Frågebehållare med explicit stil ger mer kontroll över vad som efterfrågas

Om, säg, vi skulle skriva en stilfråga för padding , det finns inget tillförlitligt sätt att avgöra den bäst matchande behållaren, oavsett om vi arbetar med en explicit namngiven behållare eller närmaste direkta förälder. Det är för att padding är inte en ärvd egendom.

Så i de fallen borde vi använda container-name att explicit informera webbläsaren vilka behållare de kan hämta från. Vi kan till och med ge en behållare flera explicita namn för att få den att matcha fler villkor:

.card {
  container-name: card layout theme;
}

Åh, och container-name accepterar valfritt antal valfria och återanvändbar namn på en container! Det är ännu mer flexibilitet när det gäller att hjälpa webbläsaren att göra ett val när de söker efter matchningar.

.theme {
  container-name: theme;
}
.grid {
  container-name: layout;
}
.card {
  container-name: card layout theme;
}

Jag undrar lite om det också kan betraktas som en "fallback" i händelse av att en container passeras.

Stilfrågor kan kombineras

Smakämnen or och and operatörer tillåter oss att kombinera saker för att hålla saker torra:

@container bubble style(--arrow-position: start start) or style(--arrow-position: end start) {
  .bubble::after {
    border-block-end-color: inherit;
    inset-block-end: 100%;
  }
}

/* is the same as... */
@container bubble style(--arrow-position: start start) {
  /* etc. */
}
@container bubble style(--arrow-position: end start) {
  /* etc. */
}

Växla stilar

Det finns en liten överlappning mellan behållarstilsfrågor och arbete som görs för att definiera en toggle() fungera. Vi kan till exempel cykla igenom två font-style värderingar, säg italic och normal:

em, i, q {
  font-style: italic;
}

@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Häftigt. Men förslaget till CSS Toggles antyder att toggle() funktion skulle vara ett enklare tillvägagångssätt:

em, i, q {
  font-style: toggle(italic, normal);
}

Men allt utöver denna typ av binära användningsfall är var toggle() är mindre lämpligt. Stilfrågor är dock bra att gå. Miriam identifierar tre tillfällen där stilfrågor är mer lämpliga än en toggle():

/* When font-style is italic, apply background color. */
/* Toggles can only handle one property at a time. */
@container style(font-style: italic) {
  em, i, q {
    background: lightpink;
  }
}

/* When font-style is italic and --color-mode equals light */
/* Toggles can only evaluate one condition at a time */
@container style((font-style: italic) and (--color-mode: light)) {
  em, i, q {
    background: lightpink;
  }
}

/* Apply the same query condition to multiple properties */
/* Toggles have to set each one individually as separate toggles */
@container style(font-style: italic) {
  em, i, q {
    /* clipped gradient text */
    background: var(--feature-gradient);
    background-clip: text;
    box-decoration-break: clone;
    color: transparent;
    text-shadow: none;
  }
}

Stilfrågor löser "Custom Property Toggle Hack"

Lägg märke till att stilfrågor är en formell lösning för "CSS anpassad egenskap växla trick". Där ställer vi in ​​en tom anpassad egenskap (--foo: ;) och använd den kommaseparerade reservmetoden för att "växla" egenskaper på och av när sedan anpassad egenskap är inställd på ett verkligt värde.

button {
  --is-raised: ; /* off by default */
  
  border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
  box-shadow: var(
    --is-raised,
    0 1px hsl(0 0% 100% / 0.8) inset,
    0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2)
  );
  text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:active {
  box-shadow: var(--is-raised, 0 1px 0.2em black inset);
}

#foo {
  --is-raised: initial; /* turned on, all fallbacks take effect. */
}

Det är superhäftigt, också mycket arbete som stilbehållarfrågor gör trivialt.

Stilfrågor och CSS-genererat innehåll

För genererat innehåll producerat av content egendom av ::before och ::after pseudoelement, är den matchande behållaren det element på vilket innehållet genereras.

.bubble {
  --arrow-position: end end;
  container: bubble;
  border: medium solid green;
  position: relative;
}

.bubble::after {
  content: "";
  border: 1em solid transparent;
  position: absolute;
}

@container bubble style(--arrow-position: end end) {
  .bubble::after {
    border-block-start-color: inherit;
    inset-block-start: 100%;
    inset-inline-end: 1em;
  }
}

Stilfrågor och webbkomponenter

Vi kan definiera en webbkomponent som en behållare och fråga efter stil. Först har vi av komponenten:


  
… …

Då använder vi :host pseudo-element som en behållare för att ställa in en container-name, en container-type, och några högnivåattribut på den:

:host {
  container: media-host / inline-size;
  --media-location: before;
  --media-style: square;
  --theme: light;
}

Element inuti kan fråga parametrarna för element:

@container media-host style(--media-style: round) {
  [part='img'] {
    border-radius: 100%;
  }
}

Vad kommer härnäst?

Återigen, allt jag har antecknat här är baserat på Miriams anteckningar, och de anteckningarna är inte en ersättning för den officiella specen. Men de är en indikation på vad som diskuteras och var saker kan landa i framtiden. Jag uppskattar att Miriam kopplade ihop en handfull enastående diskussioner som fortfarande äger rum som vi kan följa för att hålla koll på saker och ting:

Tidsstämpel:

Mer från CSS-tricks