A presentation at Web Day Out in in Brighton, UK by Manuel Matuzovic

BREAKING WITH HABITS ć Manuel Matuzovič, Web Day Out, March 12th, 2026 Hello! Thank you so much for having me. It’s great to be here in Brighton. Today I’m here to talk about CSS. Three years ago, I was in Amsterdam to talk about CSS, as well,…
matuzo.social matuzo.at/cssday23 I gave a talk at CSSDay called „That’s not how I wrote CSS 3 years ago”. I explain about how I began styling websites with presentational elements and attributes in HTML, how I learned CSS in the early 2000s, and I mention some of my biggest milestones over the past 20 or so years. I also talk about the present, and I give an outlook for the future. I claim that many of the new features in CSS will significantly change the way we write and think about CSS. The thing is, that was three years ago, and nothing much has changed, at least for me, because I haven’t had a project yet that I could use to put my predictions into practice. That’s why I decided to create my own project and do everything I was talking about. I’m working on a no-class CSS framework called Oli and a web component library called Hanni, named after two of my kids. In this talk, I present my learnings and some of the things that already had a big impact on me.
matuzo.social Reset style sheets Structure and organisation Scalability Customisation Componentisation I want to focus on 5 main areas.
matuzo.social Reset Style Sheets I haven’t been using reset style sheets at all in the last five plus years or so because I just didn’t see the point.
There used to be a time when reset style sheets were really useful because the browser landscape was more diverse than today, and browsers rendered pages very differently by default. Eric Meyer famously created the first popular reset stylesheet, reset.css, which removed margins, padding, and borders and reset other properties.
The thing is, that was a long time ago, and both style sheets solved many problems that don’t exist anymore today, but reset style sheets are still a thing. There are modern style sheets, such as “The New CSS Reset” by Elad Shechter.
matuzo.social
#yo
lo
*:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) { all: unset; display: revert; }
The new CSS reset by Elad Shechter
His approach is to reset almost everything. He selects most of the elements and sets all: unset, and you can also see a lot of none and revert keywords in his style sheet. That’s pretty radical, but there are also other modern style sheets that take a different approach.
So, this is the first area where I’m challenging my usual approach. I’m trying to find out whether it might make sense to use a reset stylesheet in 2026 and whether modern CSS can solve any problems I may have.
: : : : Of course, in my reset stylesheet, I’m doing some of the basic, well-established stuff, like using the traditional box model, but now we are mostly interested in the new CSS stuff.
label:has( + :where( input:not([type=”radio”], [type=”checkbox”]), select, textarea) ) { display: block; } Here’s an example. If you create a label and an input field, it looks like this by default in the browser. The input field is placed next to the label. I would say that in 98% of cases, that’s not what you want, because we know that for UX and accessibility, it’s best to place your form elements below the label. That’s what this rule does. Every label that is followed by an input, select, or textarea sets display block. Of course, that doesn’t apply to radio buttons and check boxes.
matuzo.social Here’s another example: By default, if you open a dialog, it just appears and disappears without a transition. The problem is that the dialog is hidden, and we can’t animate hidden elements, or at least we weren’t able to do that for the longest time.
:
:
Today, it’s possible by not just transitioning opacity but also the display property using the allow-discrete keyword, which enables the animation or transition of discrete properties. The same goes for the overlay property, which allows us to fade out the dialog’s backdrop.
: : To allow our dialog to fade in as well, we have to define a starting style and set the opacity to 0.
Now you see a subtle, but nice transition in and out.
Another great example is popovers. By default, popovers are positioned at the centre of the screen, but we usually want them very close to the button that controls them. What’s great about popovers is that they come with an implicit CSS anchor. That means that you can position them without having to define an anchor explicitly,
Normally, you have to explicitly define an anchor and a reference in the element you want to position. That’s nothing we want in a base style sheet.
For popovers, you don’t have to do that because they come with implicit anchors. It just works. I’m removing the margin, and I’m using the position-area property to position the element, and I’m also providing fallbacks.
Now you can see how the popover is attached to the bottom of the button. And if there’s not enough space to show it below the button, it flips to the upper side of the button. That’s a much better default. You may want to revisit your existing style sheets, create one, and see what modern CSS can do for you.
matuzo.social fokus.dev/tools/uaplus My reset style sheet is available on GitHub, and I also created a website for it. It’s online at fokus.dev. Fokus with a k, because all the goods domain names are gone. The website is just a placeholder, but you will find a breakdown of all the rules I’m using, and I also have a demo that’s really cool. It includes most of the elements in HTML, and you can compare how it looks with my reset stylesheet and the browser’s default rendering, and you can also compare it against other style sheets.
matuzo.social Structure and Organisation The next big topic for me was structure and organisation.
matuzo.social @layer @property div#bu tton { @font-face backgr ound: re * {}} d !impo rtant p {} button {} .card [aria-current=page] #header .is-hidden !important ITCSS by Harry Roberts For most of my career I followed an approach very similar to Harry Robert’s ITCSS, which is represented with this inverted pyramid. The idea is that at the beginning of your CSS, you have generic code that applies to the entire website. In a pre-processor like Sass, that would be mixins, functions, etc. Then you get more specific by writing base rules using tag selectors. Then you get more specific by using selectors for your components, and finally, you have utility classes, which are very specific. The idea is that you avoid specificity problems by starting with very broad, low-specificity selectors and becoming more specific as your file progresses. The concept is great, but it’s just a concept. There isn’t anything that enforces these rules. If you put an id selector at the beginning of the file, it will mess with the specificity.
Luckily, now we have cascade layers that allow us to enforce these rules. Instead of having to manage the specificity for an entire document, you can now split the document into layers and manage the specificity inside these layers. Layers defined later in the document overwrite earlier ones. For my project, I didn’t just want to use cascade layers. I wanted to come up with a structure I can use for any project, of any size. Here is what it looks like. We have four main layers: core, third-party, components, and utility. base consists of three sublayers. One for reset style sheets, one for tokens, and one for base rules. The third-party layer consists of two sublayers. One for imports, so that’s where your bootstrap.css or prism.css belongs, and one for overrides. If there’s anything that you want to change in these third-party styles, you don’t do it just anywhere; you do it here, so there is one dedicated place for it. The component layer consists of two layers, one for the base and one for variations.
*
/
Establish the order of layers upfront
Here is how I add my reset style sheet by using the @import at-rule and importing directly into the core reset layer. * / / Establish the order of layers upfront
Here’s how I add a utility class to the utility layer. * / utilities.css
Here’s an example for component style. * / components/card.css
What’s great about that is that it doesn’t just make working with the Cascade easier, it also allows me to split up my CSS into multiple files. I don’t have to worry about specificity because in the very first line, we have already established the order of the cascade layers. If we use them later on anywhere in the documents, we’re not redefining layers, we’re just reusing them. * / Establishes the order of layers upfront
matuzo.social CORE THIRD-PARTY COMPONENTS UTILITY Here’s how my inverted pyramid looks: Base styles overwritten by third-party styles overwritten by component styles, and finally utility styles with the highest specificity.
matuzo.social CORE RESET TOKENS BASE THIRD-PARTY IMPORTS OVERRIDES COMPONENTS BASE VARIATIONS UTILITY Here’s the pyramid with sublayers.
matuzo.social fokus.dev/tools/css-boilerplate This CSS boilerplate is also online on fokus.dev. I have a dedicated page where I explain my decisions, and I also answer some common questions.
matuzo.social Scalability The next thing I wanted to find out is if there is a way to improve the resilience and scalability of my CSS.
matuzo.social handbuch.wien.gv.at One of my clients is the City of Vienna. I created the concept for their new pattern library and wrote much of it. The way we designed it is to use native HTML and CSS, and for JavaScript, Web Components. We are big fans of progressive enhancement. The website still looks and works fine without JavaScript. It even looks fine if you don’t add any CSS classes. That’s because we designed it as a no-class framework. That turned out to be really useful when we switched from our old CMS to a new one and automatically imported thousands of pages. Because that worked so well, I decided to pay much more attention to base styles than I used to.
Here’s an example: When I work with headings, I usually take the font sizes from the design and put them in my CSS. That’s fine, but just from the CSS, these sizes look like random numbers. But there could actually be a system behind these numbers.
matuzo.social precise-type.com If you look at this website that allows you to create type scales, you will see that, in fact, there is a system. You can pick a base font size, a scale, and the tool will give you different font sizes based on that combination. The tool starts with 16 pixels and multiplies it by the scale, 1.26. The result is 20; then it takes 20 and multiplies it by the scale again, yielding 25. Then it multiplies 25 by the scale, yielding 32, and so on. It’s easy to re-create that in CSS.
I have a custom property for the scale and one for the base font size. My H6 equals the base size. The H5 is the result of the size of the H6 times the scale. The H4 takes the size of the H5 multiplied by the scale, etc. That’s fine, but I don’t like that for every font size, you have to reference the previous font size, and I also don’t like the repetition.
:root { scale: 1.26; base: 1rem; h6 h5 h4 h3 h2 h1 var( base); calc(var( base) calc(var( base) calc(var( base) calc(var( base) calc(var( base)
When I saw that, I was sure some dead dude had probably already come up with a formula for it. scale));
matuzo.social matuzo.at/pow I know that CSS now has math functions, so I went to MDN, and I tried to find the formula. I clicked on some random functions, and then I found this: “the Math.pow() static method returns the value of a base raised to a power.” That looked exactly like what I needed, so I tried it.
:root { scale: 1.26; base: 1rem; h6 h5 h4 h3 h2 h1 calc(var( calc(var( calc(var( calc(var( calc(var( calc(var( base) base) base) base) base) base)
Still a lot of repetition here, but much cooler repetition
I was really happy with that, but then I realised it only applies to headings, and I may want to use the system for other elements, too.
It’s cool that I’m now forced to use my type scale, but what if I don’t want to?
I’m still using my formula, but now it’s the fallback for a custom property. If the —font-size custom property is defined, that’s the font size. If not, it defaults to the formula.
matuzo.social matuzo.at/type-scale Here is what it looks like as an action. I have range sliders for the scale with a value between 1 and 2, and one for the base font size with a value between 10 and 20 pixels. As I’m changing the value, you can see how the scale adapts and looks different. It affects my .big and .small classes, but it doesn’t affect the .custom class. I don’t know about you, but I think that’s super cool.
Another interesting topic is colour. For the most of my part of my career, I didn’t care much about colour and also didn’t know anything about colour theory, but Marc invited me to speak at Beyond Tellerrand in Berlin…
matuzo.social matuzo.at/fckafd … and I presented a talk titled “Color in CSS Or How I Learned to Disrespect Tennis”. In preparation for this talk, I learned a lot about colour theory and also how to use it in CSS. Now I know so much more about colour, but I still fucking hate Tennis.
With my new knowledge, I switched to oklch() because it gives me more flexibility and I have access to much more colors.
The LCH color space is cylindrical. We have three channels. There is Lightness, with a value between 0 and 1 (0% and 100%). Chroma is a value between 0 and 0.4 The Hue angle is similar to the one in HSL, but they’re not identical. If you know that, the function is actually pretty easy to read.
With oklch, we now have access to 50% more colors, and all modern screens support that.
You can see I have five custom properties with five different colours, but when we work with colour, we usually need many more colours, with lighter and darker variations of our base colours.
With relative colour syntax in CSS, that’s easy to do. We use a colour function, extract the channels we need from our base colour, in this case the chroma and the hue, and then we change the lightness.
matuzo.social And this gives us this really nice colour scale for each base colour. They look nice, but they’re far from perfect. If you want to know why, watch my talk at Beyond Tellerrand or ask me later.
matuzo.social There are many more rules in my style sheet, but I would say that was the most interesting stuff. Here’s how my final demo HTML file looks like. [Scrolling through the page. URL: https://olicss.dev/demos/all] TODO: NEUES VIEO
matuzo.social That’s great, but this style sheet only includes styles for elements. This page doesn’t look like a proper website. There is a header and a main navigation, but they don’t look like it.
matuzo.social Customisation I could add rules for the header and the nav element, but that’s risky because there could also be a header in a section or a nav element within the main content. On the other hand, if I know how the markup is structured, I may want the header to look like a proper header. What I need is a level of customisation that lets me either leave it as is or get an optimised version. What I need is some kind of magic that lets me choose.
That’s why I select the root element and I set —magic to true.
matuzo.social And now the header looks like a proper header and the list in the nav element looks like a navigation.
That’s possible thanks to container style queries. In this query I check if —magic is set to true. If that’s the case, I change the layout and styling of the header, navigation and other elements.
Right now, I have three settings: magic, one for layout that allows me to switch to a horizontal layout and one that allows me to switch the font stack.
I’m using some of the font stacks from modernfontstacks.com. That’s basically a switch statement in CSS.
matuzo.social TODO: Ersetzen durch video
On top of my three custom properties with made-up values I also have a bunch of properties that I use directly without querying them. That gives me a high level of customisation.
matuzo.social Componentisation I didn’t come up with that on my own. Of course, I was inspired by the amazing Lea Verou. In a presentation, she discussed how custom properties can replace presentational attributes and classes in components. Here’s an example.
/ < / < / < That’s how it looks like: An image followed by a heading and text. / <
<div class=”card”> <div class=”card inner”> <div class=”card content”> <h3 class=”card heading”>Demo <p>Content p> div>/ < And then we get a slightly larger card. / <
<div class=”card”> <div class=”card inner”> … div> div>/ < And then we get a slightly larger card. / <
<div class=”card” style=” card size: l”> <div class=”card inner”> … div> div>/ < That changes the layout and puts the image on the left and the content on the right. / <
<div class=”card” style=” card size: l; <div class=”card inner”> … div> div>There are also other advantages.
Of course, working with classes is still fine, that’s not the point, but one of the advantages of using custom properties is inheritance. If you want all items in a parent item to adapt to a certain size, you can define the size on each child,… / <
<div class=”news”> <div class=”card” <div class=”card” <div class=”card” <div class=”card” div>/ / / / < < < < That enables you to influence the styling of multiple elements at once without having to predefine selectors. / <
<div class=”news” style=” card size: s”> <div class=”card”>… div> <div class=”card”>… div> <div class=”card”>… div> <div class=”card”>… div> div>Another advantage is that you can write DRYer code. Let’s say you define a max-width for the card. On a larger screen, you increase the max-width. You do the same in a custom class. You have to define the value once for the media query and than repeat it for the custom class.
You set up a system once, and then you just configure it.
matuzo.social olicss.com Everything I showed you is online at olicss.com. It is not reading for production yet because I’m still working on it, and Firefox doesn’t support container style queries…
matuzo.social Firefox 148 …but they are already in Nightly, which means you’ll be able to use them soon. Firefox Nightly 150
slides: matuzo.at/ webdayout26
matuzo.social Thank you! ❤ accessibility-cookbook.com matuzo.social htmhell.dev matuzo.at manuel@matuzo.at Thank you!