Skip to main content
U.S. flag

An official website of the United States government

Dot gov

Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Https

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

CSS

The purpose of the CSS coding styleguide is to create consistent CSS or preprocessor CSS code across TTS. The styleguide should be treated as a guide — rules can be modified according to project needs.

Architecture

A site’s architecture should be based on its goals and purposes. This means the guidance here should be adapted to different sites and situations.

Modular or component architecture

When using a modular or component architecture, every page is broken into a series of modular components. There are two sets of these components: components and modules. The architecture starts out with basic HTML element rules: HTML, p, a, form, etc tags that than have components and modules written on top of them. Components are very basic structure such as buttons, blurbs, navs, and positioning structures like insets, island, and enclosure. From here, modules are built with these components. This architecture also attempts to keep the specificity trend in an upwards curve as you move down in the file (more on this to come).

  • Start with an elements file for all tag rules (a, h1-h5, p, *, html, body).
  • Create component files for each structural element, such as buttons, navs, etc. These are mainly class-based and use BEM or another naming scheme.
  • Create more specific structure with modules. For instance, if the logo image and text needs very specific treatment, use a module.
    • Build modules from components through mixins, extends, and HTML.
    • Modules can have higher specificity, it’s fine to use deeper nesting.
  • Have an overrides file or folder comprised of global rules that are meant to override components and modules.
    • These can be generic utilities.
    • A good thing to put here are breakpoint-specific rules, such as hiding something at small breakpoints.

File structure

_elements.scss
_mixins.scss
_typography.scss
_util.scss
_vars.scss
component/_blurb.scss
component/_button.scss
component/_island.scss
component/_sub_nav.scss
module/_logo.scss
module/_progress_bar.scss
lib/bourbon.scss
lib/neat.scss
_overrides.scss

For the util, typography, elements, and overrides files, once they grow too large (300 lines or more) in size, split them into their own folder with sub files.

elements/_all.scss
elements/_p.scss
elements/_h.scss
typography/_body.scss
typography/_links.scss
overrides/_breakpoints.scss
overrides/_util.scss
util/_center.scss
util/_clearfix.scss

Importing

As you likely know, CSS rules that are later in the file override earlier rules. This means Sass imports can be used to control inheritance and specificity.

  • Start with base elements.
  • Move to single nested classes and utils.
  • Move next to more specific classes, often with nesting.
  • Move next to overrides, possibly with !important rules.
  • Import alphabetically.
  • Only modify import order for groups of files, not specific files.
// Bad
@import 'module/logo';
@import 'component/mask';
@import 'component/button'; /* Has to be imported after "mask" */

// Good
@import 'component/button';
@import 'component/mask';
@import 'module/logo';

}

Documentation

Sass Comments

Be intentional when you use // (silent comments) versus /* */ (which are preserved in the CSS output). When in doubt, use //.

KSS

Use KSS for documentation. More information on KSS can be found on the official site.

Example

// Button
//
// Various buttons on the site.
//
// Markup
// <a class="button ">
//  <span class="button__text">Link</span
// </a>
//
// .button-modified - A button with a different style.
//
//
// Styleguide component.button
.button {
}

.button-modified {
}

Rationale

KSS is the most common CSS documentation method to date. While it’s not perfect, the generated documentation can be modified through templates. }

Frameworks

TTS recommends using the U.S. Web Design System (USWDS) as it is specifically designed to help build fast, accessible, mobile-friendly federal government websites.

Sometimes, projects utilize other CSS frameworks such as:

  1. Bourbon
  2. BassCSS

These frameworks were chosen because they’re relatively unopinionated about design decisions while still providing the helpers that make frameworks essential to fast and accurate frontend work, for example, solutions for responsive design, grids, and common design patterns. In addition, both frameworks, through modular design and excellent documentation, make it easy for the designer or developer to only use the parts that they need, rather than including a hefty library.

Do not use Bootstrap

18F specifically does not recommend using Bootstrap for production work because it can be difficult to adapt its opinionated styles to bespoke design work.

Formatting

We recommend using Prettier, and enabling it in your editor by default. Prettier is an automatic code formatter that will make your code format consistent. This way we don’t have to argue over how to format our code - we just let the tools enforce something! Prettier works with both plain CSS and SCSS.

Do not use shorthand declarations unless you need to explicitly set all the available values.

// Bad
margin: inherit 3em;

// Good
margin-bottom: 3em;
margin-top: 3em;

margin: 3em 4em 2em 1em;

Avoid arbitrary numbers that are repeated, or linked, or dependent on other parts of the code, (aka “magic numbers”).

// Bad
.component {
  top: 0.327em;
}

// Better
/**
 * 1. Magic number. This value is the lowest I could find to align the top of
 * `.foo` with its parent. Ideally, we should fix it properly.
 */
.component {
  top: 0.327em;
}

// Good
$align_top: 100%;
.component {
  top: $align_top;
}

Order

  • Use the following ordering:

    1. variables
    2. @extend directives
    3. @include directives
    4. declaration list (property: name;)
    5. media queries
    6. pseudo-states (:checked, :target, etc.) and pseudo-elements (::after, ::selection, etc.)
    7. nested elements
    8. nested classes
  • Use alphabetical order or type order for declarations. Pick one to keep the whole project consistent.
  • Place a new line before nested selectors unless they are after the first selector.
  • Treat nested includes, such as Neat’s media includes – @include media($small-screen) — as a standard media query, rather than a Sass @include. So they would be sorted directly after the declaration list.
  • Place mixin calls with @content after nested selectors.
  • You may deviate the sorting order to better suit your project’s needs, as long as it’s consistent throughout the project.
// Bad
.module {

  .module-element {
    color: #fff932;
  }
}

// Good
.module {
  .module-element {
    color: #fff932;
  }
}

// Good
.module {
  $amount = 3;
  @extend .component;
  @include sizing($amount);
  margin-top: $amount * 1em;
  text-align: center;

  @include media($small-screen) {
    margin-top: ($amount + 10em);
  }

  &::before {
    content: "hello";
  }

  .module__ele {
    color: #fff932;
  }
}

Inheritance

Mixins

  • Use mixins for groups of properties that appear together intentionally and are used multiple times.

    @mixin clearfix {
      &:after {
        content: '';
        display: table;
        clear: both;
      }
    }
    
  • Use mixins for components to change size.
  • Use mixins when something requires parameters.

    @mixin size($width, $height: $width) {
      width: $width;
      height: $height;
    }
    
  • Do not use mixins for browser prefixes. Use Autoprefixer.

    // Bad
    @mixin transform($value) {
      -webkit-transform: $value;
      -moz-transform: $value;
      transform: $value;
    }
    

Extend

Be very careful with using @extend. It’s a powerful tool that can have disastrous side-effects. Before using please consider:

  • Where is my current selector going to be appended?
  • Am I likely to be causing undesired side-effects?
  • How large is the CSS generated by this single extend?

If you’re unsure of using @extend, follow these rules to avoid running into trouble:

  • Use @extend from within a module, not across different modules.
  • Use @extend on placeholders exclusively, not on actual selectors.
  • Make sure the placeholder you extend is present as little as possible in the stylesheet.

You can use mixins in place of selectors. While mixins will copy more code, the difference will often be negligible once the output file has been gzipped.

Linting

The styleguide provides a method of linting Sass code to ensure it conforms to the rules in the styleguide. This linting tool will go through all Sass code and issue warnings wherever the code differs from the styleguide. We’ve created a specific .scss-lint.yml file that’s configured to work with the css coding styleguide. There are two ways to set up linting:

  • locally with ruby
  • locally with node

Locally with ruby

  1. Add the .scss-lint.yml file to the base of your repository.
  2. Install the scss-lint gem with gem install scss_lint
  3. Run scss-lint: scss-lint app/assets/stylesheets/

Locally with node (experimental!)

  1. Run npm install --save-dev @18f/stylelint-rules to download the package and save it to your package.json
  2. The package provides both a gulp task and a cli interface. Detailed usage instructions can be found in the README

Shortcomings

scss-lint

The scss-lint tool currently lacks the functionality to check these rules in the CSS coding styleguide:

  • Does not limit line width to 80 characters
  • Does not check for numeric calculations in parentheses
  • Does not sort properties in quite the order we want (defaults to alphabetical)

stylelint

This tool is still being evaluated, so not every rule in our current styleguide is supported by stylelint. scss-lint is purpose built for SCSS and is therefore a bit more feature rich. The following rules are currently not supported:

  • PropertySpelling
  • UnecessaryParentReference

That being said, if you want to avoid a dependency on ruby in your project and still benefit from reliable SCSS linting, please test out this tool!

Naming

  • HTML elements should be in lowercase.

    body,
    div {
    }
    
  • Classes should be lowercase.
  • Avoid camelcase.
  • Name things clearly.
  • Write classes semantically. Name its function not its appearance.

    // Bad
    // Avoid uppercase
    .ClassNAME { }
    
    // Avoid camel case
    .commentForm { }
    
    // What is a c1-xr? Use a more explicit name.
    .c1-xr { }
    
  • Avoid presentation- or location-specific words in names, as this will cause problems when you (invariably) need to change the color, width, or feature later.

    // Bad
    .blue
    .text-gray
    .100width-box
    
    // Good
    .warning
    .primary
    .lg-box
    
  • Be wary of naming components based on content, as this limits the use of the class.

    // Danger zone
    .product_list
    
    // Better
    .item_list
    
  • Don’t abbreviate unless it’s a well-known abbreviation.

    // Bad
    .bm-rd
    
    // Good
    .block--lg
    
  • Use quotes in type pseudo selectors.

    // Good
    .top_image[type="text"] {
    }
    
  • Name CSS components and modules with singular nouns.

    .button {
    }
    
  • Name modifiers and state-based rules with adjectives.

    .is_hovered {
    }
    
  • If your CSS has to interface with other CSS libraries, consider namespacing every class.

    .f18-component
    

Naming Methodologies

When it comes to naming, the most important thing is consistency. The recommended way to do this is using an existing methodology like BEM, or use a custom one that’s clearly defined.

BEM

BEM (Block, Element, Modifier) structures CSS such that every entity is composed of (you guessed it) blocks, elements and modifiers. From Harry Roberts:

The point of BEM is to tell other developers more about what a piece of markup is doing from its name alone. By reading some HTML with some classes in, you can see how – if at all – the chunks are related; something might just be a component, something might be a child, or element, of that component, and something might be a variation or modifier of that component.

TTS generally recommends using a modified BEM methodology outlined in the next subsection. However, you might want to use standard BEM when:

  • You need a naming scheme that general CSS developers will already be familiar with or an existing naming scheme hasn’t been consistent enough.
  • When you want to use JavaScript to modify the BEM class names dynamically.

Here is an example of BEM in SCSS:

// block
.inset {
  margin-left: 15%;

  // element
  .inset__content {
    padding: 3em;
  }
}

// modifier
.inset--sm {
  margin-left: 10%;

  .inset__content {
    padding: 1em;
  }
}

// modifier
.inset--lg {
  margin-left: 20%;
}

Suggested custom methodology

The TTS recommendation for a naming methodology is a modified version of BEM. It still uses blocks, sections within blocks and modifiers, but with an abbreviated syntax.

.accordion
.accordion-item
.accordion-item-selected

.nav_bar
.nav_bar-link
.nav_bar-link-clicked

Naming methodology resources

js- flagged classes

Don’t attach styles to classes with a js- flag. These classes are reserved for javascript.

// Bad
.js-people {
  color: #ff0;
}

Rationale

A js- flagged class needs to be highly portable. Adding styles to it breaks that portability.

test- flagged classes

Don’t attach styles to classes with a test- flag. These classes are reserved for testing hooks such as those used by selenium.

// Bad
.test-people {
  color: #ff0;
}

Preprocessors

The most supported CSS preprocessor at TTS is Sass (SCSS). Using this pre-processor means you’ll get supported resources such as frameworks, libraries, tutorials, and a comprehensive styleguide as support.

In addition, TTS uses a .scss-lint.yml file to keep our CSS code compliant with our own styleguide.

That being said, any preprocessor is allowed as long as it’s a sound project and has community support.

The recommended way to compile your Sass code is through node-sass, rather than Ruby Sass. This allows eliminating the Ruby dependency for projects that don’t already require it and is the fastest method of compiling Sass.

Specificity

  • IDs should be reserved for JavaScript. Don’t use IDs for styles.

    // Bad
    #component { }
    
    // Good
    .component { }
    
  • Don’t nest more than 3 layers deep.
  • Do not fix problems with !important. Use !important purposefully.

    // Bad
    .component {
      width: 37.4% !important;
    }
    
    // Good
    .hidden {
      display: none !important
    }
    
  • Keep specificity low and trend upwards in specificity as you move further down file. See the specificity graph section for more info.
  • Don’t use unnecessary tag selectors.

    // Bad
    p.body_text { }
    
    // Good
    .body_text
    
  • If you have to hack specificity, use a safe hack: the multi class.

    // multi-class hack
    .component.component { }
    

Specificity graph

An easy rule to use when dealing with specificity is to start from a low specificity and curve to higher specificity as you move towards the bottom of the output file. Since CSS rules get replaced by rules further down in the file, you’ll override rules in an expected way.

There’s a tool that can graph your files’ specificity, CSS specificity graph. Run your final output file through this tool and strive for a curve trending upwards.

Resources

Rationale

With specificity comes great responsibility. Broad selectors allow us to be efficient, yet can have adverse consequences if not tested. Location-specific selectors can save us time, but will quickly lead to a cluttered stylesheet. Exercise your best judgement to create selectors that find the right balance between contributing to the overall style and layout of the DOM.

  • When modifying an existing element for a specific use, try to use specific class names. Instead of .listings-layout.bigger use rules like .listings-layout.listings-bigger. Think about ack/grepping your code in the future.

  • Use lowercase and separate words with hyphens when naming selectors. Avoid camelcase and underscores. Use human-readable selectors that describe what element(s) they style.

  • Attribute selectors should use double quotes around values. Refrain from using over-qualified selectors; div.container can simply be stated as .container.

  • IDs should be reserved for JavaScript. Unless you have a very good reason, all CSS should be attached to classes rather than IDs. When in doubt, use a class name. This prevents target confusion and allows CSS devs and JS devs to co-exist in the same code in peace. If you must use an id selector (#id) make sure that you have no more than one in your rule declaration.

Units

Measurements

  • Use rem units for font sizes with a px fallback. This can be done with the following mixin:

    @mixin font-size($sizeValue: 1.6) {
      font-size: ($sizeValue * 10) + px;
      font-size: $sizeValue + rem;
    }
    
  • Set the HTML font size to 10px to ensure that 0.1rem equals 1px.

    html {
      font-size: 10px;
    }
    
  • Use em units for positioning.
  • Use percentages when layout components stay relational to each other (e.g. a main content area that takes up 75% of the screen and a sidebar that takes up 25%).

    // Good
    .panel-a {
      width: 25%;
    }
    
    .panel-b {
      width: 75%;
    }
    
  • Use px units for when a measurement shouldn’t change based on user set font size or browser zooming or for when requiring pixel values below 5.

    // Bad
    selector {
      border-width: 55px;
    }
    
    // Good
    selector {
      border-width: 2px;
    }
    
  • Use unitless values for line-height as this will inherit values from the font-size.
  • Use up to 10 decimal places in em units to ensure accuracy.

    // Good
    .body_copy {
      @include rem-font-size(1.4);
      // Line height will now be 1.8 of 1.4rem, or 2.5rem.
      line-height: 1.8;
    }
    
    // Good
    .container {
      height: 12em;
      margin-left: 10.6666666667em;
      width: 82.5%;
    }
    
  • Do not use a unit with 0.

    // Bad
    width: 0px;
    
    // Good
    width: 0;
    
  • Always use a unit for dimensions, margins, borders, padding, and typography.

    // Bad
    border-width: 12;
    
    // Good
    border-width: 12px;
    

Colors

  • Use hex notation first, or then rgb(a), or hsl(a).
  • Both three-digit and six-digit hexadecimal notation are acceptable.
  • When denoting color using hexadecimal notation, use all lowercase letters.
  • When using HSL or RGB notation, always add a single space after a comma and no space between parentheses and content.
// Bad
color: #FFF;
color: rgb( 255, 0, 0 );

// Good
$light: #fff;
color: $light;

// Good
$primary: #fe9848;
color: $primary;

// Good
$secondary: rgba(255, 100, 255, 0.5);
color: $secondary;
  • If you use an rgba rule, include a fallback value in hexadecimal.

    // Good
    .illustration {
      background-color: #eee; // fallback
      background-color: rgba(221, 221, 221, 0.75);
    }
    

Variables

  • Create new variables in the following circumstances:
    • The value is repeated twice
    • The value is likely to be updated at least once
    • All occurrences of the value are tied to the variable (for example not by coincidence)
  • When building scss that will be used across multiple projects use the !default flag to allow overriding.

    $baseline: 1em !default;
    
  • The !global flag should only be used when overriding a global variable from a local scope.
  • Variables across the whole scss codebase should be placed in their own file.
  • When declaring color variables, don’t base the name on the color content.

    // Bad
    $light_blue: #18f;
    $dark_green: #383;
    
    // Good
    $primary: #18f;
    $secondary: #383;
    $neutral: #ccc;
    
  • Be careful when naming variables based on their context.

    // Bad
    $background_color: #fff;
    
  • Don’t use the value of dimensional variables in the variable name.

    // Bad
    $width_100: 100em;
    
    // Good
    $width_lg: 100em;
    
  • Name all used z-indexes with a variable.
  • Have a z-index variable for each z-index used, and a separate variable, possibly aliased for where the z-index is used.

    $z_index-neg_1: -100;
    $z_index-neg_2: -200;
    $z_index-1: 100;
    
    $z_index-hide: $z_index-neg_2;
    $z_index-bg: $z_index-neg_1;
    $z_index-show: $z_index-1;
    

Responsive Design & Breakpoints

  • Set variables for breakpoints at the top of your stylesheet. This functionality is built into Bourbon.

    $sm: new-breakpoint(min-width 0 max-width 40em $sm_cols);
    
  • Use variables to set the queries throughout so they are easy to adapt if necessary.
  • Place media queries nearest to the class they are affecting.
  • Rather than focusing on devices when deciding where to put breakpoints, focus on content; name breakpoint variables relative to each other.

    // Bad
    $iphone: new-breakpoint(min-width 0 max-width 640px 6);
    
    // Good
    $small: new-breakpoint(min-width 0 max-width 40em 6);
    $medium: new-breakpoint(min-width 0 max-width 60em 6);
    
Looking for U.S. government information and services?

Visit USA.gov