⚠️ DEPRECATION WARNING – Use waffles.datacamp.com instead!

Docs

Introduction

A front-end styleguide such as this one is useful for a number of reasons. Many companies, both small and large, have adopted this system over the last few years to bring some form of standardisation and conventions to their front-end code.

The goal of this styleguide is to:

  • document company conventions for writing front-end code
  • outline best practices for creating new components
  • provide consistency to the codebase
  • increase productivity for engineers with other areas of expertise
  • improve the on-boarding process

The styleguide is "in development" most of the time and as long as we don't have a dedicated team that maintains it, everyone should pitch in to make sure it stays up to date and reflects group decisions and changes to the layout.

What belongs in the styleguide?

Basic styles and components that define our UI such as headings, links, forms, buttons,... should live in the styleguide. More advanced UI components can be added as well but there are some requirements they need to meet.

Here are some questions you should ask yourself before adding something to the styleguide:

  • Will this component be used in other places/apps/services or is this a specific thing for the app I'm working in right now?

    Think of a page that requires a custom layout and/or styling. It doesn't make sense to add that to the styleguide because it won't or can't be reused elsewhere.

    So in short, if your thing is app specific, its styles and partials belong in that app. We can always move it out to the styleguide later if we decide we want to reuse it elsewhere.

  • Is this styling brand or partner specific? We might require styling that's specific to a brand or company other than DataCamp such as PluralSight.

    If this is the case, we can still add it to the styleguide if it meets the requirements mentioned above (reusable by other apps/services?). There is one important consideration however.

    Since the styling is tied to a different brand and typically has to meet certain requirements, it's recommended to not reuse any of our existing components. A good example would be the PluralSight flash banner component. We have a flash banner system in place but if we were to reuse that for the PluralSight variant, we would have to override 90% of the existing styling. This is typically a good indicator that the component you are trying to use is not suitable for your use case.

    Even if our component does seem suitable, the brand's styling requirements might change in the future, making it unsuitable after all. So play it safe and create a separate component even though it might look similar in some ways.

Code Style

We're using the Sass 3 aka SCSS aka Sassy CSS preprocessor which essentially adds additional functionality on top of CSS3. Extensive documentation on the language can be found here.

Some general guidelines on how we write CSS:

  • Write shorthand when possible i.e. padding: top right bottom left;
  • Use color variables instead of hex codes for maintainability.
  • Group all selectors in one media query when there's multiple overrides for a single breakpoint.
  • No need to use browser prefixes, we use Autoprefixer for that purpose.
  • Don't use extend. Create a separate component or use mixins instead.
  • Don't use hard-coded widths. We're in the responsive game!
  • When setting explicit dimensions, provide a comment explaining where those magic values are coming from.
  • Avoid scoping html elements by tag. Use classes instead.
  • Never use IDs. Use classes instead.
  • Don't scope js- prefixed classes in CSS. They are reserved for JS files.

Naming Convention

CSS naming conventions are useful for making your styles more transparent and informative. They also limit chances of conflicting styles.

We chose BEM as a naming convention because it's widely adopted and has existing documentation. BEM naming reflects a modular system in which blocks are reusable. They consist of a "Block" element which represents the root of your component. Its "Elements" are the component parts of your "Block" and "Modifiers" can be used to create variants of a "Block".

BEM in practice

HTML:

<ul class="navigation">
  <li class="navigation__item">
    <a class="navigation__link" href="/foo">Foo</a>
  </li>
  <li class="navigation__item">
    <a class="navigation__link" href="/bar">Bar</a>
  </li>
</ul>

CSS:

.navigation {
  background-color: blue;
  padding: 10px;
}

.navigation__item {
  display: inline-block;
  margin-right: 10px;
}

.navigation__link {
  color: black;
}

Modifiers

Now, suppose we want to use the same navigation, but styled slightly different. We'll need to update the styling somehow, without affecting the original component and styling from the example above. This is where the M in BEM comes in which stands for "modifier". Applying this concept, our code would like like this:

HTML:

<ul class="navigation navigation--light">
  <li class="navigation__item">
    <a class="navigation__link" href="">Foo</a>
  </li>
  <li>
    <a class="navigation__link" href="">Bar</a>
  </li>
</ul>

CSS:

/* let's override the background-color, just for this "light" variant */
.navigation--light {
  background-color: white;

  /* we can change styling for children too */
  .navigation__link {
    color: red;
  }
}

Through a minimal amount of nesting and using CSS's cascading effect, we were able to adjust the styling as needed.

Recommendations

DO:

  • Keep naming short, but meaningful. .button and .btn are good, but .b is overdoing it.
  • Use modifiers only on the root selector of your component. See example above on how to use them.
  • Address only first level children i.e. use .navigation__item and .navigation__link over .navigation__item__link. This makes the styling depend less on the markup structure.
  • Use lowercased class names separated by hyphens.

DON'T:

  • No need to nest selectors unless you're overriding something through a modifier.
  • When nesting, don't concatenate selector names through the use of &. The only exception to this rule are pseudo classes.

Grid

We rely on Bootstrap's (v3) grid system. A new v4 version is coming out soon but you can find Bootstrap's grid compiled here or view the LESS version here. We'll list some important features of this grid below, but you might want to check out Bootstrap's documentation for more in depth documentation.

Responsive and mobile-first

This grid is responsive and designed mobile-first. It allows up to 12 columns as the viewport size increases and uses predefined classes for laying out the grid.

What you need to keep in mind is that the content container and its columns are full width by default. They are assigned specific width values as the viewport size grows through the use of media queries.

The example grid below shows all of these features, including nesting another grid inside a column.

<div class="container">
  <div class="row">
    <div class="col-sm-6">.col-sm-6</div>
    <div class="col-sm-6">.col-sm-6</div>
  </div>
  <div class="row">
    <div class="col-md-4">.col-md-4</div>
    <div class="col-md-4">.col-md-4</div>
    <div class="col-md-4">.col-md-4</div>
  </div>
  <div class="row">
    <div class="col-lg-6">.col-lg-6</div>
    <div class="col-lg-6">
      <!-- nested grid -->
      <div class="row">
        <div class="col-lg-4">.col-lg-4</div>
        <div class="col-lg-4">.col-lg-4</div>
        <div class="col-lg-4">.col-lg-4</div>
      </div>
    </div>
  </div>
</div>

Grid Breakpoints

Note: the below breakpoint Sass variables are DEPRECATED unless using the Bootstrap Grid.

Bootstrap uses some predefined breakpoints and uses min-width based media queries to detect them. You can use the following breakpoints:

  • $dc-bp-xs: 480px
  • $dc-bp-sm: 768px
  • $dc-bp-md: 992px
  • $dc-bp-lg: 1200px

Gutters and padding

Even though the grid is reponsive and columns can vary in width, its gutters and padding are consistent. The gutter width (space in between columns) is always 30px which is created by adding a 15px padding on either side. This is an important thing to keep in mind when using background images for example.

Don't forget to wrap your rows in a container element which will take care of proper alignment and padding. The container uses that same 15px padding on either side to avoid your content from sticking to the side of the screen.

Responsive utilities

Bootstrap provides responsive utility classes for showing or hiding content depending on the current breakpoint. While this can sometimes be necessary, it is discouraged from toggling content visibility.

This strategy can lead to duplicate content in the DOM which can be confusing for search engines and screen readers.

Mixins

Button

Mixin Description
button-unstyled() Button utility mixin to remove browser button styles.

Clearfix

Mixin Description
clearfix() Applies a clearfix to your element in order to self-clear its children. See https://css-tricks.com/snippets/css/clear-fix/ for more context.

Layout

Mixin Description
stretch() Stretch an element's width and height. Note: This works with both absolute and fixed positioning depending on your use case. Add one of those to make this work, (e.g., full screen overlay).

State

Mixin Description
state() Combines state-related pseudo-classes. A $class option can be passed in, e.g., ('is-active')

Text

Mixin Description
text-truncate() Truncate text (one line only) when it gets too long by appending ...

Links

Mixin Description
link-unstyled() Removes default link styling and inherits styles from its parent. This can be useful when nesting a hyperlink inside a heading.
link-borderless() Removes the border from a hyperlink on hover and sets its font weight back to 300 to match body text.

Lists

Mixin Description
list-unstyled() Removes default browser list styling.

Responsive

Mixin Description
mq-retina() Write whatever specific styles you need to apply to high resolution displays using this mixin. It will output those styles inside a media query using @content. See this resource for more context.

Workflow

  • Build, test, and review additions or changes in branches.
  • Upon review approval, create a relase branch.
  • Update Styleguide version in toolkit.yml and commit.
  • Run npm run build to create the package.
  • Update the release version number and documentation. Releases are using semantic versioning and are updated with the npm version command.
Command Description
npm version patch Bug fixes to the current version.
npm version minor New components for the consuming app or apps.
npm version major Breaking changes for the consuming app or apps.
  • Merge the release branch and push npm tag to master.
  • Document the release, including what it includes and implementation steps.
  • Publish the release npm publish.
  • Announcements are automated in the #announcements Slack channel.
  • Implement the release in all consuming apps.

Adding A Component

Components are individual html files and the materials directory is watched by fabricator.

  • Add or edit a named file with the HTML in the corresponding sub-folder in src/materials. See Ordering docs for more information.
  • Add or edit a named file with the SCSS in the corresponding sub-folder in src/toolkit/styles.
  • Import the new SCSS file in the root file of the folder. (example: dc-core.scss).

Bug: When adding a new folder in materials, the watch fails occasionally. Restarting fabricator will show the component.

Spacing System

An important thing in interface design is having consistent spacing, margins, and paddings between all of the components. To increase the consistency in the UIs across the DataCamp products, we have implemented a spacing system. These sizes should be the default and there should be a good reason to use sizes other than the ones listed.

Based on 16 pixels

Our font size is 16 pixels which is a good default font size. It’s a factor of all screen resolutions (320, 768, 1024). And it provides memorable multiples greater (32, 64, 128, …) and factors less (8, 4, 2) than where it starts.

We've picked a geometric progression of spacing since it drastically reduces the amount of possibilites opposed to a linear progression.

Spatial System Values

Pixels Ems
XXS 2px 0.125em
XS 4px 0.25em
S 8px 0.5em
M 16px 1em
L 32px 2em
XL 64px 4em
XXL 128px 8em

Spacing values in the style guide CSS

Depending on the markup, combinations of classes may be needed to achieve the system spacing values. These values are available as utility classes and are approved in PRs. Variations from this list should have a comment for the reasoning behind the variation.

2, 4, 8, 12, 16, 24, 32, 48, 64, 80, 96, 128

Design Debt

1. What is Design Debt?

Design Debt is the total amount of user-facing inconsistencies¹ in a product that affects the integrity of the user experience.

¹Inconsistecies – An inconsistency is an unwanted side-effect that occurs either intentional or unintentional.

2. Why is Design Debt bad?

Having similar components that work in different ways reduces consistency and ease-of-use. If a product needs to be constantly re-learned and feels hard to use, it will result in churn. Design Debt also slows developer velocity because each visual variation comes with code that needs to be understood and maintained.

3. What causes Design Debt?

Design Debt can be caused intentionally or unintentionally.

3.1. Intentional
  • In order to gain traction on a certain feature, it’s possible that some shortcuts have been taken in order to get faster results. This is a deliberate decision made by the team and it should be flagged as Design Debt so it can be resolved in future iterations.

  • Improving a component in the design system can result in temporary Design Debt in order to adopt the improved component across applications. Design Debt should be flagged and a strategy should be created to resolve the debt.

  • Because of technical constraints, the team can opt to differ from the design system. This is intentional Design Debt and is not meant to be resolved in the near future.

3.2. Unintentional
  • Unintentional Design Debt can occur whenever components have a cascading impact, resulting in unexpected results.

  • Whenever a team is unaware of the existence of a component and implements it differently. Can be resolved by educating the company.

  • The original design may be stretched beyond its original intentions, resulting in elements that feel shoehorned in or taken past their intended capabilities (menus that were designed to accommodate 5 items suddenly have to accommodate 8, etc.)

  • Technical requirements change, and now markup that was originally in vanilla HTML is being implemented in React JSX, etc., where best practices are different.

3. What counts as Design Debt?
  • Inconsistencies between the design system and implementation.

  • Irregularities in the user experience.

  • As our app gets larger and the design language evolves, it may start to diverge.

4. How do we measure Design Debt?

We calculate a Design Debt Score (DDS) for each issue.

  1. For each occurrence in an issue, we assess the importance. The importance includes a design debt score. a. Low – 1 b. High – 5 c. Urgent – 10
  2. The sum of the design debt score of each occurrence is the total design debt score for that issue.
5. How do we report Design Debt?
  • Create a GitHub issue on the style guide repository labeled with design debt, the issue contains:
    • A description describing the design debt
    • A proposed solution for the design system
    • Identify all the occurrences across apps where this design debt occurs + grade importance
    • A roadmap to reduce design debt across applications
    • Calculate Design Debt Score
6. Resolving Design Debt
  • Tackle small pieces at a time. It’s much easier to clean up individual components than entire screens.

  • Focusing on components elements rather than whole chunks of a design will give you a more tangible sense of forward momentum. Individual components are finite and less likely to cascade into a redesign of epic proportions.

  • Work from most visible to least visible. Every digital project has dark corners, outside the usual path that users take. It might be an infrequently accessed settings screen or a sub-page of a sub-page hidden behind a footer link. While these edges might be the first elements to show signs of decay, they aren’t necessarily the first that should get attention.

  • The best defense against Design Debt is a constant stream of feedback, both internal and external.

  • No project has infinite resources, regardless of whether it’s built by a corporate behemoth or a fledgling startup. Design Debt often means you’re iterating quickly and have to triage your time to favor building new features rather than polishing old ones.