I kinda see a potential usefulness of custom attributes, but I’m still not entirely sure how they’d be better than classes. What’s the advantage of [shape-type="1"] over .shape-type-1?
I love the clean approach with classless. Documents do have a structure and is makes it easy to change by just swapping out the CSS without touching the document.
But could you not just add the class only when you really really need to break the structure? The middle ground for me would to do my utmost to avoid classes within reason. So as few exceptions as possible. I know this is selling elastic bands by the meter.
On the other end you have Tailwind CSS. I know many are happy with it and find it has a nice developer velocity. But I find it overly verbose and "framework of the day"-ish.
So for me it is classless until my head hurts. Then I'll sprinkle in a class to get work done.
Documents that experience little change don't need classes because their structure is reliable.
Documents that change often have unreliable structures, and will require frequent updates to the CSS rules to match structure changes. Using classes insulates the CSS from the document structure, mitigating the need to update CSS as the document evolves.
It also depend your development strategy. If using Vue components and writing the CSS in the same file as a dedicated, small-scoped components, it's practical to update the CSS references alongside the document changes. But when there's distance between the HTML and the CSS, or there are components in use who's structures may change unpredictably (such as from 3rd party libraries), classes provide a safer reference.
There's no need to have an ideology of using classes or not using classes. The practical approach is to assess the nature of your work, the rate of change of the documents, and to adopt practices built around those assessments.
I think the confession at the end of the article is correct, that this asks a ton of the authors of sites. But the article is also correct that accessibility is much better for this style than it is for competing ideas. Just compare to the div heavy style that is common in places like substack.
I used to follow CSS Zen, now I'm more of a "put a class on every element describing its purpose semantically". Then, when I need to change the structure of some component, adding a wrapper, changing an element type a -> button, etc, most of my styles keep working just fine. I'm not a fan of Tailwind, my method is more like BEM or Atomic CSS but with less naming-convention-rigour.
I should mention that most of my work is in building interactive components. You might be able to make the case for structural css for more flow-like content. But even then, when designers start asking for full-bleed elements in flow, you have to start breaking structural semantics and tying the two together.
I think your point is that there are not enough structural items to distinguish things for some uses without also signaling them for others? I can agree with that.
So, as a maximalist position, I agree that you should consider it an anti pattern to force yourself into minimal markup. But I also have a hard time not thinking it is an anti pattern to go the other way? Lots and lots of divs because the only way we could consider abstracting things is yet another wrapper around things.
Gets worse when I look at the noise that are the classnames generated in so many frameworks.
You're right that some coupling is inevitable, but the question is where to put it. I'd rather couple my styles to semantic intent (what something is) than to DOM structure (where something sits).
I don't add classes where I don't need to style anything, and I'm not adding _extra_ divs. Instead of using HTML element names, I'm using my own semantic naming convention that is not strictly tied to the behavior of HTML elements.
For instance `<a class="navbar-button">`, `<button class="navbar-button">`, or `<div class="animation-wrapper"><button class="navbar-button">` can all be selected by the same `.navbar-button`. Whereas the equivalent structural selector `nav > ul > li > :is(a, button, .animation-wrapper > button)`, is fragile and complex. Additionally you can't use a descendant combinator instead to simplify, because there could be other non-navbar button descendants of a navbar item (for ex. a dark/light mode switch).
This gets even worse with modern CSS features. Say you want a hover animation that uses container queries - you'll need to add a wrapper div around your button to establish the container context. That single structural change breaks not just `nav > ul > li > button`, but also related selectors like `button + button` for spacing or `li:has(button)` for parent styles. Using structural selectors leads to selector complexity as you're forced to be more and more specific with your selectors to avoid parts of structure you shouldn't be styling.
I still use semantic markup wherever possible, I think its a great benefit for accessibility tools and automated information extraction. I just don't think its a good idea to use it for styling.
MUI solves this problem by having different typography sizes defined in the theme, and then you specify variant=“h2” on an arbitrary Typography component and all the styling is consistent.
One more reason I always choose or recommend MUI for all new projects.
Implemented it this way with static generators and web apps before very easily.
Not to mention it is so stupidly quick.
Because the real problem I see is that the generic DIV is grossly overused, when built-in HTML elements already exist for exactly the use case intended.
I rarely see HEADER, FOOTER, NAV, even P because people just use the generic DIV tag instead.
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/...
It is a solid solution for blogs and apps with a distinct document feel, but for anything beyond that I found it too limiting and brittle. Back to components and Tailwind.
Tailwind actually complicates a lot more things, when you have to specify variants for example, there you go installing tw-variants, writing Javascript just so you can get different sorts of buttons.
This is fine for larger component libraries like shadcn-ui, but for simplicity, I'd pick up pure CSS for something like button .error; and button .secondary.
(yes I know you can just @apply whatever you want inside those blocks, but what's the benefit of tailwind then?)
If you read the article you'll see what they're talking about. It's not "CSS is too limiting" it's "CSS only applied to elements is too limiting".
TW+TSX is easily the most productive way for me to write UIs and has been for 5 years. Don't like it? Don't use it but please leave me alone.
That's not how HN works. HN is for discussion. If you make a comment, people can discuss.
If you want to be left alone, then perhaps "don't use" the comments section.
That’s the essence of so much of this obsessive bit-twiddling: irrelevant aesthetics. The functional aspect of this is a disaster, as others have noted, locking styling to page structure in a way that will require structural changes to the CSS just to move an element on the page.
If you’re not talking about change, the other stuff is pointless.
Basically I have JS "controllers". Every controller type gets a root HTML element. They pick the element type, these root elements automatically get a css class ala "{controller}--app"
Then from within each of these controllers I largely only use un-classed HTML - and style almost entirely based on direct ancestor ">" operators. Something like
.sidebar--app {
> ul > li > a {
// style sidebar
}
}
I generally keep the scope of what is a "controller" pretty small, a few elements deep maximum. It's a sort of hierarchical mvc and controllers have controllers have controllers, so for instance my sidebar controller has a search controller. The search controller just manages its own elements and reports changes via events. This pattern has kept the JS and CSS very clean and understandable, and the generated HTML pristine.I've been hacking on this for close to a decade, it's a fun little side project. It's pretty large in scope yet it's honestly been a delight to work on. The styling of everything is quite scoped so there's very little unexpected problems. The actual HTML is readable and pleasant.
Found "<span class=..." — What?
Read the page.
Footer : "I only got 99% of the way there. I use 11ty’s syntax highlighting plugin, which uses classes for styling."
From an outside perspective, it is perplexing to see the constant back and forth webdevs do between making website more complex and rediscovering the simpler first principles
Personally, I would first try to get the semantic structure of HTML right for the content I want to display. Then I would look at what I can do in CSS to make it look nice, but without going full overboard. Stick to things that are now standard in browsers, and that are responsive and resize and float nicely. Perhaps, if necessary even something like the checkbox hack, but probably try to avoid it, since it is a hack. Then the site already looks sufficiently good usually. At no point in this comes JavaScript into play, because this is about visuals, and that should be handled by HTML and CSS. I will use JS, when I have something dynamically changing and/or interactive on a page, and I will try to make a noscript alternative, perhaps usable by the user simply reloading the page.
However, I have also seen a lot of frontend devs, who just throw JS framework at everything and since everything is JS anyway, they also do things that could be simple HTML and CSS using JS instead. The result are those pages, where one is greeted by a blank white page, when not running JS.
So there definitely are a lot of devs, mostly frontend devs, that do this kinda thing, and it often secures their job by introducing complexity under the guise of looking fancy.
Example from a previous job: Making buttons that have 2 corners cut off, but the main navigation bugs regarding responsiveness, that led to broken layout took 3 months to fix. Transferring a navigation from one project to another? 3 weeks.
Regarding the noscript alternative solution. I do not know a single modern website relying on users refreshing the page to update content. Except for HN maybe. This approach is very very outdated and will frustrate users.
[citation needed]
Usually you have more complex styles and visuals than your HTML can express and trying to invent selectors / HTML heirarchies to describe them gets difficult very quickly
In addition you often need to support multiple style systems on a single web application whilst the design evolves, or else it becomes an all or nothing rollout when you change stuff.
It's common for engineers who weren't around back then to look at the abstractions that modern FE systems provide, and question if they're even necessary. That's healthy. What's not healthy is assuming they were only invented for fake or historical reasons, and telling everyone to just abandon them for commercial projects.
The styling should simply serve to improve the content. The content should generally stand on its own without any styling.
In semantic CSS the goal is to write your HTML with as little regard to presentation as possible. Your HTML is simply structuring human readable data. At this step you give it no thought of appearance.
Styling that structured data comes second. You write the CSS to make the structured data look nice, and ideally do not touch the HTML in the process, at all. The styling is but an affectation.
This fell out of favor because people cared more about looking flashy than quality content, they didn't want to put any thought into developing the actual page and wanted to start with the design. It's a symptom of the overall disease of the modern web.
This is more achievable with modern CSS than ever before and I pray for it to make a comeback.
That's how I was taught, you know, progressive enhancement. I didn't even know there was any other way lol
Using custom tags invariably leads to the aforementioned, and browsers need to do extra DOM traversal to target styles properly. Not so with BEM: a flat list of class selectors maps cleanly to elements, so when the browser is parsing HTML to DOM, it doesn’t need to wait for all descendants to be parsed (for :has() to work) or do extra checks to determine whether its current path in the DOM matches a nested or descendant selector. It can just keep on parsing and rendering.
This may not feel like it makes a difference if you’re browsing the web on a veritable supercomputer, but for anyone on an older device, the difference will be between instant and kinda sluggish.
[1] - https://medium.com/teutonic-css/retiring-my-own-little-css-f...
Similarly, WordPress spams tonnes of classes everywhere. Most are unused.
So I took a look at rewriting my CSS to target by logical structure, rather than just random names dotted about. It mostly worked well, although it did mean that I occasionally had to write a selector like:
`li[itemtype="Comment"] > article > div[itemprop="author"] {}`
I feel there's a mismatch between creating novel "semantic" elements, and then customising them in the markup, rather than the contextual approach (nesting, rich selectors). The mismatch is that the new elements still apply a "what" approach, but the attributes used for customisation apply a "how" approach and leave it in the mark-up. It's still like `<p class="red" />` rather than `main p { background-color: red; }`.
I get that there's a trade-off between purity and code that's nice to work with, and I think you've hit a very readable, appealing and creative balance.
> document.querySelectorAll('*[class]');
tons of elements with class
That's around 2% of the size of the single page of that article, it absolutely is a trivial amount, especially when it complexifies so much the maintenance or addition of the website.
I use a section element to enclose a heading with the paragraphs that immediately follow it, thereby scoping text to a heading.
The advantage to this approach is that you 'gain a class' (okay a section) that can be used for styling.
I am not keen on adding my own make-believe elements, however, I have found that, if you know your HTML elements, you can write surprisingly human readable HTML.
There is no need to be fundamentalist with going 'classless', classes are the way to go for situations such as when an element changes.
I also take heed of the HTML5 spec and the advice regarding the use of 'div'. There really is always a better element to use, and, with 'divs' removed, it is trivial to layout any content with CSS grid.
Scoped CSS is also a game changer, however, you have to pretend Firefox does not exist, which is fine by me for my hobby site. Scoped CSS means that you can keep the CSS simple without bizarre selectors that the next developer will not like.
It all depends on what you are trying to create. If you were to want to do your own version of Gmail then you are in a world of complexity. However, blogs, ecommerce and much else does not need to be a complex mess with a 10000 line 'add to' CSS burden.
But, I'm sticking to tailiwnd :D
It also brings back memory of 2000s internet, but merged into Today's design standards. I assume this was intentional.