Conventions
- Create and extend CSS files following global patterns. Minimize customization, as features are rarely styled in isolation.
- Avoid descendant selectors (i.e. don’t use
.sidebar h3
). - Avoid attaching classes to elements (i.e. don’t do
div.header
orh1.title
). - Avoid using
!important
, except in rare cases. - Use a LESS / SCSS compiler for readability. CSS-in-JS is popular, but not as useful in Agile teams with only basic CSS skill sets.
- Use shorthand properties everywhere (i.e.
border: 1px solid red
) - Always specify margin and padding as complete selector sets (i.e. use
margin: 1rem 0 0 0
to specify top margin rather thanmargin-top: 1rem
)
CSS Files
Aim for the smallest CSS footprints by organizing LESS / SCSS styles into separate files that can be imported selectively for each view as needed.
Order styles from general to specific. Watch out for selector order - the last selector loaded wins.
Variables
Repeating elements are good candidates for turning into variables:
@media
breakpointscolor
border
andborder-radius
box-shadow
@font-face
Declaration Order
Order declarations alphabetically under selectors (they're easier to maintain that way).
Place vendor selectors (-webkit, -moz, -ms, -o) before their conventional counterparts.
.site-header {
align-items: center;
border-bottom: 1px solid var(--color-border);
display: flex;
justify-content: flex-start;
opacity: 1;
padding: .5rem 2rem;
top: 0px;
-webkit-transition: 1s;
-moz-transition: 1s;
-ms-transition: 1s;
-o-transition: 1s;
transition: 1s;
visibility: visible;
width: 100%;
z-index: 100;
}
Selector Names
Leverage the name to describe a feature or function better than or in place of a comment (i.e. .site-header-mobile
).
Classes
- Follow a kebab lowercase case convention (
class="block-header-mobile"
), or Block_Element-Modifier (BEM) syntax when not using SCSS/LESS (class="search-card__header-mobile
). - Name and collect selectors into the blocks, features or components they describe. Avoid one-offs.
- Avoid IDs as code hooks, but if unavoidable, prefix the class name with "js-" or "ts-" (
class="js-search-button"
)
IDs
- Avoid IDs as styling hooks. Reserve for JavaScript/TypeScript hooks. Follow camelCase convention (i.e.
id="modal-Dialog-Open-Click-21"
)
Order classes to create a visual hierarchy, like:
.card-wrapper
.card
.card_header
.card_header_image
.card_main
.card_main-red
...
Create global classes that can be added as modifiers to any HTML element as needed (i.e.: hide, center and apply block shadow to the card block class="card hide center shadow"
)
Text Case
Sentence
- upper =>
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
- lower =>
the quick brown fox jumps over the lazy dog
Flat
- upper =>
THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG
- lower =>
thequickbrownfoxjumpsoverthelazydog
Pascal
TheQuickBrownFoxJumpsOverTheLazyDog
Camel
theQuickBrownFoxJumpsOverTheLazyDog
Snake
- upper (aka train case, screaming kebab) =>
THE_QUICK_BROWN_FOX_JUMPS_OVER_THE_LAZY_DOG
- lower =>
the_quick_brown_fox_jumps_over_the_lazy_dog
- pascal =>
The_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog
- camel =>
the_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog
Kebab
- upper =>
THE-QUICK-BROWN-FOX-JUMPS-OVER-THE-LAZY-DOG
- lower (aka dashcase, spinalcase) =>
the-quick-brown-fox-jumps-over-the-lazy-dog
- pascal (aka HTTP-Header-Case) =>
The-Quick-Brown-Fox-Jumped-Over-The-Lazy-Dog
- camel =>
the-Quick-Brown-Fox-Jumped-Over-The-Lazy-Dog
Units
Use rem
instead of px
units for sizing. This helps the UI scale proportionally when the browser zooms. Set the rem
base scale globally by specifying a font-size
in the <html>
and/or <body>
element.
Set this font-size
to ten pixels so rem
units can be readily be calculated as multiples of ten. For example: 10px
= 1rem
, 16px
= 1.6rem
, 250px
= 25rem
, 599px
= 59.9rem
... and so on.
html,
body {
font-size: 10px;
}
Breakpoints
Write base styles for mobile devices first and override them with breakpoint media queries for progressively larger displays.
Spec breakpoints that fall between the widths of major display types. The combination of max and min width breakpoints approach makes working with phones easier.
@media (max-width: 59.9rem) {}
@media (min-width: 60rem) {}
@media (min-width: 90rem) {}
@media (min-width: 120rem) {}
@media (min-width: 180rem) {}
Variable names can help implementation.
@phone: ~"screen and (max-width: 599px)";
@tablet: ~"screen and (min-width: 600px)";
@desktop: ~"screen and (min-width: 900px)";
@widescreen: ~"screen and (min-width: 1200px)";
@superwide: ~"screen and (min-width: 1800px)";
More expressive variable names are better.
@mixin for-phone-only {
@media (max-width: 59.9rem) {
@content;
}
}
@mixin for-tablet-portrait-up {
@media (min-width: 60rem) {
@content;
}
}
@mixin for-tablet-landscape-up {
@media (min-width: 90rem) {
@content;
}
}
@mixin for-desktop-up {
@media (min-width: 120rem) {
@content;
}
}
@mixin for-big-desktop-up {
@media (min-width: 180rem) {
@content;
}
}
Position media queries so they are close the base styles they modify.
.site-menu-desktop {
display: none;
}
@media (min-width: 600px) {
.site-menu-desktop {
display: initial;
}
}
Or
.site-menu-desktop {
display: none;
@media (min-width: 600px) {
display: initial;
}
}
Fonts
Font Families
Current favorite web font families are sans-serif => Open Sans, Whitney SSm, and Barlow. For code blocks => Monospace
Font Scale
A typographic scale for font sizing based on classic proportions like 1:2, 2:3, 2:5, 5:8, etc.
Note: a Minor Third scale can accomodate up to five header sizes. Most sites only need three.
Colors
Using Hex instead of HSL is usually easier for others on the team edit and maintain. Keep the byte count low by using the three-digit shorthand syntax.
:root {
// Theme Colors
--color-primary: #1E70B3;
--color-secondary: #4CA982;
--color-tertiary: #E6A400;
// Gray Scale Colors
--color-black: #000;
--color-gray-extra-dark: #333;
--color-gray-medium: #999;
--color-gray-light: #CCC;
--color-gray-extra-light: #EEE;
--color-white: #FFF;
// Element Color Variations
--color-nav: var(--color-primary);
--color-background-shade: #F7F7F7;
// darken(var(--color-primary), 5%);
--color-font-link-selected: #1b629d;
// darken(var(--color-primary), 10%)
--color-button-dark: #175587;
--color-nav-active: #175587;
// lighten(var(--color-primary), 5%);
--color-searchbar-background: #227ec9;
// darken primary color by 10%
--color-font-link-selected(var(--color-primary) l(-10%)
$color-black: #333;
$color-gray: #999;
$color-green: #70bf63;
$color-lightgray: #d4d4d4;
$color-red: #ff0000;
$color-white: #fff;
$color-background-darkgray: #e7e7e7;
$color-background-gray: #f7f7f7;
$color-background-panel: #007bff12;
$color-border-dark: #b4b4b4;
$color-border-light: #d4d4d4;
$color-border-primary-action: #007a87;
$color-border-secondary-action: #b4b4b4;
$color-button-primary-action: #007a87;
$color-button-secondary-action: #707070;
$color-button-disabled: #e7e7e7;
$color-global-navbar: #1063b7;
$color-global-navbar-active: #0c4a88;
$color-icon: #007a87;
$color-link: #007a87;
$color-link-selected: #595959;
Using HSL allows for more programmatic color control.
// Primary hues
@hueR: 10;
@hueO: 29;
@hueY: 42;
@hueG: 111.7;
@hueB: 210;
@hue0: 0;
// Theme color mix
@color-blu: hsl(@hueB, 84%, 39%);
@color-yel: hsl(@hueY, 95%, 63%);
@color-orn: hsl(@hueO, 86.5%, 59%);
@color-grn: lighten(hsl(@hueG, 42%, 42%),15%);
@color-red: lighten(hsl(@hueR, 100%, 50%), 25%);
// Base color variables
@color-background: hsl(@hue0, 0%, 97%); //#F7F7F7
@color-blu-light: lighten(@color-blu, 10%);
@color-blu-dark: darken(@color-blu, 10%);
@color-nav: @color-blu;
@color-nav-active: darken(@color-blu, 10%);
@color-link: lighten(@color-blu, 10%);
@color-icon: lighten(@color-blu, 20%);
Keep colors global where possible for repeatability across products.
Color Functions
/* set defaults to 'light' prior to choice of background */
:root body {
--color-background-code: #e1e8ed;
--color-background-primary: hsla(40, 95%, 100%, 1);
--color-background-secondary: hsla(40, 95%, 99%, 1);
--color-background-tertiary: hsla(220, 5%, 90%, 1);
--color-border: #ccc;
--color-border-hover: #666;
--color-font-code: hsla(220, 5%, 20%, 1);
--color-font: hsla(220, 0, 0, 1);
--color-font-headers: hsla(220, 5%, 5%, 1);
--color-font-hover: hsla(40, 5%, 70%, 1);
}
body.light {
--color-background-code: #e1e8ed;
--color-background-primary: hsla(40, 95%, 100%, 1);
--color-background-secondary: hsla(40, 95%, 99%, 1);
--color-background-tertiary: hsla(220, 5%, 90%, 1);
--color-border: #ccc;
--color-border-hover: #666;
--color-font-code: hsla(220, 5%, 20%, 1);
--color-font: hsla(220, 0, 0, 1);
--color-font-headers: hsla(220, 5%, 5%, 1);
--color-font-hover: hsla(40, 5%, 70%, 1);
}
body.dark {
--color-background-code: #293742;
--color-background-primary: hsla(220, 5%, 15%, 1);
--color-background-secondary: hsla(220, 5%, 10%, 1);
--color-background-tertiary: hsla(40, 5%, 80%, 1);
--color-background-featured: hsla(0, 5%, 15%, 1);
--color-border: #666;
--color-border-hover: #ccc;
--color-font-code: hsla(40, 5%, 90%, 1);
--color-font: hsla(40, 5%, 95%, 1);
--color-font-headers: hsla(40, 5%, 100%, 1);
--color-font-hover: hsla(220, 5%, 30%, 1);
}
Color Functions
color: color(var(--color-background-primary) l(-10%));
Icons
Font icons like Google Font Icons or IcoMoon (with the online IcoMoon App) can consume less memory than PNG and JPG sprites. These icon sets can also be versioned. The defaults are free, and you can upload your own.
@import "icomoon-variables.less";
@font-face {
font-display: block;
font-family: '@{icomoon-font-family}';
font-style: normal;
font-weight: normal;
src: local('☺'),
url('@{icomoon-font-path}/@{icomoon-font-family}.ttf?@{icomoon-version}') format('truetype'),
url('@{icomoon-font-path}/@{icomoon-font-family}.woff?@{icomoon-version}') format('woff'),
url('@{icomoon-font-path}/@{icomoon-font-family}.svg?@{icomoon-version}#@{icomoon-font-family}') format('svg');
}
// declare IcoMoon vector font icons globally
[class^="icomoon-"],
[class*="icomoon-"] {
.font-light();
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
&:before,
&:after {
font-family: '@{icomoon-font-family}' !important;
text-decoration: none !important;
}
}
Use named wrapper (container) and block classes to shape w/ width, height, overflow around base class elements.
.thisthat-wrapper {
display: flex;
margin: 0 auto;
.thisthat-block {
color: red;
.this {
font-size: 1rem;
}
.that {
font-size: .5rem;
}
}
}
Scalable Vector Graphics
Good resolution at any scale. Can be version controlled individually. This approach employs a pseudo element to add an SVG icon before or after a string of text with a mask property. It is aligned with display:inline
or display:inline-block
, and remember to add the content
selector.
Example 1
SVG code specified directly in mask-image
url.
<a class="outside-link" href="https://somewhere" target="_blank">Block Element</a>
.outside-link {
color: var(--color-font-link);
display: inline-block;
font-family: system-ui;
font-size: 1.6rem;
overflow-wrap: break-word;
text-decoration: none;
word-break: break-word;
}
.outside-link:after {
background-color: var(--color-font-link);
content: "";
mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2048 2048'><path d='M1792 256v640h-128V475l-851 850-90-90 850-851h-421V256h640zm-512 1007h128v529H256V640h529v128H384v896h896v-401z'/></svg>");
-webkit-mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2048 2048'><path d='M1792 256v640h-128V475l-851 850-90-90 850-851h-421V256h640zm-512 1007h128v529H256V640h529v128H384v896h896v-401z'/></svg>");
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-clip-path: padding-box inset(.4rem 0);
clip-path: padding-box inset(.4rem 0);
-webkit-mask-size: 1.3rem;
mask-size: 1.3rem;
outline: none;
padding: 0 1rem;
}
Example 2
SVG file path specified in mask-image
url.
<span class="globalnav__login">Login</span>
.globalnav__login {
display: inline-block;
width: 10rem;
&:before {
background-color: $color-white;
-webkit-clip-path: border-box inset(2rem 0);
clip-path: border-box inset(2rem 0);
content: "";
margin: 0 .8rem;
-webkit-mask-image: url("/assets/avatar.svg");
mask-image: url("/assets/avatar.svg");
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 1.5rem;
mask-size: 1.5rem;
outline: none;
padding: 0 .8rem;
}
}
/assets/avatar.svg
<svg xmlns="http://www.w3.org/2000/svg" width="13.64" height="16" viewBox="0 0 13.64 16"><defs><style>.a{fill:#fff;fill-rule:evenodd;}</style></defs><path class="a" d="M6.82,0A3.74,3.74,0,1,1,3.08,3.74,3.74,3.74,0,0,1,6.82,0ZM6.272,7.936H7.361a6.3,6.3,0,0,1,6.279,6.279A1.791,1.791,0,0,1,11.854,16H1.792A1.8,1.8,0,0,1,0,14.208,6.29,6.29,0,0,1,6.272,7.936Z" transform="translate(0)"/></svg>
External References
- Google Design Standards
- Google Style Guide
- Apple's IOS Human Interface Guidelines
- Bootstrap Framework
- The New Code
Architectures
- Block Element Modifier (BEM)
- Object Oriented CSS (OOCSS)
- Scalable and Modular Architecture for CSS (SMACSS)