CSS Style Guide (WIP)
TailwindCSS
We use Tailwind CSS as our utility framework of choice (more to come)
Preprocessing
We use Sass but these principles are applicable to any pre- or postprocessors out there.
BEVM
We use a BEM-like syntax with some custom accents. The 'variation' is a concept picked up from Chainable BEM modifiers.
We only use classes for styling, with the following ingredients:
.component /* Component */
.component__element /* Child */
.component__element__element /* Grandchild */
.items /* Use plurals if possible */
.item
.-modifier /* Single property modifier, can be chained */
.component--variation /* Standalone variation of a component */
.component__element--variation /* Standalone variation of an element */
.helper-property /* Generic helper grouped by type (eg. `align-right`, `margin-top-s`) */
.js-hook /* Script hook, not used for styling */
.component and .component__element
<div class="news">
- A single reusable component or pattern
- Children are separated with
__
- All lowercase, can contain
-
in name - Avoid more than 3 levels deep
<div class="news">
<div class="news__item">
<div class="news__item__publish-date">
Be descriptive with component elements. Consider class="team__member"
instead of class="team__item"
<div class="team">
<div class="team__member">
You can use plurals & singulars for readability. Consider class="member"
instead of class="members__member"
<div class="members">
<div class="member">
.-modifier
<div class="button -rounded -active">
.button {
&.-rounded {
…
}
&.-active {
…
}
}
- A modifier changes only simple properties of a component, or adds a property
- Modifiers are always tied to a component, don't work on their own (make sure you never write "global" modifier selectors)
- Multiple modifiers are possible. Each modifier is responsible for a property:
class="alert -success -rounded -large"
. If you keep using these modifiers together, consider a variation (see below) - Since modifiers have a single responsability, the order in HTML or CSS shouldn't matter
.component--variation
<div class="button--delete">
.button--delete {
/* Base button classes */
…
/* Variations */
background-color: red;
color: white;
text-transform: uppercase;
}
- A variation adds more than one properties at once to a class, and acts as a shorthand for multiple modifiers often used together
- It's used stand-alone without the need to use the base class
button
- It's a logical case to use
@apply
here, so the variation can inherit the original modifiers (under consideration) - Even variations should be generic and reusable if possible:
class="team--large"
is better thanclass="team--management"
.helper-property
<div class="align-right">
<div class="visibility-hidden">
<div class="text-ellipsis">
<div class="margin-top-s">
- Reusable utility classes throughout the entire project
- Prefixed by type (= the property that will be effected)
- Each helper class is responsible for a well-defined set of properties. It should be clear that these are not components
.js-hook
<div class="js-map …"
data-map-icon="url.png"
data-map-lat="4.56"
data-map-lon="1.23">
- Use
js-hook
to initiate handlers likedocument.getElementsByClassName('js-hook')
- Use
data-attributes
only for data storage or configuration storage - Has no effect on styling whatsoever
DOM structure
- All styling is done by classes (except for HTML that is out of our control and inline background images)
- Avoid #id's for styling
- Make elements easily reusable, moveable in a project, or between projects
- Avoid multiple components on 1 DOM-element. Break them up.
<!-- Try to avoid, news padding or margin could break the grid-->
<div class="grid__col -1/2 news">
…
</div>
<!-- More flexible, readable & moveable -->
<div class="grid__col -1/2">
<article class="news">
…
</article>
</div>
Tags are interchangeable since styling is done by class.
<!-- All the same -->
<div class="article">
<section class="article">
<article class="article">
Html tags that are out of control (eg. the output of an editor) are scoped by the component.
<div class="article">
<!-- custom html output -->
</div>
.article {
/* Tag instead of class here */
& h2 {
…
}
& p {
…
}
}
Class order
<div class="js-hook component__element -modifier helper">
Visual class grouping can be done with … | …
:
<div class="js-masonry | news__item -blue -small -active | padding-top-s align-right">
Code style
We use stylelint to lint our stylesheets.
Configuration is done a custom .stylelintrc
which extends stylelint-config-standard
.
{
"extends": "stylelint-config-standard",
"ignoreFiles": "resources/assets/css/vendor/*",
"rules": {
"indentation": [4],
"at-rule-empty-line-before": null,
"number-leading-zero": null,
"selector-pseudo-element-colon-notation": "single",
}
}
Installation
yarn add stylelint
yarn add stylelint-config-standard
Usage
Most projects have a lint script (with the --fix
flag) available in their package.json
.
stylelint resources/assets/css/**/**.css --fix -r
Examples
/* Comment */
.component { /* Indent 4 spaces, space before bracket */
@at-rule …; /* @at-rules first */
a-property: value; /* Props sorted automatically by eg. PostCSS-sorting */
b-property: value;
c-property: .45em; /* No leading zero's */
&:hover { /* Pseudo class */
…
}
&:before, /* Pseudo-elements */
&:after { /* Each on a line */
…
}
&.-modifier {
…
}
&.-modifier2 {
…
}
/* Try to avoid */
@apply …; /* Use only for variations */
&_subclass { /* Unreadable and not searchable */
…
}
h1 { /* Avoid unless you have no control over the HTML inside the `.component` */
…
}
}
/* Line between classes */
.component--variation { /* A component with few extra modifications often used together */
@apply .component; /* Only good use for @apply */
…
}
.component__element { /* Separate class for readability, searchability instead of `&__element`*/
…
}
File structure
We typically use 5 folders and a main app.css
file:
|-- base : basic html elements
|-- components : single components
|-- helpers : helper classes
|-- settings : variables
|-- vendor : custom files from 3rd party components like fancybox, select2 etc.
`-- app.css : main file
app.css
- We use
postcss-easy-import
for glob imports - Source order shouldn't matter, except for order of folders: import npm libraries, settings or utilities first
- Import is done by glob pattern so files can be added easily
@import 'settings/**/*';
@import 'base/**/*';
@import 'components/**/*';
@import 'helpers/**/*';
@import 'vendor/**/*';
Base folder
Contains resets and sensible defaults for basic html elements.
|-- universal.css
|-- html.css
|-- a.css
|-- p.css
|-- heading.css (h1, h2, h3)
|-- list.css (ul, ol, dl)
`-- …
Components folder
Stand-alone reusable components with their modifiers and variations.
|-- alert.css
|-- avatar.css
`-- …
Helpers folder
Stand-alone helper classes for small layout issues.
|-- align.css
|-- margin.css
|-- padding.css
`-- …
Settings folder
Settings for colors, breakpoints, typography, etc. You can start small with one settings.css
and split them up in different files if your variables grow.
|-- breakpoint.css
|-- color.css
|-- grid.css
`-- …
Vendor folder
Imported and customized CSS from 3rd party components (this is the syntactical Wild West, you probably don't want to lint this).
|-- pikaday.css
|-- select2.css
`-- …