Search the site:

Copyright 2010 - 2023 @ DevriX - All rights reserved.

Tips for CSS Structure for Business Websites (or Any for That Matter)

CSS and HTML are simple to understand. However it takes years of practice to learn about the best architectural approaches when building websites (and apps) in a way that makes them reusable, maintainable in the future, and keeps developers happy.

What is meant by architecture here? It’s the structure of the CSS code. The way you separate it into files, the rules behind class names, the depth of selectors, the way it cascades, what is inherited, how you set up components, pages, elements, and modifiers.

To apply best practices to all these website components with hundreds of pages, various types of content, screen views, edge cases, and with the consideration of adding more on top and modifying existing ones is the hard part.

Build With Components, Not Pages

This is one of the major parts to consider. You shouldn’t style based on the page you are on. Don’t do a .homepage … {} styles. If your page has a section, style the section. With that, you can reuse it on other pages too. If you have a button, style the button like .button {} and reuse it elsewhere. It is valid for all views.

This is the most common advice and the best performing approach (so far) that you can utilize.

Style with components in mind

Now, how do you manage page-specific differences? Because this is the most common reason to style per-page? Well, there are a few approaches:

Use modifiers.

In “BEM”, the “M” stands for Modifier. This is the .block__child–modifier look. Even if you don’t use BEM, modifiers exist anyways. If there is a variation to a component or a section, add a modifier for it.

Ideally, the designer should be thoughtful and keep variations to a minimum to keep the code clean, but you shouldn’t be afraid to add more to it. Variations should ideally just overwrite a few properties and should work with the same markup. It’s a good way to approach components in the HTML phase – add the tags you will need and keep them consistent across the site. Don’t add new ones because of a modifier class.

Block element modifier

Style children’s components.

The other approach is to style based on context. A button is always a button, it has its .button class and everything, but you can still adjust it if it’s part of another component. This is generally not a good idea as it creates inconsistencies, but it’s also quite a realistic use case. Otherwise, you would end up with 20 modifiers with weird names.

Context-wise styles are when you style one component only if it’s a child of another. Let’s take an article card for example. It has its styles by default. But if it’s part of a colorful section with some text to the side, the design requires that the card is to have a few other elements around it (like animated shapes etc).

In that case, you style with the .parent .card {} selector. You will only have to overwrite a few properties like you would do with a modifier. When you do that, the card itself doesn’t add more complexity to it’s styles, but it will still behave properly on that specific edge case.

When you think about this, you can also see how it can be applied on a “per page” basis. If there are some weird edge cases in the design and there are some minor differences from the standard component views (and the way they all interact together), then you can style just that with the .homepage {} selector. Just keep in mind to use that sparingly. In our experience, such styles rarely exceed a few lines of code.

Important note to add: Context-wise styles are NOT a good practice in general. Ideally you shouldn’t even need that. Most of the time, you would have modifiers that should do the job well enough. Even though it’s realistic in some builds, diving too deep into good abstracted code with strict rules can be too expensive.

Work in Sections

Most business websites (as well as many others for that matter) separate content into sections. Each section is a component on its own with a modifier class that defines the various properties. One suggestion to go with the structure of the classes would be:

Separate landing pages in sections

  • section.section-container – this could be the “component name” if you want, which holds the consistent paddings/margins or whatever is needed.
  • section.section-border-top – is a modifier. This doesn’t use BEM, but you can “translate” it to section-container–border if needed.
  • section.section-welcome – would be the name of the section.

The naming convention again is irrelevant here. With such sections, you would gain the freedom to adjust the styles to reusable components in edge cases created by the design (be it due to inconsistencies that have to be followed or just more complicated views).

Files separation

Most likely you would use Sass or another similar preprocessor. In terms of files separation, there are many approaches, but the one we take follows the following general structure:

  • General – General usually consists of setup code like making a grid work, styles to HTML tags, resets/normalizer, some CMS-specific styles and the likes.
  • Pages – The page styles as explained above. Ideally, you should keep very little code here.
  • Components – The core of the build – the various components reside here. One tip is that you can have “elements” or “misc” that would fit smaller chunks of components into one file instead of 80 files. Bigger ones of course ideally should go in separate files.
  • Layout – Global styles, for example, on the Header, Footer and then page layouts, modifiers to your grid and so on.
  • Plugins – Anything external that is generated from a plugin, extension or whatever. It’s nice to separate them as you can then reuse them in other projects.

Keep Selectors Clean

A good sign of clean code is how simple it looks. No weird properties, everything has its purpose, there is low indentation. “Looking smart” selectors when unnecessary are not making your code “cool”. If you can substitute something like #container > .row div[rel=”something”] with .rel-something (imagine that is a meaningful class name), then you should just update the markup a bit. The goal of this is to keep everything simpler.

Keep indentations low. You rarely need to go more than three levels. Let’s see for example an .entry class:

.entry { ... }
.entry-title { ... }

See that there is no need really to indent .entry-title inside .entry. Later, when you add a modifier down the file you can have .entry-modifier {} and .entry-modifier .entry-title {}

With this approach, overwriting styles in the future is way easier. Let’s take a look at another common example: You have the markup of > ul.list-menu > li.list-items*5>a (emmet)

Now, to style, all you need is:

.site-nav {} - component 1

.list-menu {} - component 2
.list-menu .list-item {}
.list-menu a {}

If you had more components inside, like additional dropdowns, you can nest them directly inside .list-menu. You don’t have to write .site-nav .list-menu .list-item .dropdown{} (4 levels deep) when you can have two levels of .list-menu .dropdown {}

Write More Abstract Class Names for Modifiers

This one goes for maintainability. Ancommon example you would find in similar posts would be not to set a color variable to $red, you would instead set it to $primary or $secondary.

The reason is that when a change is needed down the line, the variable $red would output blue. It makes way more sense that you want to change your primary color, not your red color, right?

Same would go for other types of colors and properties. Let’s say you have lines separating some content (like <hr> tag). You name that .line-dash because it’s dashed. Makes perfect sense. But then a change comes and it has to be dotted. Do you go and rename it to .line-dotted? This is not a modifier, this is the component. Instead of this, you could name it as .line-separator. And then if you want to be specific, you can add a modifier for .dotted or .dashed. This type of naming is often what takes the most time when building a site.

In Summary

There are countless good and bad practices. One way to achieve better results is to define rules and follow them. It’s hard to come up with such rules, so one good suggestion would be to browse around the web and try to gather all the information possible on architecture such as naming conventions, good practices, how to write maintainable code and more.

To produce good code takes a lot of time and hundreds of thousands of lines of code. While doing all that, always ask yourself “Would that scale?”, “Can I reuse it?”, “Did I overwrite too much?”, “Does it make sense to name it like this?”. The more you do that, the more optimal your decisions will become and the more your work speed will improve.

An investment in good fundamentals will result in less back and forth on your projects and any future changes that need to happen will be way easier to implement.