Skip to content

Instantly share code, notes, and snippets.

@jayperryworks
Last active August 29, 2015 14:23
Show Gist options
  • Save jayperryworks/5e683af78e98cc744336 to your computer and use it in GitHub Desktop.
Save jayperryworks/5e683af78e98cc744336 to your computer and use it in GitHub Desktop.

Bivee CSS Framework

CSS is quite possibly the best visual design tool ever invented. Its ability to control an interface’s layout, type, color, and behavior “unobtrusively” — changing the way the interface is presented without disturbing the content — is, as far as I know, unparalleled in this industry or any other. CSS is the best tool for executing website design, but it is quickly finding broader uses (native apps, vector graphics, electronic books, and even print design, among others). It uses a syntax so simple you can learn its basics in a few days, but you can spend a lifetime exploring its nuances. Much of its power comes from the way it teaches you to think about how interface and content work together. A few months’ writing CSS and you’ll find yourself seeing your work in a different way.

The language, however, is in an interesting place these days. The web — and interactive design in general — has come a long way from where it started in the early 1990s. Where web pages were originally seen as documents, we now tend to think in terms of platforms and applications. We expect rich experiences that work uniformly across many devices and types of screens (and screen readers). In turn our approach has shifted toward the architectural and the modular. We don’t make “pages” anymore, we strive to build systems of integrated components. Complexity has increased frighteningly fast, and we struggle to manage it all via package managers, workflows, pre-compilers, and post-processors.

CSS has not only kept pace, it has become more relevant than ever. But a few shortcomings remain from simpler days, and developers are hunting for workarounds. Ironically, the major issues we’re seeing are unforeseen consequences of CSS’s great innovation: the cascade.

Specificity, the cascade, and the unexpected

CSS is a declarative language, structured around selectors:

button { background-color: red; }

The above selects all button elements and sets their background color to red. Straightforward.

But what happens if you have conflicting selectors?

button { background-color: red; } 
button { background-color: blue; }

In a case like this, CSS makes an executive decision based on a series of rules called the “cascade.” That is, some selectors are allowed to override others. In this case, the second line would win, because it comes later in the stylesheet.

Another factor in the cascade is specificity, or how “specific” a particular selector is considered to be. For instance button is less specific as a selector than header > button. In the first case, we’re selecting all buttons on the entire website. In the second, we’re selecting only buttons inside the header element. So:

header > button { background-color: red; }
button { background-color: blue; }

In this case, you would see red buttons throughout your site, except in the header, where they would be blue. header > button, therefore, is able to override button due to its greater specificity, even though it appears earlier in the source.

The actual list of rules governing the cascade and specificity is very long and provisions for a staggering array of conflict cases. It looks like utter overkill. The result, however, is that there’s essentially no situation in which CSS isn’t able to make a decision and keep going. In that sense, it’s an extremely forgiving language. Many languages, like XML, employ a “draconian” approach to errors and conflicts: if the interpreter encounters anything out-of-sorts, it simply stops. CSS, on the other hand, keeps going at almost any cost. Conflict? Use the cascade and render the ‘winning’ rule. Invalid property? Ignore it. Unsupported element or selector? Ignore it. The mantra is keep going, keep going.

Specificity and the forgiving nature of the cascade can get out of hand, however: html body header.global#newsHeader button:not(:disabled) would be an example of an overqualified selector. That is, it’s incredibly (and needlessly) specific and, therefore, also very hard to override. On a project of any scale this can quickly get out of control, with coders being forced to used more and more specific selectors to get their overrides to appear. This is called specificity creep and it can be a huge problem on any project where more than one person is writing code.

The other problem, thanks to CSS’s unrelenting attempts to resolve conflicts and keep working, is that surprising behaviors sometimes emerge. You might add a selector to your stylesheet with an (entirely reasonable) expectation that one thing will happen, only to see a completely different result, thanks to some other selector, lurking somewhere in the darkest depths of your code, overriding the one you just wrote. On a large project with thousands of lines of CSS, searching through stylesheets to troubleshoot specificity issues can be a nightmare.

Both situations tend to boil down to problems of scope: two things that CSS currently has no mechanisms to support. Every CSS selector has “global” scope: it applies to the entirety of any HTML it’s linked to. Therefore, any one CSS selector is eligible to be overridden by any other.

A short history of the great OOPs

Not surprisingly, coders have struggled to manage conflicting instructions before. This was, in fact, an issue as early as the 1950s when some very smart people at the MIT devised the notion of writing logic with “objects.” This eventually evolved into a collection of ideas called Object Oriented Programming, or OOP.

It’s a complex set of principles that I can’t explain thoroughly here, but if you’ve ever worked in Lisp, C, Java, Python, or Ruby, chances are you’ve at least brushed against it, or perhaps have come to love/hate the whole deal.

Essentially, OOP says that your program should consist of lots of self-contained parts which are all a little bit stupid (or at least rather narcissistic). That is, each part, or “object,” is designed to be lost in self-absorption and, therefore, ignorant of the workings of the larger program around it. Objects are inherently private, but they can allow other objects to learn about their properties by explicitly designating certain ones as “public.”

The classic example you might have seen before: imagine a space battle video game. It has spaceships that shoot at each other, and also some planets, etc. Each spaceship in this game will likely share certain traits even while they seem to act independently. So, we’d create a “Ship” class which defines those basic traits all ships share: color, speed, shields, damage, ammunition, fuel, etc. As we create “ship” objects (that is, individual instances of our overall Ship class), each will inherit some default values: a full tank of fuel, 600 laser shots, etc.

Alright: it’s a battle game. Say the people living on Mars have got some beef with the people of Europa (Jupiter’s moon). So we’ll have 2 kinds of ships: Martian and, er, European. Both kinds are “Ships” (which have speed, shields, damage, etc.) but each will subclass — or become a “child” object — of the parent Ship class. The child Ship objects will therefore modify the default properties. Martian ships, for instance, will naturally be red, and maybe they have better shields. European ships will, naturally, be unreliable but quite fast.

We need a place for the battle to occur, so we’ll also set up Solar System and Planet classes (which have their own properties, like mass, gravity, atmosphere, etc.). Let’s also make some Starbase objects, to mobilize our Ship fleets.

What we’ve got now is a system of objects which are able to interact in specific ways, but each object — being the navel-gazer it is — will know as little as possible about the rest of the game. Each Ship knows nothing about the properties of other Ships, the Planets or the Solar System. It only knows its own speed, fuel, damage, etc.

When ships interact, they remain self-absorbed. If a Martian ship shoots a European ship, each records the effects of the interaction only on its own properties. The European ship registers the hit: its damage property goes from “1” to “3”, it’s shields go from “90%” to “50%”, and its speed decreases. The Martian ship, meanwhile, realizes that it has less ammunition.

However, as we “zoom out” from small- to large-scale in the game, we find the more complex objects have wider fields of view. A Martian Starbase knows that it started the game with 100 ships and now has 83. Mars itself, a Planet object, knows that it has 5 Starbases and that 22 ships have crashed on its surface. The Solar System, finally, knows that it has 8 Planets, each with different gravity and mass properties affecting their orbits.

Why build a game this way? By following basic OOP principles of encapsulation, polymorphism, inheritance, and abstraction, we’ve organized our code in such a way that our game will be vastly easier to manage. It will also be easier to expand it later if, say, we want to add another kind of Ship to the battle.

Encapsulation refers to the narcissism of objects that I described above. An object is encapsulated such that it doesn’t know anything outside of itself. It is, likewise, able to protect the privacy of its internal properties and prevent other objects from modifying them. This object can expose properties that other objects may see or modify by explicitly designating them as “public” (or, depending on the language/framework, any of several other privacy levels like “protected” or “internal”). The benefit of this is that it forces objects within your program to be loosely coupled: that is, they interact only via designated public channels and are otherwise ignorant of one another’s inner state. These public channels are called an object’s “interface.”

If the various parts of your codebase are loosely coupled, it becomes much easier to modify one object without breaking the functional logic of another. The explicit privacy designations create a documented link between your objects. If a function is marked public, you know that it is part of this object’s interface and that other objects may be calling it (and that, for instance, changing its name will have consequences elsewhere).

Abstraction is, in a sense, a broad use of encapsulation. When you abstract code, you essentially make it ignorant of its original context. This allows code to be reused (and extended) in different contexts more easily. Abstracted code is, by its nature, loosely coupled from its specific applications. For instance, our initial Ship class is essentially an abstraction of the Marian and European ship objects that we used in the game. Each type of ship draws from a common “toolbox” that the Ship class defines (i.e. the default properties of speed, fuel, ammunition, etc.). Changing the Ship class, likewise, will change the behavior of both Martian and European ships in the game. Abstraction often helps with avoiding redundancy in your code, often called DRY (Don’t Repeat Yourself).

Inheritance is another DRY technique: one object can subclass another and, in doing so, “inherit” the first object’s properties. The Martian ship, for instance, inherits from the Ship class. It gets all of Ship’s properties (speed, color, etc.), and then adds it’s own specific modifications (stronger shields, red color). Perhaps, in addition, the Martian ship has both the default laser gun and a Photon torpedo launcher.

The benefits of an inheritance mechanism is that your program can easily be extended and modified without needing to “hack the source,” or change your original program logic. If we wanted to add a third type of ship, say a Venusian one, we could do so easily, without needing to modify either the Ship class or the existing Martian and European ship types. Further, what if we wanted to create 2 types of Martian ships? We could simply subclass the Martian Ship object into, say, Bomber and Fighter.

This works in reverse, too. If we needed to add a radar feature to all the ships in the game, we could add this to our parent Ship class, and all the child ships would inherit the new capability.

Finally, polymorphism can be seen as the net result — and goal — of abstraction, encapsulation, and inheritance. A polymorphic object is able to do many different things with the same underlying interface. We used our Ship class, for instance, to create several different kinds of ships without needing to change any of Ship’s own code. Another example is a generic Shape class that could be used to create Circle, Square, and Triangle objects. An object, by being ignorant of its context or surroundings, is able to be reused and repurposed easily.

Organizing code in this particular way will hopefully result in a codebase that is easier to maintain, easier to grow, and easier to understand.

“Objects” versus selectors

All of these object-oriented ideas are excellent, proven ways to manage scope and complexity in program logic. And they sound awfully nice to any front-end engineer or designer who’s ever done battle with a mountain of overqualified CSS selectors. The problem, of course, is that CSS is not object-oriented. It doesn’t even have logic. It’s strictly a declarative language.

Yet, more and more over the past few years, the vocabulary of OOP keeps cropping up in discussions of CSS. A number of systems are appearing under the banners of “Object-Oriented CSS” or “Modular CSS.” The acronyms proliferate: OOCSS, SMACSS, BEM, MVCSS. The soup thickens when the pre- and post-processors enter the fray: SASS, LESS, Compass, Gulp, Grunt, Bower, Eyeglass.

What’s going on here is part of a cycle that is endemic to web standards, and perhaps to open-source development in general. First, a specification like CSS 3 is developed and refined and argued into existence over several (or more) years. Then it’s (often slowly) implemented by browser and device companies. Web developers begin to use the spec in their projects (border-radius, anyone?) and a set of best practices and conventions gradually emerge. The web itself continues to evolve alongside the spec: new requirements and use cases inevitably emerge. A lot of “can CSS be made to do XYZ?” conversations percolate around trade blogs, Stack Overflow, Github, Twitter, etc. A lot of ideas emerge to solve the new use cases, and the discussion shifts its focus from best ways to use the current spec to how does the next version need to change to meet the new needs we have? Eventually, in response, a new specification is developed and refined and argued into existence… and it all begins anew.

Right now, in this case, we need more sophisticated ways to organize our CSS to suit the more sophisticated ways we’re approaching interactive design. We have to manage a fluid interface that scales from tiny to huge screens. We need ways to write maintainable code as a team without stepping on one another’s toes. We need our front-end to scale easily as we build web-based products over longer periods of time. Object-oriented programming offers a proven set of solutions to this, and even though CSS itself doesn’t have those mechanisms built in, we’re trying to see how far we can take it in that direction. All these frameworks and tools and articles floating around, like bubbles in soap, are the artifacts of this conversation: lots and lots of different ways to solve the problem with the tools in front of us. And gradually the conversation is shifting to what we need in the next version. More on that later.

What “object-oriented” CSS really boils down to, in its various forms, is a discipline that your team agrees to. CSS doesn’t have scoping or privacy mechanisms, so you have to find a way to behave as though it did. CSS doesn’t have namespacing, so you have to find a way to sneak namespacing into it. Like finding the right holes in swiss cheese.

Semantics vs. architecture

[http://nicolasgallagher.com/about-html-semantics-front-end-architecture/]

The most important feature of CSS is the way it allows designers to separate “presentation” (the CSS itself) from “content” (the HTML). CSS is, inherently, unintrusive; the same HTML can be radically transformed in appearance and behavior simply by linking different stylesheets to it. About a decade ago, CSS Zen Garden was created specifically to demonstrate this (and I’m excited to see that new themes continue to be added to it).

A big part of that discussion is semantics, or the convergence of human-readable information with machine-readable information. Well-formed HTML describes structured content in a remarkable way that is meaningful to both computers and people. Prior to CSS, HTML included a lot of markup describing its presentation (color, font size, etc.) alongside its content. CSS freed HTML up to describe content and only content. In turn, websites suddenly became a million times easier to manage.

The traditional approach to CSS

Atomic design: toward more narcissistic CSS

Keeping it DRY: a CSS API

<a class=“button primary large has-dropdown” href=“”>Button</a>

The future: modular front-end with Web Components [https://www.youtube.com/watch?t=417&v=QHxrr6Q82yI] [http://webcomponents.org/presentations/web-components-and-the-future-of-css/]

CSS components and context [http://simurai.com/blog/2015/05/11/nesting-components/] [http://philipwalton.com/articles/extending-styles/]

Modules

The module is the basic unit of our framework: a self-contained, single-purpose, reusable element. They can be combined and arranged like building blocks or, more apropos, like atoms and molecules. Modules are “self-absorbed:” they care only about their insides. They do not know anything about what is happening outside of them — that is, their context.

The most important thing about modules is that they must be treated as “open for extension, but closed for modification,” as Philip Walton puts it. Which is to say everything “inside” a module is private, but it may be extended or adopted from “outside” — that is, by a parent module.

An example: say we have a .button module. The module, being self-absorbed, is only concerned with itself and not with how it might interact with any other modules or elements.

.button {
	display: inline-block;
	position: relative;
	margin: 1em;
	padding: 1em;
	font-size: 1.2em;
	color: white;
	background-color: blue;
}

Note that the button’s style rules apply mainly to things inside its own borders: font size, color, padding, etc. It does set its own default margins, but it makes no provision for any special context (for instance, how those margins might change if it’s contained inside a horizontal menu).

The only exceptions to this “contextual ignorance” are feature testing and responsiveness. Say our button displays an SVG icon and we need to use fallbacks if SVG isn’t supported. In that case, we’d write Modernizr parent selectors right into the module. Likewise, if we wanted the button to, say, changes its font size for smaller screens, we would nest the media query inside the module.

.button.has-icon {
	/* default styles */
	font-size: 1.2em;

	.svg & {
		/* styles for SVG icon */
	}
	
	/* use a larger font on a larger screen */
	@media($screen—medium) {
		font-size: 2em;
	}
}

Submodules

A module can also have “child modules” or submodules. These are child elements that have special styles that are specific (and exclusive) to the module. For instance,

Handling context

There are two ways modules handle context: variation and adoption.

Atomic Design

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment