Stylesheet Patterns – Anti-Patterns – Patterns



Stylesheet Patterns – Anti-Patterns – Patterns

0 0


stylesheet-patterns


On Github press22day / stylesheet-patterns

Stylesheet Patterns

Stylesheet Design Patterns for Better CSS

Josh Renner / @joshrenner / josh@renner.mn

demo.rooftop.mn/stylesheets

Made with Reveal.js

Anti-Patterns

Style Bleed

<div id="main-container">
  <div class="sidebar container">
    <h1 class="title">Chippewa</h1>
    <!-- stuff -->
  </div>
</div>
.sidebar .title {
  font-size: 1.2em;
  color: #333;
}

#main-container .title {
  color: #000;
  line-height: 1.6;
}

.sidebar {
  font-size: 1.1em;
}

.container h1 {
  color: #000;
}

.title {
  font-size: 2em;
  line-height: 1.4;
}

Side effects, no clear hierarchy

Style Bloat

.title {
  font-size: 2em;
  font-weight: bold;
}
.headline {
  font-size: 2em;
  font-weight: 900;
}
.hdln {
  font-size: 18px;
  font-weight: bold;
}
.head {
  font-size: 2em;
  font-weight: bold;
}

multiple classes all solving the same problem. clear shared purpose and duplication

Parent-Down Modifications

.widget {
  color: red;
  border: 1px solid black;
  width: 50%;
}

#sidebar .widget {
  width: 100%;
}
body.homepage .widget {
  border: none;
}

Software entities should be open for extension, but closed for modification.

Overly Aggressive Resets

* {
  margin: 0;
  padding: 0;
  border: none;
  /*
    and it goes on
  */
}
p, h6, h5, h4, h3, h2, h1, a, em, strong, li {
  font-variant: normal;
  font-weight: normal;
  font-style: normal;
  text-decoration: none;
  /*
    and it goes on
  */
}

Increasing Specificity

.title {
  color: #000;
}
.main .title {
  color: #222;
}
.main > div .title {
  color: #000;
}
#callout .title {
  color: #922;
}
#callout.muted .title {
  color: #644;
}
.main .title {
  color: #000 !important;
}

And Sometimes Y

.widget p { /* descendant */ }

#chippewa { /* ids */ }

h3.title { /* overly qualified */ }

.chippewa {
  margin: 1em !important;
}

Patterns

Namespacing & Seperation of Concerns

<div class="mod-accordion state-collapsed js-accordion-container x-placesToGoMenu">
  <div class="wrapper js-accordion-title test-clickable">
    <h4 class="title">Places to Go</h4>
  </div>
  <div class="wrapper js-accordion-content test-collapsible">
    <ul class="mod-menu">
      <li class="mod-menu-item">
        <a class="test-link">Someplace</a>
      </li>
    </ul>
  </div>
</div>

Module Scoping

.widget {
  //elements
  .title {}

  .body {}

  > button {}

  //states
  &.collapsed {}

  //other variations
  .ie-8 & {}
}

Block, Element, Modifier

BEM [...] is a front-end naming methodology thought up by the guys at Yandex. It is a smart way of naming your CSS classes to give them more transparency and meaning to other developers. They are far more strict and informative, which makes the BEM naming convention ideal for teams of developers on larger projects that might last a while.

Block

.block {}
.site-search {}

Highest level of an abstraction or component.

Element

.block__element {}
.site-search__field {}

Descendant of a block that forms the block as a whole.

Modifier

.block--modifier {}
.site-search--full {}

Different state or version of a block.

The point of BEM is to tell other developers more about what a piece of markup is doing from its name alone. By reading some HTML with some classes in, you can see how – if at all – the chunks are related

  • http://bem.info/
  • http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/

SMACCS

At the very core of SMACSS is categorization. By categorizing CSS rules, we begin to see patterns and can define better practices around each of these patterns.

Jonathan Snook

Base Rules

Layout Rules

Module Rules

State Rules

Theme Rules

  • reset, normalize, default styles for elements
  • grid, major site component layout
  • discrete component of the page. navigation bars, dialogs, widgets. sit inside Layout components. can sometimes sit within other Modules. can easily be moved to different parts of the layout without breaking
  • something that augments and overrides all other styles. can apply to layout and/or module styles. indicate a JavaScript dependency
  • aren't as often used. could override base styles. could change module elements. could affect layout with different arrangements. could also alter how states look.

OOCSS

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

Nicole Sullivan

  • https://github.com/stubbornella/oocss/wiki
  • http://www.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/

Separation of Structure From Skin

<div class="widget-1 light">
  <button class="button bright">push me</button>
</div>
<div class="widget-2 bright">
  <button class="button">push me</button>
</div>
Structure
.button {
  height: 50px;
}
.widget-1 {
  width: 500px;
  min-height: 200px;
  overflow: auto;
  .button {
    width: 90%;
    margin: 0 auto;
  }
}
.widget-2 {
  width: 50%;
}
Skin
.light {
  border: solid 1px #ccc;
  background: linear-gradient(#ccc, #222);
  box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}
.bright {
  background: #00f;
  color: #eef;
}
.button.bright {
  border: 1px solid #009;
}

With OOCSS, we’re encouraged to give more forethought to what is common among different elements, then separate those common features into modules, or objects, that can be reused anywhere.

Separating structure and skin can also mean using classes to name your objects and their components, rather than relying solely on the semantics of HTML.

Difficult to reuse these styles

.widget h3 {
  font-size: 14px;
  color: #333;
  padding-top: 10px;
}

Better

.widget {
  padding-top: 10px;
}
h3 {
  font-size: 14px;
  color: #333;
}

Best

.title {
  font-size: 14px;
  color: #333;
}

Separation of Containers and Content

  • Essentially, this means “rarely use location-dependent styles”. An object should look the same no matter where you put it.
  • all unclassed H2s will look the same
  • all elements with the category class (called a mixin) will look the same
  • If you have fewer styles that are repeated in your CSS, then this will lead to smaller file sizes
  • all unclassed H2s will look the same
  • all elements with the category class (called a mixin) will look the same

Single Responsibility Principle

Loosely, the single responsibility principle states that every module or chunk of code (a function etc) should do one job well and one job only. The benefits of this are mainly in the way of maintainability and extensibility.

Harry Roberts

Loosely, the single responsibility principle states that every module or chunk of code (a function etc) should do one job well and one job only. The benefits of this are mainly in the way of maintainability and extensibility.

it makes code too specific in its job to be portable and reusable

Abstracting chunks of functionality into several responsibilities means we can reuse a lot more of our code and recombine it over and over

A class of .wrapper is a good example of the SRP at play

  • https://github.com/stubbornella/oocss/wiki
  • http://www.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/
<div class="island content">
  <h2>
    <span>Buy now with promo code </span>
    <strong class="promo">0MG4WE50ME</strong>
  </h2>
</div>

<div class="island  sub-content">
  <a class="island promo">Buy now!</a>
</div>

.island {
  display: block;
  padding: 20px;
  margin-bottom: 20px;
}
.promo {
  background-color: #09f;
  color: #fff;
  text-shadow: 0 0 1px rgba(0,0,0,0.25);
  border-radius: 4px;
}
.content {
  width: 640px;
  float: left;
  margin-right: 20px;
}
.sub-content {
  width: 280px;
  float: left;
}
  • DRY
  • you can make far-reaching changes to your designs by simply modifying a base abstraction only once.
  • You can make safer changes because you know that when editing a class you are only ever altering one responsibility.
  • You can combine responsibilities to make a variety of components from a lot of abstracted classes.

Attribute Modules

Moving beyond class-based styling

This one attribute holds everything, from enormous BEM-style names [...] to utilities [...], to JavaScript hooks [...], and so we spend a lot of time coming up with names that don't conflict with any others but are still vaguely readable.

Glen Maddern

attribute~="value"

Space-separated Attribute Selector

.chippewa { /* styles */ }
[class~="chippewa"] { /* styles */ }
<div am-Row>
  <div am-Column>Full</div>
</div>

<div am-Row>
  <div am-Column="1/3">Thirds</div>
  <div am-Column="1/3">Thirds</div>
  <div am-Column="1/3">Thirds</div>
</div>

[am-Row] {
  /* max-width, clearfixes */
}
[am-Column] {
  /* 100% width, floated */
}
[am-Column~="1/12"] { /* 1/12th width */ }
[am-Column~="1/6"] { /* 1/6th width */ }
[am-Column~="1/4"] { /* 1/4th width */ }
[am-Column~="1/3"] { /* 1/3rd width */ }
[am-Column~="5/12"] { /* 5/12ths width */ }

Matching space separated attribute values since IE7. Works in the same way class selectors do, but with any attribute.

Attribute Modules, or AM, at its core is about defining namespaces for your styles to live in.

The presence of an attribute, e.g. am-Button, can and should be styled. The particular values of each attribute then alter and adapt these base styles.

Media Queries

Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).

Preprocessors

Cons

CSS is still CSSSupersets are inclusive. Architecture can be.

Layers often impede before they payoffSkill sets and timelines may take precedence.

Scalability is not the main objective But scalability is not the only reason to choose a preprocessor...

Pros

The styles need to scale

  • Multiple applications with a common brand
  • Application with multiple brands
  • Complex visual states/views

The styles are distributed

  • Locations - modularity
  • Time - maintainability
  • Disciplines - adoption

Nesting

.page {
  width: 100%;
  .widget {
    width: 50%;
    .button {
      width: 200px;
    }
  }
}
.page {
  width: 100%;
}
.page .widget {
  width: 50%;
}
.page .widget .button {
  width: 200px;
}

Referencing the Parent Selector: &

a {
  color: gray;
  &:hover {
    color: black;
  }
}
.block {
  width: 100px;
  .ie-6 & {
    width: 102px;
  }
}
a {
  color: gray;
}
a:hover {
  color: black;
}
.block {
  width: 100px;
}
.ie-6 .block {
  width: 102px;
}

Nested Media Queries

.screencolor {
  @media screen {
    color: green;
    @media (min-width:768px) {
      color: red;
    }
  }
  @media tv {
    color: black;
  }
}
@media screen {
  .screencolor {
    color: green;
  }
}
@media screen and (min-width: 768px) {
  .screencolor {
    color: red;
  }
}
@media tv {
  .screencolor {
    color: black;
  }
}

Variables

$width: 200px;

.widget1 {
  width: $width;
}
.widget1 {
  width: 200px;
}

!default flag

$height: auto !default;

.widget1 {
  height: $height;
}
.widget2 {
  $height: 100px;
  height: $height;
}
.widget1 {
  height: auto;
}
.widget2 {
  height: 100px;
}

!global flag

#main {
  $width: 5em !global;
  width: $width;
}

#sidebar {
  width: $width;
}
#main {
  width: 5em;
}

#sidebar {
  width: 5em;
}

Mixins

Mixins allow you to define styles that can be reused throughout the stylesheet.

@mixin color {
  color: blue;
}

.aMixed {
  @include color;
  width: 200px;
}

.anotherMixed {
  @include color;
  width: 100px;
}
.aMixed {
  color: blue;
  width: 200px;
}
.anotherMixed {
  color: blue;
  width: 100px;
}

Mixin Arguments

@mixin color($color) {
  color: $color;
}

.aMixed {
  @include color(blue);
}

.anotherMixed {
  @include color(red);
}
.aMixed {
  color: blue;
}
.anotherMixed {
  color: red;
}

Mixin Argument Defaults

@mixin foo($width) {
  width: $width;
}
@mixin bar($color: blue) {
  color: $color;
}

.aMixed {
  @include foo;
  @include bar;
}
.anotherMixed {
  @include foo(200px);
  @include bar(red);
}
.aMixed {
  color: blue;
}
.anotherMixed {
  width: 200px;
  color: red;
}

Mixin Keyword Arguments

@mixin foo($width: 100px, $height: 100px, $color: blue) {
  width: $width;
  height: $height;
  color: $color;
}

.foo {
  @include foo($color: red);
}
.foo {
  width: 100px;
  height: 100px;
  color: red;
}

Passing Nested Content to Mixin

@mixin apply-to-ie6-only {
  * html {
    @content;
  }
}

@include apply-to-ie6-only {
  #logo {
    background-image: url(/logo.gif);
  }
}
* html #logo {
  background-image: url(/logo.gif);
}

Functions

@function double($number) {
  @return $number * 2;
}

.foo {
  width: double(100px);
}
.foo {
  width: 200px;
}

Number Operations

p {
  width: 1in + 8pt;
}
p {
  font: 10px/8px;
  width: $width/2;
  height: (500px/2);
  margin-left: 5px + 8px/2px;
}
p {
  width: 1.111in;
}
p {
  font: 10px/8px;
  width: 500px;
  height: 250px;
  margin-left: 9px;
}

Number Relational Operators: < <= >= >

Relational Operators for all types: == !=

Color Operations

color: #010203 * 2
color: #020406;
$translucent-red: rgba(255, 0, 0, 0.5);

color: opacify($translucent-red, 0.3);
background-color: transparentize($translucent-red, 0.25);
color: rgba(255, 0, 0, 0.9);
background-color: rgba(255, 0, 0, 0.25);
color: rgba(#f00, 0.8);
color: rgba(255,0,0,0.8);
ie-hex-str(green)
#FF00FF00

Interpolation

$text-size: 13px;
$line-height: 1.3;
$selector: "body";

#{$selector} {
  font: #{$text-size}/#{$line-height};
}
body {
  font: 13px/1.3;
}

Partials, @import, @include

_text.scss

p {
  font-size: 1em;
}
h1 {
  font-size: 2em;
}

@mixin foo {
  font-family: Helvetica;
}

common.scss

@import "text";

body {
  @include foo;
}

common.css

p {
  font-size: 1em;
}
h1 {
  font-size: 2em;
}
body {
  font-family: Helvetica;
}

@extend

%block {
  display: block;
  box-sizing: border-box;
}

.error {
  @extend %block;
  border: 1px #f00;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}
.criticalError {
  @extend .seriousError;
  background: #fdd;
}
.error,
.seriousError,
.criticalError {
  display: block;
  box-sizing: border-box;
  border: 1px #f00;
}
.seriousError,
.criticalError {
  border-width: 3px;
}
.criticalError {
  background: #fdd;
}

@if, @else if, @else

$fancy = 0;

.foo {
  @if $fancy == 1 {
    border-style: solid;
  }
  @else if $fancy == 2 {
    border-style: dotted;
  }
  @else if $fancy == 3 {
    border-style: double;
  }
  @else {
    border-style: none;
  }
}
.foo {
  border-style: #none;
}

@each

$buttons: 'facebook' 'twitter' 'linkedin' 'github';

@each $button in $buttons {
  .button-#{$button} {
    background: url("icons/#{$button}.png");
  }
}
.button-facebook {
  background: url(icons/facebook.png);
}
.button-twitter {
  background: url(icons/twitter.png);
}
.button-linkedin {
  background: url(icons/linkedin.png);
}
.button-github {
  background: url(icons/github.png);
}

SASS vs SCSS syntax

@import "base"

=mixin ($arg: "default")
  &:before
    content: $arg

.widget
  +mixin("chippewa")
  border: solid blue thin

.title
  font-size: 2em
  @extend .headlines

Additional Resources:

A Scalable CSS Reading List