Components and patterns built with Bourbon and Neat.

New Library! Try out Empties: the unstyled Refills.

Managed by thoughtbot, instructions at GitHub

Refills requires Bourbon 5.0+ and Neat < 2.0, ≥ 1.6. We're working on another version with a focus on accessibility, which you can preview here.
<ul class="accordion">
  <li>
    <a href="javascript:void(0)" class="js-accordion-trigger">Accordion Item</a>
    <ul class="submenu">
      <li>
        <a href="javascript:void(0)">Sub Item 1</a>
      </li>
      <li>
        <a href="javascript:void(0)">Sub Item 2</a>
      </li>
    </ul>
  </li>
  <li>
    <a href="javascript:void(0)" class="js-accordion-trigger">Another Item</a>
    <ul class="submenu">
      <li>
        <a href="javascript:void(0)">Sub Item 1</a>
      </li>
      <li>
        <a href="javascript:void(0)">Sub Item 2</a>
      </li>
    </ul>
  </li>
</ul>
.accordion {
  $base-border-color: gainsboro !default;
  $base-line-height: 1.5em !default;
  $base-spacing: 1.5em !default;
  $dark-gray: #333 !default;
  $base-font-color: $dark-gray !default;
  $accordion-menu-border-color: $base-border-color;
  $accordion-menu-color: $base-font-color;
  $accordion-menu-border: 1px solid $accordion-menu-border-color;
  $accordion-menu-background: lighten($accordion-menu-border-color, 10%);
  $accordion-menu-hover: lighten($accordion-menu-background, 2%);
  $accordion-menu-sub-background: darken($accordion-menu-background, 5%);
  $accordion-menu-sub-inset: darken($accordion-menu-sub-background, 6%);
  $accordion-menu-sub-hover: lighten($accordion-menu-sub-background, 2%);
  $accordion-menu-list-padding: ($base-spacing / 2) $gutter;

  background-color: $accordion-menu-background;
  border: $accordion-menu-border;
  margin: 0 0 $base-spacing 0;
  padding: 0;

  ul {
    margin: 0;
    padding: 0;
  }

  li {
    border-bottom: $accordion-menu-border;
    list-style: none;

    > a {
      color: $accordion-menu-color;
      display: block;
      padding: $accordion-menu-list-padding;
      text-decoration: none;
    }

    &:last-child {
      border: 0;
    }

    &:focus,
    &:hover {
      background-color: $accordion-menu-hover;
    }
  }

  ul.submenu {
    display: none;

    li {
      background-color: $accordion-menu-sub-background;

      &:first-child {
        border-top: $accordion-menu-border;
        box-shadow: inset 0 1px 1px $accordion-menu-sub-inset;
      }

      &:focus,
      &:hover {
        background-color: $accordion-menu-sub-hover;
      }
    }
  }

  .is-expanded {
    display: block;
    padding-bottom: 0;
  }
}
$('.js-accordion-trigger').bind('click', function(e){
  jQuery(this).parent().find('.submenu').slideToggle('fast');  // apply the toggle to the ul
  jQuery(this).parent().toggleClass('is-expanded');
  e.preventDefault();
});
$('.js-accordion-trigger').bind 'click', (e) ->
  jQuery(this).parent().find('.submenu').slideToggle 'fast'
  jQuery(this).parent().toggleClass 'is-expanded'
  e.preventDefault()
  return
Animate
This component is based on Dan Eden’s Animate.css. It sets up the JavaScript for triggering an animation for another element. Check out the Animate.css website to view the animations in action and the GitHub page for the CSS.
<div>
  <div class="animate-target"></div>
  <button class="animate-trigger">Press to Animate.css</button>
</div>
$action-color: #477dca !default;

.animated {
  animation-duration: 1s;
  animation-fill-mode: both;

  &.infinite {
    animation-iteration-count: infinite;
  }

  &.alternate {
    animation-direction: alternate;
  }

  &.iteration {
    animation-iteration-count: 2;
  }
}

// Tweak the keyframes below. Or swap them with another animation pattern at
// https://github.com/daneden/animate.css/tree/master/source

@-webkit-keyframes zoomOut {
  0% {
    opacity: 1;
  }

  50% {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
            transform: scale3d(0.3, 0.3, 0.3);
  }

  100% {
    opacity: 0;
  }
}

@keyframes zoomOut {
  0% {
    opacity: 1;
  }

  50% {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
            transform: scale3d(0.3, 0.3, 0.3);
  }

  100% {
    opacity: 0;
  }
}

.zoomOut {
  -webkit-animation-name: zoomOut;
          animation-name: zoomOut;
}

/////////////////////////////////////////////////////////////

.animate-target {
  @include size(6em);
  border: 22px solid #e2e2e3;
  border-radius: 50%;
  margin: 0 auto 3em;
  text-align: center;
  -webkit-transform-style: preserve-3d;
}

button {
  display: block;
  margin: auto;
  outline: none;
  padding: 0.8em 1em;
  text-align: center;
}
$(function() {
  var animationClasses = 'animated alternate iteration zoomOut';
  var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';

  $('.animate-trigger').on('click',function() {
    $('.animate-target').addClass(animationClasses).one(animationEnd,function() {
      $(this).removeClass(animationClasses);
    });
  });
});
$ ->
  animationClasses = 'animated alternate iteration zoomOut'
  animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'
  $('.animate-trigger').on 'click', ->
    $('.animate-target').addClass(animationClasses).one animationEnd, ->
      $(this).removeClass animationClasses
      return
    return
  return
Badges
4 4 4 4 4
<div>
  <span class="badge-default">4</span>
  <span class="badge-alert">4</span>
  <span class="badge-error">4</span>
  <span class="badge-notice">4</span>
  <span class="badge-success">4</span>
</div>
$small-font-size: 0.75em;
$badges: (
  "alert": #fff6bf,
  "default": #999,
  "error": #fbe3e4,
  "notice": #e5edf8,
  "success": #e6efc2,
) !default;

@each $badge-type, $color in $badges {
  .badge-#{$badge-type} {
    background-color: $color;
    border-radius: $small-font-size * 5;
    color: darken($color, 60%);
    display: inline-block;
    font-size: $small-font-size;
    line-height: 1;
    padding: 0.4em 1.2em;
  }
}
Button Group
<div class="button-group">
  <label>
    <input type="radio" name="button-group" value="item" checked>
    <span class="button-group-item">Item</span>
  </label>
  <label>
    <input type="radio" name="button-group" value="other-item">
    <span class="button-group-item">Other But Longer Item</span>
  </label>
  <label>
    <input type="radio" name="button-group" value="other-item">
    <span class="button-group-item">Third</span>
  </label>
  <label>
    <input type="radio" name="button-group" value="third">
    <span class="button-group-item">Last Item</span>
  </label>
</div>
.button-group {
  $base-border-color: gainsboro !default;
  $base-border-radius: 3px !default;
  $base-line-height: 1.5em !default;
  $base-spacing: 1.5em !default;
  $base-font-size: 1em !default;
  $base-background-color: white !default;
  $action-color: #477DCA !default;
  $dark-gray: #333 !default;
  $large-screen: 53.75em !default;
  $base-font-color: $dark-gray !default;
  $button-group-background: $base-background-color;
  $button-group-color: lighten($base-font-color, 30%);
  $button-group-border: 1px solid silver;
  $button-group-inner-border: 1px solid lighten(silver, 18%);
  $button-group-background-checked: $action-color;
  $button-group-color-checked: white;
  $button-group-border-checked: darken($button-group-background-checked, 15%);

  input {
    display: none;
  }

  label {
    margin-bottom: 0;

    @include media($large-screen) {
      float: left;
    }

    .button-group-item {
      background: $button-group-background;
      border-left: $button-group-border;
      border-radius: 0;
      border-right: $button-group-border;
      color: $button-group-color;
      cursor: pointer;
      display: inline-block;
      font-size: $base-font-size;
      font-weight: normal;
      line-height: 1;
      padding: 0.75em 1em;
      width: 100%;

      @include media($large-screen) {
        border-bottom: $button-group-border;
        border-left: 0;
        border-right: $button-group-inner-border;
        border-top: $button-group-border;
        width: auto;
      }

      &:focus,
      &:hover {
        background-color: darken($button-group-background, 3%);
      }
    }

    &:first-child .button-group-item {
      @include border-top-radius($base-border-radius);
      border-top: $button-group-border;

      @include media($large-screen) {
        border-bottom-left-radius: $base-border-radius;
        border-left: $button-group-border;
        border-top-left-radius: $base-border-radius;
        border-top-right-radius: 0;
      }
    }

    &:last-child .button-group-item {
      @include border-bottom-radius($base-border-radius);
      border-bottom: $button-group-border;

      @include media($large-screen) {
        border-bottom-left-radius: 0;
        border-bottom-right-radius: $base-border-radius;
        border-right: $button-group-border;
        border-top-right-radius: $base-border-radius;
      }
    }

    input:checked + .button-group-item {
      background: $button-group-background-checked;
      border: 1px solid $button-group-border-checked;
      box-shadow: inset 0 1px 2px darken($button-group-background-checked, 10%);
      color: $button-group-color-checked;
    }
  }
}
Expander
Expandable section

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio mollitia fugiat facilis enim accusamus quisquam aut, repellendus incidunt quod optio facere labore illo numquam ipsum beatae vero debitis, fugit excepturi.

<div class="expander">
  <a href="javascript:void(0)" class="expander-trigger expander-hidden">Expandable section</a>
  <div class="expander-content">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio mollitia fugiat facilis enim accusamus quisquam aut, repellendus incidunt quod optio facere labore illo numquam ipsum beatae vero debitis, fugit excepturi.</p>
  </div>
</div>
.expander {
  $base-font-size: 1em !default;
  $base-line-height: 1.5em !default;
  $base-spacing: 1.5em !default;
  $action-color: #477DCA !default;
  $dark-gray: #333 !default;
  $light-gray: #DDD !default;
  $base-font-color: $dark-gray !default;
  $expander-arrow-width: 0.7em;
  $expander-toggle-size: 1em;
  $expander-toggle-arrow-size: $expander-toggle-size;
  $expander-toggle-margin: 1em;

  width: 60%;

  .expander-trigger {
    border-bottom: 1px solid $light-gray;
    color: $action-color;
    cursor: pointer;
    display: block;
    font-size: $expander-toggle-size;
    margin-bottom: $expander-toggle-size;
    padding-bottom: $expander-toggle-size / 4;
    text-decoration: none;
    user-select: none;

    &::before {
      content: "\25BC";
      font-size: $expander-arrow-width;
      margin-right: 0.5em;
    }
  }

  .expander-content p {
    color: $base-font-color;
    line-height: $base-line-height;
  }

  .expander-hidden {
    &::before {
      content: "\25BA";
      font-size: $expander-arrow-width;
    }
  }

  .expander-hidden + .expander-content {
    display: none;
  }
}
$(document).ready(function() {
  $('.expander-trigger').click(function(){
    $(this).toggleClass("expander-hidden");
  });
});
$(document).ready ->
  expanderTrigger = document.getElementById('js-expander-trigger')
  expanderContent = document.getElementById('js-expander-content')
  $('#js-expander-trigger').click ->
    $(this).toggleClass 'expander-hidden'
    return
  return
Fade In
This box will fade in when it's 300px above the bottom of the window.
And disappear as it goes below the bottom of the window.
If JavaScript is disabled, it will still show up.
<div id="js-fadeInElement" class="fade-in-element">
  This box will fade in when it's <b>300px above the bottom</b> of the window.
  <br>
  And disappear as it goes <b>below the bottom</b> of the window.
  <br>
  If JavaScript is disabled, it will still show up.
</div>
.fade-in-element {
  $base-border-color: gainsboro !default;
  $base-border-radius: 3px !default;
  $base-background-color: white !default;
  $base-line-height: 1.5em !default;
  $base-spacing: 1.5em !default;
  $dark-gray: #333 !default;
  $base-font-color: $dark-gray !default;

  @include clearfix;
  background: $base-background-color;
  border: 1px solid silver;
  border-radius: $base-border-radius;
  box-shadow: 0 2px 2px transparentize(black, 0.8);
  line-height: $base-line-height;
  margin-bottom: 4em;
  padding: $base-spacing;
  text-align: center;
  width: 100%;

  p {
    color: $base-font-color;
    line-height: $base-line-height;
  }
}

.js-fade-element-hide {
  opacity: 0;
}

.js-fade-element-show {
  opacity: 1;
  transition: all 0.4s ease-in-out;
}
$(document).ready(function() {
  var element = document.getElementById("js-fadeInElement");
  $(element).addClass('js-fade-element-hide');

  $(window).scroll(function() {
    if( $("#js-fadeInElement").length > 0 ) {
      var elementTopToPageTop = $(element).offset().top;
      var windowTopToPageTop = $(window).scrollTop();
      var windowInnerHeight = window.innerHeight;
      var elementTopToWindowTop = elementTopToPageTop - windowTopToPageTop;
      var elementTopToWindowBottom = windowInnerHeight - elementTopToWindowTop;
      var distanceFromBottomToAppear = 300;

      if(elementTopToWindowBottom > distanceFromBottomToAppear) {
        $(element).addClass('js-fade-element-show');
      }
      else if(elementTopToWindowBottom < 0) {
        $(element).removeClass('js-fade-element-show');
        $(element).addClass('js-fade-element-hide');
      }
    }
  });
});
$(document).ready ->
  element = document.getElementById('js-fadeInElement')
  $(element).addClass 'js-fade-element-hide'
  $(window).scroll ->
    if $('#js-fadeInElement').length > 0
      elementTopToPageTop = $(element).offset().top
      windowTopToPageTop = $(window).scrollTop()
      windowInnerHeight = window.innerHeight
      elementTopToWindowTop = elementTopToPageTop - windowTopToPageTop
      elementTopToWindowBottom = windowInnerHeight - elementTopToWindowTop
      distanceFromBottomToAppear = 300
      if elementTopToWindowBottom > distanceFromBottomToAppear
        $(element).addClass 'js-fade-element-show'
      else if elementTopToWindowBottom < 0
        $(element).removeClass 'js-fade-element-show'
        $(element).addClass 'js-fade-element-hide'
    return
  return
Flashes
This is a success message with a link
This is an error message with a link
This is an notice message with a link
This is an alert message with a link
<div class="flash-success">
  <span>This is a success message <a href="#">with a link</a></span>
</div>

<div class="flash-error">
  <span>This is an error message <a href="#">with a link</a></span>
</div>

<div class="flash-notice">
  <span>This is an notice message <a href="#">with a link</a></span>
</div>

<div class="flash-alert">
  <span>This is an alert message <a href="#">with a link</a></span>
</div>
$base-spacing: 1.5em !default;
$flashes: (
  "alert": #fff6bf,
  "error": #fbe3e4,
  "notice": #e5edf8,
  "success": #e6efc2,
) !default;

@each $flash-type, $color in $flashes {
  .flash-#{$flash-type} {
    background-color: $color;
    color: shade($color, 60%);
    display: block;
    margin-bottom: $base-spacing / 2;
    padding: $base-spacing / 2;
    text-align: center;

    a {
      color: shade($color, 70%);
      text-decoration: underline;

      &:focus,
      &:hover {
        color: shade($color, 90%);
      }
    }
  }
}
Hover Tile Animation

Hidden Copy

Lorem ipsum dolor provident eligendi fugiat ad exercitationem sit amet, consectetur adipisicing elit. Unde, provident eligendi.

<div class="hover-tile-outer">
  <div class="hover-tile-container">
    <div class="hover-tile hover-tile-visible"></div>
    <div class="hover-tile hover-tile-hidden">
      <h4>Hidden Copy</h4>
      <p>Lorem ipsum dolor provident eligendi fugiat ad exercitationem sit amet, consectetur adipisicing elit. Unde, provident eligendi.</p>
    </div>
  </div>
</div>
.hover-tile-outer {
  $base-border-color: gainsboro !default;
  $base-line-height: 1.5em !default;
  $medium-screen: 40em !default;
  $hover-tile-height: 10em;
  $base-spacing: 1.5em;

  background: url("https://raw.githubusercontent.com/thoughtbot/refills/master/source/images/mountains.png");
  background-color: beige;
  background-position: top;
  background-size: cover;
  border: 1px solid $base-border-color;
  cursor: pointer;
  height: $hover-tile-height;
  margin-bottom: $base-line-height;

  @include media($medium-screen) {
    width: 40%;
  }

  .hover-tile-container {
    height: $hover-tile-height;
    overflow: hidden;
  }

  .hover-tile-container:hover > .hover-tile {
    transform: translate(0, -100%);
  }

  .hover-tile {
    background: inherit;
    color: white;
    height: inherit;
    overflow: hidden;
    padding: $base-spacing;
    transition: all 0.2s ease-in-out;
  }

  .hover-tile-hidden {
    background: transparentize(#000, 0.5);

    p {
      color: transparentize(#fff, 0.3);
      line-height: $base-line-height;
    }

    h4 {
      margin: 0 0 0.5em 0;
    }
  }
}
Image with gradient overlays

Dynamic height container

<div class="image-gradient-dynamic">
  <img src="https://raw.githubusercontent.com/thoughtbot/refills/master/source/images/mountains.png" alt="">
  <div class="overlay"></div>
  <div class="copy">
    <p>Dynamic height container</p>
  </div>
</div>
.image-gradient-dynamic {
  $image-gradient-color-top: red;
  $transparency-top: 0.9;
  $image-gradient-color-bottom: green;
  $transparency-bottom: 0.6;
  $vertical-angle: 0deg;
  $image-gradient-color-left: teal;
  $transparency-left: 0.8;
  $image-gradient-color-right: yellow;
  $transparency-right: 0.8;
  $horizontal-angle: 90deg;

  line-height: 0;
  position: relative;
  width: 100%;

  img {
    height: auto;
    left: 0;
    position: relative;
    top: 0;
    width: 100%;
  }

  .copy {
    left: 50%;
    margin: auto;
    position: absolute;
    text-align: center;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 999;

    p {
      color: white;
      line-height: 1.5em;
      padding: 1em 2em;
      position: relative;
    }
  }

  .overlay {
    @include position(absolute, 0);
    background-color: transparentize(
      $image-gradient-color-top,
      $transparency-top
    );
    background-image: linear-gradient($vertical-angle,
      transparentize($image-gradient-color-top, $transparency-top),
      transparentize($image-gradient-color-bottom, $transparency-bottom)
    );
    display: block;

    &::after {
      @include position(absolute, 0);
      background-color: transparentize(
        $image-gradient-color-left,
        $transparency-right
      );
      background-image: linear-gradient($horizontal-angle,
        transparentize($image-gradient-color-left, $transparency-left),
        transparentize($image-gradient-color-right, $transparency-right)
      );
      content: "";
      display: block;
    }
  }
}
Pagination
<div class="pagination">
  <ul>
    <li class="page-prev"><a href="javascript:void(0)">Prev</a></li>
    <li>
      <ul>
        <li><a href="javascript:void(0)">1</a></li>
        <li><a href="javascript:void(0)">2</a></li>
        <li><a href="javascript:void(0)">3</a></li>
        <li><a href="javascript:void(0)">4</a></li>
        <li><a href="javascript:void(0)">5</a></li>
        <li><a href="javascript:void(0)">6</a></li>
        <li><a href="javascript:void(0)">7</a></li>
      </ul>
    </li>
    <li class="page-next"><a href="javascript:void(0)">Next</a></li>
  </ul>
</div>
.pagination {
  $base-border-color: gainsboro !default;
  $base-border-radius: 3px !default;
  $base-spacing: 1.5em !default;
  $action-color: #477DCA !default;
  $dark-gray: #333 !default;
  $large-screen: 53.75em !default;
  $base-font-color: $dark-gray !default;
  $pagination-border-color: $base-border-color;
  $pagination-border: 1px solid $pagination-border-color;
  $pagination-background: lighten($pagination-border-color, 10);
  $pagination-hover-background: lighten($pagination-background, 5);
  $pagination-color: $base-font-color;

  text-align: center;

  ul {
    display: inline;
    margin: 0;
    padding: 0;
    text-align: center;

    li {
      display: inline;
      list-style: none;
    }

    ul li {
      display: none;

      &:nth-child(1),
      &:nth-child(2),
      &:nth-child(3) {
        display: inline;
      }

      @include media($large-screen) {
        display: inline;
      }
    }

    li a {
      background: $pagination-background;
      border-radius: $base-border-radius;
      border: $pagination-border;
      color: $pagination-color;
      outline: none;
      padding: ($base-spacing / 4) ($gutter / 2);
      text-decoration: none;
      transition: all 0.2s ease-in-out;

      &:hover,
      &:focus {
        background: $pagination-hover-background;
        color: $action-color;
      }

      &:active {
        background: $pagination-background;
      }
    }
  }
}
Parallax
Parallax Window
<div id="js-parallax-window" class="parallax-window">
  <div class="parallax-static-content">
    <b>Parallax Window</b>
  </div>
  <div id="js-parallax-background" class="parallax-background"></div>
</div>
$parallax-window-height: 30em;
$parallax-background-height: $parallax-window-height * 2;

.parallax-window {
  max-height: $parallax-window-height;
  overflow: hidden;
  position: relative;
  text-align: center;
  width: 100%;
}

.parallax-static-content {
  color: #9A9A8A;
  padding: 8em 0;
  position: relative;
  z-index: 9;
}

.parallax-background {
  background: url("https://raw.githubusercontent.com/thoughtbot/refills/master/source/images/mountains.png") repeat;
  background-position: top;
  background-size: cover;
  background-color: beige;
  height: $parallax-background-height;
  left: 0;
  position: absolute;
  top: - $parallax-window-height / 3;
  width: 100%;
}

// Based on http://codepen.io/skeurentjes/
$(document).ready(function() {
  if ($("#js-parallax-window").length) {
    parallax();
  }
});

$(window).scroll(function(e) {
  if ($("#js-parallax-window").length) {
    parallax();
  }
});

function parallax(){
  if( $("#js-parallax-window").length > 0 ) {
    var plxBackground = $("#js-parallax-background");
    var plxWindow = $("#js-parallax-window");

    var plxWindowTopToPageTop = $(plxWindow).offset().top;
    var windowTopToPageTop = $(window).scrollTop();
    var plxWindowTopToWindowTop = plxWindowTopToPageTop - windowTopToPageTop;

    var plxBackgroundTopToPageTop = $(plxBackground).offset().top;
    var windowInnerHeight = window.innerHeight;
    var plxBackgroundTopToWindowTop = plxBackgroundTopToPageTop - windowTopToPageTop;
    var plxBackgroundTopToWindowBottom = windowInnerHeight - plxBackgroundTopToWindowTop;
    var plxSpeed = 0.35;

    plxBackground.css('top', - (plxWindowTopToWindowTop * plxSpeed) + 'px');
  }
}
parallax = ->
  if $('#js-parallax-window').length > 0
    plxBackground = $('#js-parallax-background')
    plxWindow = $('#js-parallax-window')
    plxWindowTopToPageTop = $(plxWindow).offset().top
    windowTopToPageTop = $(window).scrollTop()
    plxWindowTopToWindowTop = plxWindowTopToPageTop - windowTopToPageTop
    plxBackgroundTopToPageTop = $(plxBackground).offset().top
    windowInnerHeight = window.innerHeight
    plxBackgroundTopToWindowTop = plxBackgroundTopToPageTop - windowTopToPageTop
    plxBackgroundTopToWindowBottom = windowInnerHeight - plxBackgroundTopToWindowTop
    plxSpeed = 0.35
    plxBackground.css 'top', -(plxWindowTopToWindowTop * plxSpeed) + 'px'
  return

$(document).ready ->
  if $('#js-parallax-window').length
    parallax()
  return
$(window).scroll (e) ->
  if $('#js-parallax-window').length
    parallax()
  return
Progress Bars
<div class="progress-bar">
  <span class="meter" style="width: 60%"></span>
</div>
.progress-bar {
  $base-border-color: gainsboro !default;
  $base-background-color: white !default;
  $base-border-radius: 3px !default;
  $action-color: #477DCA !default;
  $progress-border-color: $base-border-color;
  $progress-border: 1px solid $progress-border-color;
  $progress-meter-border-color: $action-color;
  $progress-meter-border: 1px solid darken($progress-meter-border-color, 15%);
  $progress-meter-color: $progress-meter-border-color;
  $progress-background: darken($base-background-color, 5);
  $progress-animation-duration: 0.7s;
  $progress-height: 30px;

  background-color: $progress-background;
  border: $progress-border;
  border-radius: $base-border-radius;
  box-shadow: inset 0 0 3px 0 rgba(darken($progress-background, 50%), 0.15);
  height: $progress-height;
  margin: 0 auto;
  padding: 2px;
  width: 100%;

  > span.meter {
    animation: progress $progress-animation-duration linear infinite;
    background-color: $progress-meter-color;
    background-image: linear-gradient(
                        -45deg, rgba(#fff, 0.15) 25%,
                        transparent 25%,
                        transparent 50%,
                        rgba(#fff, 0.15) 50%,
                        rgba(#fff, 0.15) 75%,
                        transparent 75%
                      );
    background-repeat: repeat-x;
    background-size: 40px 40px;
    border: $progress-meter-border;
    border-radius: $base-border-radius / 1.5;
    box-sizing: border-box;
    display: block;
    height: 100%;
    width: 60%;
  }
}

@-webkit-keyframes progress {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 40px 0;
  }
}

@-moz-keyframes progress {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 40px 0;
  }
}

@-ms-keyframes progress {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 40px 0;
  }
}

@-o-keyframes progress {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 40px 0;
  }
}

@keyframes progress {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 40px 0;
  }
}

60%

<div class="progress-bar-indication">
  <span class="meter" style="width: 60%">
    <p>60%</p>
  </span>
</div>
.progress-bar-indication {
  $base-border-color: gainsboro !default;
  $base-border-radius: 3px !default;
  $base-background-color: white !default;
  $base-line-height: 1.5em !default;
  $action-color: #477DCA !default;
  $progress-border-color: $base-border-color;
  $progress-border: 1px solid $progress-border-color;
  $progress-meter-border-color: $action-color;
  $progress-meter-border: 1px solid darken($progress-meter-border-color, 15%);
  $progress-meter-color: $progress-meter-border-color;
  $progress-background: darken($base-background-color, 5);
  $progress-animation-duration: 0.7s;
  $progress-color: white;

  background-color: $progress-background;
  border-radius: $base-border-radius;
  border: $progress-border;
  box-shadow: inset 0 0 3px 0 rgba(darken($progress-background, 50%), 0.15);
  margin: 0 auto;
  width: 100%;

  > span.meter {
    background-color: $progress-meter-color;
    background-repeat: repeat-x;
    background-size: 40px 40px;
    border: $progress-meter-border;
    border-bottom-right-radius: 0;
    border-radius: $base-border-radius /1.5;
    border-top-right-radius: 0;
    box-sizing: border-box;
    display: block;
    height: 100%;
    width: 60%;
  }

  p {
    color: $progress-color;
    line-height: $base-line-height;
    margin: 0;
    padding: 0.1em 0.5em;
    text-shadow: 0 0 1px black;
  }
}
Ribbon
Ribbon
<div class="ribbon-box">
  <div class="ribbon-wrapper">
    <div class="ribbon">Ribbon</div>
  </div>
</div>
.ribbon-box {
  @include size(100px);
  border: 1px solid #bbb;
  position: relative;
}

.ribbon-wrapper {
  $action-color: #477dca !default;
  $ribbon-background: $action-color;
  $ribbon-size: 85px;

  @include size($ribbon-size);
  overflow: hidden;
  position: absolute;
  right: -1px;
  top: -1px;

  .ribbon {
    background-color: $ribbon-background;
    box-shadow: 0 0 3px rgba(#000, 0.3);
    color: #fff;
    font-size: 0.8em;
    left: -5px;
    line-height: 1.5em;
    padding: 2px 7px;
    position: relative;
    text-align: center;
    top: 15px;
    transform: rotate(45deg);
    width: 120px;
  }
}
Responsive Video Embed
<div class="video">
  <div class="video-wrapper">
      <iframe src="http://www.youtube.com/embed/8ItNE_DX6Cc?showinfo=0&iv_load_policy=3&controls=0" frameborder="0" allowfullscreen></iframe>
  </div>
</div>
.video-wrapper {
  height: 0;
  overflow: hidden;
  padding-bottom: 56.25%; // For ratio 16:9. 75% if ratio is 4:3
  position: relative;

  embed,
  object,
  iframe {
    @include position(absolute, 0 null null 0);
    @include size(100%);
  }
}
Sliding Panel
<button type="button" class="js-menu-trigger sliding-panel-button">
  Click for Sliding Panel
</button>

<nav class="js-menu sliding-panel-content">
  <ul>
    <li><a href="javascript:void(0)">Item 1</a></li>
    <li><a href="javascript:void(0)">Item 2</a></li>
    <li><a href="javascript:void(0)">Item 3</a></li>
  </ul>
</nav>

<div class="js-menu-screen sliding-panel-fade-screen"></div>
.sliding-panel-content {
  $action-color: #477DCA !default;
  $dark-gray: #333 !default;
  $sliding-panel-border-color: $dark-gray;
  $sliding-panel-background: lighten($sliding-panel-border-color, 5%);
  $sliding-panel-color: #fff;
  $sliding-panel-border: 1px solid $sliding-panel-border-color;
  $sliding-panel-background-hover: $action-color;
  $sliding-panel-color-hover: #fff;
  $sliding-panel-background-focus: lighten($sliding-panel-background, 5%);

  @include position(fixed, 0 auto 0 0);
  @include size(220px, 100%);
  background: $sliding-panel-background;
  -webkit-overflow-scrolling: touch;
  overflow-y: auto;
  transform: translateX(-220px);
  transition: all 0.25s linear;
  z-index: 999999;

  ul {
    padding: 0;
    margin: 0;
  }

  li {
    list-style: none;
  }

  li a {
    border-bottom: $sliding-panel-border;
    color: $sliding-panel-color;
    display: block;
    font-weight: bold;
    padding: 1em;
    text-decoration: none;

    &:focus {
      background-color: $sliding-panel-background-focus;
    }

    &:hover {
      background-color: $sliding-panel-background-hover;
      color: $sliding-panel-color-hover;
    }
  }

  &.is-visible {
    transform: translateX(0);
  }
}

.sliding-panel-fade-screen {
  @include position(fixed, 0);
  background: black;
  opacity: 0;
  transition: all 0.2s ease-in-out;
  visibility: hidden;
  z-index: 999998;

  &.is-visible {
    opacity: 0.4;
    visibility: visible;
  }
}

.sliding-panel-button {
  cursor: pointer;
  display: inline-block;
  outline: none;
  padding: 10px 16px;
  position: relative;

  img {
    height: 1.3em;
  }
}

// Based on code by Diego Eis
$(document).ready(function(){
  $('.sliding-panel-button,.sliding-panel-fade-screen,.sliding-panel-close').on('click touchstart',function (e) {
    $('.sliding-panel-content,.sliding-panel-fade-screen').toggleClass('is-visible');
    e.preventDefault();
  });
});
$(document).ready ->
  $('.sliding-panel-button,.sliding-panel-fade-screen,.sliding-panel-close').on 'click touchstart', (e) ->
    $('.sliding-panel-content,.sliding-panel-fade-screen').toggleClass 'is-visible'
    e.preventDefault()
    return
  return
Switch
<label class="label-switch">
  <input type="checkbox" />
  <div class="checkbox"></div>
</label>
.label-switch {
  $action-color: #477DCA !default;
  $base-background-color: white !default;
  $switch-width: 52px;
  $switch-padding: 2px;
  $switch-height: 32px;
  $switch-radius: $switch-height;
  $knob-size: $switch-height - ($switch-padding * 2);
  $knob-radius: $switch-height - ($switch-padding * 2);
  $knob-width: $knob-size;
  $switch-background: $base-background-color;
  $switch-border-background: darken($base-background-color, 9%);
  $switch-shadow: 0 2px 5px transparentize(black, 0.6);

  border-radius: $switch-radius;
  cursor: pointer;
  display: inline-block;
  height: $switch-height;
  position: relative;
  width: $switch-width;

  input[type="checkbox"] {
    display: none;

    + .checkbox {
      background: $switch-border-background;
      border: 0;
      border-radius: $switch-radius;
      cursor: pointer;
      height: $switch-height;
      margin: 0;
      padding: 0;
      position: relative;
      transition: all 0.3s ease;
      width: $switch-width;
      z-index: 0;

      &::before {
        @include position(absolute, 2px 0 0 2px);
        background: $switch-background;
        border-radius: $switch-radius;
        content: "";
        height: $knob-radius;
        transform: scale(1);
        transition: all 0.3s ease;
        width: $switch-width - ($switch-padding * 2);
        z-index: 1;
      }

      &::after {
        @include position(absolute, 2px 0 0 2px);
        @include size($knob-size);
        background: $switch-background;
        border-radius: $knob-radius;
        box-shadow: $switch-shadow;
        content: "";
        transition: all 0.3s ease;
        z-index: 2;
      }
    }

    &:checked {
      + .checkbox {
        background: $action-color;

        &::before {
          transform: scale(0);
        }

        &::after {
          left: $switch-width - $knob-width - ($switch-padding);
        }
      }
    }
  }
}
Stats
  • 98Items
  • 298Things
  • 923Objects
  • 98More
<div class="stats">
  <ul>
    <li>98<span>Items</span></li>
    <li>298<span>Things</span></li>
    <li>923<span>Objects</span></li>
    <li>98<span>More</span></li>
  </ul>
</div>
.stats {
  $base-spacing: 1.5em !default;
  $dark-gray: #333 !default;
  $base-font-color: $dark-gray !default;
  $action-color: #477dca !default;
  $stats-color: lighten($action-color, 10%);

  padding: $base-spacing / 2;

  ul {
    padding: 0;
  }

  li {
    border-right: 1px solid transparentize($base-font-color, 0.8);
    color: $stats-color;
    display: inline;
    float: left;
    font-size: 1.2em;
    line-height: 1.1em;
    padding: 0 0.7em;

    &:first-child {
      padding-left: 0;
    }

    &:last-child {
      border-right: 0;
    }
  }

  span {
    color: $base-font-color;
    display: block;
    font-size: 0.7em;
    font-weight: normal;
  }
}
Textures

Legend

Instructions

Each texture has two versions, shown in each box in the pattern; a normal version and an inverted one. Some versions work better on dark backgrounds, some on light. Switch between the two by changing between 0 and 1 for the final argument passed in the @include.

The mixin can change background color – or gradient colors – for the texture, along with gradient angle.

The two squares below is what will be rendered if you copy-paste the code. Use the numbers in the legend above to switch textures in the code. This is done by changing the texture number in the @include for .texture-normal or .texture-inverted. The default selection is texture number 67. Most of these textures are from subtlepatterns.com

<div class="texture">
  <div class="texture-inverted"></div>
  <div class="texture-normal"></div>
</div>
@mixin texture($gradient-angle, $bg-color-1, $bg-color-2, $texture-number, $inverted) { $texture-list:
  "paper.png"             // Fabric and paper
  "rice_paper.png"
  "noise_lines.png"
  "fabric_plaid.png"
  "first_aid_kit.png"
  "texturetastic_gray.png"
  "lil_fiber.png"
  "tex2res5.png"
  "skin_side_up.png"
  "light_noise_diagonal.png"
  "chruch.png"
  "45degreee_fabric.png"
  "bgnoise_lg.png"
  "little_pluses.png"
  "squairy_light.png"
  "white_texture.png"
  "binding_light.png"
  "double_lined.png"      // Grid, mesh and squares
  "grid.png"
  "absurdidad.png"
  "grid_noise.png"
  "fancy_deboss.png"
  "graphy.png"
  "noise_pattern_with_crosslines.png"
  "old_mathematics.png"
  "ps_neutral.png"
  "subtle_freckles.png"
  "wavegrid.png"
  "az_subtle.png"
  "tiny_grid.png"
  "wavegrid.png"
  "gridme.png"
  "noisy_grid.png"
  "subtlenet2.png"
  "white_carbon.png"
  "lghtmesh.png"
  "p5.png"                 // Dots
  "worn_dots.png"
  "subtle_dots.png"
  "brillant.png"
  "farmer.png"
  "retina_dust.png"        // Lines
  "pinstripe.png"
  "line_horizontal1.png"
  "line_horizontal2.png"
  "linen.png"
  "vintage_speckles.png"   // Patina and irregular
  "subtle_grunge.png"
  "old_wall.png"
  "brushed.png"
  "stucco.png"
  "mooning.png"
  "husk.png"
  "concrete_wall_2.png"
  "noisy.png"
  "concrete_wall_3.png"
  "subtle_surface.png"
  "dust.png"
  "light_alu.png"
  "corrugation.png"
  "kindajean.png"           // Diagonal
  "line_diagonal1.png"
  "line_diagonal2.png"
  "groovepaper.png"
  "brushed_alu.png"
  "rough_diagonal.png"
  "diagonal-noise.png"
  "cross_scratches.png"
  "striped_lens.png"
  "debut_light.png"
  "diagonal_waves.png"    // Special
  "honey_im_subtle.png"
  "pw_maze_white.png"
  "subtle_zebra_3d.png"
  "white_wave.png"
  "circles.png"
  "crosses.png"
  "grilled.png"
  "pw_pattern.png"
  "struckaxiom.png"
  "vichy.png"
  "wavecut.png"
  "whitey.png"
  "cream_pixels.png"
  "grey.png"
  "shinedotted.png"
  "stacked_circles.png"
  "noisy_grid_simple.png";

  @if ($inverted == 1) {
    background: url("https://raw.githubusercontent.com/thoughtbot/refills/master/source/images/textures/#{nth($texture-list, $texture-number)}"),
                linear-gradient($gradient-angle, $bg-color-1, $bg-color-2),
                center no-repeat $bg-color-1 scroll;
  }

  @else {
    background: url("https://raw.githubusercontent.com/thoughtbot/refills/master/source/images/textures/inverted/#{nth($texture-list, $texture-number)}"),
                linear-gradient($gradient-angle, $bg-color-1, $bg-color-2),
                center no-repeat $bg-color-1 scroll;
  }
}

// The mixin above should preferably be in a file of its own. Below is the code for the object that has the texture.

.texture {
  $texture-top-background: lighten(#ddf0ef, 1%);
  $texture-bottom-background: darken(#ddf0ef, 10%);
  height: 5em;
  width: 100%;

  .texture-normal {
    // gradient color 1, gradient color 2, gradient angle, texture-number, 0=inverted or 1=normal
    @include texture(180deg, $texture-top-background, $texture-bottom-background, 67, 1);
    float: left;
    height: 5em;
    width: 50%;
  }

  .texture-inverted {
    // gradient color 1, gradient color 2, gradient angle, texture-number, 0=inverted or 1=normal
    @include texture(180deg, $texture-top-background, $texture-bottom-background, 67, 0);
    float: left;
    height: 5em;
    width: 50%;
  }
}
Tooltip
Hover for Tooltip

Lorem ipsum doldae oluptate aperiam unde voluptates quas.

<div class="tooltip-item">
  Hover for Tooltip
  <div class="tooltip">
    <p>Lorem ipsum doldae oluptate aperiam unde voluptates quas.</p>
  </div>
</div>
.tooltip-item {
  $base-border-color: gainsboro !default;
  $base-border-radius: 3px !default;
  $base-line-height: 1.5em !default;
  $dark-gray: #333 !default;
  $base-font-color: $dark-gray !default;
  $tooltip-background: white;
  $tooltip-color: $base-font-color;
  $tooltip-max-width: 16em;
  $tooltip-arrow-width: 8px;
  $tooltip-shadow: 0 2px 2px silver;
  $tooltip-distance-from-item: 3em;
  $tooltip-arrow-distance-from-box: -1.3em;

  background: $tooltip-background;
  border: 1px solid $base-border-color;
  border-radius: $base-border-radius;
  display: inline-block;
  padding: 0.5em 1em;
  position: relative;
  text-align: center;

  &:focus,
  &:hover .tooltip {
    opacity: 1;
    visibility: visible;
  }

  .tooltip {
    @include position(absolute, null 0 $tooltip-distance-from-item 0);
    background-color: $tooltip-background;
    background: #fff;
    border-radius: $base-border-radius;
    box-shadow: $tooltip-shadow;
    color: $tooltip-color;
    font-size: 0.9em; // Make sure you use -webkit-backface-visibility: hidden; on the body element to prevent 1px nudging bugs.
    line-height: 1.5em;
    margin: 0 auto;
    max-width: $tooltip-max-width;
    opacity: 0;
    padding: 1em;
    text-align: center;
    transition: all 0.2s ease-in-out;
    visibility: hidden;
    z-index: 10;

    p {
      color: $base-font-color;
      line-height: $base-line-height;
      margin: 0;
    }

    &::after {
      @include position(absolute, null 0);
      border: $tooltip-arrow-width solid transparent;
      bottom: $tooltip-arrow-distance-from-box;
      color: $tooltip-background;
      content: "▼";
      font-size: 1.4em;
      margin-left: -$tooltip-arrow-width;
      text-align: center;
      text-shadow: $tooltip-shadow;
    }
  }
}