9 ms·
CSS Classes Considered Harmful
- peterlada 2y agoNo. I mean data-no=jose
- tiborsaas 2y agoAll these examples of "scaling CSS" tends to end with "don't write CSS". Relying on HTML attributes to describe presentation could have its neat uses, but suggesting it as a new generic paradigm that everybody should jump on regardless of problem space is not the way to go. HTML is not the escape hatch out of CSS. The main issue is that it's just not better than a class soup of Tailwind classes. It maybe a bit more readable, but if you stuff a DIV with endless `data-junk` then it will be just as bloated as the rest of these specifications if used without care and if you avoid writing CSS it will be pretty bloated.
- tannhaeuser 2y ago> HTML is not the escape hatch out of CSS If anything, CSS is kind of an escape hatch out of HTML/SGML attributes which were specifically introduced for attaching any meaningful info to text nodes not displayed as such to the reader. This is especially evident if you compare the enormous amount of extensions and added non-type-checked ad-hoc syntax to CSS with the relative lack of HTML evolution. The only reason appears to be that HTML was organisationally locked in a W3C process for such a long time, while CSS could be easily extended. Attributes are used for rendering details all the time with the original concept of markup. The idea that you need a special syntax and separate item-value space that's neither here nor there, like CSS is, when organising item-value assignments is the entire point of attributes and even hard-code a vague structure/presentation dichotomy that's then up for interpretation for like 30 years is beyond absurd. SGML, from before 1986, can already assign attributes (so called link attributes) based on context-dependent rules much like CSS.
- easyThrowaway 2y agoI mean, this could've been called "CSS Considered harmful". Also suggesting the use of attributes to mix state with style is... quite the bold strategy. What if the same component is used multiple times in a page with a slightly different appearance and same behaviour? Do you keep adding more attributes? You're gonna end up rebuilding Tailwind, but using an even more opaque syntax.
- donatj 2y agoThe problem is the modern webs hyper focus on appearance over content. Content first, style second. I still say that the answer is just semantic HTML/CSS. You're coming at the problem from the wrong direction naming things for how you want them to appear. Name them for what they are. Write the HTML agnostic to how it's intended to appear, and then just use CSS as a tool to style the already existing document. SCSS and extending placeholder[1] selectors makes this insanely easy and viable. You can write your "how things appear" classes as placeholder classes, and then simply extend those from your "what they are" classes. Super easy to keep organized. 1. https://naxoc.net/2014/01/28/placeholder-selectors-in-sass/ https://naxoc.net/2014/01/28/placeholder-selectors-in-sass/
- have_faith 2y agoIs `my-card` not a declaration of what something is (supposed to be)? The attributes are then encapsulating the state of the thing e.g. "is-loading", "size", etc. And the style then naturally follows, becoming a function of state e.g. "large loading indicator". The extend selector solves the problem of one style definition inheriting styles from another style definition, but the author is trying to solve the problem of how styles are mapped to stateful elements. It's the mapping process that introduces the combinatorial problems of class names.
- the_other 2y ago`.my-card` is semantic, but `.big` is presentational/visual. You could maintain the semantics with rules like: .my-card { width: normal; } .homepage .my-card { width: bigger; } .sidebar .my-card { width: smaller; } (where 'normal', 'bigger', and 'smaller" should be replaced with real values). BEM suggested state labels (e.g. `.my-card__loading`). I'm on the fence about those vs `.my-card.loading`. The class-based syntax seems shorter than either BEM-style or attribute selectors, and thus I prefer it. You might get a bit more rigour from the more longer-form methods, but I'm not convinced the energy isn't better spent training your team to be more careful. Shared code, CSS toolkits, shareable widgets present bigger challenges... the thing is that none of the tools suggested actually fix the problems. Your code can still break 3rd party code and vice-versa because of the global nature of CSS. Specificity needs management, but so do type systems and class hierarchies. Stop pretending the cascade and global namespace don't exist and just do the work.
- floppiplopp 2y agodon't write css in the class attribute, write the css in a custom attribute. even better, implement a custom element, that uses custom attributes! how totally brilliant, absolute genius, what a visionary!
- bythreads 2y agoJust use web components, and use trie attributes instead.
- skrebbel 2y agoWow I like this solution a lot, and I’m sad that it’s presented after a million paragraphs of ranting and a terrible title. The tl;dr is: if you use custom element names (instead of “.Card”) and attributes (instead of “.Card—size-big”), things get very clear and you lose many downsides of other approaches (such as name clashes and unmaintainable soup): So write HTML like this: <my-card data-size="big"></my-card> And CSS like this: my-card { /* ... */ } my-card[data-size=big] { width: 100%; } my-card[data-size=medium] { width: 50%; } my-card[data-size=small] { width: 25%; } Personally I think this idea is really great. I’m not sure yet that I prefer it over Tailwind in all situations, but it seems obviously better than eg BEM, CSS Modules, inline styles, styled-components and all those other approaches. I really love the idea, it feels like a “woa why didn’t I realize this before” kind of thing. EDIT: Thinking on this a bit, I’m not sure I’d replace every single div with a custom element like the author suggests, I think a div-with-a-class works just as well and is easier to grok for other readers of the code cause everybody knows how a div works. Readers don’t have to figure out whether it’s a defined custom element (ie a JS class) or just a CSS level thing, etc. But the idea of using attributes instead of .my-thing—-level-3 is genius in my book. It means you only ever need one class per element at most. No more concatenating long className props, no more repetitive prefixing, it’s scoped by definition, etc. Also all the logic in the HTML rendering code is nicely siloed: <div className="my-thing" data-level={someFunc(props.foo)} data-size={props.size ?? "big"} etc > So much nicer than some nested classname object hack!
- vbezhenar 2y agoI'm out of touch, I guess. Can I just use any tags with ordinary HTML? Is it correct HTML?
- cbeach 2y agoThe author's solution mixes style declarations (i.e. centering text) into HTML, which surely goes against the spirit of CSS? .Card[data-align=center] { text-align: center; } Filling HTML with data-* style attributes would intertwine layout and content in a single file, meaning we're no longer able to use CSS to flexibly re-style the content.
- err4nt 2y agoIt looks like you didn't read all the way to the end, and custom data attributes can be for whatever you desire. Presently a lot of people are putting style information right in class names so this distinction you're speaking about isn't present now and developers are already violating it in a big way. The solution in the article is at least using tag names, attributes, and attribute values in ways they're meant to be used. There's a warning about abusing class names in the CSS spec that reads like this: Note: Because CSS gives considerable power to the "class" attribute, authors could conceivably design their own "document language" based on elements with almost no associated presentation (such as div and span in HTML) and assigning style information through the "class" attribute. Authors should avoid this practice since the structural elements of a document language often have recognized and accepted meanings and author-defined classes may not. https://www.w3.org/TR/selectors-4/#example-f9c08b5b https://www.w3.org/TR/selectors-4/#example-f9c08b5b
- debesyla 2y agoClearly the best solution is just to write styles inline, because classes are evil and attributes are just classes with a hat.
- sakjur 2y agoIsn’t this mostly moving the namespacing problem from classes to tags and attributes? It’s not something I mind, but since the other potential solutions were dismissed as flawed for similar reasons, I’m not sure why the solution presented in the post is better than the other solutions. That criticism aside, I learned something new about CSS today, and the dynamic attribute based values is something I’d like to play around with to see if I like it. I also appreciate the enumeration of different strategies, that is something I can reference in a conversation with a colleague even if my conclusion is different from that of the author. Thanks for sharing.
- meerita 2y agoI complete disagree with this statement that lacks evidence and it just want to duplicate functionality: "At first blush a utility class system might seem like a boon to a design system, but when applied to the markup we quickly see the problems: being unable to represent components easily in markup leads to a design system looking for other solutions such as providing markup with attached class names to represent a component - which usually results in the design system implementing components across a multitude of frameworks." In this era of component frameworks, looking at HTML and its classes is the least of our problems. Our real challenge is to build an additional architecture on both the component side and the CSS side. Additionally, both components and CSS can have (or end up with) more than just styles; they can include behavior, animation, and effects, which adds complexity, making the entire project difficult to memorize, scale, and maintain. OOCSS projects often result in unmaintainable code, with developers ending up with large CSS files and creating more specific classes to implement new features. FCSS (Functional CSS, or Atomic CSS as exemplified here) solves this problem by separating concerns and removing the mental load from the equation. The problem with Atomic CSS, Tailwind and others I just the syntax. It takes time to master Tailwind toolset of classes. At first it may seems easy and intuitive but it’s deceiving. FCSS has natural language https://www.fcss.club/syntax https://www.fcss.club/syntax and it’s hundreds of times more intuitive. The only real issue of FCSS or Tailwind is achieve inheritance only using CSS inheritance model. Let me give you an example: .color—blue { … }\ .color—red { … } <div class=“color—red color—blue”>text</div> In this situation, you might expect the text to be blue, but it will actually be red due to inheritance weight. This issue can be resolved on the component side easily with JavaScript https://www.fcss.club/customization https://www.fcss.club/customization, providing a more controlled solution. “There are a plethora of other issues with the Utility CSS methodology, and with it a plethora of articles. If you consider this a suitable solution, I'd encourage you to invest time researching the pitfalls, but I don't want to spend too long on this." I’ve been reading dozens of articles regarding the atomic approach and I didn’t find any phletora of issues. The only thing I found was a bunch of arguments in favour of OOCSS, all of them listed here. https://www.minid.net/2019/8/12/in-defense-of-functional-css https://www.minid.net/2019/8/12/in-defense-of-functional-css
- beardyw 2y agoThe author seems to be under the impression that classes didn't exist before OO. Of course they have always been used to describe broad groups of things and in that context their use in CSS is perfectly consistent.
- whywhywhywhy 2y agoI don't understand how "<div class="Card" data-size="big"></div>" is better than just <div class="Card big">, then using a .card.big selector? The article just says "but this can cause specificity issues, which can create problems further down the line" to move the same thing into attributes rather than just use the thing for what it was designed for there has to be a good reason.
- iLoveOncall 2y agoIt's not and that's the reason why the author doesn't address it in their article. It's just bad faith. Especially with CSS pre-processors (because they make writing that syntax in the CSS file super easy) which basically everyone is using, ".card.card-big" is the obviously right solution. It is easy to read, specific, doesn't risk collisions, doesn't risk adding broken styles if you forgot the "card" class on the element, etc. It addresses all the negatives outlined in the "BEM is not the solution" section of the article. The only "argument" that still sticks against this one is that "it's verbose", but not once in my career have I seen a __good__ software engineer discard a perfect solution because "it's verbose".
- msoad 2y agoAs this article argues, problem with CSS Classes is that a CSS Class can be applied to _any_ element where custom properties will be scoped to the elements that the CSS was meant to be used for.
- Denvercoder9 2y agoCustom properties can be applied to any element as well. You can constrain the effects of either to only the elements they should affect in your CSS rules.
- wavemode 2y ago> Custom properties can be applied to any element as well. Well, no, they can't. That's the point the article is making. Classes can compose arbitrarily, i.e. class="size-small size-big" Whereas data-size="small big", while valid syntax, would not be styled by a rule targeting elem[data-size="small"]
- mirzap 2y agoHow is using markup filled with data-attributes better than "messy" atomic CSS? I don't see how this is an improvement over simply using classes. Maybe it's just me.
- joestrouth1 2y agoAtomic CSS requires a class for every CSS property you wish to set, at each breakpoint, each active/hover/focus state, each color scheme you support, etc. With data-attributes one could specify that the element is a primary, full-width, large button and all the properties that make that happen live in the stylesheet.
- dsego 2y ago> Now your design system team is tasked (or burdens other engineering teams) with orchestrating all of this tooling. Design system team? I'd like to have that problem.
- codeflo 2y ago> Conceptually you could think of these as classical inheritance (so class Card extends Div) - inherit the semantics and base styles of div while making a re-usable style for a Card class. You could, but you’d be wrong. The CSS selector specificity algorithm has nothing in common with OO inheritance, and classes are only one of many equivalent ways to interact with that algorithm.
- Tade0 2y agoSounds like a call to return to the days of <P ALIGN="LEFT">. I occasionally use attribute selectors - really useful when you have crazy requirements like "Here's a table with rows having generated data-index attributes. Make the background of each[0] prime-indexed row blueish". For other uses classes are quite simply shorter. Also, if I have to write a lot of CSS then either: -The stakeholders insist on an "unique look", in which case kill me. -I'm making a component framework of sorts. If it's an internal project, also kill me. [0] well, not "each". Just the first 100 or so. Users don't read long lists anyway.
- mananaysiempre 2y agoI mean, Tailwind already is that with extra steps.
- riiii 2y agoIf you're not Dijkstra then you're considered harmful if you steal his valor in your title.
- blowski 2y agohttps://meyerweb.com/eric/comment/chech.html https://meyerweb.com/eric/comment/chech.html “Considered Harmful Essays Considered Harmful”.
- RegW 2y agoWell it's not as rampantly inciting as "is Evil". https://www.infoworld.com/article/2160788/why-extends-is-evil.html https://www.infoworld.com/article/2160788/why-extends-is-evi... https://www.infoworld.com/article/2161183/why-getter-and-setter-methods-are-evil.html https://www.infoworld.com/article/2161183/why-getter-and-set...
- lioeters 2y agoThe only exception may be "Eval is evil," which is generally true and requires a high bar for using safely.
- peoplefromibiza 2y agoI could be wrong, but I reckon I read somewhere that the "considered harmful" was added in review by Niklaus Wirth, who, at the time, was editor for CACM.
- lompad 2y agoA snowclone is not "stolen valor", it's a completely normal part of modern writing. Especially considering that Dijkstra didn't even coin the phrase himself.
- riiii 2y agoIt's a stretch at best to call "Considered Harmful" in this context a snowcone.
- lrtz 2y agoThere's big performance downside replacing classes with attributes at least when using javascript query selectors. Browsers have special data structures for id and class search optimizations. Matching attributes scans through the dom top down.
- HelloNurse 2y ago"Scaling class selectors" is a false problem: actually, classes and pseudo-classes "increase the granularity of control over elements" (which are much closer analogues of classes in the OOP sense than CSS classes) better than in 1997 because useful new selectors and pseudoclasses have accumulated over the years. Class selectors (and everything else) become weak, as should be expected, with monkeys ("With mutually exclusive classes like Big and Small, it is possible for elements to apply both classes at once") working with no actual design ("parameterise Card to take a size option which is either Big Medium or Small, a rounded boolean, and an align option which is either Left, Right, or Center").
- hnthrowaway121 2y agoI don’t like the proposed method but I’m glad to see thoughts a little outside the main lines of utility CSS vs Module isolation vs naming conventions like BEM. One bad consequence of the custom element name convention is that the outer layer of a component then has no semantic meaning and accessibility has to be fully implemented by the developer, which requires a lot of knowledge and effort compared to using correct semantic HTML. Inaccessible code is a real business risk and making all components generic elements with no affordances for screen readers would not help. It’s a pity the article doesn’t consider this at all as its recommended patterns would lead to some needlessly bad experiences.
- rasso 2y agoYou can tell custom elements what they are using the „is“ attribute or by extending the appropriate HTMLElement class if you define the custom element via JS.
- hnthrowaway121 2y agoThat seems like quite a hoop/footgun if you are only having devs do this for CSS reasons.
- vogon_laureate 2y agoThis gives big bikeshedding vibes. I always liked the idea of semantic markup, but humans are messy and complex and we will always find ways to bring that chaos to utopian systems. Classes and IDs work fine most of the time. Documenting your styles is good practice. Using BEM or Tailwind has the benefit of adhering to a system and base classes that others can make sense of without much effort. CSS is OK. Our energies are better spent on more important things.
- langcss 2y agoTailwind solves this but not in the way you think! The rise of React and SSG/SSR means there is a world where the classes are React components and the attributes are props. Even for static pages, for DX you can use the same tooling as you are used to for dynamic pages. And something like NextJS can generate the static html. And this makes even more sense for dynamic stuff like date pickers. In this world you use utility classes because tags are too granular and components are the logical unit. But if you are hand crafting the html then tailwind might be more of a pain. I am a bit undecided on that. I still have a soft spot for CSS Zen Garden.
- ericyd 2y ago> Our energies are better spent on more important things. This is true about most things in tech though, yes?
- Pesthuf 2y agoIn the CSS that we have today, they are still probably the least shitty solution in most cases. attr() for things that aren't content isn't really supported and manually doing [data-gap="1"] {} [data-gap="2"] {} and so on sucks for various reasons. Attributes like [card-size="big"] look good, but unlike classes, they don't get autocompleted so you always have to remember the magic strings (ugh) and errors don't get caught by static analysis. It's not just the class attribute that's harmful legacy garbage, but the core design of CSS. But we're stuck with it.
- Etheryte 2y agoThis is just class names with extra steps and more syntax, it's a downgrade, not an improvement.
- deleted 2y ago[deleted]
- dzonga 2y agowe really need a separation between documents and applications. for documents - css classes & BEM are more than adequate. for applications - we seem to have short memories. why not learn from what complex UI frameworks used in industry do e.g desktop frameworks case in point - https://doc.qt.io/qt-6/stylesheet-reference.html#background-color https://doc.qt.io/qt-6/stylesheet-reference.html#background-...
- 000ooo000 2y agoGood job everyone. We're almost back there again. Twenty years later, 2026 will be the year of semantic CSS, though I guess it will have a new, cool name. Complete Stylesheets or something I dunno I'm not in marketing.
- azangru 2y ago> Twenty years later, 2026 will be the year of semantic CSS Alas, people are too deep in tailwind for that :-(
- ericyd 2y agoI expected to hate this article but ended up finding it interesting. The author didn't convince me but I thought they had style interesting points.
- catapart 2y agoLots to dismiss in this, but I was pretty impressed by the bit about psuedo selectors! I had no idea this could be done, and it's pretty awesome! Can't think of when I would use it, given that you have to publish your implementation details for it to be useful (even if you only publish them for your internal team). But for personal projects, or well-moduled projects, I might reach for that little trick in the future.
- lofaszvanitt 2y agoCSS is like a wooden spoon. Conclusion: you are overcomplicating things. This ain't rocket science. CSS is dead simple, only you making it complicated.
- emmacharp 2y agoFor the curious, here is a set of "simple" rules for CSS authoring that, coupled with linting (through Stylelint, for instance) can address a lot (most?) of the issues raised in the article and the comments: https://ecss.info/en/ https://ecss.info/en/ Interested in what you may think about these rules and the principles behind them. PS: still a work in progress!
- url00 2y agoThis is impressive and an easy read, well done. Regardless, the fact that so many rules are needed to write good, safe, efficient CSS indicates to me at least a disconnect with the technology and how people want to use it. Indeed, just a casual look around the world of CSS tooling reinforces my opinion. CSS has such an odd mix of declarative-feeling design but with occasional "hacks" to get some imperative-like behaviors that more than once in my career I've thrown in the towel and just reached for a bit of JS to get the result I needed. Sorry a bit rant-y.
- emmacharp 2y agoCSS was not meant to be used by programmers but by designers... so yeah there's a disconnect there. But that does not mean programmers cannot use it the way it was intended to instead of making it, at great cost, like what they would have wanted it to be from the beginning! Also, wouldn't you say that there are also many rules and guiding principles necessary to write good javascript? Genuinely curious here.
- mattkenefick 2y agoIt's unfortunate how many people misunderstand CSS today and how web applications should be structured. Articles like this make me sad.
- Xen9 2y agoThe title is made by a person with very bad taste: Dijkstra would have given a different name to the title had he got a chance to return to past and his attitude to person writing articles about CSS would probably have been despising largely.
- lioeters 2y agoLink to the CSS Values 5 specification, where it describes how HTML attribute names can be referenced in CSS properties. 6.4. Attribute References: the attr() function https://drafts.csswg.org/css-values-5/ https://drafts.csswg.org/css-values-5/ With the ability to specify attribute types, including dimension units, and to provide a default value - it does look like a new way to connect HTML to CSS beyond the limitations of classes. --- > The data- prefix can be a little unwieldy but it allows for the widest compatibility with tools and frameworks. Using attributes without some kind of namespace can be a little dangerous, as you risk clobbering HTML's global attributes, but as long as your attribute name has a dash it should be quite safe. I wonder if this last part is true. For custom elements, since they're required to have a dash, I imagine future HTML tags are guaranteed to not contain a dash. But for attributes.. Are all future HTML global attributes guaranteed to not include a dash? Maybe the author means, as long as your custom attribute has a name with unique prefix, it should be "quite safe".
- Keithamus 2y agoWhile it hasn't been expressly resolved by the HTML working group to not propose new attributes with dashes, and while I don't speak for everyone in the HTML working group, I would assert that many members would object to a proposed attribute that included a dash (including myself). There is also a proposal in the works to allow web developers to define custom attributes - much like custom elements - which would likely follow the same or similar rules around dashes, at which point I imagine the HTML spec would guarantee that no dashes would be used in "built in" attributes. New attributes have been proposed that reasonably _could_ have had a dash, for example `popovertargetaction`, but instead they were compounded to one word precisely to cave out this path.
- lioeters 2y agoVery interesting to know, thank you. There are template languages that extend the HTML syntax but render to valid HTML, and I imagine this kind of guarantee of future naming scheme is important to ensure the extended syntax does not have the potential to conflict with new attributes added to the HTML specs. > proposal in the works to allow web developers to define custom attributes Looks like this is it: Proposal: Custom attributes for all elements, enhancements for more complex use cases https://github.com/WICG/webcomponents/issues/1029 https://github.com/WICG/webcomponents/issues/1029 Searching for "dash" does bring up a thread of discussion around whether to require dashes or not. --- I wonder if starting the custom attribute with a dash is allowed or not. Searching around, I see colon ":" and underscore "_" are OK, but dash "-" or period "." is only allowed after the first character. > Any namespace-less attribute that is relevant to the element's functioning, as determined by the element's author, may be specified on an autonomous custom element, so long as the attribute name is XML-compatible and contains no ASCII upper alphas. https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-core-concepts https://html.spec.whatwg.org/multipage/custom-elements.html#... XML-compatible attribute name: NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] Name ::= NameStartChar (NameChar)* https://www.w3.org/TR/xml/#NT-NameStartChar https://www.w3.org/TR/xml/#NT-NameStartChar
- replete 2y agoI've been building websites for over 20 years and I've come back to frontend after a few years out of the industry recently, horrified to see what developers are doing with current trend tailwindcss trend, but understand why you'd want it. I pity whoever picks up those legacy projects I thought about using custom tags before for components (not web components, which have a few gotchas) but it came down to SEO. If there were more certainty on the impact of custom tag on SEO I'd already be doing this.