CSS Architecture: Goals and Best Practices
CSS is a simple language. Its basic purpose is to define selectors to target HTML elements and apply attributes to them, which is easy to grasp. When working on large and complex projects, though, some CSS features might be a little trickier to deal with.
On the DOM, elements also inherit properties from their parents. Additionally, it allows other selectors to override properties based on their order on the sheet or selector specificity. This can easily get out of control and chaotic if not well organized. This is where CSS architecture comes in handy.
A CSS architecture brings reasoning to CSS authoring. Imagine it as a collection of guidelines and best practices to help developers write code that’s predictable, maintainable, scalable and more reusable. The goals of good CSS architecture, in my opinion, should be similar to the goals of all good software development.
As it is stated, there are 4 main goals of a good CSS architecture, which are to make CSS:
- Predictable:
CSS that is predictable means that your rules will act as you expect them to. When you add or change a rule, it should not have an unintended effect on other sections of your site. This isn’t as critical on small sites that don’t change much, but it is on large sites - Reusable:
CSS rules should be abstract and decoupled enough that you can easily create new components from existing parts without recoding patterns and problems you’ve already solved. - Scalable:
If your site increases in size and complexity, you’ll probably need more developers to keep it running. CSS that is scalable can be handled by a single individual or a large engineering team with ease. - Maintainable:
When new components and features are implemented, modified, or rearranged on your page, existing CSS should not need to be refactored.
In an environment that many people disagree over even the basic things CSS authors should and shouldn’t do, it is not possible to make a common definition of CSS or what to “exactly” to do in order for us to achieve good architecture. Following the best practices outlined below, however, will ensure that your CSS is predictable, reusable, scalable, and maintainable.
BEST PRACTICES
1- Be intentional
The more complex a selector is, the more tightly it is bound to HTML. Using HTML tags and combinators keeps your HTML tidy, but it makes your CSS dirty.
All of the above examples make logical sense, right? If and only if this HTML were never going to change YES they do but, how realistic is it to assume the HTML will never change?
These examples are not reusable in any way. How does another component with a different HTML structure reuse those styles if the selector points to a specific location in the markup? Consider the first selector: what if a similar-looking component was required on a different page that wasn’t contained within the #main-search element? You’d have to recreate the entire style.
If the HTML needs to be modified, these selectors are also very unpredictable. If a developer replaced the <div> in the third example with the <section> tag, the entire rule will be broken.
Finally, since these selectors only work if the HTML stays stable, they’re not maintainable or scalable by definition.
The best way to make sure your selectors don’t style unwanted elements is to not give them the opportunity.
A selector like #main-search ul li ul li div could very easily leads unwanted elements as your HTML structure changes . A style like .subsearch, on the other hand, has no risk of being applied to an undesirable feature by mistake.
The best way to keep your CSS predictable is to apply classes directly to the elements you want to style.
2- Separate your concerns
Components should be able to style themselves and do so well, but they should not be in charge of their layout or positioning, nor should they make too many assumptions about how they’ll be positioned in relation to other components.
Lets deep dive into the rule set below! You could create a visual component that needs to be 10 pixels from the top left corner of a website section:
Then you’ll need to use this exact same part in a different location down the road. Since the above CSS isn’t reusable in various contexts, it won’t work.
The issue is that you’re doing too much of this single selector. Within the same rule, you’re determining the look, as well as the layout and position.
While this can seem to be harmless at first, it frequently leads to copying and pasting from developers who are less CSS-savvy. If a new team member wants something to look like a specific component, such as an .right-block, they’ll will start with that class. But, if that doesn’t work because it places the new block in an undesirable position, what will they do? They’ll probably only copy and paste the lines of code required for this specific instance into a new selector, duplicating code unnecessarily.
3- Namespace your classes
Almost every site has a visual feature that looks the same in every instance, with the exception of one. And when faced with this one-off situation almost every new CSS developer (and even experienced ones) handles it the same way. You find (or create) a special parent for this one specific occurrence, and you write a new rule to handle it.
The code above will appear to be fairly harmless at first glance, but let’s review it in light of the objectives set out above.
First, the widget in the example is not predictable. A developer who’s made several of these widgets will expect it to look a certain way, yet when she uses it in the sidebar or on the homepage, it will look different, despite the markup being exactly the same.
First, the widget in the example is not predictable. Even if the markup is identical, a developer who has created some of these widgets would expect it to look a certain way when the developer uses it in the sidebar or on the homepage.
Furthermore, it isn’t very scalable or reusable. What happens if you want it to look as it does on the homepage on another page? It would be necessary to add new rule set.
Lastly, it’s difficult to manage because if the widget is redesigned, it will need to be revised in many places in the CSS. So, it is not maintainable too.
A much better approach is applying namespaces to the classes themselves. If an element is a member of a visual component, every one of its sub-element classes should use the component’s base class name as a namespace.
Namespacing your classes keeps your components self-contained and modular. It reduces the specificity needed to style child elements and reduces the probability of a conflict with an existing class.
4- Extend components with modifier classes
Create a modifier class to expand an existing component when it needs to look slightly different in a specific context.
It has been already stated the downsides of modifying components based on one of their parent elements. but to restate: A modifier class can be used anywhere. Modifier classes can be used as many times as necessary. Finally, modifier classes show the developer’s purpose quite clearly in the HTML. Location-based classes, on the other hand, are totally invisible to a developer who is just looking at the HTML, raising the chances of it being missed.
5- Use CSS Methodology
By providing a class-based system for breaking up large web designs into several small, modular, discrete units, all CSS methodologies address the scalability and maintainability issue in CSS. Each UI module can be reused in a design and even ported from one project to the next if the CSS methodologies are the same.
CSS methodologies accomplish a lot more than just solving the CSS scalability problem. They make developing and iterating a design much simpler. They make it easier to read and understand front-end code, provide ready-made documentation, and enable multiple people to work on a design.
Most popular CSS methodologies are:
- OOCSS (Object Oriented Css)
- BEM (Block Element Modifier)
- SMACSS (Scalable and Modular Architecture for CSS)
- ITCSS (Inverted Scalable CSS)
- ATOMIC CSS
6- Use preprocessors
CSS preprocessors are scripting languages that extend CSS’s standard functionality. They allow us to use variables, nesting, inheritance, mixins, functions, and mathematical operations in our CSS code. CSS preprocessors make it simple to automate repetitive tasks, and code bloat and errors, build reusable code snippets, and ensure backward compatibility.
Each CSS preprocessor has its own syntax, which it compiles into standard CSS so that it can be rendered on the client side by browsers. Currently, Sass, LESS, and Stylus are the three most common and reliable CSS preprocessors( There are many smaller ones as well). CSS preprocessors all perform almost same things, but in different ways and with different syntaxes.
Let’s investigate main features and functionalities LESS provides us.
- Variables
- Mixins
Mixins are a way of including (“mixing in”) a bunch of properties from one rule-set into another rule-set.
The properties of the .bordered
class will now appear in both #menu a
and .post a
.
- Nesting
Less gives you the ability to use nesting instead of, or in combination with cascading.
- Nested At-Rules and Bubbling
At-rules such as @media
or @supports
can be nested in the same way as selectors. The at-rule is placed on top and relative order against other elements inside the same ruleset remains unchanged. This is called bubbling.
outputs:
- Operations
Arithmetical operations +
, -
, *
, /
can operate on any number, color or variable. If it is possible, mathematical operations take units into account and convert numbers before adding, subtracting or comparing them. The result has leftmost explicitly stated unit type. If the conversion is impossible or not meaningful, units are ignored. Example of impossible conversion: px to cm or rad to %.
Multiplication and division do not convert numbers. It would not be meaningful in most cases — a length multiplied by a length gives an area and css does not support specifying areas. Less will operate on numbers as they are and assign explicitly stated unit type to the result.
You can also do arithmetic on colors:
You can check Less’s color functions in more details here
- Escaping
Escaping allows you to use any arbitrary string as property or variable value.
results in:
- Functions
Less provides a variety of functions which transform colors, manipulate strings and do maths. They are documented fully in the function reference.
SUMMARY
CSS isn’t just for design. Just because you’re writing CSS doesn’t mean you should leave programming best practices. OOP, DRY, the open/closed principle, separation of concerns, and other concepts also apply to CSS.
Following best practices stated in this article will result in a reusable, scalable, and predictable CSS in your projects.