ruk·si

🎨 CSS Guide

Updated at 2017-07-05 12:10

This guide contains basic syntax and tips how to write CSS markup.

CSS is a style sheet language used for describing presentation semantics of written documents.

You should also check out HTML guide and SASS guide as they are closely linked with this guide.

Architecture

CSS can be inserted in four ways:

  • Inline CSS loaded with style-attribute in body.
  • Internal CSS loaded with <style> in head.
  • External CSS loaded with <link> in head.
  • Added later using JavaScript.

Avoid using inline and internal CSS. Always prefer using external CSS. JavaScript CSS is sometimes required for interaction. There are few cases where inline and internal CSS may be used but avoid them as much as possible.

<!-- bad -->
<div style="width: 100%; height 120px;">
</div>

<!-- good -->
<div class="main-box">
</div>

Avoid browser specific CSS hacks. Always try if you can do it in a way that works in all your target browsers. After that, if you must fix only IE, try to use conditional classes.

<!--[if lt IE 7]>  <html class='ie ie6'> <![endif]-->
<!--[if IE 7]>     <html class='ie ie7'> <![endif]-->
<!--[if IE 8]>     <html class='ie ie8'> <![endif]-->
<!--[if IE 9]>     <html class='ie ie9'> <![endif]-->
<!--[if gt IE 9]>  <html>                <![endif]-->
<!--[if !IE]><!--> <html>                <!--<![endif]-->

Keep your CSS files under 200 lines long. You should split your CSS files to multiple <200 line files according to components and specific page. These should then be merged and minified for a single CSS file that is served. Using CSS precompiler like SASS or LESS helps.

Consider having a shame file. Shame file should include all quick hacks, temporary styles and !important rules that should be cleaned up. Included as the last in minification. Usually named "shame.css".

Markup Layout

Prefer indention of 4 spaces. Any indention is fine but be consistent within a project. Always consider using indention of the framework or libraries you are using. Never mix spaces and tabs.

/* bad */
.box {
  width: 30px;
    height: 40px;
}

/* good */
.box {
    width: 30px;
    height: 40px;
}

Prefer single quotes ' in CSS. Prefer double quotes " in HTML.

<!-- bad -->
<a class='maia-button maia-button-secondary'>Sign in</a>
<style>
    html {
        font-family: "Open Sans", Arial, sans-serif;
    }
</style>

<!-- good -->
<a class="maia-button maia-button-secondary">Sign in</a>
<style>
    html {
        font-family: 'Open Sans', Arial, sans-serif;
    }
</style>

Remove trailing space. Use non-breaking space &nbsp; in HTML if you require end of line space. Helps merging by version control software and someone in the team will remove them anyway.

/* bad */
.box {
    width: 30px;/* trailing space */
}

/* good */
.box {
    width: 30px;
}

Indent all blocks. If there are braces, there should be indention.

/* good */
@media screen {
    html {
        background: #fff;
        color: #444;
    }
}

Add a space between selector and brace. For consistency.

/* bad */
.side-b{
    padding: 10px;
}

/* good */
.side-b {
    padding: 10px;
}

Media queries should be at the bottom. Place media queries at the bottom of the CSS files and media queries should be in the same file as the class that it changes.

Naming

Use dashed lower case to name classes, identifiers and other values. Dashed lower case is the most widely used standard and helps to separate these names from other variables in JavaScript and server language so they are easier to search.

/* bad */
.box_big {
    width: 60px;
}
.boxSmall {
    width: 60px;
}
.promoimage {
    height: 100px;
}

/* good */
.box-big {
    width: 60px;
}
.box-small {
    width: 60px;
}
.promo-image {
    height: 100px;
}

Keep class and identifier names as short as they are understandable. It is ok to shorten names as long as they stay understandable in the context of the project. Be consistent.

/* bad */
.navigation {}
.atr {}
.w {}
.b {}
.bgr {}

/* good */
.nav {}
.author {}
.window {}
.btn {}
.btn-grp {}

Avoid presentational and meaningless class names. Make class names specific.

/* bad */
.yee {}             /* meaningless */
.190x {}            /* meaningless */
.button-green {}    /* presentational */
.red {}             /* presentational */

/* good */
.header-login {}    /* specific */
.profile-gallery {} /* specific */

Avoid generic class names. In larger projects, generic class names tend to mix up styles. Also makes searching a lot easier. Prefer inventing own words or using the project name if you do not want to refer to the visual style.

<!-- bad, all the class names are way too generic -->
<style>
    .popup {}
    .popup .title {}
    .popup .contents {}
    .popup .button {}
</style>
<div class="popup">
    <h3 class="title">Oh!</h3>
    <div class="contents">
        You liked the article?
        <button class="button">Like us</button>
    </div>
</div>

<!-- good, using random identifiers is better than generic -->
<style>
    .mossup {}
    .mossup-title {}
    .mossup-contents {}
    .mossup-button {}
</style>
<div class="mossup">
    <h3 class="mossup-title">Oh!</h3>
    <div class="mossup-contents">
        You liked the article?
        <button class="mossup-button">Like us</button>
    </div>
</div>

Comments

Comment your CSS. There should be a blank line before every comment that starts a line. Comments that start a line should be commenting next selector or section. End of the line comments should be commenting that specific rule.

/* good */
/* Cell states */
.cell.is-animating {
    background-color: #fffdec;
}
.cell.is-selected {
    background-color: #222222;
    border: 0; /* IE7 fix */
}

Selectors

Selectors are way to specify on what elements the style applies.

div             /* tag selector, avoid */
#login-form     /* identifier selector, avoid */
.nav-item       /* class selector, use */

Avoid identifier selectors in CSS. Identifiers like #page-content should only be used on unique elements on layout. They reduce style re-use as a page can have only one element with the same id. Never have multiple identifiers in one selector. You may use identifiers in HTML e.g. for JavaScript.

/* really bad */
#header .search #box {}

/* bad */
#box {}
#big-box {}
#box .title {}

/* good */
.box {
    width: 30px;
}
.box.box-big {
    width: 60px;
}
.box-title {
    color: #222;
}

Avoid tag selectors. Tag selectors should be avoided because they refer to the HTML markup while style and structure should be kept separate. There are a few cases where tag selectors are a better choice e.g. when specifying website wide font style or when specifying font style inside a component.

/* bad */
div.bigger-box {}   /* tag with class */
ul.example {}       /* tag with class */
div.error {}        /* tag with class */

/* bad, usually */
h1 {}
p {}

/* good */
body { /* to set font and basic font size */}
.content h1 {}
.content p {}
.modal h1 {}
.modal p {}

Avoid structural selectors. The more complex selectors you use, the more it is linked to the document structure. You presentation and structure should be kept strictly separate.

/* bad */
ul.sidebar > li > ul {
    /* submenu styles */
}

/* good */
.sidebar-submenu {
    /* submenu styles */
}

Avoid parental references outside a component. Parental references can create a maintenance nightmare. When writing parental restrictions, always consider is the target of reference in the same logical group or not.

<!-- bad, parental reference to "article" which is not part of a "list" -->
<style>
    .article .list {}
    .article .list .item {}
    .article .list .item.bigger {}
</style>
<div class="article">
    <div class="list">
        <div class="item">
            Oh wow, what a bang!
        </div>
        <div class="item bigger">
            Bada boom!
        </div>
    </div>
</div>

<!-- good, no parental references outside "list". -->
<style>
    .artcl-list {}
    .artcl-list-item {}
    .artcl-list-item.bigger {}
</style>
<div class="article">
    <div class="artcl-list">
        <div class="artcl-list-item">
            Oh wow, what a bang!
        </div>
        <div class="artcl-list-item bigger">
            Bada boom!
        </div>
    </div>
</div>

Prefer using specific syntax over modular syntax. Being specific when naming classes helps in the future maintenance. CSS preprocessors like SASS and LESS can help with this with their mixins.

<!-- bad -->
<style>
    .box {
        color: blue;
        border: 2px solid blue;
        border-radius: 5px;
        padding: 20px;
    }
    .box-special {
        color: red;
        border-color: red;
    }
</style>
<div class="box box-special">
    This is a special box.
</div>

<!-- good -->
<style>
    .box-normal,
    .box-special {
        color: blue;
        border: 2px solid blue;
        border-radius: 5px;
        padding: 20px;
    }
    .box-special {
        color: red;
        border-color: red;
    }
</style>
<div class="box-special">
    This is a special box.
</div>

Define one selector per line. Easier to read and modify.

/* bad */
a:focus, a:active {
    position: relative;
    top: 1px;
}

/* good */
a:focus,
a:active {
    position: relative;
    top: 1px;
}

Group related selectors together. Easier to find specific parts of the CSS.

/* good */
html {
    background: #fff;
}

body {
    margin: auto;
    width: 50%;
}

.notification {
    padding: 10px;
}
.notification.success {
    color: green;
}
.notification.error {
    color: red;
}

Avoid universal selector *. It's needed only in very rare cases so don't use it if you don't know what you are doing. Universal selector has a notable hit on the performance.

/* very bad */
.calendar * {}

Browsers read selectors from right to left. This helps how you think about the performance, bad CSS can kill a website on older computers and mobile devices.

/* This has very bad performance.
 * Goes through every single element in the page.
 */
.content * {}

/* This has good performance. */
.content .intro {}

/* This has bad performance.
 * Goes through every single anchor element in the page.
 */
.menu a {}

/* This has a good performance. */
.menu .menu-item {}

Prefer basic selectors. But you always end up needing a few complex selectors here and there.

/* Child, list_item must be in first level under list. */
.list > .list-item

/* Following sibling, first paragraph after heading. */
h2 + p

/* Attribute, check elements with given HTML attribute. */
a[title]                /* a elements with title attribute */
input[type="text"]      /* input elements with type value text */
a[href*="home"]         /* a elements with href value containing home */
a[href^="http"]         /* a elements with href value starting with http */
a[href$=".jpg"]         /* a elements with href value ending with .jpg */
a[data-my~="vava"]      /* a elements with data-my attribute value vava */
                        /* while data-my is space separated list */

Consider pseudo classes : and elements ::. Just remember to check on what browsers they work first.

/* Works in IE8+, mobile and modern browsers. */
:before                 /* Adds content before the element. */
:after                  /* Adds content after the element. */
p::first-letter {}      /* First letter of a paragraph. */
p::first-line {}        /* First line of a paragraph. */
ul:first-child          /* Select first child of <ul>. */
a:hover                 /* When mouse is over the element. */
a:visited               /* Links visited by current browser. */
a:active                /* Being clicked or activated. */
input:focus             /* When browser is focused on the element. */

/* Works in IE9+, mobile and modern browsers. */
div:not(.music)         /* Each <div> except ones with class music. */
div:empty               /* Each <div> that has no children or content. */
ul:last-child           /* Select last child of <ul>. */
ul:only-child           /* Select child if it is only child of <ul>. */
ul:first-of-type        /* Select first <ul> in each parent. */
ul:last-of-type         /* Select last <ul> in each parent. */
ul:only-of-type         /* Select only if <ul> is only <ul> in parent. */
li:nth-child(even)      /* Select even children. */
li:nth-child(odd)       /* Select odd children. */
li:nth-child(3n+1)      /* Select every third child, starting from 2. */
li:nth-of-type(X)       /* Select Xth of this selector type found. */
li:nth-last-of-type(X)  /* Select Xth of this selector type found. */
input:checked           /* When element is checkbox and checked. */
input:enabled           /* When element accepts focus and can be activated. */
input:disabled          /* When element does not accept focus and cannot be */
                        /* deactivated. */

/* Non-standard but works in IE9+, modern browsers and most mobile. */
::selection             /* Text highlight style. */

/* Do not use, non-existent support. */
input:indeterminate   /* When radio buttons are not chosen or unchosen. */

Properties

Avoid using !important. Using it usually means that you don't understand CSS. Although, there are a few rare places it is ok e.g. extending a third party style or browser hacks.

/* good, as you know that you will always want errors to be red
   and can't possibly know the contents */
.error-text {
    color: #cc0000 !important;
}

Define one property per line. Easier to read, reuse and maintain.

/* bad */
a:focus,
a:active {
    position: relative; top: 1px;
}

/* good */
a:focus,
a:active {
    position: relative;
    top: 1px;
}

Always close property definition with a semicolon. Even if it would work without it.

/* bad */
.example {
    height: 100px
}

/* good */
.example {
    height: 100px;
}

Add a space after property name colon. For readability and consistency.

/* bad */
.example {
    font-weight:bold;
}

/* good */
.example {
    font-weight: bold;
}

Prefer shorthands on directional properties. Don't use half-shorthands, they are just confusing.

/* bad */
.box {
    padding-top: 0;
    padding-right: 1px;
    padding-bottom: 2px;
    padding-left: 1px;
}

/* bad */
.box {
    padding: 0 2px 1px;
}

/* good */
.box {
    padding: 0 1px 2px 1px;
}

Specify colors using hexadecimals #. hsl format would be better but IE8 does not support that. All letters in the hexadecimals should be in lower case for consistency and as it would be more troublesome to type capital letter between numbers. More about color systems in the color notes.

/* bad */
.example {
    color: #E1E1E1;
}

/* good */
.example {
    color: #e1e1e1;
}

Avoid shorthand color notation. For consistency, easier to modify and other applications don't support it.

/* bad */
.example {
    color: #222;
}

/* good */
.example {
    color: #222222;
}

Omit units after zeroes. Zero units specifies useless information, the units. Nothing is always nothing in CSS context.

/* bad */
.example {
    margin: 0px;
}

/* good */
.example {
    margin: 0;
}

Include leading zeroes. Consistent and easier to modify.

/* bad */
.example {
    font-size: .8rem;
}

/* bad */
.example {
    font-size: 0.8rem;
}

Always change box sizing to border-box. There is no reason not to as all browsers support it and, with the right global definition, it won't affect with 3rd party CSS components you download from the Internet. Check out the box sizing snippet.

Make sure your properties work on your target browsers. When using non-standard properties.

/* Works in IE8+, modern browsers and mobile. */
.example {
    display: table;
    min-width: 100px;
    min-height: 100px;
    max-width: 200px;
    max-height: 200px;
    word-break: break-all;
    opacity: 0.5; /* IE8 has filter property /*
}
@font-face {
    /* IE8 supports only EOT fonts. */
    /* Safari iOS <4.1 only SVG fonts. */
}

/*  Works in IE9+, modern browsers and mobile. */
.example {
    box-shadow: 1px 1px 2px #222222;
    background-image: url(cow.png), url(bell.png);
    background-position: center bottom, left top;
    transform: 0; /* Some transforms can be made in IE8 with Matrix filters. */
}
@media screen and (min-width: 100px) {
    /* Media queries. */
}

/* Work in IE10+, modern browsers and mobile. */
.example {
    text-shadow: 1px 1px 2px #222222;
    transition: all 1s;
}

/* Do not work in any IE or mobile browsers. */
.example {
    resize: both;
}

Units

Most CSS units have real world equivalents, but the sizes are not always equal.

UNIT | DEFINITION         |  CM
------------------------------------------
in                        => 2.54cm
cm                        => 1cm
mm                        => 0.1cm
pc   => 12pt              => ~0.42cm
pt   => 1/72nd of inch    => ~0,035cm
px   => 0.75pt            => ~0.0265cm

CSS pixel is an abstract unit. 1 CSS pixel corresponds to 1 device pixel, at least most of the time, 1 CSS pixel is the same as 1 rendered pixel. But there are differences with higher screen density e.g. retina displays. More about this in responsive design.

/* 200x300px area on most devices. */
/* 400x600px area on high screen density devices. */
div.box {
    height: 200px;
    width: 300px;
}

/* Serving special image for high screen density devices. */
@media
    only screen and (-Webkit-min-device-pixel-ratio: 1.5),
    only screen and (-moz-min-device-pixel-ratio: 1.5),
    only screen and (-o-min-device-pixel-ratio: 3/2),
    only screen and (min-device-pixel-ratio: 1.5)
{
    .icon {
        background-image: url(example@2x.png);
    }
}

Prefer specifying width-related properties with percentages. Then elements will fill all allowed space nicely. If you need absolute sizing, always use pixels px.

/* bad */
.container {
    width: 600px;
}
.box-first {
    width: 275px;
}
.box-first {
    width: 275px;
    margin-left: 50px;
}

/* good */
.container {
    /* sometimes you may want to limit this to a pixel count */
    width: 100%;
}
.box-first {
    width: 45%;
}
.box-second {
    width: 45%;
    margin-left: 10%;
}

Create a baseline pixel height and use that for all the height-related. Makes the design more structured and harmonious. 8px, 9px and 10px have proven to be good baselines, depending on the font. Preprocessors like SASS help a lot in baseline usage.

/* Baseline is 9px. */
body {
    font-size: 17px; /* does not define height = no need to follow baseline */
    line-height: 27px; /* 3*9=27 */
}
h1 {
    font-size: 44px; /* does not define height = no need to follow baseline */
    line-height: 45px; /* 5*9=45 */
}
p {
    margin-bottom: 27px; /* 3*9=27 */
}
.box {
    padding-top: 9px; /* 1*9=9 */
    padding-bottom: 9px; /* 1*9=9 */
}
.baseline-margin-top {
    margin-top: 9px;
}
.baseline-margin-top-2x {
    margin-top: 18px;
}

Avoid relative units like % or em. Prefer using rem or px for all height related properties, I personally prefer good old px. Relative units may cause problems e.g. if you set font size as % to a .text class and there is another same class .text element inside it, the percentage is applied twice. rem is the size of type as computed relative to the type size of the top level html element rather than a parent element like with em. And pt units are meant for print only. Be warned that px may not zoom properly on older IEs.

/* bad */
p {
    font-size: 62.5%;
    line-height: 1.5;
}

/* good */
p {
    font-size: 18px;
    line-height: 27px
}

/* ok */
html {
    font-size: 18px;
}
p {
    font-size: 1rem;
    line-height: 1.5rem
}

Avoid breakpoints, but make adjustments when layout breaks. Don't use breakpoints popularized by Bootstrap and the bunch. Always use the fluid equivalent e.g. container-fluid and apply media queries to sizes that you notice the design breaking. It's far more responsive design to specify applied media queries in case-by-case basis.

Check yourself when font becomes too big or small for the container and change the font size on that point. Don't use the breakpoints predefined by your framework, they will rarely be in the perfect spot.

Consider using custom fonts. Allows delivering uniform experience across all operating systems. Host them yourself or use web fonts. There are notes for selecting fonts and a list of good fonts for the web.

<!-- Loading Google web fonts so they do not flash. -->
<head>
<script type="text/javascript">
    WebFontConfig = {
        google: { families: ['Boogaloo::latin'] }
    };
    (function() {
        var wf = document.createElement('script');
        wf.src = (
            'https:' == document.location.protocol ? 'https' : 'http'
        ) + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
        wf.type = 'text/javascript';
        wf.async = 'true';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(wf, s);
    })();
</script>
</head>

Display Mode

Understand how CSS display modes work. All elements have a display property e.g. block, inline-block or table-cell. This display mode affects how the element takes space on the document.

  • Block elements (block) use whole parent container width. Default block elements are div, p, ul, ol, li and h1-h6.
  • Inline elements (inline) take only width their content takes. You cannot manipulate their full box model e.g. padding, margin, border, width. Default inline elements are span, em, cite and img. All unknown tags are inline by default.
  • Inline block elements (inline-block) take only width of their content takes but you can manipulate their full box model e.g. padding, margin, border, width.
  • Flex containers (flex, inline-flex) allow higher degree of control on child element dimensions. What portions child elements take inside the parent container and how they are aligned. I don't use them as they are not supported by IE10, which still has pretty high usage rate.
  • Invisible elements (none) don't show up on the document. Default invisible elements are head, title, style. You could make them visible but really should not.
  • Various table modes (table, table-row, table-cell, etc.) make elements behave like tables. Default table elements are table, tr and td.

Document Flow

All documents have the basic element flow. Elements that are in the document flow take space on the document, and each element is simply placed after the previous one, at least in theory. But then there are elements that don't go into the normal document flow e.g. floats and absolute positioning. Various properties affect to the document flow e.g. position, float, display and visibility.

position: static;

  • Element is in the document flow.
  • Position is limited by parent.
  • Does not provide coordinates for relatively positioned children.
  • Default element positioning.
  • Cannot use offset properties: top, right, bottom, left.
  • Cannot use z-index property.

position: relative;

  • Element is in the document flow.
  • Position is limited by parent.
  • Can use offset properties: top, right, bottom, left.
  • Can use z-index property.

position: absolute;

  • Element is not in the document flow.
  • Position is limited by first non-static parent.
  • Can use offset properties: top, right, bottom, left.
  • Can use z-index property.

position: fixed;

  • Element is not in the document flow.
  • Position is not limited by parent.
  • Can use offset properties: top, right, bottom, left.
  • Can use z-index property.
  • Stays in viewport when scrolling.

float: left/right

  • Element is kind of in the document flow.
  • Siblings wrap around the element.
  • Container element don't count floated children in total area height without a clearfix.

display: none;

  • Element is not in the document flow.
  • Is not shown in any way.

visiblity: hidden;

  • Element is in document flow.
  • Take space on page but is not visible.

Sources