I've talked before about how it's possible to filter a set of elements, in response to user requests, using only HTML + CSS. But did you know that you can also sort a set of elements. And it's pretty easy!
Sortable animals
Here are some animals for you -
Sort animals by:
- Goat IntelligenceNot so smart Aggression6 Speed6 Strength6 Size7 Beauty4
- Butterfly IntelligenceStoopid Aggression2 SpeedSlow Strength1 Size2 Beauty14
- Dog IntelligenceSuperb Aggression7 SpeedPretty fast Strength10 Size6 Beauty8
- Tortoise IntelligenceStoopid Aggression1 SpeedIncredibly slow Strength5 Size4 Beauty4
- Cow IntelligenceOkay Aggression3 SpeedA bit slow Strength10 Size13 Beauty4
- Sheep IntelligenceNot so smart Aggression2 Speed6 Strength6 Size7 Beauty4
- Cat IntelligenceSuperb Aggression7 SpeedModerately fast Strength5 Size5 Beauty8
- Horse IntelligenceGreat Aggression8 SpeedSuper fast Strength11 Size15 Beauty13
- Snake IntelligenceGood Aggression10 SpeedPretty fast Strength14 Size5 Beauty4
How it works
This example leverages two CSS technologies: variables, and the order:
property. The list of animals gets display: flex; (display: grid; would also work), and each animal gets n integer representation of each of its
attributes as CSS variables, e.g.:
<li style="--int: 5; --agg: 6; --spd: 6; --str: 6; --siz: 7; --bty: 4;"> <span>๐</span> <span>Goat</span> <!-- other stuff to display about goats --> </li> <li style="--int: 3; --agg: 2; --spd: 3; --str: 1; --siz: 2; --bty: 14;"> <span>๐ฆ</span> <span>Butterfly</span> <!-- other stuff to display about butterflies --> </li> <!-- other animals... -->
Then, we can use the parent:has(select option[value="..."]:checked) pattern to set the order: of each animal based on whether or not a parent container (e.g.
the <body>) has an appropriate <select>'s <option> chosen. That might look like this, for example:
/* If the <... class="sortable-wrapper"> contains a <select id="my-select"> * whose <option value="int"> is selected, then any <li>s within the * <... class="sortable-wrapper">... */ .sortable-wrapper:has(#my-select option[value="int"]:checked) li { /* ...are ordered by their --int: variable. */ order: var(--int); } /* If the <... class="sortable-wrapper"> contains a <select id="my-select"> * whose <option value="agg"> is selected, then any <li>s within the * <... class="sortable-wrapper">... */ .sortable-wrapper:has(#my-select option[value="agg"]:checked) li { /* ...are ordered by their --agg: variable. */ order: var(--agg); }
If any two items share the same variable value, the "tie-breaker" is their order within the HTML source code. If any items lack an value for a sort, they're treated as if
they had order: 0;. This could be used for a "default" sort that puts the animals (or whatever) into alphabetical order, for example.
The direction checkbox uses the same :has(...:checked) pattern, but swaps the flex-direction: column to flex-direction: column-reverse. Simple
enough!
The whole thing works entirely without JavaScript, making it suitable for environments where JavaScript might not be available... or just for performance and resilience. Even without CSS support available, it degrades gracefully into a static list, incapable of being sorted. And it can be combined with the filtering I wrote about earlier to provide a rich filter-and-sort interface, although for complex datasets you might like to use a static site generator or other compilation process to generate the HTML code.
0 comments