12k
All articles

Select-Dropdowns mit base-select gestalten

Gestalten Sie native select-Dropdowns mit appearance: base-select. Erfahren Sie mehr über Picker, Optionen, Symbole, Barrierefreiheit, Browser-Support und Einschränkungen.

OpenReplay Team
OpenReplay Team
Select-Dropdowns mit base-select gestalten

Wenn Sie appearance: base-select auf ein <select> und auf dessen Pseudo-Element ::picker(select) anwenden, können Sie das gesamte Steuerelement per CSS gestalten: den Button, das Dropdown, den Pfeil, das Häkchen und jede einzelne Option.

Viele Teams pflegen eine eigene Dropdown-Komponente aus nur einem Grund: Das native <select> ließ sich nicht an das Design anpassen. Tastaturbedienung, Fokusverwaltung und ARIA-Verknüpfungen kamen dabei als Nebeneffekt hinzu. appearance: base-select beseitigt diesen Grund.

Dieser Leitfaden geht die gesamte Styling-Oberfläche durch, vom Opt-in bis zu Rich Options, und zeigt, worauf Sie dabei verzichten. Für klassische Techniken mit appearance: none bietet der Artikel Select-Elemente mit modernem CSS gestalten einen umfassenderen Überblick, geht auf base-select aber nur kurz ein. Ältere Ansätze werden in Select-Dropdowns ohne JavaScript gestalten behandelt.

Die wichtigsten Punkte

  • appearance: base-select muss sowohl auf select als auch auf ::picker(select) gesetzt werden. Wird es nur auf den Picker angewendet, passiert nichts.
  • Der Picker ist ein Popover im Top Layer, das über eine implizite Anker-Referenz am Select-Button verankert ist. Sie benötigen also weder z-index noch Portals oder anchor-name.
  • <selectedcontent> gehört in einen <button>, der das erste Kind des <select> ist, und zeigt im geschlossenen Steuerelement einen Klon der ausgewählten Option.
  • Jede Option sollte Text enthalten, denn reine Icon-Optionen liefern Screenreadern nichts zum Vorlesen und erscheinen in nicht unterstützten Browsern leer.
  • Mit base-select verzichten Sie auf den Picker des mobilen Betriebssystems, auf die Darstellung außerhalb des Browserfensters und auf die automatische Breitenanpassung an die längste Option.

Die Komponente, die Sie löschen können

Eine eigene Select-Komponente existiert meist nur, um das Erscheinungsbild des nativen <select>-Elements zu korrigieren, und übernimmt dabei jede Menge Verhalten. Laut dem Launch-Post des Chrome-Teams können Sie ab Chrome 135 das <select>-Element per CSS gestalten, wobei die Barrierefreiheit und das Standardverhalten eines eingebauten Steuerelements erhalten bleiben. WebKit zog mit Safari 27.0 nach, wo sich das echte <select> nun von oben bis unten neu gestalten lässt. Sobald ein Browser dies unterstützt, entfällt der Hauptgrund für eine div-basierte Listbox – und damit auch das Verhalten, das Sie dafür nachbauen mussten.

Wie aktivieren Sie appearance: base-select?

Für das Opt-in wenden Sie appearance: base-select sowohl auf das <select> als auch auf dessen Picker an. Der MDN-Leitfaden zu anpassbaren Selects erklärt, dass das Select auch allein aktiviert werden kann, der Picker jedoch nur funktioniert, wenn auch das Select aktiviert ist. Die WebKit-Empfehlungen zu anpassbaren Selects raten dazu, diese Erweiterungen in @supports (appearance: base-select) zu kapseln. Platzieren Sie alle weiteren Regeln im selben Block, damit Browser ohne Unterstützung das einfache Select erhalten.

@supports (appearance: base-select) {
  select,
  ::picker(select) {
    appearance: base-select;
  }
  /* all following rules go here */
}
BestandteilSelektorHinweise
Geschlossenes SteuerelementselectDer Select-Button
Pfeil::picker-iconNicht im Accessibility Tree
Geöffneter Zustand:openGreift, solange der Picker angezeigt wird
Dropdown::picker(select)Alles außer dem Button an erster Stelle
Einzelne AuswahloptionStandardmäßig display: flex
Ausgewählte Optionoption:checked
Auswahlmarkierung::checkmarkNicht im Accessibility Tree
Inhalt im geschlossenen ZustandselectedcontentKlon der ausgewählten Option

Das geschlossene Steuerelement gestalten

Das geschlossene Steuerelement ist das select-Element. Der Pfeil ist ::picker-icon, und :open greift, solange der Picker angezeigt wird. So können Sie den Pfeil beim Öffnen des Selects drehen.

select {
  border: 2px solid #d4d4d8;
  background: #f4f4f5;
  padding: 10px;
  transition: 0.4s;
}

select:hover,
select:focus {
  background: #e4e4e7;
}

select::picker-icon {
  color: #71717a;
  transition: 0.4s rotate;
}

select:open::picker-icon {
  rotate: 180deg;
}

Die transition auf rotate sorgt dafür, dass sich der Pfeil bei jedem Öffnen und Schließen des Pickers flüssig dreht.

Die geöffnete Liste gestalten

Die geöffnete Dropdown-Liste eines anpassbaren Selects ist ::picker(select). Der Browser fasst darin alles innerhalb des <select> zusammen, mit Ausnahme des Buttons an erster Stelle. Optionen sind standardmäßig Flex-Container. :checked adressiert die ausgewählte Option, ::checkmark die Markierung daneben.

::picker(select) {
  border: none;
  border-radius: 8px;
}

option {
  display: flex;
  gap: 16px;
  padding: 10px;
}

option:hover,
option:focus {
  background: #ede9fe;
}

option:checked {
  font-weight: bold;
}

option::checkmark {
  order: 1;
  margin-left: auto;
  content: "✔";
}

Mit order: 1 in Kombination mit margin-left: auto wandert das Häkchen vom Anfang an das Ende der Zeile.

Positionierung und Animation

Bei einem anpassbaren Select ist das Dropdown ein Popover, das sich im Top Layer öffnet. Der Browser verknüpft es selbstständig per CSS Anchor Positioning mit dem Select-Button, sodass Sie weder z-index noch ein Portal oder eine anchor-name-Verknüpfung benötigen. Button und Picker stehen in derselben Invoker/Popover-Beziehung, die der Leitfaden zu Invoker Commands für Buttons und Popovers beschreibt. Die Standardstyles verschieben den Picker zudem auf eine andere Seite, falls er über den Bildschirmrand hinausragen würde. Mit anchor() können Sie auch eine eigene Position festlegen:

::picker(select) {
  top: calc(anchor(bottom) + 1px);
  left: anchor(10%);
}

Um den Picker ein- und auszublenden, lassen Sie ihn zwischen seinen Popover-Zuständen übergehen:

::picker(select) {
  opacity: 0;
  transition: all 0.4s allow-discrete;
}

:open::picker(select) {
  opacity: 1;
}

@starting-style {
  :open::picker(select) {
    opacity: 0;
  }
}

allow-discrete ermöglicht es, die diskreten Eigenschaften display und overlay in die Transition einzubeziehen. Ohne diese Angabe würde der Picker den Top Layer verlassen, bevor das Ausblenden sichtbar wird. @starting-style legt die Deckkraft fest, bei der das Einblenden beginnt.

Rich Options und selectedcontent

Platzieren Sie <selectedcontent> in einem <button>, der das erste Kind des <select> ist. Es zeigt im geschlossenen Steuerelement einen Klon des Inhalts der ausgewählten Option und lässt sich unabhängig von den Optionen im Dropdown gestalten. Optionen können nun Markup wie Icons und Bilder enthalten.

<label for="status">Status</label>
<select id="status" name="status">
  <button>
    <selectedcontent></selectedcontent>
  </button>
  <option value="draft">
    <span class="icon" aria-hidden="true">📝</span>
    <span class="option-label">Draft</span>
  </option>
  <option value="review">
    <img class="icon" src="review.svg" alt="">
    <span class="option-label">In review</span>
  </option>
  <option value="published">
    <span class="icon" aria-hidden="true">✅</span>
    <span class="option-label">Published</span>
  </option>
</select>

MDN weist darauf hin, dass Icons den geschlossenen Button höher machen und den Pfeil verschieben können. Sie können sie ausschließlich im geschlossenen Zustand ausblenden:

selectedcontent .icon {
  display: none;
}

Die Optionen im geöffneten Picker behalten ihre Icons. Der Select-Button ist inert, d. h. darin platzierte Links oder Buttons lassen sich nicht einzeln fokussieren oder anklicken – das Ganze verhält sich wie ein einziger Button. MDN merkt außerdem an, dass zusätzlicher Inhalt im Button verändern kann, was Screenreader als Wert des Selects ausgeben. Beschränken Sie den Inhalt des Buttons daher auf <selectedcontent>.

Was übernimmt der Browser für Sie?

Mit base-select übernimmt der Browser all das, was ein eigenes Dropdown nachbauen muss: Tastaturinteraktion, Fokus, Stacking im Top Layer, das Übermitteln des Werts beim Absenden des Formulars sowie die Semantik für Screenreader. Laut WebKit sind Tastaturunterstützung und Barrierefreiheit bereits eingebaut, da es sich weiterhin um das browsereigene Steuerelement handelt. Beim Session Replay div-basierter Dropdowns treten häufig Tastatur- und Fokusfehler zutage – genau die Fehler, die ein natives Select konstruktionsbedingt vermeidet. Wie viel Code der manuelle Ansatz erfordert, zeigt dieser Leitfaden zum Bau eines barrierefreien Dropdown-Selects von Hand.

Zwei Regeln gelten weiterhin:

  • Jede Option braucht Text. Die wichtigste Regel von WebKit lautet, dass jede Option echten Text oder ein Attribut mit zugänglichem Text benötigt. Visuell ausgeblendeter Text wird von Screenreadern dennoch vorgelesen. Dekorative Bilder erhalten alt="", die Bedeutung transportiert das Text-Label.
  • Eigene Icons sind reine Dekoration. Screenreader ignorieren alles, was Sie per content in ::checkmark und ::picker-icon einfügen. Ein eigener Pfeil oder ein eigenes Häkchen wird also nie vorgelesen.

Bestehendes JavaScript, das den Wert des Selects ausliest, funktioniert weiterhin. Hat eine Option kein value-Attribut, entspricht der Wert des Selects dem textContent der ausgewählten Option ohne führende und nachfolgende Leerzeichen. Setzen Sie value daher immer, wenn Optionen Rich Markup enthalten.

Unterstützung, Fallback und Kompromisse

appearance: base-select ist in Chrome ab Version 135, in anderen Chromium-basierten Browsern wie Edge sowie in Safari 27.0 verfügbar. Firefox hat mit der Implementierung hinter einer Einstellung begonnen: Der Wert appearance: base-select ist in Firefox 149 hinter einem Flag gelandet, aber standardmäßig nicht aktiviert. MDN stuft die Funktion weiterhin als eingeschränkt verfügbar ein, prüfen Sie also die Kompatibilitätstabellen auf den MDN-Referenzseiten. Safari liefert eigene Standardstyles für base-select, sodass ungestylte Steuerelemente nicht in jedem Browser gleich aussehen.

Browser ohne base-select-Unterstützung ignorieren das <button>- und <selectedcontent>-Markup. Sie rendern ein gewöhnliches, voll funktionsfähiges Select, in dem jede Option auf ihren Text reduziert wird. Dieser Fallback ergibt nur Sinn, wenn der Text auch vorhanden ist.

Der Chrome-Post nennt außerdem drei Verhaltensweisen, auf die Sie verzichten:

  • Die geöffnete Liste bleibt innerhalb des Browserfensters.
  • Auf Smartphones und Tablets erhalten Sie den Picker des Browsers statt des betriebssystemeigenen.
  • Die Breite des Selects richtet sich nicht mehr nach der längsten Option. Setzen Sie daher selbst eine width auf select, ::picker(select).

Base-select ändert zudem, wie der HTML-Parser den Inhalt eines <select> interpretiert. MDN warnt, dass dies zu Konflikten mit JavaScript-Frameworks führen kann: Manche blockieren die Features, bei anderen kann Server-Side Rendering die Hydration beeinträchtigen. Testen Sie die Funktion in Ihrem Framework, bevor Sie Ihre bestehende Komponente entfernen.

Fazit

Ein natives Select mit appearance: base-select gibt Ihnen die Kontrolle über das Styling, während der Browser weiterhin Tastatur, Fokus, Stacking und Barrierefreiheit übernimmt. Um es auszuprobieren, wählen Sie ein einfaches eigenes Dropdown aus, bauen es innerhalb eines @supports-Blocks mit Text in jeder Option neu auf und testen es in einem Browser ohne base-select-Unterstützung. Funktioniert es dort weiterhin als einfaches Select und sind die oben genannten Kompromisse akzeptabel, können Sie die eigene Komponente entfernen.

FAQs

Was ist der Unterschied zwischen appearance: none und appearance: base-select bei einem Select?

appearance: none entfernt lediglich das native Styling des geschlossenen Buttons. Die Dropdown-Liste und ihre Optionen nutzen weiterhin den Picker des Browsers oder Betriebssystems, und die meisten CSS-Regeln erreichen sie nicht. appearance: base-select, gesetzt auf select und ::picker(select), ermöglicht zusätzlich das Gestalten von Picker, Optionen, Pfeil und Häkchen und erlaubt Rich HTML innerhalb von Optionen. Browser ohne Unterstützung ignorieren die Angabe und behalten das klassische Steuerelement bei.

Wie erkenne ich die Unterstützung für base-select per JavaScript?

Rufen Sie CSS.supports('appearance', 'base-select') auf. Die Methode gibt nur in Browsern true zurück, die den Wert kennen. Sie prüft dasselbe wie die CSS-Bedingung @supports (appearance: base-select). So können Sie entscheiden, ob eine Fallback-Dropdown-Komponente geladen oder eine Klasse hinzugefügt werden soll, die eigene Styles aktiviert. Setzen Sie auf Feature Detection statt auf User-Agent-Sniffing, da jede Engine die Unterstützung zu einem anderen Zeitpunkt eingeführt hat.

Funktioniert base-select mit select multiple oder dem size-Attribut?

Ja, im Listbox-Modus. Das Attribut multiple oder ein size-Wert größer als 1 zeigt die Optionen als Box auf der Seite an statt als Button, der ein Popup öffnet. Ab Chrome 145 funktioniert base-select auch in diesem Modus, sowohl für Einfach- als auch für Mehrfachauswahl. Das Mehrfachauswahl-Popup, das bei multiple mit size=1 entsteht, wird noch nicht abgedeckt. Eine anpassbare Listbox hat keinen Picker, daher greifen ::picker(select), ::picker-icon und selectedcontent dort nicht. Prüfen Sie die Kompatibilitätstabellen von MDN für die Unterstützung in anderen Engines.

Digital experience platform

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.