De CSS-eigenschap appearance: base-select maakt het mogelijk om een <select>-element en de kiezer (via ::picker(select)) op te maken als gewone HTML-elementen.
In combinatie met moderne CSS-opmaaktechnieken zoals Grid of Flexbox kun je afstappen van de traditionele gestapelde verticale lijst en een rijke visuele beleving maken zonder aangepaste JS.
Je kunt bijvoorbeeld display: grid gebruiken om de opties op te maken als een 2D-raster van kaarten.
Dit is ideaal voor kleurkiezers, emoji-kiezers of productvarianten waarbij een uitgebreid visueel menu effectiever is dan een lijst.
Implementeren
1. Basisstijl activeren
Pas appearance: base-select toe op zowel het element <select> als het interne pseudo-element voor de kiezer select::picker(select):
select.grid-picker {
&, &::picker(select) {
appearance: base-select;
}
}
2. De kiezercontainer opmaken
Target select:open::picker(select) en pas display: grid (of display: flex) toe.
Definieer kolommen en tussenruimtes zoals u dat voor elke container zou doen.
select.grid-picker:open::picker(select) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
BELANGRIJK: De :open is vereist. Als je select::picker(select) opmaakt, wordt de kiezer zichtbaar, zelfs als de selectie is gesloten.
3. De opties opmaken
Target de <option>-elementen om ze op te maken als rasteritems of kaarten.
U kunt er afbeeldingen, SVG's of complexe indelingen aan toevoegen.
/* Style options as grid cards */
.grid-picker option {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
/* padding, border-radius, background, etc. */
}
4. De geselecteerde optie opmaken
Gebruik option:checked om de geselecteerde optie op te maken. Voorbeeld:
.grid-picker option:checked {
position: relative;
border: 2px solid var(--primary-color);
background-color: oklab(from var(--primary-color) l a b / 8%);
}
Net als bij elk ander UI-bedieningselement moet je de status niet alleen met kleur communiceren, maar meerdere visuele indicatoren gebruiken (bijvoorbeeld kleuren, randdikte, vinkje) om aan te geven welke optie is geselecteerd.
5. (Optioneel) Het vinkje voor de geselecteerde optie opmaken
Standaard geeft de browser een vinkje weer als het eerste onderliggende element van elke <option>.
Het gedraagt zich als een ::before met content: "✓" / "", weergegeven vóór een door de auteur verstrekte ::before, als die bestaat.
Als de optie niet is geselecteerd, wordt het vinkje verborgen via visibility: hidden.
Je kunt het element opmaken met het pseudoelement ::checkmark of het helemaal verbergen als je de selectie alleen via opmaak wilt overbrengen of een aangepaste indicator wilt gebruiken.
Als je het bijvoorbeeld in de rechterbovenhoek van de optie wilt plaatsen, kun je iets doen als:
select.grid-picker {
> option::checkmark {
/* Hide the checkmark for unselected options */
display: none;
}
> option:checked {
position: relative;
&::checkmark {
position: absolute;
inset-block-start: -.4em;
inset-inline-end: -.5em;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-inline-size: 1.6em;
aspect-ratio: 1;
padding-right: 0.12em;
padding-left: 0.05em;
border-radius: 50%;
background: var(--primary-color);
color: white;
font-size: 70%;
font-weight: bold;
}
}
}
Tip: Als je een container (bijvoorbeeld een cirkel) rond het vinkje tekent, duw je deze een beetje naar boven (zoals we hier hebben gedaan met padding-bottom en padding-left) voor optische centrering die rekening houdt met de vorm.
Gebruik hiervoor NIET padding-inline-start of padding-block-end, omdat deze veranderen afhankelijk van de schrijfmodus, terwijl de vorm van het vinkje niet verandert.
6. (Optioneel) De weergave van de geselecteerde optie aanpassen
De <select> toont standaard een tekstversie van de geselecteerde optie.
Als je uitgebreide content wilt tonen voor de geselecteerde waarde, gebruik je <selectedcontent> in een <button> en/of toon je de geselecteerde optie op een andere manier (verberg bijvoorbeeld bepaalde details).
Gebruik <selectedcontent> NIET zonder de <button> eromheen, omdat de <selectedcontent> dan gewoon onderdeel wordt van de kiezer, net als elk ander contentelement.
Praktische tips
- Test het gedrag van de lay-out. Als je
appearance: base-selectinstelt, wordt het standaard browsergedrag verwijderd om de selectie te schalen op basis van de breedte van de langste optie. U moet misschien een vaste breedte instellen of flex-/rasterbeperkingen gebruiken om indelingsverschuivingen te voorkomen. - Zorg dat uw
<select>een kenmerknameen een bijbehorende<label>heeft. Zo blijft de component zelfs met een aangepaste UI toegankelijk voor schermlezers en werkt deze correct met standaard formulierinzendingen. - Gebruik standaard
value-kenmerken voor opties om te zorgen dat het indienen van formulieren precies zo werkt als voorheen.
Toegankelijkheid
Een systeemeigen <select> verwerkt alleen lineaire navigatie met de pijl-omhoog/pijl-omlaag. De pijl-links/pijl-rechts doen dus niets in de geopende kiezer en met de pijl-omhoog/pijl-omlaag navigeer je nog steeds opeenvolgend door items, ongeacht rijen en kolommen.
Als je ruimtelijke navigatie wilt implementeren die het raster volgt, kun je keydown onderscheppen en de focus naar de dichtstbijzijnde optie in de ingedrukte richting leiden (bijvoorbeeld met getBoundingClientRect()) als de pijltoetsen worden gebruikt.
Gebruik DOM-methoden die werken op de werkelijke geometrie (bijvoorbeeld getBoundingClientRect()) in plaats van CSS-tekenreeksen zoals grid-template-columns of grid-column te parseren om het juiste volgende/vorige item te bepalen. Dit kan onstabiel en foutgevoelig zijn voor complexe rasters, subrasters, RTL en andere lay-outscenario's.
Maak het niet onnodig ingewikkeld. Gebruik de invarianten waarvan je weet dat ze waar zijn voor de vormgeving om deze logica te vereenvoudigen.
Opmerking: Dit heeft alleen betrekking op toetsenbordnavigatie. De <select> en de bijbehorende ::picker() hebben nog steeds hun standaardrollen (menu of listbox). Deze kunnen niet worden overschreven en mogen geen element met role="grid" bevatten.
Een wrapper met role="grid" nesten in de kiezer werkt in de praktijk, maar is niet geldig en kan gebruikers van ondersteunende technologie verrassen.
In sommige gevallen kan het gebruik van <optgroup> om opties te groeperen een goede UX voor schermlezers opleveren zonder role="grid".
Anders is het misschien nodig om over te schakelen naar een aangepaste oplossing met de popover-API, ankerpositionering en role="grid" om de gewenste UX te bereiken voor gebruikers van schermlezers.
Reservestrategie
Fallbacks en browserondersteuning voor aanpasbare <select>
Voor browsers die appearance: base-select nog niet ondersteunen, wordt het element <select> op een nette manier gedegradeerd naar een standaard dropdownmenu van het besturingssysteem.
- Niet-tekstcontent genegeerd: Oudere browsers verwijderen HTML-tags (zoals
<svg>of<div>) binnen<option>-tags en geven alleen de tekstnodes weer. Zorg dat de tekstcontent van de<option>op zichzelf leesbaar en betekenisvol is. - Verwerking van HTML-structuur: Standaard parsers negeren de tags
<button>en<selectedcontent>in<select>of behandelen ze als ongeldig. Er zijn geen zware JavaScript-polyfills vereist voor progressive enhancement als je standaardtekst als een leesbare fallback beschouwt.
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});