CSS methodologies
Although Panther does not require you to organize your CSS in any particular way, we’ve chosen to author our UI component CSS using the following methodologies:
BEM
We use BEM with Harry Roberts’ modified class-naming syntax. Please read the following articles to familiarize yourself with BEM and Harry Roberts’ style:
ITCSS
The CSS in this project is organized according to the ITCSS (Inverted Triangle CSS) methodology. If you aren’t already familiar with ITCSS, please watch this video.
Import order
ITCSS is primarily concerned with CSS selector specificity, and it’s goal is to reduce or eliminate the need to override CSS. In order for it to perform its magic, you must organize your CSS into the following categories. Each category is numbered and corresponds to a layer in the inverted triangle diagram:
\ ------------------------------------ /
\ Settings /
\ -------------------------------- /
\ Tools /
\ ---------------------------- /
\ Generic /
\ ------------------------ /
\ Base /
\ -------------------- /
\ Objects /
\ ---------------- /
\ Components /
\ ------------ /
\ Trumps /
\ -------- /
- Settings
Global variables, site-wide settings, config switches, etc. - Tools
Site-wide mixins and functions. - Generic
“Ground-zero” styles. Low-specificity, far-reaching rulesets (e.g. normalize, resets, box-sizing). - Base
Unclassed HTML elements (type selectors:a {}
,blockquote {}
,address {}
, etc.) - Objects
Objects, abstractions, and “cosmetic-free” design patterns (e.g..o-media {}
). - Components
Designed components. Discrete, complete chunks of UI (e.g..c-carousel {}
). - Trumps
High-specificity, very explicit selectors. Overrides and helper classes (e.g..u-hidden {}
).
InuitCSS is a CSS framework that employs ITCSS principles. You can browse through its codebase for inspiration. InuitCSS’ ITCSS layers are similar to our own.
Differences between Objects and Components
Components are responsible for displaying themselves, and nothing more. They shouldn’t need to know anything about the context in which they appear. Objects can be used to govern layout, whereas components are meant to be discrete, standalone “lego blocks” that can be used anywhere.
In the following example, the o-container
object creates a layout context for the c-button
component. The c-button
doesn’t attempt to position itself in any way. Instead, it leaves that job to the o-container
object:
<div class="o-container">
<button class="c-button">Click me!</button>
</div>
.o-container {
padding: 10px;
border: 1px solid black;
}
.c-button {
background-color: blue;
color: white;
}
Media queries
Unfortunately, media queries are only able to provide information about the viewport’s dimensions and not the dimensions of individual elements on the page. This presents a challenge because our pattern library components could be nested inside of elements that are less wide than the viewport (i.e. sidebar columns, grids, etc.). Our components possess no knowledge of the dimensions of their containers, so we’re forced to target our media queries to the global viewport scope. This article describes the problem in more detail.
Someday, when CSS element/container queries exist (or we decide to use a JavaScript “prolyfill”), we’ll be able to write truly encapsulated components. Until then, we’ll have to define our patterns’ breakpoints in higher-level, parent container contexts. For example:
Contents of patterns/stylesheets/foo.scss
:
.foo {
display: flex;
align-items: flex-start;
}
.foo__body {
flex: 1;
}
@mixin foo-narrow {
.foo {
display: block; // Stack elements in a single column
}
}
Contents of specimen-assets/foo/style.scss
:
@media all and (max-width: 650px) {
.left-column {
@include foo-narrow;
}
}
Instead of defining a media query inside of the pattern SCSS itself, we provide a mixin that external SCSS can @include
when we need to put the pattern into the “narrow” state (since the pattern itself isn’t capable of knowing when this needs to happen).
Resources
You may find it helpful to refer to the following resources for ideas on how to organize your CSS: