Skip to content

CSS guidance

adam edited this page Jan 30, 2019 · 1 revision

All components styles will be nested inside a namespaced class and concatenated using BEMIT notation

A useful starting point for BEMIT notation

Stylus can concatenate anything using the & operator, make sure that you use it properly in order to ensure minimal repetition and over-specificity.

This is not acceptable

CSS

  .c-product-tags {
      text-align: center;
      padding-left: 20px;
      padding-right: 20px;
  }

  .c-product-tags__title {
      margin-top: 0;
      margin-bottom: 35px;
  }

This is acceptable

CSS

  .c-product-tags {
      text-align: center;
      padding-left: 20px;
      padding-right: 20px;

      &__title {
          margin-top: 0;
          margin-bottom: 35px;
      }
  }

NOTE: Utilities and objects may occasionally require two classes to work.

e.g.

  .o-title {
    margin-bottom: 2rem;
    &--page {
      font-weight: bold;
    }
  }
  <h1 class="o-title o-title--page">Welcome</h1>

View on codepen

If the first 'o-title' is omitted the margin-bottom property will not be applied

Stick to the following naming convention prefixes

All components should be prefixed with a 'c'. All reusable single items (like a button for example) is an object and should be prefixed with 'o' Any class which modifies the behaviour of another item is a utility class and should be prefixed with'u'. Layout Items (which set the general layout of a template) should begin with 'l'. Template items will allow you to define overrides on components for specific templates and will begin with 't'.

e.g.

Component: .c-product-tags Object: .o-button Utility: .u-highlight Layout: .l-primary Template: .t-primary

In fact...

Avoid 'raw' classes

A raw class is any class that doesn't have a prefix and/or namespace on it and should be avoided at all costs. As this is a wordpress site and wordpress generates it's own classes occasionally, there will be times where it is unavoidable but you should never add a raw class yourself.

A Mobile-first approach must be used unless absolutely impossible

Stylus has a plugin called 'Rupture', this makes media queries incredibly simple by slicing the page up into named breakpoints and then referencing that breakpoint by using a mixin:

e.g.

  +above(m) {
    margin: 0;
  }

The Clarity toolkit has 7 breakpoints which are as follows:

  • xxs : 320px
  • xs : 410px
  • s : 500px
  • m : 740px
  • l : 1020px
  • xl : 1280px
  • xxl : 1700px;

Additional breakpoints can be explicity specified if needed but please do this as a last resort.

e.g.

  +above('990px') {
    display: none;
  }

Just like with normal media queries, a mobile-first approach should be used, use of `+below()' or '+between()' is possible but discouraged. Only use these if it's absolutely needed and be prepared to justify it in a code review.

e.g.

This is not acceptable

  &__tags {
       li {
          margin-right: 6px;
          margin-bottom: 10px;
          display: inline-block;
          +below(m) {
              margin-right: 0;
          }
      }
    }

This is acceptable

  &__tags {
       li {
          margin-bottom: 10px;
          display: inline-block;
          +above(m) {
              margin-right: 6px;
          }
      }
    }

Don't neglect print stylesheets

There is a global print stylesheet file in src/globals/css which should handle most of the possible things that will happen in printing but it does make sense to keep print styles as component-based as the rest. In order to create a print stylesheet for your component, simply create a filed called style.print.styl and add your styles in there, they will be automatically compiled into the main print stylesheet.

Remember. Don't use regular values for print stylesheets, use millimetres. Also print stylesheets should not use jeet or rupture.

Keep IE fixes to a minimum

IE stylesheets should be created inside each component and be named style.ie.styl. You should also replace any references to the _toolkit file with _ietoolkit as this one is tailored for IE.

Do not duplicate styles. The IE stylesheet should ONLY be used for overrides.

Please note: The use of !important is allowed here but please use it sparingly

Always adopt the DRY (Don't Repeat Yourself) standard

If you are using code which is similar to code used in another function, turn that code into an object, utility or mixin and use/extend it. Don't create a new instance of something which is (practically) identical.

e.g.

This is not acceptable

HTML

  <a class="o-tag-btn"></a>

CSS

  .o-tag-btn {
    background-color: $light;
    border: 1px solid $border;
    color: $gray;
    cursor: pointer;
    outline: none;
    font-size: $innercore;
    text-transform: none;
    display: inline-block;
    &:hover, &:active, &:focus {
      text-decoration: none;
      color: currentColor;
    }
  }

  .o-btn {
    background-color: $dark;
    border: 1px solid $border;
    color: $gray;
    cursor: pointer;
    outline: none;
    font-size: $innercore;
    text-transform: none;
    display: inline-block;
    &:hover, &:active, &:focus {
      text-decoration: none;
      color: currentColor;
    }
  }

This is acceptable

HTML

  <a href="o-btn o-tag-btn"></a>

CSS

  .o-tag-btn {
    background-color: $light;
  }

  .o-btn {
    background-color: $dark;
    border: 1px solid $border;
    color: $gray;
    cursor: pointer;
    outline: none;
    font-size: $innercore;
    text-transform: none;
    display: inline-block;
    &:hover, &:active, &:focus {
      text-decoration: none;
      color: currentColor;
    }
  }

This is also acceptable

HTML

  <a href="o-tag-btn"></a>

CSS

  .o-tag-btn {
    @extend .o-btn;
    background-color: $light;
  }

  .o-btn {
    background-color: $dark;
    border: 1px solid $border;
    color: $gray;
    cursor: pointer;
    outline: none;
    font-size: $innercore;
    text-transform: none;
    display: inline-block;
    &:hover, &:active, &:focus {
      text-decoration: none;
      color: currentColor;
    }
  }

If this sort of thing was common, it may be appropriate to create a mixin instead.

Don't add browser prefixing

The build tool will have auto prefixing enabled via the 'autoprefixer' plugin for Grunt, provided that the software is kept up-to-date then this will ensure that the correct prefixing is always being used for the right browsers. Because of this, don't add prefixing to a project, let the tool do it for you.

This is not acceptable

CSS

  -o-transition: background-color .6s ease-out;
  -ms-transition: background-color .6s ease-out;
  -moz-transition: background-color .6s ease-out;
  -webkit-transition: background-color .6s ease-out;
  transition: background-color .6s ease-out;

This is acceptable

CSS

  transition: background-color .6s ease-out

Ensure all links have a :focus property and that it is appropriately styled

Screen readers and people who have to use a keyboard, pad to navigate a site can't rely on hover states. However they still need visual feedback.

As this closely follows GDS there is a focus property on every link but ensure that it works visually for the link you have created.

Stick to variables where possible

Occasionally, using a custom value is necessary but by default you should use the pre-existing variables to specify things like breakpoints, font-sizes, margin/padding etc...

This is not acceptable

CSS

  +above(1024px) {
     .c-main-nav {
        margin: 10px;
     }
   }

This is acceptable

CSS

  +above(l) {
     .c-main-nav {
        margin: $spacing;
     }
   }

Abstract often-used variables

If a variable is used all over the place and could conceivable be changed in the future, make sure the variable name doesn't have any non-transferrable meaning to it. Creating an abstract (but memorable) variable name is the better option.

e.g.

These are not acceptable

    $16px = 1.6rem
    $large = 1.6rem
    $header2 = 1.6rem

This is acceptable

  $outerCore = 1.9rem

This way, if another level needs to be added or if the font-size changes in the future. We don't have a ton of variables which need to be found and replaced as they no longer make sense.

Don't use PX values

Unless something has to be a fixed width then don't use Pixel values. Use either em/rem units or percentages (if you are laying something out to the grid then use jeet, see below). Always take the above point into account though, if it's possible to use a variable then use that instead.

This is not acceptable

CSS

.box {
  width: 400px;
  margin: 20px;
}

This is acceptable

CSS

.box {
  width: 20%;
  margin: $spacing*2
}

Consider the grid

Whilst we are on the subject of widths, wherever possible, widths should be either flexible or set to the grid. As we are using Jeet, you should set the width using that wherever possible.

This is not acceptable

CSS

.box-aligned-to-grid {
  width: 400px;
  margin: 20px;
}

This is acceptable

CSS

.box-aligned-to-grid {
  column(1/4);
}

Be aware of existing resets

Don't automatically add box-sizing:border-box or margin:0 to elements. The reset stylesheet will cover most of these. In fact it is always a good idea to refactor any css class you work on to ensure all properties are actually needed.

This is not acceptable

CSS

.list {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  list-style-type: none;
  color: $primary;
}

This is acceptable

CSS

.list {
  color: $primary;
}

Avoid the use of the !important flag

Contrary to popular believe, the !important flag is not something which should never be used, however there are only three situations where it can reasonable used in:

1: Active development Sometimes you want to test something and adding !important to a property will make it easier, provided you remove it as soon as your testing is done, that's fine.

2: Global classes Some global classes should never be overwritten by other styles, if this is the case then using !important can ensure this. This is actually what it was created for.

3: Media Queries Sometimes, a media query needs to overwrite something that already exists, rather than add undue nesting or referencing other components in order to achieve the override then the use of !important is acceptable. However if the component is structured correctly, this shouldn't be required.

Note: Even in the above examples. Only use !important if you absolutely have to, the flag should only be added when you've exhausted all other options.

Don't mix namespaces

As we've namespaced all our components (see the first standard) we can be assured that there will be no styling overlaps or conflicts. Each component should only have access to two scopes: Itself and the global namespace. Don't put any styles from other components within the current one and don't put any component related styles in the global namespace. If overrides are required then keep them to the 'Parent overrides' section and use them only when required. (Also ensure they are documented).

Note: There is one exception to this rule and that is layouts if a component CONTAINS a layout (e.g. 'c-footer') then you can reference that component in the layouts.styl file. If a component should change depending on it's parent layout then that is a parent override and belongs in the component stylesheet.

Any global styles should be added to one of the global stylesheets

This one is a basic rule, don't add global styles to any stylesheet which isn't a global stylesheet.

Don't use ID's for Styling

ID's may be in the HTML, but these ID's should never be used to attach styles to.

This is not acceptable

HTML

<div id="container"></div>

CSS

#container {
  color: $primary;
}

This is acceptable

HTML

<div id="container" class="c-container"></div>

CSS

.c-container {
  color: $primary;
}

Don't use 'js-' prefixed classes for styling

Javascript classes (prefixed with js-) should be kept separate from styling classes. Never apply styling to an element using the JS class. This ensures that behaviour and appearance are kept separate from one-another, so if the javascript or style class has to change for any reason, the other class is unaffected.

The naming convention should follow the same process as naming components. So instead of 'c-header', use 'js-header'.

The reverse of this rule is also true, don't use styling classes for JS hooks.

Caveat: If a style is directly attached to a js behaviour and wouldn't be styled at all without that behaviour, then it is acceptable.

This is not acceptable

HTML

<div class="js-header"></div>

CSS

.js-header {
  font-weight:bold;
}

This is acceptable

HTML

<div class="js-header c-header"></div>

CSS

.c-header {
  font-weight:bold;
}

Avoid universal selectors

Universal selectors (* is a good example of this) are not great for performance as they have to match multiple elements before they can finish their task. In reset stylesheets, this is an acceptable overhead. However they shouldn't be used as a matter of course.

Don't worry about descendant selectors

There is a oft-quoted idea that using descendant and child (>) selectors has a performance impact. Whilst this is true, the impact is negligable.

So if you want to write .list > li a instead of .list-item-link{} then go ahead. Just make sure you follow the next rule when you do it.

Don't be overly specific

In the example above, I targeted a link in a list like this .list > li a, whilst there may be a few valid reasons for doing this (for example you only want to target the links in the top level of a nested list), it's often overkill to specify that much. .list a will work just find for cases where you don't need the extra specificity.

Don't qualify classes

This one ties to the one above. Don't write css like this:

div.container {
  padding: $padding
}

Whilst this will work fine if the HTML looks like this:

<div class="container"></div>

Specifying things at a tag level will add to performance, also what happens to the styling if the HTML changes to this:

<article class="container"></article>

Suddenly that CSS which specified that only div tags can have a class of 'container' fails as there was nothing to accommodate an article tag with a container class on it.

Consider future-proofing when using location specific selectors

Location specific selectors (e.g. :nth-child(n), :last-child etc...) are often useful but extra care must be taken with these to ensure that the future of the document is being taken into consideration. If the HTML structure changes at all, how will that impact your styling?

Don't apply units to zero value properties

The performance impact here is minor but as there is no valid reason to specify a unit then it's still good practice to not do so.

This is unacceptable

CSS

margin: 0px;

This is acceptable

CSS

margin: 0;
Clone this wiki locally