Reduce, Reuse, Recycle:

MAINTAINABLE & MODULAR CSS

 

 

Kendall Totten  @kendalltotten

Cassondra Roberts  @castastrophee

UX Dev Team, redhat.com

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);
                    }
                }
            }
        }
    }
}

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 the game.

Spot the patterns,

spot the differences

Establish a vocabulary

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

Components

Component classes

YAY!

.rh-icon-panel-icon

.rh-cta-link

.rh-quote-attribution

.rh-video-embed-caption

 

BOO!

.jboss-headline

article > h2 

.maximize-productivity

Layouts

  • Band
  • Group
  • Card
  • Carousel
  • Raw

Components + Layouts = Patterns

Components + Layouts = Patterns

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

Components + Layouts = Patterns

data-rh-layout="6 6"

Rules to break the cycle

The Road Runner Rules

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

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

.rh-band-body [data-rh-layout="4 4 4"]

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

[data-rh-layout~="gallery2"] {
  > * {
    @include breakpoint(480px) {
       float: left;
       @include span(6 of 12);
    }
  }  
}

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;
    }
}
%title {
    color: blue;
    [data-rh-theme="dark"] & {
        color: white;
    }
}

%subheading {
    color: red;
    [data-rh-theme="winter"] & {
        color: LightGray;
    }
}

%date {
    color: purple;
    [data-rh-theme="dark"] & {
        color: white;
    }
    [data-rh-theme="subtle"] & {
        color: LightGray;
    }
}
<div class="rh-card--layout" 
     data-rh-background="black" 
     data-rh-theme="dark">
    <div class="rh-icon-panel-content">
        <h4 class="rh-icon-panel-title">
            Increase productivity        
        </h4>
        <div class="rh-icon-panel-summary">
            Maximize your team's productivity...
        </div>
        <div class="rh-icon-panel-cta">
          <a class="rh-icon-panel-cta-link">
           Read more
          </a>
</div></div></div>

The Road Runner Rules

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

<div class="rh-band" data-rh-band-background="gray" data-rh-background-fixed="">
  <div class="rh-band-container">
       <header class="rh-band-header" data-rh-theme="light">
          <div class="rh-band-header--component">
             <h2 class="rh-band-header-title">CERTIFICATION TOOLKIT</h2>
             <h3 class="rh-band-header-headline">The information you need...</h3>
             <p class="rh-band-header-summary">It shouldn't be a chore to ...</p>
          </div>
       </header>
   </div>
</div>

The Road Runner Rules

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

The Road Runner Rules

Every element (item in a component) has a one component-scoped class.

All styles are applied directly to that selector and are modified only by contexts, such as themes.

Elements are just the items inside of components.

<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 one component-scoped class, modified only by contextual data attributes.

.rh-band-header-title {
  color: red;
  [data-rh-theme="dark"] {
    color: white;
  }
}

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

Read more:  benfrain.com

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 only applied to classes, never IDs or
HTML elements.*

 

*except when a WYSIWYG is used.

Rule #0:  Don't style elements or IDs

Why?

  • Avoid specificity battles!
  • Tell your SEO buddy that he can modify the mark-up at any time without breaking styles.
<h2 class=”rh-band-headline”>Eat your veggies.</h2>
<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 we'll allow it...
this one time

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>

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  OR

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 / component?

Yes!

Rule #1:  Single Source of Truth

%primary-headline {
  color: red;
  font-size: 18px;
}

%secondary-headline {
  @extend %primary-headline;
  text-transform: uppercase;
}

_headline_extends.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 placeholder that extends the original?

Sure!

.ux-component {
  &-headline-foo {
    @extend %secondary-headline;
  }
}

component-foo.scss

Rule #2

When styling elements, use the &- to append the element name to the parent selector name.

<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 class 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'm writing styles for a headline that corresponds with a specific component.

List the full element class separately?

 

Or 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;
  }
}

library/_headline_extends.scss

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; // later: add theme support
}

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, optionally extending another placeholder, for custom styles?

Yes!

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

library/custom-page.scss

Resources

Resources - bit.ly/mod-css

Demo?

 

 

Q: How do webdrupal + webrh + webux work together?

 

Command line tools

 

WebUX mixins, functions, etc.

THANK YOU!

BEACSS for UX Team

By Kendall Totten

BEACSS for UX Team

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.

  • 628
Loading comments...

More from Kendall Totten