Kombinationen aus Selektoren und @-Regeln

Webfunktionen: Container-Stilabfragen

Das Problem

Häufig müssen wir Filterkriterien verschiedener Typen kombinieren, z.B. Selektoren und Medienabfragen, Medienabfragen und Containerabfragen usw.

Beispiel: (prefers-color-scheme: dark) mit einem Selektor kombinieren, der auf Farbschema-Überschreibungen abzielt (z.B. :root:has(> head > meta[name="color-scheme"][content="dark"]) oder :root.dark), um bestimmte Stile im hellen/dunklen Modus anzuwenden, unabhängig davon, wie das Farbschema festgelegt wurde.

Der naive Ansatz führt zu dupliziertem Code:

/* 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;
  }
}

Was du stattdessen tun kannst

Speichern Sie das Ergebnis des kombinierten Filters in einer benutzerdefinierten Eigenschaft und verwenden Sie dann Container-Stilabfragen, um Regeln basierend auf dieser Eigenschaft festzulegen.

WICHTIG: Aufgrund der Funktionsweise von Containerabfragen funktioniert diese Technik nur, wenn wir die benutzerdefinierte Eigenschaft für einen Ancestor aller Regeln festlegen können, die abgeglichen werden müssen.

1. Auswahllogik von angewendeten Regeln entkoppeln

: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. Schlüssel für die benutzerdefinierte Eigenschaft für angewendete Regeln

@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. OPTIONAL: Utility-Klassen zum Ein-/Ausblenden

Ein sehr häufiger Fall ist das Ein- oder Ausblenden von Elementen, je nachdem, ob die Kriterien erfüllt sind. Utility-Klassen müssen Vorrang vor beliebigen Komponentenstilen haben (daher !important). Da ihr angezeigtes display unbekannt ist, können sie keine Überschreibungen verwenden. Schränken Sie sie mit not style(...) ein:

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

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

Fallback-Zustände

Es gibt zwei Arten von Fallbacks, je nachdem, wie kritisch die Verzweigung ist und was ein akzeptabler Fallback ist.

  1. Für kritische Verzweigungen, bei denen alle Zustände auch dann korrekt funktionieren müssen, wenn Stilabfragen nicht unterstützt werden, können Sie Stilabfragen einfach nicht verwenden. Verwenden Sie stattdessen den oben beschriebenen einfachen Ansatz und duplizieren Sie die Regeln für jeden Status. Hilfsklassen können weiterhin verwendet werden, sofern sie auch so definiert sind.
  2. Für nicht kritische Verzweigungen, bei denen ein Standardzustand akzeptabel ist, können Sie die Basisstile außerhalb von @container-Regeln definieren und Stilabfragen nur als Überschreibungen verwenden. Dadurch bleibt der Großteil des Ansatzes erhalten. Der Hauptnachteil ist die Kopplung von Werten, die durch Überschreibungen eingeführt wird.
#logo-dark {
  display: none;
}

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

  #logo {
    display: none;
  }
}