😎 cool.css

The last CSS framework I'll (hopefully) ever have to build.

Between switching jobs and starting a bunch of side-projects, I've found myself re-writing a lot of CSS in the past year. Wouldn't it be nice, I thought, if I could just write one CSS framework and use it on any project? cool.css is my attempt to do just that.

Just what the world needs—another fucking CSS framework!

Yes, I know. I feel the same way.

Dave Rupert once advocated for tiny Bootstraps, for every client, which is something that stuck with me. Why use a CSS framework that's going to add a bunch of stuff you're not going to use and require customizations and overrides on top of a bunch of new styles that are specific to your needs?

This is why I've made custom CSS frameworks for every project, including three CSS frameworks at Groupon for their consumer, merchant and internal tools products.

As I've dived deeper into design systems work, I wanted to support those systems with a CSS framework that was:

The COOL Methodology

Each new framework I had made over the years built on hard lessons learned from the last. One of the biggest issues that always eluded me was that no matter how good I made the framework, developers would always build their own styles on top of the framework, to varying degrees of success.

Eventually my process evolved into its own methodology, combining parts of SMACCS, Atomic Design, Tailwind and CUBE CSS, into something that just about does everything I want it to do.

The COOL methodology includes three types of styles.

Component Styles
Individual components are styled to be used in any context and composed into larger components using utility styles.
Utility Styles (The OO in COOL)
These single-purpose classes allow you to compose multiple components in any number of ways without having to re-declare styles in different contexts.
Layout Styles
Rather than using a generic 12-column grid, a number of pre-defined layouts are provided (using CSS grid) that can be used to organize pages of content.

COOL principles, bruh

The COOL methodology is based on a few principles.

Everything is scoped to the framework

A lot of frameworks apply styles directly to HTML elements like <body>. This is fine if you're starting a new project, but if you're coming into an existing project with legacy styles, introducing a framework can cause conflicts with those styles.

cool.css has a $prefix token that's appended to every selector in the framework. Styles which would otherwise be added directly to an element are instead scoped within a global class, .#{$prefix}-styles. (The default value of $prefix is "cool", so .cool-styles) You can apply this class to the body or, if you're slowly introducing the framework to an existing site, you can add it to a parent container where you want to use the framework.

A minimal reset

Resets like CSS Reset or normalize.css are great for making things look the same across all browsers, but often require you to redeclare basic styles for each element. Besides that, browsers have improved over the years to the point where they aren't strictly necessary.

cool.css takes a slightly different approach, based largely on Andy Bell's Modern CSS Reset.

In addition, cool.css resets margins and (some) padding defaults. Having default margins on header elements, for example, often requires developers to override the margin in different contexts. Instead, cool.css encourages the use of utility classes for controlling margins between elements.

cool.css also sets font properties to inherit. This encourages developers using the framework to select elements like headers semantically and use classes for applying visual styles, rather than selecting an <h2> because it's visually bigger than an <h3>, for example.

Not utility-first but utility-also

Pure utility frameworks like Tailwind have their advantages. However, I have two issues with them.

  1. They offset composition of visual styles from CSS to HTML. Before CSS, we had a limited number of HTML attributes that allowed us to add visual styling to elements. When CSS came around there was this whole movement around "separation of concerns". Utility frameworks basically take us back to that time of declaring all of our visual styles in the markup, only we're able to customize those styles in the CSS rather than leaving it up to the browser. It doesn't make it any easier for developers to make the website look like the picture of a website the designer gave them.

  2. They let you combine any number of styles. Design systems require a fine balance between consistent visual style and allowing the flexibility to design new features. What we don't want is for developers (or designers, for that matter) to use any combination of colors in the palette for text, backgrounds, borders, etc.

Mike's exaggerated social thumbnail
Smash that like button and subscribe!

cool.css features a lot of helpful utility classes, but they are less about styling individual atomic components more for applying variations to default styles or arranging those components into larger molecules/organisms.

The goal is that you should be able to build any design without having to add additional styles.

Let the C in CSS do its thing

Fun fact: the C in CSS stands for cascade. Methodolgies like BEM, in an effort to encapsulate styles, often result in having overly-specific selectors and a lot of redundant style declarations.

Take a card component, for example. It's pretty common to see a card marked up like:

.card
  .card--image
  .card--body
    .card--title
    .card--subtitle
    .card--button-cta

Is all that really necessary? Do styles for a title have to be scoped to a card? Or can we just have .card define the container and use more generic, reusable styles inside of it?

.card
  img
  figcaption
    .heading-3
    .text-medium.text-subdued
    .button-primary

By limiting how often we declare the same styles in different contexts, we can greatly reduce the amount of CSS and markup required to design new product features and complex components.

The 12-column grid is dead, long live the 12-column grid

CSS frameworks have made the 12-column grid ubiquitous. These grids were born from a desire to mimic print designs. But we're not Swiss Modernists, we're just making websites, y'all.

These grid systems allow you to compose elements in any combination of ways, but usually we're just using halves or quarters, or dividing a number of elements equally across the available space. You don't need the overhead of a grid system to do that.

cool.css includes utility classes for simple flexbox and grid layouts for components, as well as common, responsive layout patterns for pages. Remember the Holy Grail layout? That's like 90% of pages right there. You don't need a 12-column grid system to make that.

Powered by design tokens

cool.css is easily themable thanks to three levels of design tokens:

Stack agnostic

cool.css is built with Sass but you don't necessarily even have to use Sass to use cool.css. You can just as easily add the compiled CSS to any project.

Making CSS cool

I've worked with so many developers who struggle with CSS. The methodology I've used over the years in building custom CSS frameworks for different projects and companies has helped accelerate the process of getting ideas from concept to production.

Devs love it because it greatly reduces the amount of time they spend writing CSS.

Designers love it because the finished product ends up looking like their design with a lot less back and forth.

Managers and other stakeholders love it because it frees up their teams to focus on the things they're good at.

cool.css is not meant to compete with frameworks like Bootstrap or Tailwind. It's mostly a tool for me to quickly port common patterns between projects and make them visually distinct using design tokens. If you get some use out of the framework or the ideas of the COOL methodology, I'd love to hear about it. If you have constructive feedback I'd also love to hear it!

This incredible GIF by Corey Ginnivan