Modern CSS Code Snippets: Stop writing CSS like it's 2015
664 points by eustoria 2 days ago | 279 comments

ktpsns 2 days ago
CSS in 2025: Let's write html inlined styles as if it was 2005 and separation of formatting/representation was never invented. I talk of tailwind, of course.
reply
abustamam 8 hours ago
Having worked on teams that wrote bad (S)CSS and teams that wrote bad tailwind, I prefer bad tailwind.

With tailwind, I can guarantee that changing a style in one component will only change that component. With css, there is no such guarantee. So of course the (wrong) way many devs fix it that is to add a new class, probably doubly specific, sometimes with important, and then everyone is sad.

reply
namuol 2 days ago
The deadest horse in web development is the myth of “separation of concerns”
reply
IgorPartola 24 hours ago
I was recently doing some very specific web scraping of some very public very static documents. About 25% of them use a soup of divs with hashes for class names. Not a <main> or <article> or <section> in sight. I am fine with the idea of what tailwind does but like at least using semantic tags where appropriate could be a thing.
reply
falcor84 8 hours ago
There's also the issue that whatwg pretty much stopped adding new semantic elements; if I'm not mistaken, only <search> and <dialog> were added in the last decade, after the introduction of HTML5. <main>, <section> and <article> are increasingly being overloaded to the point of meaninglessness.

The most cited example of one that's clearly missing is <comment> for user-added content, but there are probably dozens we could add that could help deal with the div soup. By not adding any new elements, whatwg is essentially saying "You're not going to be able to use these the existing tags to fully add proper semantics anyway, so why try?"

reply
jcgl 13 hours ago
That's totally orthogonal to Tailwind though; there's nothing that precludes you from combining semantic elements with it. The only thing that would make a dev reach for <div class="m-2"> instead of <article class="m-2"> is the dev's own (lack of) taste. It's no different than writing out separate CSS or using inline styles.
reply
namuol 19 hours ago
You can grumble at web developers in general for things like div soup, but the hostility towards tailwind is misplaced. Hashed classes aren’t even tailwind, and the tools that generate them are not the problem either.
reply
akst 20 hours ago
I do sympathise as someone who has to scrape content from time to time, but that doesn't sound like a problem for the author of the content or something that impacts their intended user.
reply
luckydata 18 hours ago
that's a problem for accessibility too
reply
UqWBcuFx6NV4r 14 hours ago
No, it really isn’t. Use of Tailwind has no bearing on the semantics of your HTML. You’re conflating two completely different things.
reply
isqueiros 15 hours ago
tailwind is very much not a problem for accessibility? if your content is semantic and you add the appropriate aria tags, whether or not you have 300 classes or 1 will make no difference for screen readers
reply
eitau_1 12 hours ago
I'll add that writing userscripts became more challenging b/c of this phenomenon.
reply
burningChrome 6 hours ago
Not defending it, but this is most likely a cause of using some enterprise CMS like Adobe AEM which is notorious for doing stuff like this.

Its not right, but a lot of times CMS's are horrifically bad at adding content without a slew of nested, auto-generated <div>'s.

reply
bryanrasmussen 16 hours ago
I'm pretty sure unreadable class names are a byproduct, but perhaps some people may also consider it a feature, of their particular build process.

so they may very well have semantic tags in their development environment. Of course debugging things becomes more difficult for the developer as well unless there is some sort of lookup table to tell them that class .uv.le in the browser maps to .user.name in their codebase, in which case it only becomes marginally slower for some cases.

reply
xnx 12 hours ago
Wait until sites start rendering to canvas. You will yearn for the days of div soup.
reply
agos 9 hours ago
why should they? this has been a worry for some time, but I don't see it happening
reply
kevlened 6 hours ago
There's an active proposal for rendering html in canvas to control styling. Text rendering and accessibility are main focuses for the proposal.

https://github.com/WICG/html-in-canvas

reply
nonethewiser 8 hours ago
It already happened. WASM. And it's generally been used well.
reply
zeroCalories 21 hours ago
Tbh there's nothing really wrong with that. You don't need stuff like article or section if you set the right attributes. Often easier to just use divs to get the structure right, and figure out the meaning later.
reply
ordersofmag 20 hours ago
And there's 'nothing wrong' with just writing code with variables named 'a1, a2, a3'. But when some poor sod has to dig through your mess to figure out what you had in mind it turns out that having an easier to discern logical structure to your code (or html) makes it better. I've dug through a lot of html. And there's a ton of ugly code smell out there. Layers and layers of "I don't really know what I'm doing but I guess it looks okay and I'll make it make sense later". I'm sure it pays the bills for someone. But it makes me sad.
reply
mhuffman 20 hours ago
Well, it does help if you expect sight impaired people with software to find the site useful.
reply
zeroCalories 16 hours ago
That's why I said setting the right attributes. You can make a fully featured accessible website using only divs.
reply
raincole 20 hours ago
Exactly this.

I wonder if the people downvoted you realize that HN is basically just a big table and a bunch of div, and they use this very site just fine?

reply
dotancohen 20 hours ago
As a user, I don't care.

As a disabled user with a screen reader, I might care.

As a developer tasked with maintaining it after the original dev left, I most certainly would care a great deal.

reply
raincole 19 hours ago
As a developer you maintain the source templates, not the compiled/generated HTML. The HTML is messy doesn't mean the source templates are so.

Semantic web is basically "please think of the crawlers."

reply
zeroCalories 16 hours ago
Besides the points raised here, I think it's worth noting that using stuff like the article tag does not necessarily make things easier to maintain. You do indeed get a lower line count, but you're coupling the structure and meaning. Sometimes that's fine, but a11y can be tricky to get right and it's often easier to push it off until you've got something working first.
reply
xmprt 19 hours ago
Except that as a developer you have access to the original source code where things are well structured. It only turns into div soup after the React/Vue code gets compiled down to HTML+CSS+JS that can run on any browser.
reply
chuckadams 9 hours ago
And it's nearly impossible to customize any part of the layout with user styles because of that.
reply
oneeyedpigeon 13 hours ago
It's possible to know that HN is basically a big table, use the site just fine, and still recognise that there are disadvantages to that approach.
reply
tgv 13 hours ago
I don't think "separation of concerns" is entirely dead. Ideally, the CSS is readable and maintainable, and that implies structure. If you have a bunch of (co-)related components, you don't want to find/replace tailwind class names when you need to change the layout. So you separate that part of the layout in classes based on (layout!) functionality. You can see that as "concerns."
reply
kristopolous 22 hours ago
It's a choice. The dominant paradigms choose not to.

I disagree. And that makes me the loser here

reply
namuol 19 hours ago
I agree, it’s a choice. So is the opposite. Complaining about other people’s choice of doing things on the web is the choice I’m actually tired of.
reply
samastur 11 hours ago
Read something else then.
reply
gilcot 22 hours ago
Another loser here to second you
reply
kristopolous 22 hours ago
I wrote these libraries like

https://github.com/kristopolous/db.js and https://github.com/kristopolous/evda in the early 2010s. I spent months on them

I was all in. I swore off touching front end in 2022. It's terrible now

reply
noduerme 21 hours ago
Ah, yeah. I spent the early 2010s writing front-ends in AS3, so imagine how that turned out. I wrote my own event system too when I was forced to head to javascript, but in the end I mostly just used jquery's, and it's still what I use. I agree the event-driven paradigm leads to sloppy code, but static event names are enough of a clue to what's invoked most of the time, even in relatively large projects. And most things can sensibly just be promisified now anyway, besides user interactions.

I thought it was funny that you wrote this way back when:

>> I've often seen projects where I think "what talks to what and how? What is the separation of concerns and where does this code live?"

reply
zarzavat 14 hours ago
I don't use Tailwind, it's a solution to a problem I don't have.

I can understand how it might be useful for certain types of web development, e.g. landing pages where the content and styles are tightly coupled.

So as a technology, it's OK. But my god its userbase is toxic and obnoxious.

reply
netdevphoenix 9 hours ago
The majority of its userbase is no longer made of humans though.
reply
UqWBcuFx6NV4r 14 hours ago
Okay. I personally don’t care for anyone so chronically online that they think that their interactions with other similarly chronically online people is at all representative of the userbase of something as immensely widely used as Tailwind.

Whatever the professional equivalent of ‘touching grass’ is, I suggest you do that at your earliest convenience.

What an absurdly absolutist statement.

reply
nonethewiser 8 hours ago
I can't believe this isnt better understood. Style definitions on reusable components are good. The idea that your css doesn't have to know about your html just creates tons of problems and complexity. Global themes and reusable styled are fine.

If we are talking about statically defined html then sure. make your global css files.

reply
appplication 2 days ago
You can separate concerns without violating locality of behavior, and that’s exactly what tailwind does.

It admittedly does not do a good job at being very DRY but I think that’s poorly applied to HTML/CSS in general, and the most DRY css is often over abstracted to the point of becoming nigh uninterpretable.

reply
nine_k 2 days ago
When I write CSS, I most often do not want the locality of behavior. I instead want uniformity of behavior, hence "semantic" styles. Even the trivial light / dark mode switching is pain with Tailwind, when classes like "color-gray-200" are routinely applied.
reply
appplication 2 days ago
I’d somewhat agree with you there, but I usually use variables for uniformity. I do see arguments against tailwind but find anytime I’ve tried to do anything else it just feels like bikeshedding on internals for the same end result.

Really what I want to see is beautiful TDD for CSS so that uniformity can be enforced, but I’m not sure that exists.

reply
nine_k 24 hours ago
Variables are hugely helpful, I agreee. IDK about bikeshedding. I'm very used to writing React code that normally declares no styles for components at all, and having CSS that style components using 1-2 classes, specific to these components. Container components control margins, <body> controls general things like fonts.

It seems that what solves the problem is a good component library. "But I need red text here!" For what reason? It's a warning. OK, we've got <Text variant="warning">, it will be styled appropriately, and will look like every other warning in the application.

reply
zeroCalories 21 hours ago
I tend to think that if you're having issues with repeating yourself with stuff like tailwind you probably need to refactor your JSX/templates to share the repeated code. Keeping stuff like CSS isolated is a deliberate choice that helps massively with stuff like splitting code, and keeping changes side effect free.
reply
the_other 14 hours ago
SoC is how all maintainable software is built. A function for A, a class for B, DDD-spec'd modules and features, databases on separate machines, API definitions, queuing systems, event systems, load balancing, web servers.

You don't even need to think of the web to see how content and presentation are different. Try editing a text file with hard line breaks in and you'll quickly understand how presentation and content are orthogonal.

reply
UqWBcuFx6NV4r 14 hours ago
Please don’t be so condescending. We all know what separation of concerns is.

The comment said “web development”, and it’s inarguably that in the history of web development there have been at least a couple of major misapplications of separation of concerns, which have had practically everlasting negative consequences.

Read what you’re replying to before you reply to it.

reply
darekkay 22 hours ago
HTML vs. CSS is a separation of technologies. If HTML was really only about the content and the CSS was only about styling, we wouldn't have to write div soups to style our websites (.container-wrapper .container .container-inner { /* "separation" */ }) and we wouldn't have to adjust our HTML when we change the layout.
reply
fainpul 16 hours ago
> we wouldn't have to adjust our HTML when we change the layout.

You don't have to: https://csszengarden.com/

reply
insin 16 hours ago
Fine for a static site which is frozen at the first version forever.

So, so, painful for apps which need to change and evolve over time, which I'm currently experiencing. It's too easy to break the bits where you needed to get clever to make a layout variant work.

I did also did a Zen Garden on YouTube recently when they removed the list view option from Subscriptions, restyling their grid markup was a fun CSS exercise.

reply
ruszki 16 hours ago
But for that designers should care about the limitations. But they don’t care. Not even about the more basic ones. I’m quite sure many of them don’t even know. Mainly, because their customers are not the one who code.

I got many designs for websites where customers told me that they want a pixel perfect version. The funniest one was when my boss who supposed to be a “senior” web developer told me this. Of course, there is no such thing on the web or really anywhere. Actually, I’ve never seen a design plan in which wildly different aspect ratios and sizes were really considered.

reply
zelphirkalt 14 hours ago
This doesn't solve the problem but:

If the designer is not aware of the ins and outs of the medium they are supposedly working with, they are not a very well informed and educated designer.

Just like I don't presume to be able to make a great product packaging design, without knowing firstly much more about visual composition and design, but also secondly the material and form and shape I am designing for. Will that be a plastic wrapper, a paper wrapper or some cardboard packaging? Without knowing the limitations and properties of each, how can I expect to create a good design?

Being that uninformed to me seems like not giving a shit about the quality of work one delivers, ergo not giving a shit about ones job, or simply not having the required understanding or skill to be any good at ones job.

reply
ruszki 10 hours ago
> not giving a shit about the quality of work one delivers

I’ve learned in the past decades that people who care about quality is the minority.

Look at any B2B software. They don’t care because their customers are different than who uses their products. They care about their customers only (managers). They pay attention to users as much as minimally possible without loosing customers.

This happens at every level.

reply
easyThrowaway 4 hours ago
CSS Zen Garden is quite the opposite of a good example of your point. Even small changes to the original page layout would completely break most of the provided styles.

If I removed the .page-wrapper class it would be also nearly impossible for a different developer to reverse-engineer the issue from the existing Template and CSS files.

reply
troupo 16 hours ago
And if you read the CSS there, it's an unmaintainable mess of absolutely positioned elements
reply
WorldMaker 8 hours ago
I find that most div soup is going away with CSS Grid. CSS Grid is often best when you lose wrappers and nesting. subgrid and display: contents help pop layers when you can't touch the HTML nesting, but now a lot of nesting feels unnecessary in the first place.
reply
skeptic_ai 12 hours ago
That’s separation of technology not concerns. The concern is the component itself.
reply
peacebeard 4 hours ago
Well put.
reply
rafael-lua 12 hours ago
Are we still complaining about Tailwind? This ship has sailed. The world is so much better than the old BEM/LESS hell, it is wonderful. UnoCSS is even greater in empowering frontend developers.
reply
AltruisticGapHN 9 hours ago
BEM is actually not hell, since the whole point is to have classes with a specificity of 1, making precedence of CSS rules easy to figure out.

Non-BEM CSS with ids and multi-classes everywhere was hell.

reply
sylario 14 hours ago
I do not work frontend and yet, I always end up having to do some CSS here and there.

I have never been happy on how I manage CSS. With tailwind, I am still unhappy about my styles but I can make my ugly UIs faster.

reply
mb2100 13 hours ago
If everything in your code is a React component, I get why you would just want to write the styles right there.[0] Then again, why write `<Button>` if you could just write `<button>` and style it with standard CSS.

[0]: https://mastrojs.github.io/blog/2025-11-27-why-not-just-use-...

reply
ggregoire 4 hours ago
> If everything in your code is a React component, I get why you would just want to write the styles right there.

Even for keeping the style close to the component, you can just use standard css.

Create a folder Button, create two files Button.tsx and Button.css in that folder, import the css file in the tsx file, add a class "button" on the first element the tsx file renders, start all the rules in the css file with ".button " to encapsulate the style.

People will say it's too much work, but it took me like 5 sec.

reply
mb2100 3 hours ago
if you're not using CSS modules, why would you import the css file into your javascript? But anyway, I think we agree. Feel free to read the linked blog post ;-)
reply
ggregoire 3 hours ago
Yes we agree, my comment was for tailwind users. :) I was replying to you because you gave the example of a Button component, and it's a good example to demonstrate that you don't need tailwind to style components.
reply
rafark 6 hours ago
Because button is literally anything clickable. Not everything is a boxed button. You cannot just globally add a style to <buttton> and call it a day. For example, an upvote (^) button, a close (x) button, etc. A lot of clickable elements aren’t inside a [click me] box
reply
mb2100 3 hours ago
button, .button { /* my button styles */ }
reply
skeptic_ai 4 hours ago
What if you need 3 levels of html tags?
reply
h4x0rr 2 days ago
Yeah let's do that. You have everything related to your component on place instead of jumping between files.
reply
paradox460 21 hours ago
Vue, Svelte, and Surface manage to do this without forcing you to inline all your styles
reply
h4x0rr 17 hours ago
I worked with both. Scoped styles are nice. Tailwind is better - no naming of every element, no mental tax of jumping around in the file -
reply
Kiro 12 hours ago
Jumping up and down in the file is not much better and you still need to come up with names for classes. I want to look at an element and immediately know how it's styled.
reply
agos 9 hours ago
From Tailwind's home page:

<div class="h-112 p-4 sm:p-8 relative overflow-hidden rounded-lg bg-gray-950/[2.5%] after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:inset-ring after:inset-ring-gray-950/5 dark:after:inset-ring-white/10 bg-[image:radial-gradient(var(--pattern-fg)_1px,_transparent_0)] bg-[size:10px_10px] bg-fixed [--pattern-fg:var(--color-gray-950)]/5 dark:[--pattern-fg:var(--color-white)]/10">[...]

"immediately" is a stretch

reply
cush 6 hours ago
I think your point has very little to do with tailwind and everything to do with CSS. Tailwind is optimized for modification and maintainability. We could replace your example with

<div class="hero-header hero-header--large">...

but now any time we want to modify hero-header, we're trolling through the whole site to find where else these classes might be used so we know what to test to avoid breaking anything

Sure it's easy to look at the element you shared and say it's too complex (it's really not, it's very declarative), but the complexity must live somewhere, and I'd choose Tailwind over any other prevailing system because it's isolated and safe to modify

reply
rafark 6 hours ago
Thanks for proving the point. I haven’t even seen that element rendered and I already have a good mental picture of what it is and what it looks like.
reply
Kiro 8 hours ago
You can fold it, format it, and IDEs preview it. This is like me posting the equivalent CSS in one big line. But even without all that I still prefer this over dealing with cascading styles in stylesheets. Never again.
reply
agos 8 hours ago
dealing with the cascade and tailwind are not the only two options
reply
zelphirkalt 14 hours ago
What stops you from doing the same thing in CSS? It is trivial to assign a specific CSS class to an element that is the root node of a "component" and scope rules under that.
reply
lawn 2 days ago
Is jumping between files supposed to be difficult or something?
reply
mattacular 8 hours ago
I find it to be more difficult. Especially if I can't pane the files in view comfortably (ie. beyond 2 or 3 it gets significantly harder to work across them).

Some frameworks or coding styles really lean into having lots of tiny files. That necessitates a more complicated directory structure for the project. Locating files eventually tends to requires search capability rather than being able to look through the tree in a sidebar.

None of this is "hard" per se but I find the opposite is nicer to work with typically.

reply
chrisweekly 2 days ago
Colocation is a useful principle in component-based architecture.
reply
temporallobe 18 hours ago
As much hate as it gets, this is one thing I like about Angular.
reply
cush 6 hours ago
imo Angular would have won had it figured out a stronger path from v1 to v2
reply
apsurd 2 days ago
In my lived experience, shared components just become another problem. Especially in a fledgling company, the iteration velocity is actually negatively affected by shared libs because there's always overhead to (not) break legacy. so shared components bloat to address every evolving need.

And now with AI generated code i see so many wrapper patterns that forward endless props down, it's crazy!

TLDR: i almost always end up branching out into evergreen "reusable" components anyway.

Very unlikely the component library the CTO asked claude to DRY up the code with, is the one to rule them all.

reply
halfcat 21 hours ago
FWIW, “colocation in component-based architecture” doesn’t necessarily mean shared code. It can just mean the one thing has all of its parts in one place, instead of having HTML in one file, CSS in another, JS in another.

You’re right about DRY and code reuse very often being a premiere (wrong) abstraction, which is usually more of a problem than a few copy/pastes, because premature wrong abstractions become entrenched and harder to displace.

reply
afiori 2 days ago
Without a lot of discipline it is very easy to end up with a css with lots of unclear and hard to guess effects. Eg consider the case of <A type=1><B><A type=2></A></B></A> where A and B are complex templates. Any selector with the " " operator on A risk expanding to the inner A even if it was intended only for the outer. Similarly a :has selector might catch a descendant of the wrong element.

@scope fixes a lot of this, but it is a complex problem. With tailwind you mostly have to worry about inheritance

reply
the_other 14 hours ago
> Any selector with the " " operator on A risk expanding to the inner A even if it was intended only for the outer.

Then <a type=b> is potentially a <c>. Consider a small refactor?

reply
robertoandred 2 days ago
This problem was solved a long time ago with CSS Modules.
reply
christophilus 2 days ago
I prefer almost anything to CSS modules, so this bike shedding topic is probably very subjective.
reply
onion2k 16 hours ago
Yes.

The problem is that the styles for something can be defined in multiple places, and that makes it hard. Especially with CSS and (potentially) having specificity issues if things aren't managed well. Having them as a part of the component means that problem goes away.

reply
ewuhic 2 days ago
Is staying in one file supposed to be difficult or something?
reply
luckylion 2 days ago
this is grey text from tailwindcss.com, I wouldn't call it easy and readable.

<div class="relative before:absolute before:top-0 before:h-px before:w-[200vw] before:bg-gray-950/5 dark:before:bg-white/10 before:-left-[100vw] after:absolute after:bottom-0 after:h-px after:w-[200vw] after:bg-gray-950/5 dark:after:bg-white/10 after:-left-[100vw]"><p class="max-w-(--breakpoint-md) px-2 text-base/7 text-gray-600 max-sm:px-4 dark:text-gray-400">Because Tailwind is so low-level, it never encourages you to design the same site twice. Some of your favorite sites are built with Tailwind, and you probably had no idea.</p></div>

reply
what 23 hours ago
That’s actually disgusting.
reply
paradox460 21 hours ago
It gets worse

Check out the Netlify admin dashboard screenshot in my blog post

https://pdx.su/blog/2023-07-26-tailwind-and-the-death-of-cra...

reply
troupo 15 hours ago
There's nothing in Tailwind that makes the craftsmanship dead, and your proposed solution with scoped styles somehow a revival of said craftsmanship.

Note how your solution literally depends on a build tool (Vue) to work. Whereas Tailwind can work with no build tools (tailwind build tools removes unused classes, and that's mostly it).

And then you go:

--- start quote ---

Juniors still come along and just do margin: 13px. In tailwind, they do m-[13px]. No difference. At least with CSS its centralized.

--- end quote ---

When your scoped CSS example is literally decentralized per-file CSS that has `margin: 5px` in it. That gets compiled into a meaningless `class-678x8789g` by the build tool.

> The people I've seen who are most excited over tailwind are generally those that would view frontend as something they have to do, not something they want to do.

Tailwind is the product of its era: where even sites are composed out of components. That is, this separation of concerns: https://x.com/simonswiss/status/1664736786671869952

As a comparison, here's Youtube's expertly crafted CSS (which is actually an improvement over their original 6B file). Note how much endless repetition there is: https://www.youtube.com/s/_/ytmainappweb/_/ss/k=ytmainappweb...

reply
UqWBcuFx6NV4r 13 hours ago
[flagged]
reply
halfcat 21 hours ago
In my editor this looks like this, with an extension like Tailwind Fold or Inline Fold:

    <div class="...">
      <p class="...">
        Because Tailwind is so low-level, it never encourages you to design the same site twice. Some of your favorite sites are built with Tailwind, and you probably had no idea.
      </p>
    </div>
reply
dwb 15 hours ago
Ok, and how does it look when you want to read or edit the “classes”?
reply
dagi3d 14 hours ago
yeah, Tailwind feels to me like a "write-only" solution.
reply
runarberg 2 days ago
Also modern CSS is often written in a <style> tag either in a native web component or in a framework which supports single file component like vue or svelte.
reply
dbbk 22 hours ago
You can literally command click a class to go into a styled components css file. I do not understand what the big issue is.
reply
halfcat 21 hours ago
Cognitive load of looking at 12 open files trying to understand what’s happening. Well, in fairness some of those 12 are the same file because we have one part for the default CSS and then one for the media query that’s 900 lines further down the file.
reply
bobthepanda 18 hours ago
Css modules gets away from the larger sins of the massive global css files.

If modules had existed much earlier it probably would’ve gotten rid of most of the awfulness.

reply
agos 9 hours ago
CSS Modules are way older than Tailwind, but alas it was not enough
reply
UqWBcuFx6NV4r 13 hours ago
Oh, great. So let’s just 2x all our files then! All for, what exactly?

It sounds like you just want to write Java.

reply
bobthepanda 5 hours ago
If you have a complaint about your styles being so complicated and in a giant 900 line mega file, I don’t see how you address physical size other than breaking up the file.

Granted, nesting support was also added fairly recently in the grand scheme of things, which boggles the mind given how it was such an obvious problem and solution that CSS preprocessing came about to address it.

reply
dbbk 7 hours ago
What do you mean 12 files? It’s 2 files. One for your component and one for its styles module.
reply
nilslindemann 20 hours ago
Yeah. There is no need to obfuscate your code, just use Tailwind.
reply
namuol 19 hours ago
Obfuscate? I can learn tailwind and use it in dozens of projects. I can use tailwind in my project and onboard dozens of developers immediately. I can learn your CSS conventions and use them in exactly one project.
reply
nilslindemann 19 hours ago
Why can you use CSS conventions in only one project?
reply
manuelmoreale 17 hours ago
He wrote "learn your CSS conventions" which implies that every team and every project will have a different set of conventions. Hidden inside that statement is the fact that he just accepted that Tailwind should be THE CSS convention, something I personally disagree with but to each their own.
reply
UqWBcuFx6NV4r 14 hours ago
Can we all just drop words out of comments that we reply to if it makes it easier to make our point?
reply
saidinesh5 4 hours ago
It really depends on the websites no?

If you're building a "webapp" where you think in terms of components, no point keeping the style sheet separate..

If you're building a "website" which is basically a list of hyperlinked documents with the same styling, having just one style sheet would make sense...

Of course, there's a lot of gray area in between the two...

At the end of the day, the most that most of us can really do is be annoyed at the quirks of these leaky abstractions in the large codebases that's thrust upon us.

reply
chasd00 9 hours ago
If I may be so bold, the coding agents are really good at this stuff. Save yourself the pain of front end and make a clanker do it. Or at least make the clanker to the heavy lifting and just do tweaks yourself.
reply
Sateeshm 21 hours ago
Tailwind is not what you're describing.
reply
digitalPhonix 21 hours ago
Isn’t that what utility classes are? Shorthand for inline styles?

Not saying it’s good/bad, but it feels like that’s the use case

reply
francislavoie 18 hours ago
It's much more than that because it can make use of CSS pseudo selectors like hover, which is not possible with inline styles.
reply
rafark 6 hours ago
Under that definition any css class is a shorthand for inline styles
reply
Sateeshm 6 hours ago
Media queries, pseudo selectors, extensible design system with sensible and practical defaults, and many more
reply
raincole 20 hours ago
Yeah and it's a really good idea. You can't really 'separate layout from style.' The layout and the style are both parts of the UI. HTML isn't the content, it's the layout.

Even if you believe separation of concerns is the eleventh commandment, HTML and CSS are the same kind of 'concern' anyway. They're both at representation layer. Pretending you can decouple them is just burying the head in the sand.

reply
Sateeshm 6 hours ago
Thank you. This just makes sense. In fact, seperating them into different files don't make much sense when you think about it.
reply
crooked-v 2 days ago
Tailwind is a direct response to how the "C" in "CSS" actually sucks, so there's no surprise that it's so popular.
reply
spartanatreyu 24 hours ago
The "C" (Cascade) in CSS doesn't suck, the education about it sucks.

People don't know how it works, then things go wrong so they learn to work around it.

That's what led to things like div + class soup that you get with the BEM naming convention or Tailwind.

The cascade is actually awesome, super powerful and if you know how to use it, it can greatly simplify your code.

Education is the problem and the solution.

---

To anyone outside the CSS space, this is the closest analogy I can find:

In the American education system, there was a recent-ish change where children are "taught" to read using a method of just learning the shape of every word (e.g. "thermally" has a th at the start and ly at the end, so it must be the word "thermally", despite other similar looking words like thematically).

The method was disproven but the American education system still uses it.

Now illiteracy rates are climbing where almost 1/4 Americans (USA) can't read.

It's basically the same thing with CSS, where developers don't know what the code they're reading/writing is actually going to do.

reply
gf000 14 hours ago
It has nothing to do with education, software development is not learnt in a centralized way so you could hardly claim anything based on that.

Cascading simply fails to scale/work with web applications, especially when multiple people work in parallel.

HTML both describes content AND layout, so you simply can't separate the two. This was a nice dream when the internet was "markdown encoded in html", but the moment you write a nested <div> for layout purposes you lost. So HTML has to be written together with CSS, so we get no separation. Now what is it that you could meaningfully cascade? (If anything, variables are all that we needed)

Add to it that people are using third-party components as well, and now many "widgets" starts by resetting outside styling rules.

reply
WorldMaker 8 hours ago
Reading word shapes comes from a place of good, studied intent. Reading word shapes is how people who read quickly read. It is in many ways an advanced way of reading. Trying to jump to it was a hope that you could shortcut some of the literacy curve. Unfortunately, trying and sometimes failing to read word shapes is also how some neurodivergent brains work naturally (the family of dyslexias as the big complex elephant in the room). If your brain jumps directly to word shape, and somewhat often gets it wrong, being forced to slow down, break words apart and start from smaller basic building blocks can be helpful.

It's a reminder that different people learn at different paces.

I think overall the additional details expand and perhaps better the metaphor: a lot of people want to jump directly to the advanced CSS stuff and skip the fundamentals. For some people that works and may be a shortcut. Other people need to spend more time breaking their teeth on the fundamentals, getting them wrong, learning from their mistakes, and getting rock solid on the slower building blocks before trying to do anything advanced.

reply
TonyStr 15 hours ago
What do you think is lacking from CSS education?

I don't think anyone in this thread is arguing that inheritance or specificity is hard to understand.

My issue with cascading style sheets is mainly that namespace pollution (as every selector is defined in the same global namespace) means that short selectors (.separator, .highlight, .button) are likely to collide with completely unrelated parts of the application. BEM and tailwind are popular because they localize styles to specific components, preventing namespace issues. Today, most web frameworks deal with components, so it makes a lot of sense to localize the styles to the components. Scoped css in vue/svelte allows you to write short selectors, and have them only apply to the component they are written in, without needing to prefix them with a component name.

reply
cush 5 hours ago
Instead of assuming that everyone who has a different preference than you is ignorant, it might be helpful to look at the problems that each technology solves and the requirements they fulfill.

The cascade is a huge issue for teams need to be able to safely modify one area of a site without accidentally impacting others. Tailwind solves organizational problems by colocating the styles with the elements - allowing changes to be surgical, declarative, and predictable. Editing and removing styles is as easy as modifying the content of the page.

Yes, the cascade is super powerful, but it needs to be contained somewhat to scale to many developers and large codebases.

> 1/4 Americans (USA) can't read

Also even though your analogy has nothing to do with CSS, I have to point out that this absolutely isn't true (which in the context of your argument about education is pretty ironic)

reply
paradox460 21 hours ago
I call it cargo cult developing. People develop bags of spells and tricks that they attempt to apply to a situation to solve a problem. Usually they can arrive at a solution by trying a number of these incantations, but not always. And never actually ask them what the incantation does

It's not just css either. At a job I've worked, we had a VPN client that would get into a weird state, where it needed to be killed to restart. An incantation that made use of ps, grep, awk, and xargs was provided, instead of just using pkill

reply
troupo 15 hours ago
> That's what led to things like div + class soup that you get with the BEM naming convention or Tailwind.

You could try and think why people end up with BEM or Tailwind. And the answer isn't "because people are not educated about cascade". Both BEM and Tailwind came form people who are very much aware of the cascade.

The problem is that cascade is very much a hindrance in quite a few cases. Especially when you deal with components and design systems.

> To anyone outside the CSS space, this is the closest analogy I can find

All analogies are bullshit.

The truth is that CSS is designed for documents, and for a few decades people have been trying to use it to design/build components: https://x.com/simonswiss/status/1664736786671869952 Cascade is good for the former, and is death for the latter.

And browser vendors have been surprisingly stubborn when it comes to making any improvements to the DX in this area. That's why instead of locally scoped CSS, CSS nesting, CSS mixins (and a bunch of other improvements from SASS and various JS Frameworks) we first got 15 000 JS-only specs around web components, of which 14 999 can be covered by improvements to CSS.

> where developers don't know what the code they're reading/writing is actually going to do.

Lol. Tailwind has made people more aware of what CSS does, with better documentation, than decades of bullshit articles and millions of words of existing docs.

reply
dbbk 22 hours ago
I mean, the cascade really doesn't suck though does it. You really want to set font families and sizes on every p tag?
reply
mattlondon 2 days ago
Wait until you see React & JSX...

At least html and CSS are both presentation. React/JSX now confuses presentation and business logic.

reply
lateforwork 2 days ago
> React/JSX now confuses presentation and business logic

React was originally designed to be the "V in MVC". You can still use it that way. React becomes very simple when you only use it as the V in MVC.

reply
est 21 hours ago
> React was originally designed to be the "V in MVC"

React was originally desingned to be php in the browser.

php5 -> HHVM -> Hack -> XHP -> JSX

reply
rafark 6 hours ago
Omg yes finally someone acknowledges this. I am always pointing out how react and jsx are a port of XHP. This is why react was class based at first (because php is a class based OO language).

Hack was created later though. XHP was a php 5 extension created around 2008

reply
azangru 2 days ago
What are the M and the C, and how do they talk to the V in this case?
reply
apsurd 2 days ago
react can be pure functions that take in props. Given a set of props, ideally data primitives, the outputted view is guaranteed. it's nice.

In practice, the entire JS ecosystem enjoys flying off the rails, every season, but it's not strictly react's fault.

To answer your question, however those props get into the component is up the the M & C. can be async server, or shoved in as json in the script tag.

reply
azangru 2 days ago
If you move the data (the M and the C) entirely out of react, and only pass it in via props, there would be only one place — the root react node — where the props could get into react. Is this what you have in mind? Or are you envisioning multiple root nodes?
reply
WorldMaker 8 hours ago
That is the classic Flow model or Redux model (if you prefer the common implementation name over the Facebook paper name). Build a central store. Pass the single store down to all necessary components via prop-drilling, then later Contexts (and HOCs) to skip layers as a nice-to-have.

Redux is a lot less fashionable today, but hasn't entirely disappeared as an M and C option.

reply
apsurd 2 days ago
Well, i've always been a fan of the island architecture that effectively mounts root nodes as little islands of isolated state, yes.

Mainly this avoids the hell that global state SPA patterns produce: redux, reducer patterns in general, and 8 thousand context providers.

I do think there's use cases that warrant global in-memory state, but it's such a pain in the ass to maintain and evolve, i'd always plan against it. Every html node in your app does not need to know about literally everything going on and react instantly to it. it just doesn't.

Just make another page!

Also: so the islands pattern can be as fancy or rudimentary as desired. they can bootstrap themselves via async endpoints, they can be shipped as web components even, or they can be static, pre-hydrated in some manner.

reply
seer 21 hours ago
Do you need react at this point? Isn’t it just html/css/components?

I remember the birth of React was because Facebook had a problem - you would add a comment and your notification bar would sometimes not get updated.

They had so many bugs with normal html / css that they wanted to solve this on the application layer - to make inconsistent UI elements unrepresentable.

So they came up with react with global state - because in their use case changing one thing does affect a bunch of other unrelated things, and they all need to sync together.

I mean honestly that’s what I use React _for_ - especially with contexts it’s very easy to express all of this complex interconnected state of a webapp in a consistent way.

And of course there are other ways to solve it - for example with elixir/phoenix you just push all that complexity to the backend and trust in websockets and the BEAM.

I just feel that if you really don’t need global state, then react kinda isn’t needed as well…

reply
gf000 14 hours ago
> I just feel that if you really don’t need global state, then react kinda isn’t needed as well…

I don't know, in my mind "re-render (efficiently) when state changes" is the core point of react and similar frameworks. That requirement still stands even if I have a smaller, local state.

reply
kittbuilds 2 days ago
The islands pattern is underrated for maintainability. I've found the biggest win isn't even the state isolation — it's that each island can have a completely independent upgrade path. You can rewrite one island from React to vanilla JS (or whatever comes next) without touching anything else.

The global state SPA pattern fails for a more fundamental reason than just being painful to maintain: it creates an implicit contract between every component in the app. Change one reducer and you're debugging side effects three layers away. Islands make the contract explicit — each one owns its data, full stop.

The one gotcha I've hit is cross-island communication. PostMessage works but gets messy. Custom events on a shared DOM ancestor end up being the cleanest pattern for the rare cases where islands genuinely need to coordinate.

reply
tim1994 2 days ago
With signals you can avoid the prop drilling. I think signals can help a lot with this approach
reply
azangru 2 days ago
I think the parent wants to separate the V from the M/C. If you smuggle signals inside of components to avoid prop drilling, you would be coupling the M/C and the V. I suppose that's not what the parent has in mind.
reply
lateforwork 2 days ago
M stands for Model layer. This layer handles business logic and knows nothing about UI. It does not have any html or CSS.

V stands for View. This layer handles HTML and CSS. You can use React here.

C stands for Controller. Controllers know about Views and Models and which model objects to instantiate for which view. It makes REST API calls and does caching, and handles errors. Controllers know about the application state and decide what page to display next.

For an application written in this style see: https://github.com/wisercoder/eureka/tree/master/webapp/Clie...

(This app doesn't use React, but does use TSX, and you could use React as well).

reply
cbarrick 2 days ago
- M for Model: your data model. - V for View: views of your data. - C for Controller: does stuff with your data.
reply
Chris_Newton 24 hours ago
In the original MVC architecture, the fundamental idea was that the model was responsible for storing the application state, a view was responsible for rendering output to the user, and a controller was responsible for responding to user interactions.

The model can be completely unaware of any specific views or controllers. It only needs to provide an interface allowing views to observe the current state and controllers to update that state.

In practice, views and controllers usually aren’t independent and instead come as a pair. This is because most modern UIs use some kind of event-driven architecture where user interactions are indicated by events from some component rendered by the view that the controller then handles.

My go-to example to understand why this architecture is helpful is a UI that features a table showing some names and a count for each, alongside a chart visualising that data graphically. Here you would have a model that stores the names and counts as pure data, and you would have two view+controller pairs, one managing the table and one the chart. Each view observes the model and renders an updated table or chart when the model state changes. Each controller responds to user interactions that perhaps edit a name or change its count — whether by typing a new value as text in an editable table cell or by dragging somewhere relevant in the chart — by telling the model to update its state to match (which in turn causes all views observing the model to refresh, without any further action from whichever controller happened to be handling that user interaction).

In practical terms for a React application, we might implement this with a simple object/Map somewhere that holds the names and values (our “model”) and two top-level React components that each get rendered once into some appropriate container within the page. Each component would have props to pass in (a) the current state and (b) any functions to be called when the user makes a change. Then you just write some simple glue logic in plain old JavaScript/TypeScript that handles keeping track of observers of the model, registering an observer for each top-level component that causes it rerender when the state changes, and providing a handler for each type of change the user is allowed to make that updates the state and then notifies the observers.

There are lots of variations on this theme, for example once you start needing more complicated business logic to interpret a user interaction and decide what state change is required or you need to synchronise your front-end model state with some remote service. However, you can scale a very long way with the basic principle that you hold your application state as pure data in a model that doesn’t know anything about any specific user interface or remote service and instead provides an interface for any other modules in the system to observe and/or update that state.

reply
kristopolous 22 hours ago
Mvc is why there's 3 languages: HTML CSS and JavaScript

The separation is already there

People have just failed to understand it

reply
azangru 12 hours ago
> The separation is already there

I wonder how you would map these three onto M, V, and C :-)

reply
gf000 14 hours ago
Then we surely don't need to add random additional elements to our Content, purely to properly layout the content right, which is the job of the View?
reply
madeofpalk 2 days ago
I think you're confusing business logic with view logic.
reply
bromuro 2 days ago
React is great for MVVM indeed. Who is still using MVC in 2026?
reply
flowerlad 2 days ago
MVVM was invented by Microsoft for 2-way syncing in WPF. Today we know 2-way syncing is a mistake.

Who uses MVC in 2026? Pretty much every framework out there, including Java frameworks and Python frameworks and .net

reply
recursive 19 hours ago
I've heard many people assert that 2 way binding is a mistake, but I didn't think it was settled. It still seems simpler to me than so called uni-directional data flow.
reply
ozim 24 hours ago
You have any more sources on MVVM being a mistake?

I found WPF rather nice to work with. Same with knockout.js and Angular I don’t see much downsides.

Everyone can write bad code of course in each of them but I think it was working quite well.

reply
lateforwork 23 hours ago
When React launched in 2013, its defining idea was strict one-way data flow: parents pass data down via props, and updates happen in a clear, explicit place. Children can't mutate parent state directly; they signal changes through callbacks. The result is predictable, traceable state changes.

This contrasted with MVVM frameworks like early AngularJS, Knockout, and WPF, which relied on two-way data binding. That automatic syncing felt convenient for small apps, but at scale it often led to hidden coupling and hard-to-trace update chains.

Over time, many developers came to view pervasive two-way binding as a design mistake in complex systems. React's unidirectional model gained traction because it favored clarity and control over "magic."

reply
ozim 17 hours ago
Thanks GPT but I know all of that. I was expecting some eye opening new evidence because person I was asking seemed really confident and using strong words.

But that’s just generic „blablabla”. MVVM is not a mistake and is still plenty useful.

reply
lateforwork 6 hours ago
If it is useful for you then it is not a mistake. For you.
reply
aloisdg 24 hours ago
Isn't Vue also MVVM?
reply
ozim 17 hours ago
Yes VUE is quite a descendant of knockout.js.

People confidently write strong opinions on the internet.

reply
interlocutor 2 days ago
Ever heard of Django? ASP.NET? Most UI frameworks, including ASP.NET Core, Spring Boot (Java based framework), Ruby on Rails, and Django (Python) are all based on MVC.
reply
Kerrick 24 hours ago
Those are all stateless MVC over HTTP, which is a very different architecture from stateful MVC for long-lived UI. The latter was invented for Smalltalk by Trygve Reenskaug, and is far more relevant to front-end web.

Stateful MVC uses Publisher/Subscriber (or Observer) to keep Views and Controllers up-to-date with changing Models over time, which is irrelevant for stateless MVC over HTTP. Plus, in stateful MVC the View and Controller are often "pluggable," where a given Controller+Model may use a different View for displaying the same data differently (e.g. table vs. pie chart), or a given View+Model may use a different Controller for handling events differently (e.g. mouse+keyboard vs. game controller). Whereas, in stateless MVC over HTTP, the controller is the "owner" of the process, and won't generally be replaced.

And in the world of front-end web, stateful MVC really is mostly dead. MVVM and Component-based architectures (using the Composite pattern) have replaced it. A runtime is usually responsible for wiring up events, rather than individual controllers. Controllers don't need to be swappable because events can be given semantic meaning in components, and Views don't need to be swappable because you can instead render a sub-composite to change how the data is shown.

reply
gf000 14 hours ago
Is the Controller not in a coupled pair with a View? We could imagine an interface where it could be completely separate (e.g. a kiosk TUI where stuff like "press 'r' for X" is displayed), but in the vast majority of UIs the View has state, and the Controller has to depend on that state (e.g. did this keypress happen with a text field focused). Sure, this is abstracted away via the UI framework and we operate on usually some form of event system.

But even then, I don't really see how we could have a non-coupled controller-view. In fact, I seem to remember that it was described in a similar way for Smalltalk even.

reply
black3r 12 hours ago
You can have decoupled Controllers from Views using React. That's the basis of the "original" Flux/Redux architecture used by React developers 10+ years ago when React was just beginning to get traction.

A flux/redux "Store" acts as a Model -> contains all the global state and exactly decides what gets rendered. A flux/redux "Dispatcher" acts as a Controller. And React "Components" (views) get their props from the "Store" and send "events" to "dispatcher", which in turn modifies the "Store" and forces a redraw.

Of course they aren't "entirely decoupled" because the view still has to call the controller functions, but the same controller action can be called from multiple views, and you can still design the architecture from Model, through Controller (which properties can change under what conditions) and then design the Views (where the interactions can happen).

reply
gf000 9 hours ago
I was asking more in the abstract. Web UI frameworks usually sit on top of considerable abstraction (in the form of the DOM, eventing system, etc), so I'm not sure your reply exactly answers my question.
reply
lateforwork 24 hours ago
Whether application state is short-lived (e.g., request/response CRUD) or long-lived (e.g., an in-memory interactive UI) is orthogonal to MVC. MVC is a structural separation of responsibilities between model, view, and control logic. The duration of state affects implementation strategy, not the applicability of the pattern itself.
reply
Chris_Newton 23 hours ago
MVC is a structural separation of responsibilities between model, view, and control logic.

Yes, but the “MVC” pattern used by various back-end web frameworks that borrowed the term a while back actually has very little to do with the original MVC of the Reenskaug era.

The original concept of MVC is based on a triangle of three modules with quite specific responsibilities and relationships. The closest equivalent on the back-end of a web application might be having a data model persisted via a database or similar, and then a web server providing a set of HTTP GET endpoints allowing queries of that model state (perhaps including some sort of WebSocket or Server-Sent Event provision to observe any changes) and a separate set of HTTP POST/PUT/PATCH endpoints allowing updates of the model state. Then on the back end, your “view” code handles any query requests, including monitoring the model state for changes and notifying any observers via WS/SSE, while your “controller” code handles any mutation requests. And then on the front end, you render your page content based on the back-end view endpoints, subscribe for notifications of changes that cause you to update your rendering, and any user interactions get sent to the back-end controller endpoints.

In practice, I don’t recall ever seeing an “MVC” back-end framework used anything like that. Instead, they typically have a “controller” in front of the “model” and have it manage all incoming HTTP requests, with “view” referring to the front-end code. This is fundamentally a tiered, linear relationship and it allocates responsibilities quite differently to the original, triangular MVC.

reply
techpression 2 days ago
Adding to sibling comments, Phoenix. And it’s a damn nice experience at that.
reply
rafark 7 hours ago
And thank god (or Adam) for that. Tailwind makes me much more productive.
reply
cush 6 hours ago
Separation of formatting/representation was invented? Where was I?
reply
josephcsible 22 hours ago
I dislike the examples here where the "old" way works in all browsers, but the "new" way only works in Chrome/Edge. IMO, it's irresponsible to include such examples, since it makes the Blink monoculture worse.
reply
oddevan 21 hours ago
Agreed; wish the default filter was "newly available" since that includes all 3 major browsers (Chrome/Edge, Safari, Firefox) but still includes new stuff that isn't "baseline" yet.
reply
virtujoel 12 hours ago
Even "newly available" doesn't seem correct. For example:

https://modern-css.com/smooth-height-auto-animations-without... This claims `interpolate-size` is newly available and works in all major browsers.

https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/P... This states `interpolate-size` only works in Chrome/Edge.

I tested the demo and it's definitely not working in my copy of Firefox.

reply
maxloh 21 hours ago
This site shouldn't be the one to blame. Instead, we should probably ask Mozilla to be more standards-compliant.
reply
pchangr 6 hours ago
This is an experimental feature. Not part of the standard yet .. imho.. shouldn’t be included (yet)
reply
zelphirkalt 8 hours ago
I don't see Mozilla obligated to follow whatever Google cooks up and declares a "standard".
reply
ehsankia 17 hours ago
Sometimes I'm developing an internal tool or something only for myself / handful of people. I'm perfectly fine saving time and complexity using a one liner modern CSS solution instead of having to rely on some hacky unreadable code to support 10 years of legacy browsers.
reply
josephcsible 16 hours ago
The latest version of Firefox is not a legacy browser.
reply
demetris 13 hours ago
I didn’t pay close attention to the domain and I thought it was the other one:

https://moderncss.dev/

One of the best educational resources for modern CSS.

BTW, one of the reasons I love modern CSS is front-end performance. Among other things, it allows you to make smaller DOMs.

I talk about a modern CSS technique that does that here:

https://op111.net/posts/2023/08/lean-html-markup-with-modern...

It is an idea I started playing with when custom properties landed in browsers, around 2016 or 2017? Around 2021 I started using the technique in client sites too.

Now I want to write a 2026 version of the post that talks about container queries too. The technique becomes more powerful if you can rely on container queries and on the cqw unit. (You cannot always. That stuff is still new.)

For an example of the convenience cqw offers if you can rely on it, see the snippets I have in this:

https://omnicarousel.dev/docs/css-tips-know-your-width/

reply
senfiaj 23 hours ago
My top list of recent CSS improvements:

1) Nested selectors.

2) :has(...).

3) :is(...), before you had to write :not(:not(...)).

4) :where(...), similar to :is(...), but the selector weight inside :where becomes 0. Useful when you need deep/complex selectors without increasing the selector weight.

reply
jjcm 17 hours ago
big +1 on #1.

As a tip - most LLMs are unaware it exists due to either knowledge cutoff or not having enough training data for it.

As a recommendation, include some examples of it in your AGENTS.md. Here's what I use:

--------------------------------------

## CSS nesting (required) When writing CSS (including component `css()` strings and `soci-frontend/soci.css`), *use modern CSS nesting* where it improves readability and reduces repetition.

- Prefer nesting with the `&` selector for pseudo-classes / pseudo-elements and compound selectors. - Avoid duplicating the parent selector when nesting can express it once. - Reference: [MDN `&` nesting selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/S...)

### Quick reference

#### Pseudo-classes / no-whitespace attachment

```css .button { color: var(--text);

  &:hover {
    color: var(--text-secondary);
  }
} ```

#### Pseudo-elements

```css .fade { position: relative;

  &::after {
    content: '';
    position: absolute;
    inset: 0;
  }
} ```

#### Descendant nesting (whitespace implied)

```css .card { padding: 12px;

  .title {
    font-weight: 600;
  }
} ```

#### “Reverse context” (`.featured .card`) using `&`

```css .card { .featured & { border-color: var(--brand-color); } }

reply
Eric_WVGG 17 hours ago
Mine is text-box: trim. Twenty years of trying to explain to graphic designers why it’s next to impossible possible to get the top of a capital letter to a box, I feel free like a bird.
reply
k33n 15 hours ago
Once Firefox supports it, we will be golden.
reply
nilslindemann 20 hours ago
My favorite is @layer
reply
silverwind 23 hours ago
I like 2-4 but I despise nested selectors. They make selectors ungreppable.
reply
pphysch 22 hours ago
Why/how do you grep selectors? Seems overly optimistic to be able to guess the particular rule pattern that is applying a style. Browser tools are much more reliable.
reply
porker 16 hours ago
Let's say you're thrown into a website you've never worked on before and asked to fix a styling problem. You can look in the browser tools, but the website will only be running the compiled production version, and if the team knows what they're doing there won't be source maps available.

So you've now found selectors in DevTools that you think are causing the problem, and you want to find them in the source code. In the case of many projects, that means searching through hundreds of small CSS files.

That's why you grep selectors, and where the pain comes. You have to start with the most specific rules that you found in DevTools, then start deleting parts from them until you find a non-nested rule that's in the source, yet still specific enough that you haven't got hundreds of matches to go through.

It would be great if something like ast-grep could take a CSS rule copied from DevTools and search for nested CSS that would compile to match it.

reply
rozab 12 hours ago
If this is the only option, I would suggest that means the team doesn't know what they are doing.
reply
moho 17 hours ago
In case the author happens to read these - final statement in native CSS nesting is no longer true.

"The only small difference from Sass: for element selectors you need the & prefix. In Sass you could write a { color: red } inside a parent, but native CSS requires & a { color: red }."

It was true for a bit, but fixed within 2-3 releases iirc. You can now freely nav { a { color: red; } }

reply
ffsm8 12 hours ago
reply
cwillu 2 days ago
Stop pinning things to the edges of the screen and window. Some sites have literally over 50% of the viewable area taken up by irrelevant static elements. Let the content scroll, like god intended.
reply
naeemnur 9 hours ago
Hey all, creator here. Went through the comments and made a bunch of fixes based on the feedback: https://modern-css.com/changelog/
reply
Bengalilol 2 days ago
Me: cool, let's be creative, I love 2026.

Browsers: Yeah, but beware of limited availability, most of those creative examples are in the 40-50% browsers support range.

reply
graypegg 2 days ago
In the past this was a major issue that meant useful features were only ever usable after IE/Safari finally supported them half a decade later, but it has seriously gotten better. Sadly as a result of Chromium's overbearing presence, but it's a helpful outcome at least.

https://wpt.fyi/interop-2025

reply
presentation 14 hours ago
Problem with safari though is that it’s tied to OS updates that many people just defer for insanely long periods of time. So unlike the other browsers, it’s not evergreen, so if you need to support any iOS users or Mac users who don’t use chrome etc, you’re out of luck
reply
graypegg 9 hours ago
Yeah that definitely sucks. I have seen MacOS Safari updates come though separately in Preferences.app > General > Software Update, but I think that release channel is for security issues.

Saying that, MacOS and iOS generally (up until recently, from what I've heard) have very good uptake rates for major updates. It's become less awful standards-wise as time has gone on in my experience at least.

reply
maxloh 21 hours ago
It takes time. Browser vendors add support for those features now, in the hope that they will become widely available one day.
reply
thih9 24 hours ago
> most of those creative examples are in the 40-50% browsers support range.

Not if you filter the examples. Click "widely available".

reply
MD87 23 hours ago
First widely available one I saw was this: https://modern-css.com/staggered-animations-without-nth-chil...

That would actually fix some ugly CSS I have. The demo works. Neat.

Except... the demo doesn't use either the old syntax or the new syntax. The browser support is wrong (Firefox doesn't support it, the site says Firefox 16+; it says Chrome 43+ but in reality it's much newer: Chrome 148+). It says "Since 2018" but the spec was introduced in 2024.

So maybe an interesting overview of things that might be available or might not, but the filtering and data on the site doesn't seem to be useful.

reply
josephg 22 hours ago
Yeah a lot of these demos say the features are widely available, but they don't actually work in my browser (Firefox on Macos).

Makes me wonder if these demos (or the browser support tables) were made by LLMs. They clearly haven't tested the demos in firefox.

reply
m-schuetz 14 hours ago
Firefox is pretty irrelevant nowadays. They've dragged their feet for years when it comes to implementing new stuff, and now web devs don't even bother checking Firefox. Because devs know it won't work on ancient browsers, no need to confirm.

My personal trigger events were when Firefox didn't optimize DataView for the longest time, initially refused to implement import maps, and couldn't get WebGPU support done. At that point I lost interest in supporting it.

reply
demetris 5 hours ago
Firefox could (should?) be better in several aspects but it seems excessive to say it is pretty irrelevant.

It has 4.5% market share in Europe, 9% in Germany (statcounter numbers).

It is the browser that got the Google Labs folks to write a Rust jxl decoder for it, and now, thanks in part to that, Chrome is re-adding support for jxl.

You can be unhappy with Firefox (I often am myself), and Firefox HAS lost relevance, but can you really say it has become pretty irrelevant?

reply
agos 9 hours ago
"widely available" has a precise meaning that includes Firefox (both desktop and Android). it might be irrelevant for some, but let's not twist industry definitions
reply
m-schuetz 5 hours ago
Based on marketshare, Firefox can easily be excluded from "widely available"
reply
tech_hutch 9 hours ago
It's 2026, the most useful stuff was implemented over a decade ago. Stop trying to make the web platform do everything when it wasn't designed for that.
reply
m-schuetz 5 hours ago
It doesn't matter what it was designed for 30 years ago. Computers also weren't designed to be put in your pocket, yet here we are. Things evolve, and browsers that do not keep up will eventually stop being used.
reply
thih9 5 hours ago
> First widely available one I saw

Could you try again? I couldn’t replicate this and when I follow your link it says “limited availability”.

reply
jen729w 18 hours ago
Also one of the features that I want -- scrollbar-gutter: stable -- is shown everywhere as being stable for many Safari versions, but when I try it it just didn't work. ¯\_(ツ)_/¯
reply
vruppert 6 hours ago
I was excited to see if I had any of the old centering method in my CSS code, turns out I do! So I tried to replace it with the new grid/place-items method... doesn't work because I have multiple children all needing to be placed in the center layered on top of each other. So I wouldn't call this an easy swap
reply
grabshot_dev 7 hours ago
The field-sizing: content one for textareas is the kind of thing that makes you wonder how we tolerated the JS workaround for so long. I've shipped that "measure scrollHeight, set height, reset" hack in probably a dozen projects. Same with scroll-state queries replacing IntersectionObserver for sticky headers.

What's nice about this collection is it's organized by the problem, not the property. Makes it easy to find the thing you're actually trying to solve rather than browsing a spec.

Would love to see one for the new anchor positioning API. That one could replace a lot of tooltip and popover JS.

reply
efortis 6 hours ago
I just refactored my colors to use the light-dark function:

https://github.com/ericfortis/mockaton/commit/acf21803480412...

…and it deduplicated autocomplete suggestions in WebStorm

reply
lol768 2 days ago
I'm confused, many of these examples state that they don't work in my browser (Firefox) - but the live demo works fine? Are the demos poly-filled?
reply
extra88 23 hours ago
Their labeling of feature availability is messed up. It says sibling-index() is widely available when it's not even available in Firefox yet.
reply
nine_k 2 days ago
No, it says: "Limited availability".

These green percentage points at the bottom left show how many of the Web audience (IDK by what measurements) runs a browser supporting this feature. A ton of them are even below 50%.

So I suppose it's not "modern CSS", but more like "latest Chrome CSS". Best viewed in Internet Explorer 5.

reply
Kailhus 24 hours ago
Absolutely. So no thank you, I won't stop writing CSS like it's 2015 if those features aren't at least supported by all latest browsers.
reply
Twey 7 hours ago
This site claims the `initial-letter` property is newly supported, but actually it is still unsupported in most recent Firefox and Safari.
reply
hk1337 9 hours ago
The one I don’t see that often used is color-scheme and light-dark. Both of those with css variables are so helpful with theming and having light and dark themes. Plus it works in all of the main browsers and the effect happens immediately, so you have your page up and switch your system from light to dark, you see it transition without having to refresh.

https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/V...

reply
trepaura 10 hours ago
These look like some interesting css options, though I'll comment that adding grid placement options is less new, and more just minor syntactic sugar. Plus, basic grid layout styling being added to css will rarely do what we actually want them to do.

Accounting for scrollbar appearance and auto-updtating the width of the frame/window is pretty useful, though. Not having the horizontal scroll bar suddenly appear when a vertical bar is needed due to dynamic elements is highly preferable.

reply
suralind 3 hours ago
Love this website. Definitely bookmarking for future reference.
reply
deadghost 2 days ago
So where are we at with utility libs (tailwind/tachyon) vs inline css in js vs preprocessors (sass/scss) vs vanilla modern css?
reply
gassi 2 days ago
Wherever you want to be.

  Tailwind works for your team? Go for it.
  Inline CSS for your solo project? By all means.
  Still stuck on SASS? It'll keep working just fine.
  All in on modern CSS? More power to ya!
reply
ed_mercer 22 hours ago
Missed opportunity to turn this into a poem
reply
susam 18 hours ago

  Go for it if Tailwind works for your team.
  Inline CSS for your solo project?  Chase your dream.
  Still stuck on SASS?  It'll work just fine.
  All in on modern CSS?  Go ahead and shine.
reply
ed_mercer 14 hours ago
<3
reply
bovermyer 5 hours ago
This site taught me a new way to do drop-caps. It looks good on multiple types of device!

If nothing else, I'm grateful for that.

reply
p0w3n3d 17 hours ago
Live demo for input:user-invalid does not work on my phone.

It'd always been the same: those ugly patches of JavaScript were being added to maintain compatibility with all the browsers... That's why the newest CSS tricks were always out of reach for us

reply
selimco 13 hours ago
The drop cap example is also broken for me on the latest Firefox version. It still requires a manually set line-height of 1.
reply
throwaway290 13 hours ago
Also broken on latest Safari & iOS. They should add screenshots how it supposed to look
reply
p0w3n3d 12 hours ago
however, it would defeat the purpose of the live demo...
reply
szszrk 14 hours ago
yeah, it doesn't work on desktop Firefox or desktop Chrome-based browser either.

That one is a dud. Most others work for me, though.

reply
folbec 9 hours ago
there is a bug in the CSS :

explicit border:2px solid var(--border); on the input which takes priority over the class :

.demo-uv-input:user-invalid { border-color: var(--red-muted); background: rgba(239,68,68,.04); }

YMMV but when corrected it works for me on latest Edge, Chrome, Firefox (Windows versions)

reply
mdavid626 16 hours ago
Probably half of it still doesn’t work in Safari.
reply
wildrhythms 12 hours ago
Don't worry, I'm sure in a year about 30% of it will work! (But not on mobile)
reply
mdavid626 9 hours ago
I wish we could just ignore Safari on iOS.
reply
vaylian 8 hours ago
Safari is the new IE6
reply
krystofee 8 hours ago
Funny that this age was probably written by AI hence the "how to write modern css" is completely irelevant.
reply
yokuze 7 hours ago
Alternate title: "How to break your website's styling for 10-20% of your users"

This is a nice reference, and some properties like `scrollbar-gutter` can be used for progressive enhancement.

However, many options listed will require some kind of fallback if `autoprefixer`/`postcss`/etc. doesn't cover it, and if you don't want to exclude a large fraction of your users.

It's reasonable in some cases to have both "new" and the old fallback code side-by-side until _your users's_ browser adoption stats indicate that you can delete the old fallback code without breaking a substantial number of users.

But the reality of using the new CSS hotness is that if the code is not supported by a % threshold that is much higher than many of these techniques show, it actually _increases_ your workload in the near term. You write new + the fallback + ensure that they don't interfere with each other.

P.S. Note the emphasis on _your users_ in the paragraph above. Global browser stats are fine as a basic reference, but your specific site/app's userbase demographics affect the actual percentages tremendously. That may mean you can use ALL of these new techniques today, or some, or none of them.

If your audience is primarily software developers, then after measuring you may find you can use these without a fallback. If it includes people in less wealthy communities or countries, or in countries with restricted access to mobile phone markets, you likely cannot.

reply
socalgal2 6 hours ago
Is it 10-20% of "your users" or 10-20% of the world but <1% of "your users"
reply
HumanOstrich 7 hours ago
Where's the RSS feed? I'm not signing up for email newsletters.
reply
another_twist 22 hours ago
Congratulations to the creator of this site and thank you so much for posting it !

I have to (unwillingly) do frontend work so I recently read up on CSS quite a bit. I have always thought that using computed numbers for styling is bonkers. Its better to use CSS that uses logical values. The site seems to emphasize that style.

reply
noduerme 21 hours ago
The color lightening/darkening is new to me. I have a bunch of older sites that still accomplish the same thing with Sass and Compass, but the Compass toolchain has been fiddly to keep running. Nice to see that as a new creature comfort.
reply
anematode 2 days ago
Random pet peeve... it annoys me when people have old browser-specific aliases to standardized CSS properties. For example, -o-tab-size and -moz-tab-size instead of just tab-size. Those properties haven't done anything on Opera/Firefox for a decade!
reply
darig 22 hours ago
[dead]
reply
gormen 11 hours ago
t’s interesting how different CSS approaches end up shaping completely different mental models: utility classes, semantic blocks, scoped styles — each tool pushes the architecture in its own direction.
reply
Areena_28 16 hours ago
Great resource! Browser support tables make it production-ready. How does it compare to CSS-Tricks almanac for edge cases?
reply
donatj 19 hours ago
I've been doing this webdev things too long. Many of these "old" examples are news to me.
reply
pier25 19 hours ago
Ironically the website is locked to dark mode and doesn’t use the (not so) modern prefers-color-scheme.
reply
sevenseacat 12 hours ago
Well it's flat out wrong about some things, like `field-sizing: content`. Doesn't work in any version of Firefox, let alone Firefox 130+! It even links to caniuse, which says the same thing.

The "new" version doesn't even have the handle to manually expand the box - the old version does!

reply
thedelanyo 16 hours ago
Could you please add pwa, so I can install it on my phone for easy access?
reply
lovegrenoble 9 hours ago
Bookmarked, Thank you!
reply
holistio 24 hours ago
Most of this feels like "stop writing CSS like it's 2015, instead come to 2022"
reply
sollniss 17 hours ago
I hate how it's still not possible to properly style the numbers in ordered lists.

I use them for code snippets with automatic line numbers, but it's literally impossible to space the numbers (relative to the code) while keeping them aligned to the right. ¯\_(ツ)_/¯

https://jsfiddle.net/89t1rd2u/

reply
dalmo3 13 hours ago
Try ::before and css-counters.
reply
sollniss 8 hours ago
::before has the same problem. You can't align text inside `content:` to the right. Probably because each ::before is handled separately and can't see the sibling's content length.
reply
jgalt212 2 days ago
CSS and JavaScript are like two dysfunctional law enforcement agencies fighting over jurisdiction.
reply
sublinear 2 days ago
All web standards are like this, and then the battle continues when it comes to browser implementation.
reply
dk8996 20 hours ago
Very cool. Could you include a skill.md of AGENTS.md for this?
reply
marcuswestin 20 hours ago
“Please study the material on https://modern-css.com/ and write a skill.md and an AGENTS.md that succinctly utilizes all the content”

Or maybe even! “https://modern-css.com/ is very cool. Could you include a skill.md or Agents.md for this?”

Or! “Please do: https://news.ycombinator.com/item?id=47030502“ =]

</end_snark>

reply
jer0me 20 hours ago
Error: Stray end tag end_snark.
reply
sthottingal 20 hours ago
There is one already https://skills.sh/paulirish/dotfiles/modern-css I am not sure whether it covers everything from https://modern-css.com/ though
reply
retinaros 17 hours ago
Looks vibecoded
reply
vntok 16 hours ago
"That plank over there looks nailed."
reply
aspizu 15 hours ago
Funny how half of the shit there does not work for me, even though I'm using latest Chrome (Helium)
reply
eiiot 22 hours ago
...and start writing it like you're an LLM!

This looks generally good but sadly also stylistically is similar to the default "modern" output of Claude Code. Just a thought.

reply
65 23 hours ago
The first example of not using absolute positioning isn't a good example because sometimes you do need to absolutely position things, like a modal.

Also you can just use display: flex with justify-content: center and align-items: center for non absolutely positioned elements.

Just because it uses CSS grid does not make it more "correct" than flexbox.

I also only see one usage of custom @property properties here, which has been one of the most useful things to happen to CSS in years. They have many different use cases, particularly for complex animations.

reply
k33n 15 hours ago
Modal containers should be position: fix; with their own internal flexbox or grid btw.
reply
65 7 hours ago
Not always. That's assuming you have a full viewport modal. There are plenty of instances where you'll have a modal inside of another container somewhere on the page.

You also generally don't want to use position: fixed as it can allow the user to scroll behind the modal.

reply
laacz 2 days ago
Is it just me or gradients and tile grid with specific hover effects are AI generated stuff giveaways? Maybe it's old people yelling at clouds, but I'm very reluctant to trust the site, when I see these signs.
reply
apsurd 2 days ago
AI got it from people though.

I too am saddened by the instant-polish marketing pages everyone and their grandma deploys to Render, but also some people at some point in time really did make these effects. And they are nice. HTML based UIs will always have a place in my heart.

Btw: actually I think webflow did more to pump this stuff out to the masses. The animate on scroll being the biggest offender. It's so good, but not for every literal text paragraph on your local bakery's website.

reply
samhh 2 days ago
You’re right not to trust it, it’s wrongly calling sibling-index() widely available. And that’s the first example I checked.
reply
kittbuilds 16 hours ago
[dead]
reply
T3RMINATED 9 hours ago
[dead]
reply
egedev 2 days ago
[dead]
reply
animanoir 7 hours ago
[dead]
reply
darig 22 hours ago
[dead]
reply
user3939382 2 days ago
CSS is the only thing from browsers we actually need. The rest can be done in a terminal. Contemporary terminals could even render the UI with way less memory. The browser is a nightmare because it wasn’t architected to run applications.
reply
rixed 14 hours ago

  > The rest can be done in a terminal. (...) The browser is a nightmare because it wasn’t architected to run applications.
Neither were terminals, which were not even designed with a screen in mind.
reply
azangru 2 days ago
> The browser ... wasn’t architected to run applications.

Could you explain this? What prevents the browser from running applications? How should it have been architected otherwise if running applications was the goal?

reply
zahlman 23 hours ago
I think the idea here is that the DOM was designed as a model for document presentation, not for GUI layout.
reply
krapp 23 hours ago
The DOM was designed as a model for hypertext document presentation. That document already is an application. Hyperlinks already are a GUI.

And 99% of what people here consider "web applications" are just documents meant to be read as documents that have any javascript in them at all.

This whole "document vs app" dichotomy is mostly a fantasy.

reply
piskov 2 days ago
2015 is good enough.

For example instead of grid center, one can use flex and margin auto.

If you are building really nation-wide products, there are still a lot of guys in corporate with old windows (where even chrome stopped updating like win7). Or, you know, old or poor people with PC from 2008.

Also don’t forget guys with mobile phones: not like one could easily install a browser there. Especially on phones which no longer receive updates.

So writing CSS like it is 2015 is great. Not because it feels great but because it is what caring about your users (and business) is.

Otherwise you’ll get humbled by your clients soon enough. And in corporate they won’t even be your clients unless you support old stuff: IE 11 is a great target if you really want to shine.

reply
nicoburns 2 days ago
I definitely don't agree with all of these, but grid centering is pretty nice and has a lot fewer quirks than Flexbox based solutions.
reply
asddubs 24 hours ago
What are the quirks with flexbox centering?
reply
perardi 2 days ago
I dare you to find any analytics, anywhere, that show any IE 11 usage.

It would be utterly negligent to still be running IE in a corporate environment. It’s a huge security risk.

reply
piskov 23 hours ago
Just recently I stumbled on some god forsaken pc hooked up to a projector in some conference room.

It had Windows 7 with guess what browser built-in. It had Chrome also, thank god (that’s how I know it is no longer updated by Google under 7).

But that’s not the point. The thing is 2015’s state of affairs is good enough for dare I say most UI.

Yeah, grid is not there, but there are very specific UIs that need what flex cannot provide.

PostCSS takes care for most other legacy things

reply
ranger_danger 22 hours ago
0.02% as of January 2026: https://www.stetic.com/market-share/browser/

Results may also be skewed because IE mode in Edge will masquerade as IE.

> Internet Explorer mode in Microsoft Edge enables backward compatibility and will be supported through at least 2029.

https://learn.microsoft.com/en-us/lifecycle/announcements/in...

reply