The USX approach to CSS and SASS
- Terminology
- File Structure
- Documentation
- Rules
- Indentation
- Formatting
- Nesting
- Selectors
- Properties
- SASS Variables
- SASS Components
- Reserved Classes
-
1.1 Block: A block is the name given to a selector (or a group of selectors) with an accompanying group of properties.
.listing { font-size: 1.1rem; line-height: 1.2; }
-
1.2 Selectors: In a block, selectors are the bits that determine which elements in the DOM tree will be styled by the defined properties. The selectors can match HTML elements, as well as an element's class, ID, or any of its attributes.
.my-element-class { /* ... */ } [aria-hidden] { /* ... */ }
-
1.3 Properties: Finally, properties are what give the selected elements of a block their style. Properties are key-value pairs, and a block can contain one or more property declarations.
/* some selector */ { background: #F1F1F1; color: $gray; }
-
2.1 Use the .scss syntax, never the deprecated .sass syntax.
Bad
touch foo.sass
Good
touch foo.scss
-
2.2 All SCSS/CSS should go into the
.scss
files found within a ModulesResources/assets/sass/*
directory. It should never be put into Vue single-file component.Why? Even though component SCSS/CSS can be conveniently modularized, and scoped, using Vue single-file components it can be hard to track down within the codebase. It also doesn't get run through the stylelint linting on webpack build or PR Bamboo build. Having it within our SASS files keeps our SCSS centralized and is easier to find and debug.
-
2.3 Below is a breakdown and description of a Modules
Resources/assets/sass/
directory structure:Resources/assets/sass/
- The primary module import files. These files should contain no actual styling code, just order-specific
@imports
for building the modules CSS output.
- The primary module import files. These files should contain no actual styling code, just order-specific
Resources/assets/sass/breakpoints
- This contains general rules for the primary global framework separated by the applications pre-determined breakpoints. This should remain lean. Most styles are broken down into more specific SASS files described below.
Resources/assets/sass/components
- USX Vue Component styles by file. Example:
modal.scss
contains all the style code for the USX Vue Modal Component.
- USX Vue Component styles by file. Example:
Resources/assets/sass/globals
- This is the primary directory for global, reusable styles pertaining to specific functionality or types (but not partials or any page specific styles).
Resources/assets/sass/modules
- Modules refer to SASS specific module code. These include:
Resources/assets/sass/pages
- Specific styles used by a single page represented by a single file. Example:
view-order.scss
would contain non-reusable styles that pertain solely to the order page.
- Specific styles used by a single page represented by a single file. Example:
Resources/assets/sass/partials
- Contains code grouped by global entities or components. Example:
sidebar-navigation.scss
contains all the style code for the ModuleCow sidebar. - The
partials
directory is also a good place to abstract out reusable code that can be used by multiple pages or the framework in general. Example: Create a reusable.employee-card
class (and nested blocks) that may be used on the employee profile page, but also used again within other pages, such as the employee "About Us" admin page. If you ever build a styled class for a page and think it may have benefits as a reusable entity, you should abstract it out into thepartials
directory.
- Contains code grouped by global entities or components. Example:
Resources/assets/sass/vendor-extensions
- This is used to extend, and override, classes and styles that are vendor specific. Meaning that the vendor files themselves are loaded through Yarn and the primary vendor SASS/CSS file resides within the
node_modules
directory. This directory contains style rules that will extend or override those vendor defaults. The SCSS filename should match the vendor tool name.
- This is used to extend, and override, classes and styles that are vendor specific. Meaning that the vendor files themselves are loaded through Yarn and the primary vendor SASS/CSS file resides within the
-
3.1 File Documentation: All new SCSS files should contain a DocBlock style multi-line comment providing a general explanation of the category or grouping the styles in that file represent.
/** * ModuleCow Bulma Extensions and Overrides * * These are exact class specific overrides to Bulma classes. Use this modular file * when overriding or extending a rule or rule set on an already existing Bulma class. */ /* initial block */ { ... }
-
3.2 Prefer
//
inline comments to standard single line CSS block comments (SCSS files only)./* bad */ .foo { ... } // good .foo { ... }
-
3.3 Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.
.foo { // bad ... } .foo { // bad .bar { ... } } // good .foo { ... } .foo { // good .bar { ... } }
-
4.1 Our primary style and structure is dictated by our configured styelint rule set.
Please see the .stylelintrc file for all enforced CSS/SASS style rules.
Detailed rule explanations can be found here.
-
4.2 Use
0
instead ofnone
to specify that a style has no border..Bad
.foo { border: none; }
Good
.foo { border: 0; }
-
5.1 Use soft tabs (space character) set to 4 spaces.
Bad
.foo { ∙font-size: 1.2rem; } .foo { ∙∙font-size: 1.2rem; }
Good
.foo { ∙∙∙∙font-size: 1.2rem; }
-
6.1 Below are our general formatting guidelines. For a complete overview of enforced CSSS/SCSS formatting see our .stylelintrc configuration file.
-
When using multiple properties in a rule block, give each property its own line.
/* bad */ .foo { background: $white; color: $black; } /* good */ .foo { background: $white; color: $black; }
-
Put a space before the opening brace
{
in rule blocks/* bad */ .foo{ background: $white; } /* good */ .foo { background: $white; }
-
In properties, put a space after, but not before, the
:
character./* bad */ .foo { background:$white; } /* good */ .foo { background: $white; }
-
Put closing braces
}
of rule blocks on a new line/* bad */ .foo { background: $white;} /* good */ .foo { background: $white; }
-
Put blank lines between rule blocks
/* bad */ .foo { background: $white; } .bar { color: $black; } .foo { background: $white; .bar { color: $black; } } /* good */ .foo { background: $white; } .bar { color: $black; } .foo { background: $white; .bar { color: $black; } }
-
-
7.1 Nesting blocks is required, even if no properties exist on the selector itself.
Why? Updating and extending selectors is much easier when the nesting structure is already in place.
Bad
.foo { font-size: 1.2rem; } .foo .bar .faz { color: $grey; }
Good
.foo { font-size: 1.2rem; .bar { .faz { color: $grey; } } }
-
7.2 Attempt to keep primary selector nesting to no more than 4 levels deep, not including pseudo-elements or pseudo-classes. We won't include enforced linting for this rule but it is best practice.
Why? When selectors become this long, you're likely writing CSS that is:
- Strongly coupled to the HTML (fragile) —OR—
- Overly specific (powerful) —OR—
- Not reusable
Bad
.page-container { .profile { .employee { .employee-history { .icon { /* STOP! */ } } } } }
Good
/* create reusable employee card styling */ .employee-card { .history { .icon { ... } } }
-
8.1 Always use slug naming (dashed compounds) for class and ID selector names.
Bad
.fooBar { ... } .foo_bar { ... }
Good
.foo-bar { ... }
-
8.2 Prefer classes over IDs. IDs should rarely be used.
Why? While it is possible to select elements by ID in CSS, it should generally be considered an anti-pattern. ID selectors introduce an unnecessarily high level of specificity to your rule declarations, and they are not reusable. For more on this subject, read CSS Wizardry's article on dealing with specificity.
Bad
#foo { ... }
Good
.foo { ... }
-
8.3 JavaScript Hook Classes: Avoid binding to the same class in both your CSS and JavaScript. Due to the fact that we utilize the Vue framework throughout our application, a specific JavaScript hook class should rarely, if ever, be required (currently none exist at the time of this writing).
However, if it is required that you use JavaScript to "reach" into the DOM to manipulate a specific element you should use a unique, JavaScript specific, class prefixed with
.js-
.Why? Conflating the two often leads to, at a minimum, time wasted during refactoring when a developer must cross-reference each class they are changing, and at its worst, developers being afraid to make changes for fear of breaking functionality.
Create a JavaScript-specific class to bind to, prefixed with
.js-
:<button class="btn btn-primary js-request-prescription">Request Prescription</button>
-
9.1 Properties should be ordered alphabetically. We have a Gulp command to implement this automatically. It should be run before submitting a PR request.
Why? It adds consistency and makes it easier to locate properties within a block.
Bad
.foo { z-index: $base-z-index + 5; font-family: Arial, sans-serif; background: $white; width: 100vw; font-size: 1.1rem; display: block; font-weight: bold; }
Good
.foo { background: $white; display: block; font-family: Arial, sans-serif; font-size: 1.1rem; font-weight: bold; width: 100vw; z-index: $base-z-index + 5; }
-
10.1 Always use slug naming (dashed compounds) for SASS variables.
Bad
$fooBar: #FFF; $foo_bar: #FFF;
Good
$foo-bar: #FFF
-
10.2
z-index
properties should always extend from one of the basez-index
SASS variables.Bad
.foo { z-index: 999; }
Good
.foo { z-index: $base-z-index + 5; }
-
10.3 SASS colors should always be variables. Most framework colors should already be defined and you should attempt to use the preset colors defined within the SASS variables to keep the UI consistent. If you must utilize a new color that isn't already defined then you should add it to the
variables.scss
file with a semantic name.Why? This allows for consistent, and reusable, color palettes for the UI. It also allows us to change color options site wide with a single variable.
Bad
.foo { color: #F1F1F1; background: #333; }
Good
.foo { color: $off-white; background: $gray; }
- 11.1 Mixins: Mixins should be used to DRY up your code, add clarity, or abstract complexity--in much the same way as well-named functions.
- 11.2 Extend Directive:
@extend
should be avoided because it has unintuitive and potentially dangerous behavior, especially when used with nested selectors. Even extending top-level placeholder selectors can cause problems if the order of selectors ends up changing later (e.g. if they are in other files and the order the files are loaded shifts). Gzipping should handle most of the savings you would have gained by using@extend
, and you can DRY up your stylesheets nicely with mixins.
-
12.1 The following are reserved classes used by the USX framework, vendor libraries, or components.
.dropzone
.animated