Photo by Alexandra on Unsplash

CSS Approaches for 2021: Semantic and Non-Semantic Styling for the Current State of Web Development

Marcos Sandrini
Geek Culture
7 min readMar 22, 2021

--

Not too long ago, I wrote a very thorough series of articles about Web history and HTML. The second one in this series is about deprecated HTML tags (here’s the first and the third, by the way). On this text, I emphasized that, during the early years of Web, HTML took care of functionality, content and presentation. Later, following conventions borrowed by other fields from computing, we came up with something usually called Separation of Concerns. This means that, when it came to functionality, JavaScript took over, and for the presentation we were offered cascading style sheets, or CSS for short. For the functionality part, the separation made complete sense, but the presentational part seems to have some deeper discussions going on.

Semantic vs. non-semantic CSS

When analysing popular CSS/layout frameworks like Bootstrap or, for that matter, Tailwind CSS, or even conventions like OOCSS, one may notice that they do not follow HTML5’s specs when it says class names have to be semantic (that is, to have class names that describe the role of the object, rather than its visual features). This goes conceptually back to the point where HTML held both content and presentation together and, even though all of those examples above do use CSS’s powers, it is still the same conceptual way of writing presentation on HTML regardless of whether it is <p class="text-center"> or an HTML tag <center>.

It surely sounds like a backwards movement, but I am not judging this as something necessarily bad. In fact, one can say it is actually a good thing.

Non-semantic CSS and the promise of a “themed” web

When CSS was in its early period, one website called Zen Garden proposed to show people the powers of CSS by having a fixed HTML code that could be visually changed in its entirety only via CSS. It felt amazingly new and fresh and every Front-end web developer back then knew about that website and got some degree of influence from it. That website was a showcase for the promise of theming, one of the biggest selling points of CSS on its infancy.

Back then, in the times of simple forums and blogs, theme customisation on websites was a very cool possibility in the for the web developers. At that time they were often left to deal with CSS only, to be applied over inherited HTML which, on its turn, was rendered on back-end servers that they could not touch or did not want to touch. With AJAX and single-page applications this changed rapidly, but those were not widespread until much later.

Curiously, even if themes are not a big thing any more we still have it as one of the biggest influential points for CSS, and also for semantic CSS. Perhaps having a semantic HTML/CSS pairing like class="box" may be reminiscent of a long gone theme-hungry era, where you could have an actual advantage from being able to style and re-style the box in any way you wanted.

Other factors also play a role in the recent rise of non-semantic CSS: template structure is less fragmented and easier to traverse with its aspects more centralised, name clashing is basically eliminated and styles can be more standardised when it comes to colours and measurements.

Semantic CSS is readable

This does not mean the whole semantic story is a horrible mistake. Semantic CSS is still the approach officially recommended and, although themes are usually not a concern any more, we still have situations where one has to change a global layout parameter, like the colour of all links for example. This situation in a “pure” non-semantic CSS project turns into a real problem. The biggest issue with this approach for me, though, is how unreadable the template code becomes. Readability is always a problem, but depending on your codebase it may be a decisive factor. Being myself an Uncle Bob follower, I never dare to underestimate it.

It is also worthy to mention that a selling point of non-semantic CSS, name clashing, can be safely tackled in a number of ways, including more specific naming and scoping (as I will mention later).

What is our actual problem though?

Right now we are not using HTML and CSS the same way as 20 years ago, for the most part. Not only we have different possibilities now, we also have a very different set of problems.

Now, to build our massively dynamic websites, we have a massive use of single-page app frameworks as React, Vue and Angular behind bundlers like Webpack and Parcel, sometimes with new tools like styled components. Although we still have to generate assets to the user that should be as small as possible, with all the available tools just mentioned above important aspects of coding front-end apps changed. Instead of spending lots of time pre-optimising assets to be delivered directly to the user, now we can focus on having a workable, readable and performant codebase, as bundlers will take care of minifying and delivering.

For new requirements, new approaches

The HTML + CSS pair was conceived in a time where there were no single-page app frameworks and bundlers like today, so we don’t need to feel any obligation of using CSS as a file or series of files called in a HTML template. In fact, today most “index” templates are just dumb callers to a JS bundled file that will basically do everything: the logic, the dynamic HTML that is to be generated on the fly and also the CSS, often pre-processed or even written directly in JS code (CSS-in-JS).

Does this semantic vs non-semantic discussion make sense at all in this new situation? I would say that yes, but partly. It is still a contrast that is important to know in order to envision what we want, but when the vanilla approach to CSS do not always make sense any more, we can take advantage of many possibilities that can be much more optimal, depending on the situation.

Rapid prototyping

Bulma or Bootstrap are still quite handy for those and, if you don’t mind learning that many classes, Tailwind can also be useful. Those, though, seem to me the ideal only for POCs and projects that need to be crafted fast, as they are limiters for designers and they may add a lot of unnecessary weight to a codebase. Also, if you find yourself spending too much time on customisation for those solutions, you may consider just move on to the following suggestions.

Smaller projects

For those, using scoped styles on components like in .vue files or imported style files to be bundled together later may allow for a semantic solution that brings readability while it can completely avoid name clashing. This may be ideal for when you have only a couple global CSS definitions and many local components (UI or functional components, it should not matter too much for smaller projects).

Bigger projects

The most important projects are usually structured behind frameworks and bundlers and, especially considering those tools, it may be wise to rely on CSS-in-JS libs like Emotion and JSS. They can be ideal for a bigger codebase as they borrow the whole power of JS variables to CSS, while being able to take their part in a whole orchestra of well optimised code. Not only that, but these libs can also be used for an approach that has both advantages of semantic and non-semantic, if used together with meaningful descriptive classes:

// ... in a separate file
const redText = 'color: red;'
const sidePadded = 'padding: 8px 0;'
// ... in the component file, after imports
const useStyles = createUseStyles({ myTitle: redText + sidePadded })
const MyTitle = () => {
const styles = useStyles()
return <h1 className={styles.myTitle}>...</h1>
}

If you don’t mind the silliness of the hypothetical JSS + React JSX app above, you may find that we are using non-semantic variables that gather on semantic CSS properties, for both readability and better standardisation according to the ways that the designers have chosen. Also, the generated code will be easier to inspect and global styles will be easier to change. Of course, this is meant to be used alongside global definitions.

Regardless of that, it is important to have global definitions written on top of the design requirements for the project. Standard shapes, colours, fonts and measurements that will serve for everything need to be centralised in a point that is easily accessible.

As a final note, there are quite a few talks about how CSS does everything well and does not need preprocessing of any kind, or how CSS variables are a must-use as they prevent you to repeat yourself (too much, at least), but those remarks miss the point to me. Despite edgy cases that you need to rely on things like Canvas, CSS is obviously something that works, and you can make it work in pretty much any way you want, even in a 1996-ish setup without bundling and structured projects at all.

Our goal here should not only to achieve something that works for the end user. Whether the way described above is followed or not, the goal should be one adjusted to what is important today: organisation. In a big, professional project, to be able to find one’s way in a codebase or in a generated template, to adjust things easily on the fly and make use of the power of JS to enhance the distribution of code are goals much more suited to how we work today.

P. S.: With all that in mind, I think that the pair CSS and HTML, after so many adaptations and evolutions, may be falling short on most of their current use cases. What would be of us in the web dev world if we had an alternative for this pairing? I plan to write about this further in the future, but for I will keep focused on what is feasible right now.

--

--

Marcos Sandrini
Geek Culture

Designer and front-end programmer with 20+ years of experience, also a keen observer of the world and its people