Seb Dancer-Michel
Web developer – Q4 2024
Hi there!
I’m Seb, a web developer with 5+ years of experience making
With an affinity for front-end development and UI design, I focus my efforts on crafting adequate interfaces between humans and technology.
30 years old, living in Amsterdam, Netherlands and originally from Toulouse, France. Curious about mostly everything. An optimist at heart, always tries to focus on the bright side of things. A lover of memes, cats, red pandas, and cute animals in general. A millenial in mind and spirit. Will always strive to improve our world in the smallest ways.
Solving puzzles is what I love doing most.
To find pragmatic solutions to practical problems and ideas. Whether it’s building a web page, a contact form, an interactive installation, or a hands-free display, I’m always looking to shape the adequate interface.
I have a broad horizontal knowledge of all things Internet and tech, and my curiosity pushes me to learn new things and adapt to the fast changing Web continuously.
What I use regularly: HTML, CSS, Javascript/Typescript, ReactJS, and ThreeJS.
Previously
- 2023-now – Freelance
- 2019-2023 – Your Majesty – full time contract
- 2018 – Werkstatt – 6 mo. apprenticeship
- 2017 – Your Majesty – 5 mo. internship
- 2016 – Cinémur – 4 mo. internship
- 2015 – Purée Maison – 3 mo. internship
- 2014 – Oréalys – 2 mo. internship
I studied at HETIC (and got my masters degree) between 2014 and 2019, going through a multi-disciplinary program teaching us design, web development, communication, marketing, entrepreneurship, and everything else.
I was involved with the largest student movement in France, the Junior-Entreprises. I worked for 1 year at HETIC's own J.E., Synerg'hetic, then volunteered to work as part of the team of 20 students managing the French confederation on a national level.
Content filters using only CSS
tl;dr: a friend asked me to help adding a simple system to filter content on her website. I did it using only CSS, and leveraging the power of :has. Here's a codepen to see it in action.
The problem
A friend of mine asked me to help her adding a simple system to filter content on her website. She has many pieces of work to present, and wanted to add categories to them. She built her website using Cargo. Nothing complicated, but I wanted to do it quickly and was curious if you could do it without any dependencies.
Turns out you can! And it's damn simple.
The solution
First, let's write our HTML: we'll have a list of items, and a list of radio inputs to filter them.
<div class="filters"> <label> <input type="radio" name="filter" value="all" checked /> <span>all</span> </label> <label> <input type="radio" name="filter" value="branding" /> <span>branding</span> </label> <label> <input type="radio" name="filter" value="motion" /> <span>motion</span> </label> <label> <input type="radio" name="filter" value="webdesign" /> <span>webdesign</span> </label> </div> <ul class="items"> <li data-tags="webdesign">one (webdesign)</li> <li data-tags="branding,webdesign">two (branding, webdesign)</li> <li data-tags="branding,motion">three (branding, motion)</li> <li data-tags="motion">four (motion)</li> <li data-tags="branding">five (branding)</li> </ul>
Nothing fancy here. You’ll note that the radio inputs all have the same name
"filter"
so toggling one toggles the others. In parallel, the items all have a custom “data-tags”
attribute, which allows us to specify the associated tags to each item.
Now for the CSS:
We style the page and the basic elements, nothing of note here:
body { font-family: sans-serif; font-size: 20px; background: black; color: white; margin: 0; padding: 20px; } .filters { display: flex; flex-direction: column; } input:checked + span { text-decoration: underline; } .items { all: unset; display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; margin-top: 20px; } .items li { aspect-ratio: 1/1; border: 2px solid white; display: flex; justify-content: center; align-items: center; }
And finally, we apply the magic:
.filters:has(input[value='all']:checked) + .items li { display: flex; } .filters:has(input[value='branding']:checked) + .items li:not([data-tags*='branding']), .filters:has(input[value='motion']:checked) + .items li:not([data-tags*='motion']), .filters:has(input[value='webdesign']:checked) + .items li:not([data-tags*='webdesign']) { display: none; }
Now if those lines look a bit cryptic, I'll translate them for you:
- The first rule says: "if the input with value 'all' is checked, then display all the items". It serves as a default/reset.
- The second rule says: "if the input with value 'branding' is checked, then display only the items that do not have the 'branding' tag" (and so on for the other inputs).
What's important to note here: We're essentially comparing the two parent elements
.filters
and .items
. It's important that the two elements being compared are at the same level. It's a simple principle, but I find it very powerful and makes the dreaded CSS Cascade working in our favour! There's really a lot of stuff that can be done using this, and I'm sure we'll see more and more of it in the future.
And here's the final result! https://codepen.io/Far0s/pen/eYXVzxz?editors=1100
Nothing is perfect
Of course this is a very simplistic solution for a problem that can be a bigger headache than in my situation.
For example, no animations. This could be added with some CSS classes and a bit of Javascript. And probably some CSS View Transitions in the mix? I'd be curious to see that.
And what about that repeated last rule? It's not very DRY and not easy to maintain as is. In a real project, I would probably:
- either make the list item a component that would write its own rules based on its tags,
- or have a bit of Javascript to generate the rules based on the items.
Regardless, this was a fun little exercise in flexing my CSS muscles and I liked doing it, especially to help out a friend. I hope you found it interesting too! I'll try and post more of those small tricks as I come across them, so stay tuned!