Skip to content

Instantly share code, notes, and snippets.

@ryngonzalez
Last active August 29, 2015 14:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryngonzalez/c2a8aba799d9aa51b645 to your computer and use it in GitHub Desktop.
Save ryngonzalez/c2a8aba799d9aa51b645 to your computer and use it in GitHub Desktop.
Thready SASS Coding Guidelines

Base Styling

Here lies the foundation of the styling system. We reset default browser styles to give a clean slate upon which we build up the styles. If you find yourself defining a ruleset that fights against base styling, it should probably go here. Definitions of nice defaults for bare HTML elements should also be here.

Components

Every conceptual bit of interface that can be reused should be encapsulated in a component. You have a thing that triggers an action when clicked? Wrap it up into the concept of a button. A thing that displays a list of people allowed to view a space? Call it a memberList.

You get the point. When you have things that need to be used repeatedly and represent some behavior or functionality, wrap it up as a component. Build an Angular directive, react component, polymer element, etc. The style and presentation counterparts for these components live here.

NAMING

So it was a hard fought battle between doing what I like and doing the thing that makes reading selectors easy and decision making when writing selectors simple. I went with the latter. Much of what follows is primarily a blatant copy and paste of @fat's excellent CSS guidelines for Medium, though some extra bits of commentary and a few minor changes (@extend and component modifiers, naming recommendations, etc.) are added in from me. Also SUIT CSS from @necolas and Twitter, though things are modified a bit from that. Anyway:

componentName

The component's name must be written in camel case. Prefer using single word names for components if possible (to mimic the functionally limited but aesthetically preferred CSS convention of dash-spacing words).

.myBanner { /* … */ }
.banner { /* … */ }
<div class="myBanner"></div>

<div class="banner"></div>

componentName--modifierName

A component modifier is a class that modifies the presentation of the base component in some form. Modifier names must be written in camel case and be separated from the component name by two hyphens. To define the relationship between a component and its modifier, use SASS's @extend directive. Using @extend means that a modifier inherits the rules of a class it's extending. It allows for simpler markup, moves the definition of inheritance relationships into CSS rather than HTML.

/* Core button */
.button { /**/ }

/* Primary button style */
.button--primary {
  @extend .button;
  /**/
}

When defining rules for modifiers, make sure to check the order of definitions (hint: check your _index.scss file for your component). Component modifiers must be defined after the base component they modify.

<button class="button--primary"></button>

componentName-descendantName

A component descendant is a class that is attached to a descendant node of a component. It's responsible for applying presentation directly to the descendant on behalf of a particular component. Descendant names must be written in camel case.

We also call these subcomponents.

<div class="post--new">
  <header class="post-metadata">
    <img class="post-avatar"></header>
  <div class="post-body"></div>
</div>

componentName.is-stateOfComponent

Use is-stateName for state-based modifications of components. The state name must be Camel case. Never style these classes directly; they should always be used as an adjoining class.

JS can add/remove these classes. This means that the same state names can be used in multiple contexts, but every component must define its own styles for the state (as they are scoped to the component).

.post { /* … */ }
.post.is-selected { /* … */ }
<li class="post is-selected"></li>

so ends the naming section. from your friends @ryngonzalez, @fat, and @necolas. fin.

TL;DR

Syntax

<componentName>[--modifierName|-descendantName]

Example

  • Base component: .button
  • Component modifier: .button--primary
  • Component descendent: .button-text
Usage:
<a class="button--home" href="/">
  <span class="button-text">Home</span>
</a>

===

Soft encapsulation

The styling of a component should not effect unrelated components. From the SUIT CSS design principles doc:

The implementation of a component should not be exposed to other components. For example: your component should not leak styles into the HTML tree fragments of other components; a component's HTML should not be directly included in the HTML for another component.

Complexity is a significant problem for large, adaptive applications. The more you can reduce the entanglement of your components, the easier it is to reason about the system.

Nesting

If your components are large and have subcomponents, consider nesting the main styling and subcomponent styling in a subfolder.

components
|- _index.scss
|- myOtherComponent.scss
|
|- my-component
   |- _index.scss
   |- myComponent.scss
   |- myComponent-subcomponentOne.scss
   |- myComponent-subcomponentTwo.scss

Sharing Rules Between Components

If you find the need to share styling specific to the current application between components, create a shared placeholder (%placeholder-class) style in ../styles and @extend that base where needed in your components.

If you see the need to add styling that can be applied in a more general sense across applications, and serves a functional selector (layout helpers, style resetting, etc.) expand or create new styling utilities in ..\utilities.

Sharing Rules Within a Component

If you find yourself with a set of rules that manifests in many parts of a component and its subcomponents, define those rules up front (in the equivalent myComponent.scss file of your component folder) and @extend a placeholder or an existing class. If you find yourself doing this, consider whether the ruleset can be further pushed up the stack (to intra-component sharing).

Style Configuration

You want to define all re-usable variables in the style system in this folder. Colors, z-index levels, custom easings, etc. should all live here.

File Naming

Since these files hold variables and configuration to be used elsewhere, you should prefix them with an underscore, so that the SASS compiler doesn't compile anything contained in these files unless imported by another file that is slated for compilation. Names should reflect the variable map being defined, and should be in the plural form.

For example: _z-indexes.scss, _font-weights.scss, etc.

Map keys

When defining a new map value, make sure the key is a string.

$colors: (
  'red':    #E46766
);

Colors

Colors should be aliased to a descriptive name (this site should be helpful) and any names describing their use in the style system. All color variables should be added to the $colors map. Space out groups of the same color with newlines, and keep the values of each group vertically aligned. You can either use the $colors map to access the color, or access via the corresponding map helper function. For example:

// Definition

$colors: (
  'red':    #E46766,
  'error':  #E46766,
  'denial': #E46766,

  'blue': #2A7BBC,

  'green':   #72B456,
  'confirm': #72B456,
  'success': #72B456
);

// Usage

.my-thing {
  color: map-get($colors, 'red');
}

.my-other-thing {
  color: colors('red');
}

.error {
  color: colors('error');
}

Easings

Easing follows the same model as colors, with a global map and helper function as well. Use descriptive naming pls.

z-index

The point of having a z-index scale is to allow for z-index issues to be fixed by modifying only one file. All z-index problems can be resolved here. If you have a new component that needs a z-index, add the appropriate z-index into the $levels map with the appropriate z-index value. Only use the bare, numeric z-index values when prototyping or in the process of building out components. Only use set of z-index values already defined (600 is preferred over an arbitrary level like 666 or 100000). Sometimes, variables will need to reordered, with the numeric values lowered. That's fine.

// Definition
$levels: (
  …
  '6': 600,
  'hovering': 600
);

.my-hovering-thing {
  z-index: levels('hovering');
}

Libraries

Sometimes bringing in outside styling is necessary. Avoid if possible. When you do decide that it's worth it, try paring down the scope/amount of outside code. If you find the need to modify library code, consider re-writing it as a system component or style.

@fat @ryngonzalez

SASS Coding Guidelines

The SASS documentation is your friend. Know your SASS functions too. Get familiar with those two links, keep them close at hand.

Table of Contents

Organization

Like CSS itself, we use a cascading organization to make sure rules are defined in the correct and expected order.

main.scss
|
|__@import 'libraries/index';
|
|__@import 'utilities/index';
|
|__@import 'config/index';
|
|__@import 'base/index';
|
|__@import 'styles/index';
|
|__@import 'components/index';
|
|__@import 'pages/index';

Our folder hiearchy goes from external, non-specific, to highly-specified and application-specific. If you want to know where to put things, read the READMEs available in each folder.

If you need to add a new subfolder (anywhere), make sure to use an _index.scss and follow the pattern shown elsewhere.

Semantics

How do you name a thing? How does a name represent the thing it describes, and how does one systematically determine that mapping given either a name or a thing? The semantic rules below describe how names map to the concept of a thing.

Functional Selectors vs Conceptual Selectors

Functional selectors describe the behavior or an aspect of a thing. Conceptual selectors describe what a thing is.

Given some markup:

<div class="lightbox is-visible"></div>

…and some styling:

.lightbox {
  background: white;
  border: 1px solid grey;
  box-shadow: 0px 1px 4px rgba(0,0,0,0.3);

  &.is-visible {
    display: block;
  }

}

.lightbox describes the concept of a lightbox ascribed to a <div>. .is-visible describes the current behavior and state of the element.

Functional Selectors

For stateful, functional selectors, always prefix them with .is-. When trying to determine the behavior and name of a functional selector, always determine what the default state of the item it's trying to modify is. For example, I have a button that's only visible when some user interaction has occurred:

<button class="button--confirm"></button>

So I determine the base state to be display: none;, and I create a functional selector that modifies the state to display: block; when appropriate:

.button--confirm {
  …
  display: none;

  &.is-visible {
    display: block;
  }
}

Formatting

Use two-space indenting.

Selector Spacing

CSS rules should be comma separated but live on new lines:

Right:

.content,
.content-edit {
  …
}

Wrong:

.content, .content-edit {
  …
}

CSS blocks should be separated by two newlines.

Right:

.content {
  …
}

.content-edit {
  …
}

Wrong:

.content {
  …
}
.content-edit {
  …
}

Quotes

Quotes are optional in CSS and SCSS. We use single quotes as it is visually clearer that the string is not a selector or a style property. Use double quotes only when you need to do SASS string interpolation.

Right:

background-image: url('/img/you.jpg');
font-family: 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial;

Wrong:

background-image: url(/img/you.jpg);
font-family: Helvetica Neue Light, Helvetica Neue, Helvetica, Arial;

Nesting

Use SASS's nesting ability with caution. Nest when you need to add a functional selector definition to styling. Also use it for the equivalent built-in CSS pseudo-selectors. Break out named subcomponents into un-nested rulesets. If you have general, non-named descendants that need to be styled, that's a fine place to nest. Just consider adding subcomponent classes to descendants and breaking them out in the future. If you have a modifier class that modifies an existing component, and needs to modify and existing component's subcomponents, that's also a good place to nest.

.button--confirm {
  …
  display: none;

  &.is-visible {
    display: block;
  }

  &:active {
    …
  }

  .button-content {
    color: green;
  }

  span {
    display: inline;
  }

}

Ruleset Organization

When writing rulesets, organize the rules within them as follows:

.button {
  @extend …;
  @include …;

  // Layout// Appearance// Typography// Modifiers// Children
  …
}

Only split things up if there are enough rules in the ruleset such that there are multiple sections. Put @extends and @includes at the top of the ruleset.

Performance

Specificity

ul.user-list li span a:hover { color: red; }

Styles are resolved during the renderer's layout pass. Selectors are resolved from right to left, resolving when it has been detected the selector does not match. Therefore, in the example above, every <a> tag has to be inspected to see if it resides inside a span and a list. As you can imagine this requires a lot of DOM walking and and for large documents can cause a significant increase in the layout time. For further reading checkout: https://developers.google.com/speed/docs/best-practices/rendering#UseEfficientCSSSelectors

If we know we want to give all <a> elements inside the .user-list red text on hover we can simplify this style to:

.user-list > a:hover {
  color: red;
}

If we want to only style specific a elements inside .user-list we can give them a specific class:

.user-list > .link-primary:hover {
  color: red;
}

Specificity Resources

Definitions

  • Selector: A sequence of class names, id names, etc. that defines the elements effected by the ruleset subsequently defined

  • Ruleset: a set of CSS declarations that are defined on a selector or group of selectors

  • SASS: the name of the CSS preprocessor and the name of the language that is processed into CSS. Also the name of a deprecated syntax that the language can be written in. The file extension for SASS syntax is .sass

  • SCSS: the name of the current and preferred syntax to write SASS language code. The file extension for SCSS syntax is .scss

===

The Mission

The goal here is to make writing the best CSS in the world easy, through well-defined rules and smart system design. Hopefully this is helpful, and does its job well. Don't be afraid to make changes and modify the rules after finding out a better way of operating.

So, go forth and make more kickass CSS.

Pages

Pages should include the rulesets that define view and state-specific styling — if you can (or do) map a state to a URL, and it has the need to modify components styling in that context or define custom styles, you should probably add a page style.

Naming

Use PascalCase (uppercase all words). For example:

  • Members.scss

  • PostView.scss

  • Members.scss

Descendent naming

Treat descendent elements of pages as components, with the same naming scheme used elsewhere (camelCase).

Mo' Page Code, Mo' Problems

Ideally, you'll want to define styles in way that components can be created and reused, and then applied in a page with minimal modification. If you find yourself writing more and more page code, you probably have a code smell. Reconsider whether the code can be refactored into components. Also consider if the design being implemented lends itself to using existing components; if you find yourself building out styles that are unnecessarily divergent from existing stlyles, you have a design smell. Talk to the designer (even if it's yourself) and work through why this new page doesn't use existing components when it could. This will lead to better, more modular code and design, and allow for flexible creation of new components & pages later.

Styles

Styles are rulesets that are used between different components, and are only useful within this particular application. Visual styles, common layout patterns, and other reusable, application-specific code should go here.

Placeholder Classes

If you're going to add a style, you're probably going to want to use a placeholder class: define the actual classes that utilize this ruleset elsewhere in your pages and components, and @extend this placeholder class from there.

// _inset.scss
%inset {
  …
}

// components/input.scss
.input {
  @extend %inset;
  …
}

// components/textarea.scss
.textarea {
  @extend %inset;
  …
}

Utilities

Utilities include SASS mixins and placeholders classes that define functional rulesets (positioning, float helpers, etc.) If you can extract some rules into a mixin or placeholder that can be used in different applications, they probably belong here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment