
I appreciate the intentionality of Vale’s CSS reset – everything has a reason. But what I found the most eye-opening were the links to the default stylesheets used by Chrome, Safari, and Firefox. These files are overwhelming, but the nerd in me really wanted to know what the differences were between each of them. In detail. Then I could make The One CSS Reset to Rule Them All. It would be better than normalize.css, better than the Meyer reset!
Analyzing the Default CSS
To do a proper analysis I needed to download the default CSS files and clean them. Safari is used by mobile, desktop, and vision and it uses directives to differentiate between them. I removed everything that wasn’t for desktop. Next, I minified each of the files to remove comments and whitespace. With clean “data” I could now analyze the files using the NPM package cssstats-cli. Here’s what I came up with.
- Chrome is 48 KB and has 298 rules.
- Safari is 26 KB and has 175 rules.
- Firefox is 15 KB and has 143 rules.
I wanted to try to see what was actually styled after the rules had been set, but it turns out this is a hard problem. I thought maybe I could write a comprehensive test page, and then programmatically walk through the document to view the computed styles for each object. Then I could compare each of the three stylesheets to see where the actual differences were.
Finding the Differences
Starting with the Chrome stylesheet, I worked my way through about a quarter of it, making HTML to test each ruleset. It was then that I realized testing this was going to be a nightmare. There are just so many rules, targeting so many different scenarios. Many of these scenarios would rarely be triggered. In fact, it may even be impossible. It was time for Claude to step in.
I created a list of every selector in the default CSS files for Chrome, Safari, and Firefox. Then I asked Claude to create a single HTML file with elements that matched every selector. That gave me a massive file with about 600 elements.
Generating the CSS Reset
Next, I created a script to open the HTML file in each browser, using Playwright to grab the computed styles for every element. The script saved all of the computed styles to JSON files. Just a reminder that there are 520 CSS properties on every element! Finally, I created a script that compared the JSON files and for every difference, selected an appropriate default and wrote that style to a CSS file.
The result was a 10,000-line monstrosity of a CSS reset that basically created the lowest common denominator of stylesheets. We’re talking Times New Roman on every element, font sizes set in pixels, etc. Upon visual inspection, I noticed that there were still differences. Queue the table flip. Claude and I added more code to normalize values, handling rounding of decimals, shorthand property differences, etc. The results were almost perfect, but there were still problems.
Optimizing the Result
After looking over the generated stylesheet, I could see that lots of similar properties were getting repeated. I thought, maybe I needed an optimization step. So, I configured it scripts to run CSSO on the generated stylesheet. That cut the size down to 5,400 lines, which was much better, but still far from what a CSS reset file should be doing. Also, it should be stated at this point that I was clearly in CSS normalization territory and not CSS reset territory. But the line between the two gets blurry.
Nuclear CSS Reset
It’s at this point that I came to the conclusion that if you seriously want to normalize/reset the default styles of every browser, there’s only one way to do it. Destroy all user-agent styles and then build from the ground up, styling only what you need. This is the second most extreme CSS reset ever created:
* { all: unset }
rip