Separation, Abstraction, and Cascading in CSS

December 29, 2012

Summary: LESS and Sass (and similar solutions) have saved CSS for three reasons: separation, abstraction, and cascading. While I welcome them, CSS still has other problems which I believe can be solved. I propose some solutions.

Introduction

A lot is said about LESS and Sass, and for good reason. CSS is hell to get right and even harder to maintain. LESS and Sass (and similar tools) make CSS into a much more useful language.

But when people talk about why they are so great, they miss the main point. It is true that your style files are now shorter and more readable. However, there is something deeper going on than mere saving of keystrokes and being able to name things.

In this essay, I will try to put into words (and some pictures) what my intuition tells me as a developer and programming language enthusiast to clarify why CSS is innately unmaintainable, does not satisfy its own design goals, and why LESS and Sass make a bad language more bearable. I also will propose solutions which would raise the bar past the high level where LESS and Sass have taken it.

Zero Degrees of Separation

Way back when, people used HTML tables to style their pages. Documents looked like this:

HTML containing content + style

Those were the days of font tags and tables.

Then CSS came along, and people talked a lot about separation of content from presentation. CSS did help you move styling concerns outside of the HTML file, but that is about it.

HTML containing content and CSS containing style

Your styles were still tied to the structure of the document they were styling. They had no grouping of their own. If you wanted to repeat a style, you either had to copy and paste or use a selector with a comma. Both were bad solutions.

An even worse solution, which is, unfortunately the most common, is to build CSS classes which name a style. We see this in the numerous and all equally bad "CSS frameworks" which litter your HTML with style information. Grid systems do this to a fault.

But it is not the fault of the authors of those frameworks, nor of the poor web developers who are in search of some solutions to their problems. No, the blame lies with the authors of CSS itself. With CSS, separation of content from presentation is possible but extremely difficult and time-consuming.

HTML and CSS are separate but not equal. HTML can exist without CSS. But what is CSS without HTML? Nothing.

If you truly want to be able to separate content from presentation, you have to set them both on equal footing, like this:

HTML, CSS, plus some third language to tie them together

Content in the HTML, style in the CSS, and you tie them together in some third language.

This is possible in LESS (LESS being the question mark), evidenced by the existence of frameworks such as LESS Elements. People talk about LESS reducing boilerplate and repetition. Or about hiding browser-specific CSS properties. But that is not the essence of the matter. What all of that talk is trying to get at is that they can finally define a style, in the form of a mixin, which exists independently of any HTML structure. It can then be tied into zero, one, or more HTML elements merely by mentioning its name.

With LESS, I can define a mixin called Dorothy:

.dorothy() {
  background-color: green;
  text-color: yellow;
  border: 1px solid red;
}

Yes, it is probably an ugly style. But it is just a style. It does not depend on any HTML structure for its existence, not even one <p> tag. Now, if I want to use it, I can use it wherever I want by relating, in a separate way, the style with some HTML.

div.main {
  .dorothy;
}

blockquote {
  .dorothy;
}

This is one of the reasons LESS makes styling HTML bearable. In addition to mixins, you can also define variables which contain sizes and colors, which is just another way to name styles (or elements of styles) to be tied to HTML later.

HTML, LESS mixins, and LESS rules

Abstraction

If you take the idea of mixins and variables even further, you will notice that they compose. I can define a mixin and use it in another mixin. I could define dorothy as the composition of three styles, red-border, yellow-text, and green-background. This type of composition suggests that there is some amount of abstraction going on.

This was not possible in HTML + CSS.

Well, I say not possible, but there were ways, they were just terrible. You could copy-paste, which is just not a solution at all, but it would get you your style. Or you could reuse non-semantic class names like in grid frameworks (blech!). Or, finally, you could do what I call "inverted-style", where the styles take precedence and the selectors take a subordinate role. That will take some explaining.

Let us say we want div.main and blockquote to be styled like dorothy. Also, div.main and p should have a top margin.

Normally, we would write this in CSS:

div.main {
  background-color: green;
  text-color: yellow;
  border: 1px solid red;
  margin-top: 10px;
}

blockquote {
  background-color: green;
  text-color: yellow;
  border: 1px solid red;
}

p {
  margin-top: 10px;
}

This does not look bad, but there is a lot of repetition and the intent is not clear. We could instead write it in inverted-style.

/* Dorothy style */
div.main, blockquote {
  background-color: green;
  text-color: yellow;
  border: 1px solid red;
}

/* Top margin */
div.main, p {
  margin-top: 10px;
}

If we discover that div.footer also needs a top margin, we add it to the selector instead of making a new rule. I bet someone else has come up with this style (and probably a better name for it), but I am unaware of it. I also guess that this was one of the original intentions of the CSS authors. In practice, in my experience, this is hard to keep up. Somehow, I do not have the discipline to keep the styles separated into their own rules. CSS properties that are related to div.main and blockquote, but not to dorothy slip into that first rule, and then all is lost. Maybe a professional could do better, but I have never met one.

I do not have that problem with LESS. It is simple to define a mixin once I identify a consistent set of properties. I can then reuse it wherever I want.

Cascading

LESS and Sass provide a pretty good, but partial, solution to the cascading problem. The cascading problem is basically one of complexity. There are too many places for the value of a particular property for a particular element to be set. And the rules for determining the precedence of all of those places are too complex.

The value of a CSS property is determined by these factors:

It is just too many factors. Yes, the wisdom is to keep everything clean and simple. That works for small projects but at some point, cascading rules will bite you.

Jason Zimdars shows the solution to cascading they came up with at 37 Signals. He shares a good analysis of the problem and how LESS can alleviate some of the pain.

For the first time we could write CSS that we knew wouldn’t cause problems later on because they couldn’t cascade out of control. This opened us up to create elements with self contained styles that could be dropped onto most any page and they’d just work.

Sounds like the holy grail of separation of presentation from content!

By using nested LESS rules and child selectors, we can avoid much of the pain of cascading rules. Combining with everything else, we define our styles as mixins (and mixins of mixins) and tie the styles to the HTML with nested rules which mimic the structure of the HTML.

Perfect, final solution? Not quite.

Further

There are a few more issues to deal with. LESS and Sass were defined as supersets of CSS. That means that your valid CSS files are automatically LESS files as well, which means you can just start using the LESS compiler.

But it also means that LESS has inherited all of the problems it has no solution for. What I will suggest is that we need a subset of CSS to move further, and I will attempt to choose that subset. I would love to hear your suggestions, as well.

Yes, nested rules help you deal with cascading, but there are other issues with cascading. Mixins cannot really help you with the box model. No amount of variables and arithmetic can make two divs have the same width.

I will go through the problems one by one.

Cascading, again

Let me put it bluntly, cascading was a mistake on the part of the authors of CSS. It has a nice abstract purity to it, but it does not work well in practice.

With hindsight, we see that we really only want one level of cascading. The CSS reset was a beautiful invention which neutralized differences between browsers. The CSS reset cut off cascading from the default browser styles and gave you a fresh base to start with. That is really all of the cascading that you want: cascading to a sane default. Other than that, it turns into a mess of spaghetti.

Sometimes it seems that you want some cascading. For instance, you want to set the font family of the entire document. So you declare body { font-family: 'Comic Sans'; } and call your job done. In such a declaration, you are implicitly relying on the inheritance of the font-family property down through the document tree. In fact, if you want every element to have a certain font, you should just say it: * { font-family: 'Comic Sans'; } This has the same effect as a CSS reset: set the default styles for everything in one place.

This implies a rule: Reset once, then avoid cascading. We now just have to systematically apply it. Here is what our setup looks like now:

HTML, reset, mixins, and style

No cascading means we must restrict ourselves to never select the same elements with different rules. I cannot say how we can do this strictly. But we can define some guidelines.

  1. Only bare (classless + non-nested) selectors may occur in the reset.
  2. No bare selectors may occur in the LESS rules.
  3. No selector may be repeated in the LESS rules.

These guidelines will limit the amount of cascading even further when combined with Zimdars' solution.

Common mistakes

I call them mistakes for lack of a better word, but really the blame lies on CSS.

Box model

The box model sucks. But we can avoid some of the easy errors to make.

One mistake is what happens when you define the left-margin but not the right-margin. In such a situation, where does the right-margin get determined? Cascading.

And what happens when I set the width to 100%? What if a padding is cascaded in? Oops.

How to deal with this? Do not use individual CSS properties where a compound property exists.

I propose to boycott the following properties:

To get more sane behavior, we define this mixin:

.dimension(@w,@h) {
  width       : @w;
  height      : @h;

  margin      : 0;
  padding     : 0;
  border-width: 0;
}

This forces you to set width and height at once, and it resets the margin, padding, and border (which affect actual width, thanks to the box model). You can still override them, you just have to do it explicitly. This does not solve the entire problem of the box model, but it helps cut out a lot of surprising behavior.

My argument for using this mixin is that any time you are setting the dimensions of an element, you should also be explicit about the margin, padding, and border at that point, since they affect the box model.

Font color

Now I will pick some nits.

How many times have you seen this code?

body {
  color: black;
  a:link    {color: blue;   }
  a:hover   {color: red;    }
  a:active  {color: blue;   }
  a:visited {color: purple; }
}

Too much! And I always forget one of them. Time for a mixin.

.font-color(@f,@a:blue,@h:red,@c:blue,@v:purple) {
  color: @f;
  a:link    {color: @a};
  a:hover   {color: @h};
  a:active  {color: @c};
  a:visited {color: @v};
}

Again, the pattern is clear: what you do not set explicitly gets reset to a default.

Conclusion

Separating style from content was never fully achieved with CSS. LESS (and Sass) finally allowed the separation to occur. And, using LESS, we can begin to round off the sharp edges of CSS. But instead of adopting a superset of CSS, we should be looking to subset CSS and replace problematic CSS properties with mixins. The subset could be enforced with a linter.

These recommendations are a good start, but there is still a long way to go.

Post script

There is one final reflection into CSS cascading that I wanted to mention but could not find a place for it above, mainly because it is not a problem so much as an inconvenience. I have often wondered why in CSS, element styles (styles defined in the style attribute of an HTML tag) take precedence over all other styles. Similarly, why do styles defined in the HTML (in a style tag) take precedence over those that are linked to externally? It has always made more sense to me that it should be the exact opposite. An HTML page could define default styles for its elements, which would be carried in the page, and overriden with an external stylesheet.

However, the actual rules dictate that I must edit the HTML file if I want to change the style of an element with an element style. In this not the exact opposite of the intention of CSS?

For more inspiration, history, interviews, and trends of interest to Clojure programmers, get the free Clojure Gazette.

Learn More

Clojure pulls in ideas from many different languages and paradigms, and also from the broader world, including music and philosophy. The Clojure Gazette shares that vision and weaves a rich tapestry of ideas from the daily flow of library releases to the deep historical roots of computer science.

You might also like

CSS has Weak Forms of Abstraction and Combination

February 08, 2014

Summary: According to the requirements proposed by Abelson and Sussman, CSS does not provide adequate means of combination and abstraction to be considered a powerful language.

I am trying to improve the maintainability and reusability of my CSS over the longterm. I've written about how to organize CSS before. I've learned a lot since I wrote that. I've tried lots of things and talked to lots of people, I finally seem to have found a conceptual framework to capture my new understanding. I'm trying to explore it here. Comments are welcome.

I'm going to take a cue from the first page of SICP and analyze CSS as a language.

Abelson and Sussman in SICP 1.1 (italics mine):

A powerful programming language is more than just a means for instructing a computer to perform tasks. The language also serves as a framework within which we organize our ideas about processes. Thus, when we describe a language, we should pay particular attention to the means that the language provides for combining simple ideas to form more complex ideas. Every powerful language has three mechanisms for accomplishing this:

primitive expressions, which represent the simplest entities the language is concerned with,

means of combination, by which compound elements are built from simpler ones, and

means of abstraction, by which compound elements can be named and manipulated as units.

Let's analyze CSS in terms of these three mechanisms.

Primitive Expressions

The simplest entities the language is concerned with are properties and primitive selectors. CSS properties, though they have a property name and property value part, are meaningless if split up. Primitive selectors include element name selectors (body, a, div), class name selectors (.main-wrapper), id selectors (#login-form), and pseudo-class selectors (:hover), among others. Properties appear inside the rule body ({}), while selectors appear before the rule body. The two are semantically and syntactically separated.

Means of Combination

Properties can be combined in two ways. First, multiple properties can be put inside the same rule body. This is the most obvious and most readable form of property combination. The second form is harder to reason about. It occurs automatically within the browser during rendering. That form of combination, involving the application of multiple rule bodies to the same HTML element, uses a complex ordering of properties from all bits of CSS and element styles on the page.

Tomes have been written about how difficult it is to reason about this automatic form of combination. Usually, the answer is limiting it (or avoiding it altogether) through programmer discipline, with varying degrees of success.

Primitive selectors can be combined in several ways. Without spaces between them, multiple selectors will intersect, meaning they target elements more specifically. div.main-container will target div elements that ALSO have the class main-container.

With spaces, multiple selectors indicate nesting. div .main-container matches any element of class main-container within any div. There are several operators which combine them in different ways (> indicates direct nesting, etc.). Nested selectors are associated with CSS that is strongly coupled with the structure of the HTML it is styling and therefore less reusable.

Selectors that are combined with commas create a group. These compound selectors will match any element that matches at least one of the component selectors. header, .header will match all header elements and all elements with class header.

There are more types of selector combintation operators, but they are more specialized and less frequently used.

The locus of combination, for both properties and selectors, is the rule. The rule has one compound selector and zero or more properties. Rules with zero properties have no effect.

Means of Abstraction

The means of abstraction in CSS are quite limited. There is no way to name anything. People lament the lack of named values (often refered to as variables) or named styles (sometimes called mixins). Naming is out in CSS.

The only means of abstraction is the class and id, which are labels that can be applied to HTML elements. With an id or class (or combinations), you can target precisely the elements you need to and achieve some reuse. For instance, I can "reuse" the #login-form id selector in two different rules. I can also add the class rounded-corner to two different HTML elements, effectively "reusing" the same rule twice. By a very disciplined use of class selectors by combining them with commas, one can apply "rule bodies" as a unit in a very limited way, though it is impracticable in practice.

The disadvantage to this technique of using id and class selectors is that the HTML must be modified when styles change, defeating the purpose of using CSS for content/style separation. There is a lot of discussion about using semantically named classes. For instance, call the button login-button instead of green-shiny-button. This is thought to be more robust in the face of style changes, but requires existing CSS to be thrown away in order for the page to be redesigned. CSS offers no good way to modify HTML and CSS independently.

Conclusion

CSS does not meet the criteria for a "powerful language" as used in SICP. This is no surprise. The reasonable means of combination are limited to the rule. The means of abstraction are almost non-existent. There is no way to name anything. And the other form of abstraction (ids and classes) provides no way of reusing both the HTML and the CSS. It is obvious why CSS is typically unmaintainable. With the current crop of compile-to-CSS languages (commonly known as "CSS Preprocessors"), there is hope that better means of abstraction are possible. How will compile-to-CSS languages fare in this same analysis?

For more inspiration, history, interviews, and trends of interest to Clojure programmers, get the free Clojure Gazette.

Learn More

Clojure pulls in ideas from many different languages and paradigms, and also from the broader world, including music and philosophy. The Clojure Gazette shares that vision and weaves a rich tapestry of ideas from the daily flow of library releases to the deep historical roots of computer science.

You might also like

LESS has Better Forms of Abstraction than CSS

February 12, 2014

Summary: LESS has obviously better forms of abstraction and combination than CSS. It has recursive style definitions, which is enough to consider it a "powerful language".

Ok, it's obvious that CSS has weak forms of combination and abstraction. But now we have a good framework for understanding why. "CSS Preprocessors", as they are called, are getting really popular now. We would be smart to analyze LESS in the same way that we analyzed CSS, if only to temper the glamor of trendiness that surrounds it. Comments are welcome.

Because LESS aims to be a superset of CSS, it has all of the primitive expressions, means of combination, and means of abstraction that come baked into CSS. I already went over those last time, so I will not go over them again. So what things are added by LESS?

Primitive Expressions

Besides existing CSS properties, LESS adds two new primitive expressions. Mixin application (.rounded-corners(10px);) in a rule recursively applies the primitive expressions defined in the body of the mixin to the current rule. Mixin applications can be parameterized with value expressions, or they can have no parameters. Extension (&:extend(.blue-button);) is similar, but instead of applying the primitive expressions to a rule body, it adds the selector of the rule to the rule selector of the extension. Extension is recursive as well.

Variables and mathematical expressions change the way primitive properties work. In CSS, primitive properties were comprised of a property name and a literal property value. In LESS, variables and math expressions, as well as literal values, can be in the value place (right hand side) of a property.

Variables can also be used in selectors.

Means of Combination

The principle means of combination are still the rule, but add to it the ability to nest rules, and things are more interesting. Nesting two rules is shorthand for writing out two rules (unnested) with a nested selector. While in the simple case it is simply a shorthand, when nested rules are applied as mixins, you gain a lot more than better syntax. Mixins with nested subrules allows you to name a nesting and refer to it later.

Means of Abstraction

CSS did not contain much in the way of abstraction. LESS focuses primarily in the realm of abstraction, probably to appease the will to power of front-end designers. Variables allow property values to be named, and naming is a form of abstraction. Variables are a good way to name values that all have the same meaning and would therefore change at the same time. For instance, a shade of green that is used throughout the styles is a perfect use for variables. Variables can be used in a similar way to name selectors.

A more powerful form of abstraction comes from the ability to define mixins, apply mixins, and use of :extend(). In LESS, any rule using a single class or id selector can be used as a mixin. This is essentially a way to name a rule--our principle form of combination. In addition, if you put empty parentheses after the class selector in the rule, the rule is not outputted into the generated CSS, which can save bytes. Mixins can also have parameters (scoped variables), so they can be abstracted over a variety of values. Extend allows a similar kind of abstraction which promises to be more efficient.

Mixins are very powerful. In fact, this is the kind of abstraction that is needed for LESS to be powerful, as defined by Abelson and Sussman. Because you can now name a group of styles (mixin) and then use that name in another group of styles (mixin application), LESS has full-on recursive style definitions. With extension, it also has recursive selector definitions. In LESS, we can talk of "levels of abstraction" whereas in CSS there was only one.

Conclusion

LESS has recursion. It lets you define and name groups of properties, then refer to those groups by name in other groups of properties. We can consider LESS powerful enough to express useful abstractions. Yet though it is more powerful than CSS, it still has many of the problems of CSS (especially complex rules governing the combination of multiple rules to a single element). How can LESS be leveraged to gain its power but tame its weakness? Is there a subset of LESS that can gerrymander the good parts away from the bad parts?

For more inspiration, history, interviews, and trends of interest to Clojure programmers, get the free Clojure Gazette.

Learn More

Clojure pulls in ideas from many different languages and paradigms, and also from the broader world, including music and philosophy. The Clojure Gazette shares that vision and weaves a rich tapestry of ideas from the daily flow of library releases to the deep historical roots of computer science.

You might also like

Pre-West Prep: Priyatam Mudivarti

April 04, 2015

Talk: Responsive Grids with Garden & Clojurescript

Priyatam Mudivarti's talk at Clojure/West is about creating responsive grids with Garden.

Background

Most web shops use a so-called CSS Preprocessor, which means a language that transpiles to CSS. These preprocessors typically add variables, rule nesting, and a few more features to fill out the holes in CSS.

Garden is a Clojure library which converts well-formed Clojure data structures into CSS. What this means is that your Clojure (and ClojureScript) code can generate the CSS dynamically, with the full richness of Clojure behind it. Variables come free, because Clojure has variables. But Clojure also has lambdas, which is not possible in any preprocessor I've ever seen.

At the same time, making a responsive grid is a huge undertaking. Lots of projects promise to do the hard work for you. If it could be done easily using a more powerful language, we would not have to rely so much on these frameworks. Priyatam Mudivarti is going to talk about how to make responsive grids using several techniques. He has created a nice repo containing an exploration of several types of grids.

About Priyatam Mudivarti

Homepage - GitHub - Twitter


This post is one of a series called Pre-West Prep, which is also published by email. It's all about getting ready for the upcoming Clojure/West, organized by Cognitect. Conferences are ongoing conversations and explorations. Speakers discuss trends, best practices, and the future by drawing on the rich context built up in past conferences and other media.

That rich context is what Pre-West Prep is about. I want to enhance everyone's experience at the conference by surfacing that context. With just a little homework, we can be better prepared to understand and enjoy the talks and the hallway conversations.

Clojure/West is a conference organized and hosted by Cognitect. This information is in no way official. It is not sponsored by nor affiliated with Clojure/West or Cognitect. It is simply me (and helpers) curating and organizing public information about the conference.

You might also like

Separation of Presentation and Content

April 17, 2014

Summary: One reason to separate style from content is to reuse HTML or CSS. Ultimately, we would like a solution where we can reuse both.

Reusable Content

There is an economic reason to separate presentation from content. Publishers have thousands of pages of HTML on their site, yet they want to enhance the style of their pages over time. It would cost a lot of money to change every single page to match their new style. So they invest a little more time writing each page so that the HTML markup does not refer to styles but to the semantics of the content (referred to as semantic HTML). Then they hire a designer to write CSS to make their existing content look new. The HTML is permanent and reusable, and the CSS is temporary and not-reusable. The separation is only one way: the HTML doesn't know the CSS, but the CSS does know the HTML.

Examples: CSS Zen Garden, newspaper websites, blogs

Characteristics: Semantic markup, CSS tailored to classes/structure of HTML

Reusable Styles

Yet another economic reason is a relatively newer phenomenon. It has become very easy to create a new web site/application. Writing (or generating) lots of HTML is cheap, and it changes often during iterative development. What is relatively expensive is to design each of those pages each time the pages change. CSS is not good at adapting to page structure changes. So people have built CSS frameworks where the CSS is (relatively) permanent and the HTML is temporary. In these cases, the HTML knows the CSS, but the CSS doesn't know the HTML. The separation is again one way--this time the other way.

Examples: Open Source CSS, Bootstrap, Foundation, Pure

Characteristics: HTML tailored to classes/structure of CSS, Reusable CSS

Reusable Content and Styles

What if a newspaper site, with millions of existing HTML pages, could cheaply take advantage of the reusable styles of frameworks like Bootstrap? That is the Holy Grail of separation of concerns. What would be required to do that?

What we really want is a two-way separation. We want HTML written in total isolation and CSS written in total isolation. We want permanent HTML and permanent CSS. How can the style and content, each developed separately, finally be brought together? The answer is simple: a third document to relate the two.

We have already seen that CSS is not good at abstraction. CSS cannot name a style to use it later. However, LESS does have powerful forms of abstraction. LESS has the ability to define reusable styles and apply them to HTML that did not have those styles in mind. If you put the definition of reusable styles in one document and the application of those styles in another document, you achieve true separation. And it is already happening a little bit. You can do it in your own code.

It is a bit like a software library. We put the reusable bits in the library, and their specific use in the app.

Examples: Compass, Semantic Grid System

Characteristics: Semantic markup, Reuseable Styles, Tie-in document to relate Style to Content

Conclusion

CSS preprocessors, which began as convenience tools, are actually powerful enough to solve fundamental problems with HTML and CSS. While it is still early, LESS and other CSS preprocessors, if harnessed correctly, could dramatically transform how we build and design web sites. Typography, grids and layout, and other design concerns can be used as plugable libraries. And other languages that are specifically designed to do that may emerge. What would a systematic, analytical approach to such an approach look like?

For more inspiration, history, interviews, and trends of interest to Clojure programmers, get the free Clojure Gazette.

Learn More

Clojure pulls in ideas from many different languages and paradigms, and also from the broader world, including music and philosophy. The Clojure Gazette shares that vision and weaves a rich tapestry of ideas from the daily flow of library releases to the deep historical roots of computer science.

You might also like