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:
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.
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.
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:
padding: top right bottom left;
extend
. Create a separate component or use mixins instead.js-
prefixed classes in CSS. They are reserved for JS files.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".
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;
}
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.
DO:
.button
and .btn
are good, but .b
is overdoing it..navigation__item
and .navigation__link
over .navigation__item__link
. This makes the styling depend less on the markup structure.DON'T:
&
. The only exception to this rule are pseudo classes.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.
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>
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
: 1200pxEven 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.
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.
Mixin | Description |
---|---|
button-unstyled() |
Button utility mixin to remove browser button styles. |
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. |
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). |
Mixin | Description |
---|---|
state() |
Combines state-related pseudo-classes. A $class option can be passed in, e.g., ('is-active') |
Mixin | Description |
---|---|
text-truncate() |
Truncate text (one line only) when it gets too long by appending ... |
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. |
Mixin | Description |
---|---|
list-unstyled() |
Removes default browser list styling. |
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. |
toolkit.yml
and commit.npm run build
to create the package.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. |
master
.npm publish
.Components are individual html
files and the materials
directory is watched by fabricator.
HTML
in the corresponding sub-folder in src/materials
. See Ordering docs for more information.SCSS
in the corresponding sub-folder in src/toolkit/styles
.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.
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.
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.
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 |
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 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.
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.
Design Debt can be caused intentionally or unintentionally.
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.
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.
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.
We calculate a Design Debt Score (DDS) for each issue.
design debt
, the issue contains:
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.