CSS
CSS defines the visual presentation of a web application. The application should be fully functional without CSS.
CSS is an unusual language which can easily lead to code bloat, inconsistencies in design, and/or clashing code techniques. It is easy to end up with CSS code that is so fragile it can cause site-wide regressions with small changes.
- CSS should be valid
- No deprecated or non-standard code
- CSS should maintain a separation of concerns
- Do not apply styling and scripting with the same selector
- CSS should be accessible
- Avoid setting the pointer-events property to none on non-SVG elements
- The use of pointer-events for non-SVG elements is experimental
- Users can still interact with the element if navigating using a keyboard, and there is no communication to assistive technology that the element is disabled
- Avoid setting the pointer-events property to none on non-SVG elements
- CSS should be DRY
- Avoid duplication in the same file and across multiple files
- Reuse existing global, component, and utility classes before writing custom classes
- Do not redefine or override global styles or component styles
- Do not redefine or delete utility classes
- Do not restate default property-value combinations, e.g.: display: block; on block-level elements
- Do not redefine the line-height property value — the value of this property is set globally to meet accessibility requirements, and it should not be overridden
- Avoid writing code specific to an OS, browser, locale, or language
- CSS should have the lowest specificity possible
- Use class selectors to apply styling, rather than id or element selectors
- Classes are reusable and have lower specificity
- Ids can only be used once per page and have higher specificity
- Use id selectors as hooks for JavaScript and/or automated testing frameworks
- Use class selectors to apply styling, rather than id or element selectors
- CSS should be maintainable
- When it makes sense, use variables to semantically abstract styles in order to make future rebrand efforts easier and seamless, e.g.: Do
color: $font-color-primary;
Don'tcolor: #221f4d;
- Add space to the bottom of elements, rather than the top, in order to keep vertical spacing predictable while writing modular, component-based CSS
- Avoid setting fixed heights and widths
- When debugging, start by attempting to delete code before adding more
- Use a preprocessor when possible — we recommended SCSS
- When it makes sense, use variables to semantically abstract styles in order to make future rebrand efforts easier and seamless, e.g.: Do
Specificity
Use the minimum specificity required to achieve the desired styling. For more information on CSS specificity, check out Specificity Wars and Batificity.
!important
The !important declaration fundamentally destroys the specificity feature and can even break accessibility for some users. There is almost always another way to achieve the same goal without using the !important declaration. Treat the !important declaration like the nuclear option, only to be used in the most extreme cases, like when overriding styling from an external library that is using the !important declaration in its code.
Inline styles
Do not hard code styles into your HTML markup, either with the style attribute or with deprecated attributes such as align, border, or width.
Selectors
CSS is most maintainable with the simplest selectors. The longer the selector, the higher the specificity. The higher the specificity, the harder the CSS becomes to maintain.
- Use the minimum specificity required to achieve the desired style
- Avoid nesting whenever possible
- Avoid unnecessary ancestor selectors, e.g.: Do
.btn-primary
Don'tbutton.button-primary
ID selectors
Do not use ID selectors to apply styling. The ID selector is the most specific selector since it can only match one element. Styling should be applied using a class selector instead. ID selectors, if used at all, should be used only as access points for JavaScript or automated testing frameworks.
Class selectors
Use class selectors to apply styling whenever possible.
Element selectors
Avoid overriding the default styles of an element. Element selectors should only be used in global stylesheets to define the application’s defaults, e.g.: body { background: $coconut; }
Attribute selectors
Avoid using several attribute selectors on commonly occurring components. Browser performance is known to be impacted by these.
Naming Conventions
Semantics
- All classes and ids should have semantic meaning
- Semantic names describe what an element is or does, rather than its current presentation
- What an element looks like (its presentation) can change across devices (responsive design) or over time (rebranding efforts)
- Semantic names remain logical even if the presentation changes
- .nav-primary and .nav-secondary instead of .nav-top and .nav-left
- .btn-primary instead of .btn-blue
- $font-family-default instead of 'Open Sans'
- $font-color-primary instead of $midnight
- .u-m-0
- Avoid excessive and arbitrary shorthand, e.g.: .btn is useful for buttons, but .s is meaningless
Base + modifier classes
- Chain base and modifier classes in order to create modular, extensible CSS with reduced specificity and duplication. Don’t be afraid to apply many classes to an element!
- Use a parent-child / last-name-first approach to naming
- Create a base class that contains common styles, e.g.: .btn
- Create modifier classes, prefixed with the name of the base class, that modify/extend the base class, e.g.: .btn-primary and .btn-wide
- Prefix classes for nested elements with the name of the parent/component, e.g.: .card, .card-image, .card-content, .card-content-heading, .card-content-body
- This technique allows selectors to be bundled together when using a preprocessor, e.g.: .btn { ... &-primary { ... } }
State modifier classes
- State modifier classes describe the state of a specific component or element
- State modifier classes should never appear on their own — they should always be scoped to a specific component or element via class chaining
- State modifier classes should be treated as a boolean, e.g.: true or false
- Consider using is-* prefix, e.g.: .btn.is-enabled, .btn-is-disabled, .form-control.is-valid, .form-control.is-invalid, .accordion.is-expanded, .accordion.is-collapsed
Utility classes
- Utility classes epitomize the single responsibility principle
- Utility classes apply a single rule or a very simple and universal pattern, without side effects
- Utility classes can be applied to any element, in any context, at any time
- Utility classes must always do exactly the same thing, so utility class declarations must never change
- Use sparingly — if you notice patterns, consider creating or modifying component classes
- Consider using u-* prefix, e.g.: .u-mx-auto, .u-p-0
JavaScript hooks
- Use only for JavaScript hooks — do not attach any styling
- Keep classes for JavaScript hooks out of CSS
- JavaScript hooks are event- or action-based, so consider using verbs when naming
- Use js-* prefix, e.g.: .js-toggle, .js-edit, .js-save, .js-delete
- The js-* prefix prevents unintentional breaking changes by informing future developers to look for uses of the class in JavaScript files rather than stylesheets
Variables
- CSS variable names must be abstract and reusable
- Color palette:
- $midnight: #221F4D;
- $font-color-primary: $midnight;
- $storm: #516F90;
- $font-color-secondary: $storm;
- Utilities:
- $icon-sort-up: "\f0d8";
- $icon-sort-down: "\f0d7";
Formatting
In order to increase readability, please adhere to the following formatting standards.
- Separate selectors, declarations, and rulesets by new lines
- Use a new line for every selector
- Use a new line for opening and closing curly brackets
- Use a new line for every declaration
- Use one level of indentation (2 spaces) for nested declarations and rulesets
- Use double quotes for attribute values in selectors, e.g.: input[type=”text”]
- Use one space between the colon and value
- Use one space after each comma for comma-separated property values, e.g.: rgba($midnight, 0.3)
- Use one space before and after binary operators, e.g.: +, -, =, !=, ==, >, etc.
- Include a trailing semicolon after the value for every declaration
- Include one line of white space between rulesets
- Remove trailing white space
Nesting
- Avoid nesting whenever possible
- HTML and CSS structure changes frequently, and nested parent-child styles can quickly become obsolete and result in breaking changes
- Nesting increases specificity
- When nesting, nest sparingly and intentionally
- Limit nesting to 1-2 layers
- Reassess any nesting more than 2 layers deep
Comments
- Mark TODOs and action items with a TODO comment
/* TODO: vanilla CSS action item */
/* TODO: file-level or multi-line Sass, Less, or vanilla CSS action item */
// TODO: single-line Sass or Less action item
Casing
- Classes should be lowercase-and-hyphenated, e.g.: .my-class
- Ids should be camelCase, e.g.: #myId
- Elements should be lowercase, e.g.: h2
- Hex values should be uppercase — only use hex values in global variable files
Property values
- Include a leading 0 for property values and color parameters, e.g.: 0.5 instead of .5
- Use whole numbers when specifying values, e.g.: border-width: 1px; not border-width: .5px;
- Do not use shorthand hex values
- When allowed, use unitless 0
- Avoid specifying units for 0 values, e.g.: margin: 0; instead of margin: 0px;
- 0 values should not have units unless required, such as with transition-duration
- Line-height should be unitless unless it’s necessary to be defined as a specific pixel value
- Shorthand
- When setting property and value declarations around all four sides of an element, use one margin property and value declaration to set the values for all four sides at once, rather than using four different margin-based property and value declarations to set the margin for each individual side, e.g.: Do
padding: 0 10px 5px;
Don'tpadding-top: 0; padding-right: 10px; padding-bottom: 5px; padding-left: 10px;
- When setting property and value declarations for less than four sides of an element, shorthand should not be used. If a box only needs a bottom margin, use the margin-bottom property. This ensures that other property values will not be overwritten, and we can easily identify which side the property is being applied to without much cognitive effort, e.g.: Do
margin-bottom: 10px;
Don'tmargin: 0 0 10px 0;
- When setting property and value declarations around all four sides of an element, use one margin property and value declaration to set the values for all four sides at once, rather than using four different margin-based property and value declarations to set the margin for each individual side, e.g.: Do
- Vendor prefixes
- Don’t include vendor prefixes if the application is using an autoprefixer
- Opt for handling prefixing with a Sass mixin, if possible
- When using vendor prefixed features, put the standardized rule last to ensure browsers optimize and use the standard if they recognize it
Organization
- Large stylesheets should be broken down into several smaller stylesheets, e.g.: global styles, page-level styles, and component-level styles
- CSS rulesets should be ordered to match the order of the source code
- CSS declarations should be ordered using the “outside-in” method (based on the box model)
Declaration order
Random ordering is chaos, not poetry. Related property declarations should be grouped together. Our declaration order is set in our linter's config.
Our choice is logical or grouped ordering (“outside-in”), wherein properties are grouped by meaning and ordered specifically within those groups. The properties within groups are also strategically ordered to create transitions between sections, such as background directly before color. New features that are not yet used in our products may not have a prescribed place, but likely would fit into one of the categories in a logical manner. Just as CSS is evolving, so our standards will evolve with it.
Property value order
Top/Right/Bottom/Left (TRBL/trouble) should be the order for any relevant properties, e.g.: margin, much as the order goes in values.
- Top
- Right
- Bottom
- Left
Corner specifiers, e.g.: border-radius-*-*, should be top-left, top-right, bottom-right, bottom-left. This is derived from how shorthand values would be ordered.
Media queries
- "Mobile-first" — XS styles are default — start with the smallest breakpoint and work upwards
- Keep media queries grouped by media at the bottom of the stylesheet
- Only override what is necessary — do not redefine styles that do not change
Preprocessors
CSS preprocessors require additional formatting considerations. Different preprocessors have different features, functionality, and syntax. Conventions should be extended to accommodate the particularities of the preprocessor in use. The following guidelines are in reference to Sass.
Organization
Always place @extend statements on the first lines of a declaration block.
Where possible, group @include statements at the top of a declaration block, after any @extend statements.
Nesting
Avoid unnecessary nesting. Just because you can nest, doesn't mean you always should. Consider nesting only if you must scope styles to a parent and if there are multiple elements to be nested. Reassess any nesting more than 2 levels deep in order to prevent overly-specific selectors.
Operators
For improved readability, wrap all math operations in parentheses with a single space between values, variables, and operators, e.g.: margin: 10px 0 (@variable * 2) 10px;
Custom functions
Consider prefixing custom functions with fn-*. This helps to avoid any potential to confuse your function with a native CSS function, or to clash with functions from libraries.