CSS: Unavoidable Bad Parts
48 points by LesleyLai
48 points by LesleyLai
Nitpicking: the definition of "responsive design" is to "make web pages render well on a variety of devices and window or screen sizes from minimum to maximum display size to ensure usability and satisfaction." Media/container queries are just one tool for implementing that, and responsive design is more of a mindset than "use media query for everything". So maybe the "bad" is just "overuse media queries/break points when you can implement responsive design without it."
I'm not sure I agree with you OR wikipedia on that. "Responsive design" definitely is about breakpoints and "responding" when they're broken. The wikipedia page says "A responsive design adapts the web-page layout to the viewing environment by using techniques such as fluid proportion-based grids, flexible images,", but that's just not true. "Fluid design" is a similar, but different approach to handling dynamic layouts that just kind of fills the available space like amorphous fluid, but is distinct from "responsive design" which changes in a more controlled way based on breakpoints. I don't think "responsive design" is an anti-pattern at all (except that it's a lot of work), and is necessary to deliver a well laid-out document that's readable on many display types and sizes.
Bad: Browser defaults
There’s some misleading stuff in this section. Reset and normalisation stylesheets are very different in purpose and behaviour, doing three different things, and it conflates them.
Resets are very little to do with browsers differing from one another: they’re more about elements differing from one another. They give you a tabula rasa, where ol no longer has padding-inline-start, where button no longer has any appearance; and let you craft your styles from scratch rather than from user agent stylesheet.
Normalisations instead try to work with the user agent stylesheet. They’re generally made up of two parts: normalising differences between browsers, and changing some styles to things they considered more sensible (which I suppose could be considered a form of normalisation—between expectations and reality).
But the fact is there aren’t many things left where browsers have meaningfully different defaults. General web content authors can probably completely ignore it, which is great! Chromium has the wrong table border-color (WebKit fixed theirs 1½ years ago)… there are various differences in margins/padding/sizing on some form elements (which I think people are too eager to vitiate)… anything involving appearance is deliberately about potential variations between browsers… and I think WebKit may still not have got its act together around styling <summary> by ::marker. Honestly, that’s about it, though if you want a full two years of perfection you’ll need a couple more.
Bad: box-sizing
From my collection of dozens of sets of very rough notes that I might turn into proper posts some day, “Against box-sizing: border-box” (which is pretty high on the priority list to publish):
A contrarian view against the popular practice of
*, *::before, *::after { box-sizing: border-box }or:root { box-sizing: border-box } *, :before, :after { box-sizing: inherit }or similar.border-box is about layout, content-box about content. You should aim to design things around content, not layout: leave layout to the parent.
border-box is incompatible with ratios: you need content-box there.
border-box is only actually useful for things like making body fill the viewport.
Perhaps the biggest remaining case for border-box is
width: 100%when you have padding;width: stretchshould help there. (2025-10-20: Chromium family only, but-moz-availableand-webkit-fill-availablefill the remaining major browsers.)See also what I wrote in https://news.ycombinator.com/item?id=29357636.
This thing is listed in https://wiki.csswg.org/ideas/mistakes: I disagree that it was a mistake.
Note also what’s said later about responsive design; HTML’s “inherent responsiveness” is akin to content-box rather than border-box. Think about it.
Doubleplusungood: font-size
I dislike his take on font-size-adjust: it replaces one ubiquitous, well-understood and fairly-consistently-handleable problem with a less-well-traversed problem which will yield results slightly less bad for some people but slightly worse for others. The underlying problem is fundamentally unsolvable; and, being minor, I think it’s best to leave things at the defaults. When you use font-size-adjust, you’re making unsound and unwarranted assumptions about aspect ratios and other metrics of the user’s fonts.
This is my present opinion, but I plan to investigate it exhaustively at some point, maybe still this year. I don’t rule out changing my opinion, but I’m not optimistic about it, particularly because it messes with using ems as “px, but scalable” so that e.g. 0.25rem no longer means “probably 4px but if the browser em isn’t 16px it will be proportional to that” but rather “now even the font family gets in on the act and it could easily be 3.4567px or 4.5678px even on an otherwise ‘normal’ configuration”.
Bad: line-height
set in the same font
This is… hmm. I’d say “mostly false”, but more generally “it’s complicated”. It’s actually just about the font-sizes involved, but switching languages or using font-family: monospace can change the font-size unexpectedly. (Most notably, font-family: monospace without an absolute font-size will probably shrink the font-size by 18.75%.) Well, vertical-align too, particularly if you have inline blocks, and <sup> and <sub> become fun, and font metrics can get involved there, so… maybe I’ll drop the “mostly false” and just stick with “it’s complicated”. But the wording “set in the same font” is not strictly correct.
Chaotic Good: margin collapsing
Oh, it’s a lot worse than described. Julia Evans’ owl selector assumes only a single level of meaningful nesting, which just isn’t real. aside, ol, div, plenty more where it doesn’t cut it. Margin collapse is actually pretty pragmatic in general. But also part of flow layout. Honestly it makes for a fairly compelling case against using flex or grid for general content. Compelling reasons for those layouts definitely exist, but having to mess around with gap or margins all over the place tends to be a bit fragile, and gets old quickly.
A useful thing people may not know of: display: flow-root lets you contain children’s margins, preventing them from collapsing with the parent’s margins. Historically this involved one of a few dodgy hacks.
Bad: responsive design
Like a sibling commenter, I don’t agree with this overarching framing; but the concept it describes is sound. People use media queries for a lot of stuff that doesn’t need it, and it’s good to work with the browser rather than fight against it. There’s still a place for breakpoints, but if it’s possible to express the layout variations in terms of the content, that’s almost always preferable.
One adjacent trick I’ve made a lot of use of in the past few years is clamped linear interpolation using viewport units. Using my own syntax (disappointingly incompatible with @function; the at will need to become ,), and then showing an expansion:
margin-inline: --vw-lerp(1rem at 20rem, 2.5rem at 60rem);
margin-inline: clamp(1rem,1rem + ((2.5 - 1)/(60 - 20)*(100vw - 20rem)),2.5rem);
Last year I finally implemented this as a LightningCSS visitor; I’ve found it very nice.
“The value should be 1rem when the viewport is 20rem wide (typically 320px) or less; then interpolate linearly up to 2.5rem by a viewport width of 60rem (typically 960px) and stop there.”
Feels very nice. No harsh breakpoints, just smooth interpolation, and user-font-size-aware at that.
particularly because it messes with using ems as “px, but scalable”
I don't think that's how font-size-adjust works? It is confusing, because font-size and font-size-adjust are named the same, but affect different things: the former is the size of em-box, the latter is the size of glyph inside the em-box.
So em and rem stay the same. ch does change, but you want that to change, as it is font-dependent.
I do encourage you to write about font-size-adjust. As I've said, I am very much not an expert, so my level of confidence is very low, but so far it does seem that font-size-adjust is neglected: almost unknown, but an absolutely massive improvement. Of course, you can't make any two fonts automatically comparable under any context, but making the size of x the same (as opposed to the size of em-box) covers 90% of fonts/contexts.
Hmm… yes, I had misunderstood that. I’m not sure if that’s better or worse—again, it trades one problem for another.
Mixed feelings about this article, in that I like the spirit of it, and the perspective of people that aren't completely into HTML & CSS is very important!
But some of the bad things can be good depending on the context! CSS selectors can be easy to abuse, but they don't have to be bad. Instead of concluding that you should do A or B, you can have general rules/selectors and sprinkle exception based or utility classes where necessary. The article even shows an example of a nice selector:
section > *+* {
margin-top: 1rem;
}
You might not need media queries if you can get away with container queries.
There are probably other solutions to popular painpoints in CSS, and it'll feel like CSS is a huuuuggeee surface area. But because CSS is so widespread and you can do a lot with it, people will have different opinions and this'll clash more often, than celebrate how far CSS has come and what you can do with it in relatively little code if you buy into its concepts!
So yea I also do hope for more resources in the end, but for the curious ones: https://every-layout.dev/ helped me quite a lot with understanding how things can mesh in CSS.
Here’s another vote for Every Layout. I still don’t like CSS (I personally think the cascade is a fundamentally terrible layout model) but at least I can usually get things to look reasonable and responsive on both desktop and mobile now.
Another thing that made web layout click for me personally was to realize that good websites are fundamentally vertical designs. Elements should stack on top of each other by default. Design for mobile screens first and then spread out as a bonus on bigger screens.
I personally think the cascade is a fundamentally terrible layout model
I used to think so as well, but for some rather flat documents I really do like that it cascades, not sure if that it makes it worth it, but I'd not categorize it as unavoidable bad.
Are there any layout models you do like?
Are there any layout models you do like?
Yes, there are several that I find more intuitive and that makes it easier to achieve the layout that you want. The cascade, varying base styling and lack of namespacing in CSS means that it is very difficult and probably actually impossible to predict what the interface will end up looking like.
My primary preference are programmable immediate mode layout models like the original OpenGL, and Dear Imgui.
I also think the Qt layout (box/split) model is more suitable for designing the kinds of interfaces that the web provides these days. https://doc.qt.io/qt-6/layout.html
I also prefer Apples Uikit constraint-based model over the more declarative SwiftUI.
Yea some immediate mode libraries (nuklear is another one thats pops to mind) have some really cool "it just works" stuff.
And I've always been keen on Qt, it looked like a GUI framework I should really like, but it's toolchain and documentation always confused me, more than CSS(it's been years at this point tho)!
The cascade, varying base styling and lack of namespacing in CSS means that it is very difficult and probably actually impossible to predict what the interface will end up looking like.
I respectfully disagree with some parts of this statement, but I also don't think CSS makes it particularly easy to predict it(going back to the mixed feelings heh).
And as a sidenote, in case you might not know, a form of namespacing in CSS can be done these days with layers or scope depending on what kind of namespacing you're seeking.
Yeah a lot of my complaints with CSS have been addressed over the years and these days things are actually fairly manageable. But it's ridiculous that it took almost 30 years to get namespacing, variables, container queries etc. when all of these things were available in every single layout system that predated CSS.
I use this adjacent sibling selector on every CSS project I ever am involved in. Usually in this form:
.flow > * + * {
margin-top: var(--flow-space, 1rem);
}
This lets you override the spacing in different contexts in which you are using it.
The introduction of variables (remember when those didn't exist?) opens up a lot of different opportunities for creating utilities like this that ride the CSS cascade.
The two reasonable approaches are:
A. Conclude that CSS selectors add abstraction capability along the wrong axis, and stick to classless CSS and inline styles, using something like Tailwind to make writing inlines prettier, and something like JSX (or any other templating engine supporting composition) to avoid repetition in HTML.
B Use CSS nesting to avoid writing “far reaching” selectors and style component-per-component:
Not sure I agree with this. Nesting is syntactic sugar and doesn't meaningfully avoid the problem of overly specific selectors.
Plenty of us were out here doing selector nesting 15 years ago in Sass and arriving one-by-one at the conclusion that we'd tied our own shoelaces together by coupling our CSS selectors so tightly to the structure of our HTML.
The trap with nesting is that it doesn't bite you so much in the early days of a project when you're primarily building out new stuff. It actually feels great to write CSS this way when you're working on a greenfield project and mainly adding new code.
Then a few months in you do your first major layout tweaks and design revisions. Wrapper elements start exchanging places with each other in your HTML, and getting the CSS to follow suit feels like solving a rubik's cube on LSD.
For my money the best practices around selector specificity management peaked with the idea of flattening specificity down as low as possible with ideally most things on a simple selector (one classname) and then a small number of complex (multiple classnames) and compound (e.g. a:hover) selectors. Talking BEM, OOCSS and friends. Coincidentally this was also the last major evolution of CSS methodology before mindshare started shifting hard towards JS-centric tools-based solutions to these problems.
Even as someone who works with distributed systems, Joshua W. Comeau's CSS course made me actually enjoy writing it. He does a great job at explaining it from first principles to build a good mental model. Very little "fluff" compared to other courses.
It's unlikely systems people will pay for it, but his blog posts are just as polished:
I feel that there is some conflicting advice in the article
Good: Classless CSS
Bad: CSS selectors
As the way to write classless CSS is to relying in CSS selectors more. See: https://www.keithcirkel.co.uk/css-classes-considered-harmful/
The beginning of that article ("Class is old. Like real old.") wasn't very promising, but I like the final solution he arrives at. In the past, Web Components always had so much boilerplate it wasn't worth it, but the last example in the post was fairly elegant. I'm glad that technology has improved.
Interesting article - but I think the author is using the nested selector in places where it's a no-op?
header { /* Site Header */
margin-bottom: 2rem;
& nav { /* ampersand redundant here? */
/* Styles, specific to nav in the Header. */
}
}
Some consider the ability to elide the ampersand to be a mistake, and will always write out the &. I think it’s a reasonable position.
Me, I’m on the fence. Initially I thought it a mistake, but I’ve come to find some scenarios where it’s at least convenient—write a bunch of styles, then just wrap them in header { … } and bam, they’re scoped. Now if only you could also write @keyframes and other such not-selector-based at-rules inside there…
Hm, I hadn't considered that - as an aging user of css - I see the ampersand as noise. But keeping it in order keep syntax consistent had a certain appeal I suppose.
This is just really awful advice lol. I love reading Maklad, but this is just clearly written by someone who has not had to write CSS professionally. Basically all of these things are the bad practices of amateurs that professional CSS writing avoids.
To be more specific in my criticism:
I think for amateurs, the main thing you are designing is the content box. The CMS spits out a bunch of paragraphs with emphasis, links, and lists inside, and most of your time is spent styling that. You style that without selectors, so you end up also styling the <main> and <nav> without selectors too.
I think for professionals, you spend very little time designing the content box. You do it once early in the life of the project, and then slowly fix bugs with little things as time goes on, but basically, it's one and done. The vast, vast majority of your time is spent on making components, either custom components or reusable ones. Reusable components are harder to make, and you basically end up making your own site specific Bootstrap clone. Custom components are easier, but you end up with a lot of code and need some strategy for keeping the code from unintentionally interfering with other components (e.g. BEM, OOCSS, utility classes like Tailwind, etc.).
I think the main takeaway is that different techniques are applicable at different scales. If you find that professional CSS writing seems pointless to you, it's probably because you're working on a problem with a different scale.
I think the main takeaway is that different techniques are applicable at different scales. If you find that professional CSS writing seems pointless to you, it's probably because you're working on a problem with a different scale.
Extremely true. And most people don’t know there are scales with very different concerns than theirs. A megacorp wants to fight cascade, a smaller company might be concerned with design systems and consistency at their scale, and a person working on their personal site should never worry about any of that.
This is just really awful advice lol
I mean, they explicitly mention they've never used CSS in anger
I never wrote “production” CSS
That said, I agree with Bad: Wrappers. I have seen CSS experts write CSS using one or two files for the entire site and I've seen people that write a lot of CSS. That path eventually leads to misguided approaches like BEM, to make writing a lot CSS manageable.
The bit about everything having "layers and layers" of wrapper elements in the wild has a huge knock on effect of front end dev. If people stopped and thought "ok what should the HTML be by default, and then what should the HTML be when the user clicks this?" then web apps - even those rendered on the client - would be a lot slimmer and simpler.
I like the article, nice high-level view of what you need to jump in to styling a personal site or similar. I will echo a lot of other comments here, labelling things as 'bad' is a bit confusing, especially things like 'responsive design' or 'css selectors'. These two concepts in particular make your site work better and should lead to fewer lines of CSS/smaller payload in the long run.
The idea of a short 'intro to frontend' is a nice one - there seems to be a perception that frontend dev is easy/not really coding, so when people jump into it and can't get things working quickly they tend to complain or disparage. Just having a quick overview would definitely help out in those situations.
I am a wrong person to write this kind of thing, as I have neither the time, nor experience. I’d much rather read a book about this.
That's exactly the kind of person I'd like to read a "here's how to get the bare minimum rendering and responsive" from. My brain is too small to deal with CSS on top of everything else—and learning it would push out all of the Simpsons and Star Trek trivia I've spent years accumulating in there.
Bad: vertical rhythm [...] As if there’s invisible lined paper behind your web-page
This is entirely possible using EM values though. There would be some slight variation if you're combining different fonts because of their metrics, but even then there's flex's align-items: baseline.