HTML5 Do and Don't

Feedbacks from a "one web" enthusiast

by Guyllaume Doyer / @gudoy

Warning

Troll inside

Who Am I?

Guyllaume Doyer
Dev front & back
Senior Web Developer at MyStudioFactory
Works with html/css/js for 7 years

I like things:

  • clean
  • simple
  • efficient

Why CSS LINT sucks!

CSS Lint

...basic syntax checking as well as applying a set of rules to the code that look for problematic patterns or signs of inefficiency...

CSS Lint rules 1/2

  • Parsing errors should be fixed
  • Don't use adjoining classes
  • Remove empty rules
  • Use correct properties for a display
  • Don't use too many floats
  • Don't use too many web fonts
  • Require shorthand properties
  • Don't use too may font-size declarations
  • Don't use IDs in selectors

CSS Lint rules 2/2

  • Don't qualify headings
  • Heading styles should only be defined once
  • Zero values don't need units
  • Vendor prefixed properties should also have the standard
  • CSS gradients require all browser prefixes
  • Avoid selectors that look like regular expressions
  • Beware of broken box models
  • Don't use @import
  • Don't use !important
  • Include all compatible vendor prefixes
  • Avoid duplicate properties
bullshit

CSS is a visual language.

No "magic" tool can tell if your code is appropriate or not.

  • Parsing errors should be fixed <-- Unless explicit css filters
  • Don't use adjoining classes <-- CSS feature. Fuck IE6!
  • Remove empty rules
  • Use correct properties for a display
  • Don't use too many floats <-- Warning after 10. WTF?
  • Don't use too many web fonts <-- Use what your design requires.
  • Require shorthand properties <-- Cases where appropriate
  • Don't use too may font-size declarations <-- Design consideration
  • Don't use IDs in selectors <-- Biggest bullshit EVER. CSS feature.
  • Don't qualify headings <-- Semantic weight =/= visual weight
  • Headings should only be defined once <-- Use what your design requires
  • Zero values don't need units
  • Vendor properties should have the standard
  • CSS gradients require all browser prefixes
  • Avoid selectors that look like regular expressions
  • Beware of broken box models <-- Box sizing:border-box rocks!
  • Don't use @import
  • Don't use !important <-- Use it when appropriate.
  • Include all compatible vendor prefixes
  • Avoid duplicate properties

Most of the the rules are

  • highly contested
  • or common logic
  • or based on visual design considerations

Chances are very high you’ll do yourself more harm than good, ending up with harder to maintain, bloated code
Matt Willcox

Don't use IDs in selectors?

What? No. No. No. No no no.

When and why you SHOULD you use IDs?

IDs are perfect for contextualisation

Page contextualisation:

/* Login page specific */
#login form {}
#login label {}
#login input {}
/* and so on */

IDs are faster (although just a bit)

reminder
CSS selectors are resolved from right to left
.foo { }
#foo { } /* Faster than above */

#bar.foo { }
.foo#bar { } /* Faster than above */

#bar.foo { }
.foo#bar { } /* Faster than above */

perfect for ultra specific styling

/* Using classnames: for generic rules */
.productsList .title { color:blue; }
aside .productsList { color:grey; }

/* Using IDs: tells us it's really a specific case */
#profilePage #someProductsList .title { color:green; }

Why CSS frameworks sucks!

  • Twitter Bootstrap
  • 960gs
  • Blueprint
  • InuitCSS
  • Foundation
  • KNACSS
  • ...

good for prototyping, but...

Content is king & markup is the key

Today's web: 1 codebase, several UX

With 1 markup + css (media queries) + a bit of js:

  • Mobile ux
  • Tablet ux
  • Desktop ux
  • TV ux (soon?)
  • Watch ux (soon?)
  • ... others?
HTML5 (magic)
  • HTML (content)
  • CSS (styling)
  • JS (interactions)

Markup SHOULD NOT contain interactions

<!-- BAD: do not do this -->
<a id="foo" href="#" onclick="alert('Yo!')">click me<a>
// Better: put this in a .js file
var el = document.getElementById('foo');
el.addEventListener('click', function(){ alert('Yo!') }, false)

Markup SHOULD NOT contain styling

<!-- BAD: do not do this -->
<a id="foo" href="#" style="color:blue; font-size:32px; text-align:right;">click me<a>
/* Better: put this in a .css file */
#foo { color:blue; font-size:12px; text-align:right; }

What CSS frameworks do

<!-- BAD: Markup still contains styling information -->
<a class="color1 right inline big" id="foo" href="#">click me<a>

Another issue: grids

meaningless in RWD

<!-- BAD: markup tied to particular representation (desktop) -->
<div class="foobar grid16">
	<div class="foo grid10">foo<div>
	<div class="bar grid6">bar<div>
<div>

what's in bootstrap-responsive.css:

  .container,
  .navbar-static-top .container,
  .navbar-fixed-top .container,
  .navbar-fixed-bottom .container {
    width: 724px;
  }
  .span12 {
    width: 724px;
  }
  .span11 {
    width: 662px;
  }
  .span10 {
    width: 600px;
  }
  .span9 {
    width: 538px;
  }
  .span8 {
    width: 476px;
  }
  .span7 {
    width: 414px;
  }
  .span6 {
    width: 352px;
  }
  .span5 {
    width: 290px;
  }
  .span4 {
    width: 228px;
  }
  .span3 {
    width: 166px;
  }
  .span2 {
    width: 104px;
  }
  .span1 {
    width: 42px;
  }
  .offset12 {
    margin-left: 764px;
  }
  .offset11 {
    margin-left: 702px;
  }
  .offset10 {
    margin-left: 640px;
  }
  .offset9 {
    margin-left: 578px;
  }
  .offset8 {
    margin-left: 516px;
  }
  .offset7 {
    margin-left: 454px;
  }
  .offset6 {
    margin-left: 392px;
  }
  .offset5 {
    margin-left: 330px;
  }
  .offset4 {
    margin-left: 268px;
  }
  .offset3 {
    margin-left: 206px;
  }
  .offset2 {
    margin-left: 144px;
  }
  .offset1 {
    margin-left: 82px;
  }
  .row-fluid {
    width: 100%;
    *zoom: 1;
  }
  .row-fluid:before,
  .row-fluid:after {
    display: table;
    line-height: 0;
    content: "";
  }
  .row-fluid:after {
    clear: both;
  }
  .row-fluid [class*="span"] {
    display: block;
    float: left;
    width: 100%;
    min-height: 30px;
    margin-left: 2.7624309392265194%;
    *margin-left: 2.709239449864817%;
    -webkit-box-sizing: border-box;
       -moz-box-sizing: border-box;
            box-sizing: border-box;
  }
  .row-fluid [class*="span"]:first-child {
    margin-left: 0;
  }
  .row-fluid .controls-row [class*="span"] + [class*="span"] {
    margin-left: 2.7624309392265194%;
  }
  .row-fluid .span12 {
    width: 100%;
    *width: 99.94680851063829%;
  }
  .row-fluid .span11 {
    width: 91.43646408839778%;
    *width: 91.38327259903608%;
  }
  .row-fluid .span10 {
    width: 82.87292817679558%;
    *width: 82.81973668743387%;
  }
  .row-fluid .span9 {
    width: 74.30939226519337%;
    *width: 74.25620077583166%;
  }
  .row-fluid .span8 {
    width: 65.74585635359117%;
    *width: 65.69266486422946%;
  }
  .row-fluid .span7 {
    width: 57.18232044198895%;
    *width: 57.12912895262725%;
  }
  .row-fluid .span6 {
    width: 48.61878453038674%;
    *width: 48.56559304102504%;
  }
  .row-fluid .span5 {
    width: 40.05524861878453%;
    *width: 40.00205712942283%;
  }
  .row-fluid .span4 {
    width: 31.491712707182323%;
    *width: 31.43852121782062%;
  }
  .row-fluid .span3 {
    width: 22.92817679558011%;
    *width: 22.87498530621841%;
  }
  .row-fluid .span2 {
    width: 14.3646408839779%;
    *width: 14.311449394616199%;
  }
  .row-fluid .span1 {
    width: 5.801104972375691%;
    *width: 5.747913483013988%;
  }
  .row-fluid .offset12 {
    margin-left: 105.52486187845304%;
    *margin-left: 105.41847889972962%;
  }
  .row-fluid .offset12:first-child {
    margin-left: 102.76243093922652%;
    *margin-left: 102.6560479605031%;
  }
  .row-fluid .offset11 {
    margin-left: 96.96132596685082%;
    *margin-left: 96.8549429881274%;
  }
  .row-fluid .offset11:first-child {
    margin-left: 94.1988950276243%;
    *margin-left: 94.09251204890089%;
  }
  .row-fluid .offset10 {
    margin-left: 88.39779005524862%;
    *margin-left: 88.2914070765252%;
  }
  .row-fluid .offset10:first-child {
    margin-left: 85.6353591160221%;
    *margin-left: 85.52897613729868%;
  }
  .row-fluid .offset9 {
    margin-left: 79.8342541436464%;
    *margin-left: 79.72787116492299%;
  }
  .row-fluid .offset9:first-child {
    margin-left: 77.07182320441989%;
    *margin-left: 76.96544022569647%;
  }
  .row-fluid .offset8 {
    margin-left: 71.2707182320442%;
    *margin-left: 71.16433525332079%;
  }
  .row-fluid .offset8:first-child {
    margin-left: 68.50828729281768%;
    *margin-left: 68.40190431409427%;
  }
  .row-fluid .offset7 {
    margin-left: 62.70718232044199%;
    *margin-left: 62.600799341718584%;
  }
  .row-fluid .offset7:first-child {
    margin-left: 59.94475138121547%;
    *margin-left: 59.838368402492065%;
  }
  .row-fluid .offset6 {
    margin-left: 54.14364640883978%;
    *margin-left: 54.037263430116376%;
  }
  .row-fluid .offset6:first-child {
    margin-left: 51.38121546961326%;
    *margin-left: 51.27483249088986%;
  }
  .row-fluid .offset5 {
    margin-left: 45.58011049723757%;
    *margin-left: 45.47372751851417%;
  }
  .row-fluid .offset5:first-child {
    margin-left: 42.81767955801105%;
    *margin-left: 42.71129657928765%;
  }
  .row-fluid .offset4 {
    margin-left: 37.01657458563536%;
    *margin-left: 36.91019160691196%;
  }
  .row-fluid .offset4:first-child {
    margin-left: 34.25414364640884%;
    *margin-left: 34.14776066768544%;
  }
  .row-fluid .offset3 {
    margin-left: 28.45303867403315%;
    *margin-left: 28.346655695309746%;
  }
  .row-fluid .offset3:first-child {
    margin-left: 25.69060773480663%;
    *margin-left: 25.584224756083227%;
  }
  .row-fluid .offset2 {
    margin-left: 19.88950276243094%;
    *margin-left: 19.783119783707537%;
  }
  .row-fluid .offset2:first-child {
    margin-left: 17.12707182320442%;
    *margin-left: 17.02068884448102%;
  }
  .row-fluid .offset1 {
    margin-left: 11.32596685082873%;
    *margin-left: 11.219583872105325%;
  }
  .row-fluid .offset1:first-child {
    margin-left: 8.56353591160221%;
    *margin-left: 8.457152932878806%;
  }
  input,
  textarea,
  .uneditable-input {
    margin-left: 0;
  }
  .controls-row [class*="span"] + [class*="span"] {
    margin-left: 20px;
  }
  input.span12,
  textarea.span12,
  .uneditable-input.span12 {
    width: 710px;
  }
  input.span11,
  textarea.span11,
  .uneditable-input.span11 {
    width: 648px;
  }
  input.span10,
  textarea.span10,
  .uneditable-input.span10 {
    width: 586px;
  }
  input.span9,
  textarea.span9,
  .uneditable-input.span9 {
    width: 524px;
  }
  input.span8,
  textarea.span8,
  .uneditable-input.span8 {
    width: 462px;
  }
  input.span7,
  textarea.span7,
  .uneditable-input.span7 {
    width: 400px;
  }
  input.span6,
  textarea.span6,
  .uneditable-input.span6 {
    width: 338px;
  }
  input.span5,
  textarea.span5,
  .uneditable-input.span5 {
    width: 276px;
  }
  input.span4,
  textarea.span4,
  .uneditable-input.span4 {
    width: 214px;
  }
  input.span3,
  textarea.span3,
  .uneditable-input.span3 {
    width: 152px;
  }
  input.span2,
  textarea.span2,
  .uneditable-input.span2 {
    width: 90px;
  }
  input.span1,
  textarea.span1,
  .uneditable-input.span1 {
    width: 28px;
  }
A very long code

WTF? We don't need all that shit!

Disgusting!

Why OOCSS is not the solution!

OOCSS

Basically, a CSS “object” is a repeating visual pattern, which can be abstracted into an independent snippet of HTML, CSS, and possibly JavaScript. Once created, an object can then be reused throughout a site.

based on a good ideas

  • Trying to identify common patterns
  • Trying to reuse common styles without redefining them everywhere
  • Separate structure from skin

but biased:

  • Based on appearance (instead of semantic)
  • From a desktop only era (cf grid)
  • Add extra markup (separation containers & contents)
  • Still pollute markup with visual classnames

Ok, so what do you suggest?

No magic tool/method,
learn CSS!

... but here are some usefull tips

Use classnames (and IDs) that describe what your content is

#mainNav {}

#products {}

#products .product {}

.accounts {}

.blogPost .comments {}

#loginForm {}

section#demo {}

table.sampleData {}
...

Identify common html patterns

one is everywhere:

  • Common pages (header + body + footer)
  • Mobile app views (header + content + actionsbars)
  • Forms (legend + fields + buttons)
  • Tables (caption/thead + tbody + tfoot)
  • Popups (title + content + actions)
  • Articles (title + content + next/prev actions
  • ...

When 1 thing is cut in 3 zones: intro, content, conclusion

It's the Z Pattern:
The z pattern

very important for small screen design

Look for him, and try to markup accordingly
(cutting modules in 3 zones)
<div class="foo">
	<header class="header fooHeader" id="fooHeader">...</header>
	<div class="content fooContent" id="fooContent">...</div>
	<footer class="actions fooActions" id="fooActions">...</footer>
</div>

Use box-sizing:border-box

* { -webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box; }

More info

Use & abuse of inline-block

(and use letter-spacing:-4px fix if needed)

Except for specifc cases, avoid fixed width

Use min & max width + media queries

Consider using flexbox for your layouts

sample use cases

Use & abuse of :before & :after

Use data-attributes

Use content:attr(someattribute)

Consider using display:table & co

Use dataURL instead of sprites (+ background-size for retina & co)

Organize you css in breakpoints

app.css (default)

/* no media query, everything fluid, avoid fixed sizes */

320up.css

@media screen and (min-width:320px)
{	
}

480up.css

@media screen and (min-width:480px)
{	
}

and so on (640, 800, 960, 1280, ...)

Inline your rules (better to scan)

Grouping your css by features

  • Base and/or reset
  • Colors & gradients
  • Fonts
  • Layout
  • Shadows
  • Hacks: clearfix, text hiding/replacement
  • Actions & icons
  • Forms
  • Tables
  • ...

But group them using their selectors

.someElement,
#another,
.and > #another.one,
#oneMore + .with.anotherSelector,
.foo > .action,
.bar header
 {
  background:#f2f2f2;
  background:-webkit-gradient(linear, left top, left bottom, from(#f2f2f2), to(#c9c9c9));
  background:-webkit-linear-gradient(#f2f2f2, #c9c9c9 100%);
  background:    -moz-linear-gradient(#f2f2f2, #c9c9c9 100%);
  background:     -ms-linear-gradient(#f2f2f2, #c9c9c9 100%);
  background:      -o-linear-gradient(#f2f2f2, #c9c9c9 100%);
  background:         linear-gradient(#f2f2f2, #c9c9c9 100%);	
}

Javascript tips & best practices

Use modernizr to test features support

Consider using Zepto instead of jQuery

Use chaining and or caching/memoization

// This is bad: you are selecting the element 3 times
$('#elemId').addClass('someClass');
$('#elemId').attr('some attribute', 'some value');
$('#elemId').on('click', function(){ /* ... */ })
// Using caching
var $el = $('#elemId');
$el.addClass('someClass');
$el.attr('some attribute', 'some value');
$el.on('click', function(){ /* ... */ })
// and/or using chaining
var $el = $('#elemId');
$el
  .addClass('someClass');
  .attr('some attribute', 'some value');
  .on('click', function(){ /* ... */ })

Use event delegation

<ul id="myList">
	<li>item 1</li>
	<li>item 2</li>
	<li>item 3</li>
	<li>item 4</li>
	<li>item 5</li>
</ul>
// Instead of binding 5 events:
$('li', '#myList').on('click', function(){ });
// Bind only 1 on the parent
$('#myList').on('click', 'li' function(){ });
// Or event better: onto the document
$(document).on('click', '#myList li', function(){ });

Use smart selectors and/or dom traversing

reminder
selectors are resolved from right to left
// Bad: will get all .level1 elements (using getElementsByClassname),
// test if is a LI
// and go up through the DOM looking for a NAV
$('nav li.level1')
// Better: will use getElementsByTagName to find NAV
// test if contains LI
// and test for classname
$('nav').find('li').filter('.level1')
// Even better: will use getELementById on a hook
// look this DOM chunk for LI
// and test for classname
$('#mainNav').find('li').filter('.level1')

Thank you

Comments are welcome!