Front-End Components

The component pattern is the basis of our front-end framework.

The front-end component is a powerful design pattern for building front-end objects. The component pattern adds a level of reliability and confidence in your stylesheets you wouldn’t have otherwise.

Front-End Components Are:

  • portable
  • reliable
  • extensible
  • reusable
  • self-contained

Anatomy of a Front-End Component

There are three basic parts of a component the name (namespace), modifier classes, and attributes. As illustrated below.

<div class="name name-modifier-classes">
  <div class="name-attribute-1-class">Lorem</div>
  <div class="name-attribute-2-class">Ipsum</div>
</div>

The component’s name should always be the first CSS class listed in the root element, followed by any modifier classes.

Name, Namespace, & Root Element

Each component should start with a single HTML tag (usually a div) that wraps the whole component. This acts as the root node of the component and every other part of the component is housed inside it. The component root always has a class name, unusually one or two words, that defines the component’s namespace.

The component name is the unique name of the component; it is important you don’t give two components the same name.

The component name also acts as a namespace for all modifier classes and attributes of the component.

Consider the Price component.

12 for $ 4 99 lb. with Card

    <p class="price price-big-dollars">
      <span class="price-prefix">12 for</span>
      <span class="price-dollar-sign">$</span>
      <span class="price-dollars">4</span>
      <span class="price-cents">99</span>
      <span class="price-suffix">lb.</span>
      <img  class="price-suffix-icon" src="/assets/images/Card_45x29.gif" alt="with Card">
    </p>
Naming Order

Notice how the component name price is the first thing listed in the root element’s class attribute, followed by the price-big-dollars, modifier class, this order is important for maintaining a clear understanding of a component. If the component’s root element contained other classes, for example the responsive utility classes d-none and d-sm-block, then those would come after the component specific classes for name and modifier classes.

Order:

  1. specific component (or attribute)
  2. specific component-modifiers
  3. generic component
  4. generic component modifiers
  5. utility classes

For example:

<p class="price price-big-dollars d-none d-sm-block"></p>

Responsive class names are ordered from smaller to larger. Inline with the mobile-first philosophy.

Attributes

Observe how every attribute name begins with the component name plus a dash. Thus, what we might have called the dollar-sign attribute is actually expressed as price-dollar-sign. By strictly adhering to the namespacing of all attributes and modifier classes we are able to apply styling to a component with absolute confidence that no other component or portion of our website will be affected. This makes our design system very reliable! I can’t overstate what a benefit adhering to the namespacing approach!

Notice that every element in our price component example has an attribute name class, even those we didn’t apply styles to and don’t intend to. At times this may seem like a lot of extra and unnecessary code, but there is a hidden power and flexibility to doing this this way!

One of the obvious ways it helps to place attributes on all elements is when it comes to website theming. For example, the price-prefix class, it might be that we don’t anticipate needing any additional styling to this element, what if we had simply left off the class? What happens when we get a retailer who insists the prefix area must be italic and dark green, but the rest of our component is to remain as originally designed?

In our retailer’s theme we’d probably get some CSS that looks something like this:

.price > span:first-child {
  font-style: italic;
}

At first blush this probably seems like a practical solution, but a whole host of issues can arise from it. What if in some contexts there is no prefix, so the designer left off that element – whatever comes next would get the italics, maybe the dollar sign or the cents, who knows.

To over come this issue you’d probably have to write something like this:

.price > span {
  font-style: italic;
}
.price-overlay,
.price-dollar-sign,
.price-dollars,
.price-cents,
.price-cent-sign,
.price-suffix,
.price-suffix-icon {
  font-style: normal;
}

Now what seemed simple is starting to get complicated. How will this new solution react if the same thinking that left the attribute name off the prefix also left it off a few other elements, like maybe the dollar sign or the cent sign. We’d have to start getting pretty exotic with our selectors to catch it. Also, consider what happens if someone decided to use a different HTML tag than the span tag?

Now consider how being able to do the following is very simple, reliable, and robust.

.price-prefix {
  font-style: italic;
}

You can write this in your retailer theme and have complete confidence it’s not going to have any untended consequences.

Plus, you won’t see complicated & fragile CSS selectors like these gems lifted from the Tops website:

.circular-nav-pages-right .pagination>li>a, .circular-nav-pages-left .pagination>li>a {
     padding: 3px 7px;
}
.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.active>a:focus, .navbar-inverse .navbar-nav>.active>a:hover {
  color: #fff;
  background-color: #d31145;
  background: #d31145;
}

In a well designed component based design system you’ll never see CSS selectors that are deeply nested, or use anything other than a class name. If your selector needs more than two levels, that’s a sign your component probably has a flaw in it’s design. You should never use actual tag names or id attributes for CSS selectors!

Modifier Classes

Modifier classes allow you to enhance the default characteristics of the component or extend it with a subclass.

Modifier classes are usually placed on the component’s root element directly proceeding the component’s name class. The one exception to this is when you’re building a modifier that only affects one item in a list of items, and you need to specify which item to apply the effect to.

Modifier classes can come in many different varieties.

Example Modifier classes:

  • Size classes like name-sm or name-lg
  • Visual effects like name-shadow, name-stripped, or name-bordered
  • Colors like name-primary, name-warning, or name-danger

Note: in the examples above the name portion would be replaced by the name of your component (e.g. price-sm, price-lg).

These are just a few examples so you can get an idea of how modifier classes work.

Subclasses

A special kind of modifier class is the subclass. Those familiar with Object Oriented Programming (OOP) ought to recognize the term subclass. We are assuming you are familiar with the concept, so we won’t be going into a detailed description here. However, it’s important to understand how subclasses are implemented in our design system.

Consider the following example:

<div class="circular-item circular-item-standard"> ... </div>
<div class="circular-item circular-item-standard-promotion"> ... </div>
<div class="circular-item circular-item-standard-coupon"> ... </div>

Here we have three circular-item components. Each one shares some common styling, they are all cards on the grid, with a slight shadow, and padding at the bottom for an absolute positioned button row, rounded corners, a 1px border, & etc. They all receive this styling by having the root circular-item class.

While they are similar in many ways, they also have some distinct differences handled by their subclass. For example, of these three items only the circular-item-standard has a circular-item-heading circular-item-details attributes. Also the circular-item-standard-promotion is the only one with a square image instead of the shorter rectangle images the other two have. By subclassing we are able to apply styles common to all circular-item components and unique styles for each subclass.

SCSS Structure

The namespaceing of a component is enforced by using Sass nesting. Sass nesting allows us to enforce the namespace of the component and write reliable styles.

.name {
  margin: 15px;
  
  .name-attribute-one {
    ...
  }
  .name-attribute-two {
    ...
  }

}

Notice in the example above the name class wraps around all other class of the component. This way if we forget to namespace the name of an attribute (or otherwise have a class name collision in some other part of our code) our rule set for an attribute will only work if it is located inside our component. Also, notice we have all our CSS classes namespaced with teh same value as the root element. Consider these examples:

Price Example

.price {
  
  .price-prefix {
    ...
  }
  .price-suffix {
    ...
  }

}

Shopping List Item Example

.shopping-list-item {
  
  .shopping-list-item-name {
    ...
  }
  .shopping-list-item-quantity {
    ...
  }

}

MMMmmm… nice clean namespacing!

Components within Components

The whole point of components is to create discrete, reusable, parts. We can use simple components to build more complex components. This means we have a bunch of very simple objects that are included within more complex objects.

An easy example to understand is the button object. The button component looks something like this:

<button type="submit" class="btn btn-primary ">
  Add to Shopping List
</button>

Now look at our circular-item component we want a shopping list form at the bottom of each component, so we use a btn component.

<div class="circular-item circular-item-standard">
  <div class="circular-item-heading">Wow!</div>
  <div class="circular-item-title">Some Awesome Deal!</div>
 ... 
   <button type="submit" class="btn btn-primary ">
     Add to Shopping List
   </button>
 </div>

When you take an even closer look you’ll see the circular-item circular-item-standard component contains several more basic components:

  • price
  • icon
  • btn
  • shopping-list-item
Picture of Homegrown Amaize! Sweet Corn

LOCAL! Amaize! Sweet Corn - The Best Corn You'll Ever Eat!

Homegrown Amaize! Sweet Corn

White Sweet Corn with an Amazing Flavor, Distinctively Delicious Pop and Crunch

12 for $ 4 99 lb. with Card

Available for a Limited Time

Starts 4/10/2050

Recipe: Amaize Sweet Corn Summer Spreads

<article class="circular-item circular-item-standard card card-standard col" id="circular-item-1">
  <img src="/assets/images/circular-items/sweet_corn_1k.jpg" alt="Picture of Homegrown Amaize! Sweet Corn" class="circular-item-image card-img-top img-fluid">
  <div class="circular-item-body card-body">
    <p class="circular-item-heading">LOCAL! Amaize! Sweet Corn - The Best Corn You'll Ever Eat!</p>
    <h3 class="circular-item-title">Homegrown Amaize! Sweet Corn</h3>
    <p class="circular-item-description">White Sweet Corn with an Amazing Flavor, Distinctively Delicious Pop and Crunch</p>
    <p class="price price-big-dollars">
      <span class="price-prefix">12 for</span>
      <span class="price-dollar-sign">$</span>
      <span class="price-dollars">4</span>
      <span class="price-cents">99</span>
      <span class="price-suffix">lb.</span>
      <img  class="price-suffix-icon" src="/assets/images/Card_45x29.gif" alt="with Card">
    </p>
    <p class="circular-item-details">Available for a Limited Time</p>
    <p class="circular-item-sale-dates text-danger">Starts 4/10/2050</p>
    <p class="circular-item-recipe">
      <span class="circular-item-recipe-label">Recipe:</span>
      <a href="http://www.topsmarkets.com/Recipes/Detail/7417/Amaize_Sweet_Corn_Summer_Spreads/" class="circular-item-recipe-title-link">
        Amaize Sweet Corn Summer Spreads
      </a>
    </p>
  </div>
  <div class="circular-item-footer card-footer">
        <div class="shopping-list-item">
      <form action="/shopping_list/add/circular_item/[self->id]/qty/" class="shopping-list-item-form">
        <div class="row justify-content-end">
          <div class="shopping-list-item-qty-col col-12 col-sm">
            <div class="shopping-list-item-qty-group input-group ">
              <div class="input-group-prepend">
                <span class="shopping-list-item-qty-label input-group-text" id="shopping-list-qty-label-1">Qty</span>
              </div>
              <input type="text" name="Qty" value="12" maxlength="2" class="shopping-list-item-qty form-control"  id="shopping-list-item-qty-1" aria-label="Username" aria-describedby="shopping-list-qty-label-1">
            </div>
          </div>
          <div class="shopping-list-item-btn-col col-12 col-sm-auto">
            <button type="submit" class="shopping-list-item-btn btn btn-primary  btn-block btn-sm-inline-block">
              <i class="icon-plus-circle-solid"></i>
              <span class="d-inline d-sm-none">Add to</span>
              <span class="d-none d-sm-inline"><span class="sr-only">Add to </span>Shopping</span>
              List
            </button>
          </div>
        </div>
      </form>
    </div>

  </div>
</article>

Multi-Component Objects

If you look at the example above you’ll notice the circular-item component also has a card component on the same element. In case it should be clear that card is a less specific and more generic component, and the circular-item is a more specific and less composable object.

If you look at the example above you’ll notice the circular-item component also has a card component on the same element. We list the circular-item component and it’s modifiers first because it is the more specific component and the card component afterwards because it is more generic.

When two component are defined on the same HTML element, list the more specific component and it’s modifiers first and the more generic second. A good rule of thumb for deciding which is the more specific and which is the more generic, ask yourself this “can I add the CSS rule sets for object A and place it in object B and still call it an object B?” and then ask the reverse and see which holds more true.

For example:

A. Can I add the CSS rule sets for circular-item and place it in card and still call it a card?

This wouldn’t workout very well, it wouldn’t make sense to include a sale item’s data, and add to shopping list button and etc in all the places we use a card.

vs.

B. Can I add the CSS rule sets for card and place it in circular-item and still call it a circular-item?

This would work just fine, there probably are none (or very few) places we would want a circular item without it having a card layout.

Using this little test it should be clear the preferred way to list our classes is as follows:

<div class="circular-item circular-item-standard card card-standard"> ... </div>

Conclusions

The component design pattern is:

  • Reliable: by namespacing the component, it’s attributes, and modifiers you can have complete confidence your styles won’t inadvertently affect some other part of the website.
  • Reusable: a component can be used many times on the same page
  • Portable: a components are self-contained and able to be dropped in anywhere
  • Extensible: using modifier classes and subclasses we can build a variety of component variations