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.
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:
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.
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); } }
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.
"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; } }
Browsers: Yeah, but beware of limited availability, most of those creative examples are in the 40-50% browsers support range.
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.
Not if you filter the examples. Click "widely available".
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.
Makes me wonder if these demos (or the browser support tables) were made by LLMs. They clearly haven't tested the demos in firefox.
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.
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?
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.
https://github.com/ericfortis/mockaton/commit/acf21803480412...
…and it deduplicated autocomplete suggestions in WebStorm
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.
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/V...
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.
If nothing else, I'm grateful for that.
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
That one is a dud. Most others work for me, though.
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)
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.
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.
The "new" version doesn't even have the handle to manually expand the box - the old version does!
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. ¯\_(ツ)_/¯
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>
This looks generally good but sadly also stylistically is similar to the default "modern" output of Claude Code. Just a thought.
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.
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.
> 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.Could you explain this? What prevents the browser from running applications? How should it have been architected otherwise if running applications was the goal?
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.
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.
It would be utterly negligent to still be running IE in a corporate environment. It’s a huge security risk.
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
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...
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.
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?"
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.
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.
https://github.com/WICG/html-in-canvas
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?
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.
Semantic web is basically "please think of the crawlers."
I disagree. And that makes me the loser here
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
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?"
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.
Whatever the professional equivalent of ‘touching grass’ is, I suggest you do that at your earliest convenience.
What an absurdly absolutist statement.
If we are talking about statically defined html then sure. make your global css files.
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.
Really what I want to see is beautiful TDD for CSS so that uniformity can be enforced, but I’m not sure that exists.
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.
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.
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.
You don't have to: https://csszengarden.com/
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.
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.
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.
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.
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.
Non-BEM CSS with ids and multi-classes everywhere was hell.
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.
[0]: https://mastrojs.github.io/blog/2025-11-27-why-not-just-use-...
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.
<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
<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
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.
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.
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.
@scope fixes a lot of this, but it is a complex problem. With tailwind you mostly have to worry about inheritance
Then <a type=b> is potentially a <c>. Consider a small refactor?
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.
<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>
Check out the Netlify admin dashboard screenshot in my blog post
https://pdx.su/blog/2023-07-26-tailwind-and-the-death-of-cra...
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...
If modules had existed much earlier it probably would’ve gotten rid of most of the awfulness.
It sounds like you just want to write Java.
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.
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.
Not saying it’s good/bad, but it feels like that’s the use case
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.
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.
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.
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.
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.
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)
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
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.
At least html and CSS are both presentation. 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.
React was originally desingned to be php in the browser.
php5 -> HHVM -> Hack -> XHP -> JSX
Hack was created later though. XHP was a php 5 extension created around 2008
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.
Redux is a lot less fashionable today, but hasn't entirely disappeared as an M and C option.
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.
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…
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.
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.
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).
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.
The separation is already there
People have just failed to understand it
I wonder how you would map these three onto M, V, and C :-)
Who uses MVC in 2026? Pretty much every framework out there, including Java frameworks and Python frameworks and .net
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.
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."
But that’s just generic „blablabla”. MVVM is not a mistake and is still plenty useful.
People confidently write strong opinions on the internet.
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.
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.
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).
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.