CSS Styleguides

ITCSS

SASS is divided into seven layers ordered by three key metrics:

  • Reach (from far-reaching to localised)
  • Explicitness (from generic to explicit)
  • Specificity (from low specificity to high specificity)

The layers are:

  1. Settings
  2. Tools
  3. Generic
  4. Elements
  5. Objects
  6. Components
  7. Trumps

Settings is the most generic layer with maximum reach and lower specificity

Trumps is the most localised, explicit and with highest specificity

Layer description
Settings Settings are for variables used site-wide. Typical settings are color variables, base font size and line height, breakpoints variables, etc.
Tools Tools holds mixins and functions.
Generic Generic is the first layer actually producing some CSS. This layer is seldom modified, and is usually the same across any projects you work on. It contains things like Normalize. css, global box-sizing rules, CSS resets and so on.
Elements This contains bare, un-classed HTML elements.
The Elements layer is typically the last one in which we’d find bare, element-based selectors, and is very rarely added to or changed after initial setup. Once we have defined element-level styles, all additions and deviations should be implemented using classes.
Objects This is the first layer using classes. Classes are used to style non cosmetic design patterns (like OCSS objects).
Wrappers, layout systems and media object are perfect candidates.
Components The Components layer is where we begin to style recognisable pieces of UI. We’re still binding onto classes here, so our specificity hasn’t yet increased. However, this layer is more explicit than the last one in that we are now styling explicit, designed pieces of the DOM. We shouldn’t find any selectors with a lower specificity than one class in this layer.
Trumps It is inelegant and heavy- handed, and contains utility and helper classes, hacks and overrides. A lot of the declarations in this layer will carry !important (e.g. .u-text-center { text-align: centre !important; } ).

File structure

File structure mimics ITCSS layers.

/sass/_settings.*.scss
/sass/_tools.*.scss
/sass/_generic.*.scss
/sass/_elements.*.scss
/sass/_objects.*.scss
/sass/_components.*.scss
/sass/_trumps.*.scss
/sass/main.scss

Each layer contains a series of partials named following the convention _<layer>.<partial>.scss (for example _settings.colors.scss,
_elements.headings.scss, _components.tabs.scss).

These partials should be kept as small and granular as possible, with each one containing only as much CSS as it needs to fulfil its role.

So _elements.headings.scss would contain only the rules h1 to h6 and nothing more. If you have, for example, a Page Title component that makes a main heading (e.g. h1 ) and a subheading (e.g. h2 ) look a certain way, you would create a _components.page-title.scss partial in the Components layer and bind onto classes (e.g. .page-title, .page-title-sub), not onto HTML elements.

All scss files are kept in the root folder to maximize findability.

BEMIT

BEMIT is an evolution of BEM naming convention drafted by Harry Roberts in conjunction with ITCSS layer approach.

BEMIT extends BEM naming convention with namespacing.

Full BEMIT nomenclature is made of:
<prefix>-<block-name>__<element-name>--<modifier>

for example

.c-nav-primary__link--home

Let’s dig into namespacing.

Namespacing

namespace prefix description location
object o- repetitive, shared, and purely structural aspects of a UI (eg. layout, wrappers, containers, media can all exist as non-cosmetic styles that handle the skeletal aspect of a lot of UI components, without ever actually looking like designed ‘things’.) o- prefix indicates we’re dealing with an object, therefore it will be defined in a file named _objects.*.scss
component c- Components are finite, discrete, implementation-specific parts of our UI that most people (users, designers, developers, the business) would be able to identify: This is a button; This is the date picker; etc. c- prefix indicates we’re dealing with a component, therefore it will be defined in a file named _components.*.scss
utility u- Utilities are complete single responsibility rules which have a very specific and targeted task. It is also quite common for these rules’ declarations to carry !important so as to guarantee they beat other less specific ones. They are to be used as a last resort when no other CSS hooks are available, or to tackle completely unique circumstances, e.g. using .u-text-center to centrally align one piece of text once and once only. They are only one step away from inline styles, so should be used sparingly. u- prefix indicates we’re dealing with a utility, therefore it will be defined in a file named _trumps.*.scss
hack _ you know what I mean 🙂

every hack needs to be fully commented to facilitate refactoring

_ prefix indicates we’re dealing with a hack, therefore it will be defined in a file named _trumps.*.scss
scope s- Useful for creating a new styling context when we have no control on a section of the DOM (coming from CMS, for example). scope classes are similar to components classes and will be defined in a file named _components.*.scss
stateful is-/has- used to indicate a state (when a corresponding ARIA-ROLE is not available) stateful classes should be used following the SSoT method
javascript js- For javascript hooks
QA qa- For QA and Testing hooks
Backend be- For backend hooks

Naming blocks

There are only two hard things in computer science: cache invalidation and naming things.

Phil Karlton

Choosing a name for a block can be tricky and have big consequences.

Classes should be chosen to describe the styling’s function rather than the content’s function of the block.

We should “…derive class name semantics from repeating structural and functional patterns in a design. The most reusable components are those with class names that are independent of the content.

We shouldn’t be afraid of making the connections between layers clear and explicit rather than having class names rigidly reflect specific content. Doing this doesn’t make classes “unsemantic”, it just means that their semantics are not derived from the content. We shouldn’t be afraid to include additional HTML elements if they help create more robust, flexible, and reusable components. Doing so does not make the HTML “unsemantic”, it just means that you use elements beyond the bare minimum needed to markup the content.”

Nicolas Gallagher – “About HTML semantics and front-end architecture

DRY

Modifiers should not include the original styles but just what modifies them. Applying a modifier means adding the modifier class after the basic class.

For example, if we have a c-btn (button component) in a standard and cta flavor we should have:

.c-btn {
    display: inline-block;
    padding: 5px 10px;
    text-decoration: none;
    background-color: $color-btn;
    color: #fff;
}

.c-btn--cta {
    font-weight: bold;
    background-color: $color-btn--cta;
}
<a class="c-btn c-btn--cta">Call to Action!</a>

SSoT (Single Source of Truth)

Every class should be defined in only one place keeping together all the modifiers.

Instead of writing

_components.buttons.scss

.c-btn {
    display: inline-block;
    padding: 5px 10px;
    text-decoration: none;
    background-color: $color-btn;
    color: #fff;

    &:hover,
    &:focus {
        background-color: $color-btn-hover;
        color: #fff;
    }
}

and

_objects.header.scss

.o-header .c-btn {
    background-color: #fff;
    color: $color-btn;
}

we write

_components.buttons.scss

.c-btn {
    display: inline-block;
    padding: 5px 10px;
    text-decoration: none;
    background-color: $color-btn;
    color: #fff;

    &:hover,
    &:focus {
        background-color: $color-btn-hover;
        color: #fff;
    }

    .o-header & {
        background-color: #fff;
        color: $color-btn;
    }
}

Examples

Using BEMIT makes classes convey a lot of informations to developers and help identify the correct approach to bug fixing or feature implementation reducing regressions.

Mixing OOCSS objects and components is something very powerful but needs to be learned to avoid a single block to have elements pertaining to something not directly related.

To better understand this approach you can find two examples:

  • the first one show how to use objects and components inside the same chunk of DOM
  • the second one show how BEMIT can help understand how things work from just reading the HTML

Objects and Components (example)

We need to build a testimonial block.

Let’s start with some un-styled blockquote example

<blockquote>
    <img src="luca.jpeg" alt="" />
    <p>La famiglia è importante.</p>
    <p>Padrino</p>
</blockquote>

Our testimonial block uses the typical media object layout pattern: image on the left, text on the right.

The media object inside _objects.media.scss looks like this:

.o-media {
    display: table;
    width: 100%;
}

    .o-media__img,
    .o-media__body {
        display: table-cell;
        vertical-align: top;
    }

    .o-media__img {
        padding-right: $base-spacing-unit;

        img {
            display: block;
            max-width: none;
        }

    }

    .o-media__body {
        width: 100%;
    }

N.B. CSS elements are indented as if they where nested inside the corresponding block.

Now our HTML uses the media object:

<blockquote class="o-media">
    <span class="o-media__img">
        <img src="luca.jpeg" alt="" />
    </span>
    <div class="o-media__body">
        <p>La famiglia è importante.</p>
        <p>Padrino</p>
    </div>
</blockquote>

N.B. It’s ok to include additional HTML elements if they help create a more robust, flexible and reusable component.

Now we’re ready to apply some UI on this so we need classes for our testimonial component.

Let’s add some classes to do it:

<blockquote class="o-media  c-testimonial">
    <span class="o-media__img">
        <img src="luca.jpeg" alt=""/>
    </span>
    <div class="o-media__body  c-testimonial__text">
        <p>La famiglia è importante</p>
        <p class="c-testimonial__source">Padrino</p>
    </div>
</blockquote>

Now we can create a _components.testimonial.scss and style our newly created classes:

.c-testimonial {
    padding: $base-spacing-unit;
    border-radius: $base-radius;
    background-color: $color-shadow;
}

    .c-testimonial__text {
        font-family: Baskerville, Garamond, Palatino, "Palatino Linotype", "Hoefler Text", "Times New Roman", serif;
        font-style: italic;
        @include font-size(24px);
        vertical-align: middle;
    }

    .c-testimonial__source {
        display: block;
        font-family: Optima, Segoe, "Segoe UI", Candara, Calibri, Arial, sans-serif;
        font-style: normal;
        font-weight: bold;
        @include font-size(12px);
        text-transform: uppercase;
        letter-spacing: 0.15em;
        color: $color-gray;

        &:before {
            content: "— ";
        }

    }

We’re almost done. There’s just one bit left and it’s related to the image.
Looking at the design we see that the image for the testimonial component looks like the image used in other parts of the project so we can create an avatar component to make it reusable.

Let’s do it.

The avatar component has a rounded shape and comes in three sizes, so we’ll make them all.

.c-avatar {
    display: block;
    border-radius: 100%;
    width:  64px;
    height: 64px;
}

.c-avatar--small {
    width:  32px;
    height: 32px;
}

.c-avatar--large {
    width:  128px;
    height: 128px;
}

Now we can add the right classes to our code:

<blockquote class="o-media  c-testimonial">
    <span class="o-media__img">
        <img src="luca.jpeg" alt="" class="c-avatar  c-avatar--large" />
    </span>
    <div class="o-media__body  c-testimonial__text">
        <p>La famiglia è importante</p>
        <b class="c-testimonial__source">Padrino</b>
    </div>
</blockquote>

And we’re done 🙂

Meaningful classes (example)

Imagine you open this file for the first time:

<body>

  <article class="c-modal  c-modal--wide  js-modal  is-open">

    <div class="c-modal__content">

      <div class="s-cms-content">
        ...
      </div>

    </div><!-- /.c-modal__content -->

    <div class="c-modal__foot">

      <p class="o-layout">
        <span class="o-layout__item  u-1/3">
          <a href="c-btn  c-btn--negative  qa-modal-dismiss">Cancel</a>
        </span>

        <span class="u-hidden">or</span>

        <span class="o-layout__item  u-2/3">
          <a href="c-btn  c-btn--positive  qa-modal-accept">Confirm</a>
        </span>
      </p>

    </div><!-- /.c-modal__foot -->

  </article><!-- /.c-modal -->

  <footer class="c-page-foot">
    <small class="c-copyright  _c-copyright">...</small>
  </footer>

</body>
</html>

There’s a lot of stuff you can understand from just reading classes:

  • We have a modal component (.c-modal) which is using a wide variant (.c-modal--wide). It has some JS binding onto it (.js-modal) and it is currently open (.is-open).
  • The modal is made up of a few more pieces (.c-modal__content and .c-modal__foot).
  • There is an entire area of the DOM whose styling is defined by a Scope (.s-cms-content). This content comes from a place where we cannot get at the DOM nodes individually, so we revert to styling everything from a new context.
  • We have a layout Object (.o-layout) which is currently laying out:
    • Some layout items that are one- and two-thirds wide (.u-1/3, .u-2/3).
    • These width classes are Utilities, and therefore do not just have to be used alongside the layout Objects—they can be used anywhere.
  • Some button components (.c-btn) which have:
    • QA hooks to be bound onto for automated UI testing (.qa-modal-dismiss, .qa-modal-accept).
  • I see there’s some hack going on in the footer that may need refactoring (._c-copyright)

This code also informs me that:

  • I know there are a number of things in here that I can reuse elsewhere (Objects, Components and Utilities).
  • A number of things I can reuse, but not bind onto or alter (Objects and Utilities).
  • A number of things I just plain should not touch (JS and QA peoples’ stuff).
Scritto da lucasalvini 27/04/2018 - 10:50
Modificato da ila 27/09/2019 - 11:13