Reduce, Reuse, Recycle:

MODULAR CSS


Cassondra Roberts & Kendall Totten

User experience development team, redhat.com


http://bit.ly/modular_css

Bootstrap

Mock-ups

<!-- panel -->
<section class="spot1 band">
    <header>
        <div class="container">
            <h1>title</h1>
            <h2>Headline</h2>
        </div>
    </header>

    <div class="content">
        <div class="container">
            <article class="parts-2">
                <div>
                    <p>A column of content can go here</p>
                </div>
                <div>
                    <p>Another column of content can go here</p>
                </div>
                <a class="cta-primary">Call to Action</a>
            </article>
        </div>
    </div>
</section>
.spot1 {
    .content {
        .container {
            section {
                div {
                    @include make-sm-column(12);
                    h3 {
                        text-transform: uppercase;
                        @include rem-fallback(margin-bottom, 1);
                    }
                    h4 {
                        font-size: 1.6rem;
                        margin: 2rem 0 0;
                        @include rem-fallback(font-size, 1.6);
                        @include rem-fallback(margin-top, 2);
                    }
                    .cta-primary {
                        @include rem-fallback(margin-left, 0);
                    }

                }
            }
        }
    }
}

The Story

Deadline: yesterday

Photo Credit: mdanys

<insert framework here>

Add client requests

Photo Credit: marioanima

Evolution of design

Photo credit:

Specificity battles

Photo credit: VisualHunt

Code spaghetti

Photo credit: Vigzu

body.editorial .stat1 .content
  .container > section
  .group-3.customer .stat-box {    
    padding: 0px;
    display: table !important;
}

Actual code

Give up.

Change our

   approach

Spot the patterns, spot the differences

Describe what it is,

instead of

what it says.

Establish a vocabulary

Components

  • Icon Panel
  • Header
  • Call to Action
  • Featured Item
  • Footnote
  • Generic Text
  • Image Embed
  • Link Tile
  • Option Set
  • Promo
  • Quote
  • Video Embed

Layouts

  • Band
  • Group
  • Card
  • Carousel
  • Raw

Components + Layouts = Patterns

Components + Layouts = Patterns

.rh-band-header
.rh-icon-panel
.rh-cta

Components + Layouts = Patterns

rh-data-layout="6 6"

Rules to break the cycle

The Road Runner Rules

A layout never imposes padding or element styles on its children. It is only concerned with their horizontal or vertical alignment and spacing.

A layout never imposes padding or element styles on its children. It is only concerned with their horizontal or vertical alignment and spacing.

bit.ly/layout-styles

The Road Runner Rules

Themes and other data attributes never force changes in appearance; they are always a context that layouts, components and elements can subscribe to.

<div class="rh-card--layout"
    data-rh-theme="dark">
    <div class="rh-card-content">
        <!-- Default Component -->
        <div class="rh-default--component"
            data-rh-align="left">
            <h3>Lorem title</h3>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>...

Themes and other data attributes never force changes in appearance, they are only a context that layouts, components and elements can subscribe to.

%title {
    font-size: 22px;
    font-family: $base-font-family;
    color: blue;
    [data-rh-theme="dark"] & {
        color: white;
    }
}

The Road Runner Rules

A component always touches all four sides of its parent container. No element will have top or left margins and all last children (right or bottom) will have their margins cleared.

A component always touches all four sides of its parent container.

The Road Runner Rules

The component container never has backgrounds, widths, floats, padding or margins. Component styles only target the elements inside.

The Road Runner Rules

Every element has a single, unique, component-scoped class. All styles are applied directly to that selector modified only by contexts and themes.

<div class="rh-band-header--component" data-rh-align="left">
    <h2 class="rh-band-header-title">Band title</h2>
    <h3 class="rh-band-header-headline">Band headline</h3>
    <p class="rh-band-header-summary">Lorem ipsum.</p>
</div>

Every element has a single, unique, component-scoped class. All styles are applied directly to that selector modified only by contexts and themes.

The Road Runner Rules

Elements never use top margins. The first element touches the top of its component.

The Road Runner Rules

Javascript is never bound to any element class. Functionality is "opted in" via data attributes.

rh.webrh.themeToggle = function(target, selector) {
  var $target = $(target),
      currentTheme = $target.attr('data-rh-theme'),
      newTheme = $target.attr(selector);

  $target.attr('data-rh-theme', newTheme);
  $target.attr(selector, currentTheme);
};


$('[data-rh-theme-hover]').hover(function() {
  rh.webrh.themeToggle(this, 'data-rh-theme-hover');
});

$('[data-rh-theme-click]', context).on('click', function() {
  rh.webrh.themeToggle(this, 'data-rh-theme-click');
});

Javascript is never bound to any element class. Functionality is "opted in" via data attributes.

Less work for us later =

BEACSS

Block, Element, Attribute CSS

Playing nice with Sass

  • Keep an eye on the CSS
  • Stay DRY
  • Use semantic placeholders
<div class="rh-cta--component"      // Block Component >     
    <a class="rh-cta-link"          // Element
       data-rh-cta-type="primary"   // Attribute
       href="http://www.redhat.com"
       >Learn more
   </a>
</div>

Rule #0

Styles are applied to classes rather than IDs or elements.

(except when a WYSIWYG is used)

Rule #0:  Don't style elements or IDs

Why?

  • Avoid specificity battles!
  • Tell your SEO buddy that you can modify the mark-up at any time without breaking styles.
<div class="ux-default--component">
    <h1>Lorem ipsum</h1>
    <p>Sed semper non risus ac placerat.</p>
</div>

Rule #0:  Don't style elements or IDs

library/component.html

library/component.scss

.ux-default--component {
    p {
        @extend %default-paragraph;
    }
    h1 {
        @extend %default-h1;
    }
}

Scenario:

What if certain fields get content from a WYSIWYG where it renders without classes?

 

Okay...

Rule #1

Every element should have a single source of truth from which it derives all of its styles.

<div class="ux-component">
  <h2 class="ux-component-headline">...</h2>
</div>

Rule #1:  Single Source of Truth

library/component.html

library/component.scss

.ux-component-headline {
  color: red;
  font-size: 18px;
}
.ux-component-headline {
  text-transform: uppercase;
}

custom-page.scss

Scenario:

I want to use an existing component, but I want to add one style (all caps) on a element within that component.

 

Add additional styles to the existing element class?

No!

<div class="ux-component">
  <h2 class="ux-component-headline
              ux-component-headline-unicorn">
   ...</h2>
</div>

Rule #1:  Single Source of Truth

library/component.html

library/component.scss

.ux-component-headline {
  color: red;
  font-size: 18px;
}
.ux-component-headline-unicorn {
  text-transform: uppercase;
}

custom-page.scss

Scenario:

I want to use an existing component, but I want to add one style (all caps) on a element within that component.

 

Create a new class, and add both classes to the element?

No!

<div class="ux-component">
    <h2 class="ux-component-headline-foo">…</h2>
</div>

Rule #1:  Single Source of Truth

library/component.html

custom-page.html

.ux-component-headline-foo {
  font-size: 24px;
  color: red;
  text-transform: uppercase;
}

custom-page.scss

Scenario:

I want to use an existing component, but I want to add one style (all caps) on a element within that component.

 

Create a new custom element?

Yes!

Rule #2

When styling elements, use the &- to reference the name of the parent selector.

<div class="ux-component-A">
  <div class="ux-headline">…</div>
</div>

Rule #2: Namespace Elements with &-

library/component-A.html

library/component-B.html

<div class="ux-component-B">
  <div class="ux-headline">…</div>
</div>
.ux-headline {
  font-size: 24px;
  color: gray;
}
.component-B .ux-headline {
    color: red;
}

library/headlines.scss

Scenario:

I want to reuse my generic headline style in a few components.

Create a generic headline style that can be placed into any component?

No!

.ux-component {
  background: gray;  
}

.ux-component-headline {
    color: red;
    font-size: 18px;
  }
}

.ux-component {
  background: gray;  
  .ux-component-headline {
    color: red;
    font-size: 18px;
  }
}


Rule #2: Namespace Elements with &-

library/component.scss

Scenario:

I want to reuse my generic headline style in a few components.

List the full element class separately?

 

Nest it inside the parent?

No!

%snazzy-headline {
  font-size: 22px;
  line-height: 1.4;
  color: gray;  
}

Rule #2: Namespace Elements with &-

library/component-A.scss

Scenario:

I want to reuse my generic headline style in a few components.

Create a placeholder from which multiple components may extend?

Yes!

.ux-component-A {
  &-headline {
    @extend %snazzy-headline;
  }
}

library/component-B.scss

.ux-component-B {
  &-headline {
    @extend %snazzy-headline;
  }
}
body.editorial {
  .stat1 {
    .content .container > section {
      .group-3 {
         .stat-box {
            display:table !important;

This is why people say "Don't use Sass."

.rh-stats
  &-box {    
     display:table;
  }
}

.rh-stats-box { 
  display:table; 
}

Nest the right way and prosper!

body.editorial .stat1 .content .container > section .group-3 .stat-box {
   display:table !important;
}

Rule #3

Use extends OR write custom style properties
(not both).

.ux-unicorn-headline {
    @extend %fancy-headline;
    color: pink;
  }
}

Rule #3:  Extends OR custom styles

library/custom-page.scss

Scenario:

I want to extend a placeholder because I’m using all the same styles, but I need to override one thing!

Create a custom selector, and extend a placeholder style and then add custom styles beneath it?

No!

%fancy-headline {
  font-size: 24px;
  line-height: 1.5;
}
%snazzy-headline {
  @extend fancy-headline;
  color: pink;
}

Rule #3:  Extends OR custom styles

library/extends/headlines.scss

Scenario:

I want to extend a placeholder because I’m using all the same styles, but I need to override one thing!

Create a new placeholder that extends another placeholder and add custom styles?

Yes!

.ux-unicorn-headline {
  @extend %fancy-headline;
}
.ux-abominable-snowman-title {
  @extend %snazzy-headline;
}

library/custom-page.scss

Resources & Plan of Attack

Plan of attack

 

  1. Remove any element
    <H1> <H2> styles and replace with classes
  2. Refactor in chunks
  3. Use prefixes to avoid namespace collisions

Resources - bit.ly/modular_css

 

Bonus Goodies:

THANK YOU!

Reduce, reuse, recycle: Modular CSS

By Kendall Totten

Reduce, reuse, recycle: Modular CSS

CSS bloat is a challenge for every enterprise website. As design elements grow and change, code gets added but rarely deleted and our CSS files get larger and harder to maintain. What was that class name that makes the font color black and 22px? Where is the pattern that gives me the right layout and color scheme for this design? Modular design encourages us to look beyond classes and begin styling on the component level using data attributes. Separate content from design and your code base becomes reusable and more compact.

  • 355
Loading comments...

More from Kendall Totten