Skip to content

Instantly share code, notes, and snippets.

@necolas
Last active December 6, 2023 06:33
Show Gist options
  • Save necolas/1309546 to your computer and use it in GitHub Desktop.
Save necolas/1309546 to your computer and use it in GitHub Desktop.
Experimenting with component-based HTML/CSS naming and patterns

NOTE I now use the conventions detailed in the SUIT framework

Template Components

Used to provide structural templates.

Pattern

t-template-name
t-template-name--modifier-name
t-template-name__subcomponent-name--subcomponent-modifier-name

Examples

t-icon
t-icon--large

t-btn
t-btn--large

t-media
t-media__img
t-media__img--large
t-media__opt
t-media__body

State Components

Used to indicate the state of a component

Pattern

is-state-type

Examples

is-hidden
is-collapsed
is-expanded
is-selected

JavaScript Components

Used to provide JS-only hooks for a component. Can be used to provide a JS-enhanced UI or to abstract other JS behaviours.

Pattern

js-action-name

Examples

js-submit
js-action-save
js-ui-collapsible
js-ui-dropdown
js-ui-dropdown--control
js-ui-dropdown--menu
js-ui-carousel

About theming Components

Could reuse the Template Component naming convention, for example:

specific-name
specific-name--modifier-name
specific-name__subcomponent-name
specific-name__subcomponent-name--subcomponent-modifier-name

Or just let anything go.

<!--
What if we used more white-space around class values?
Does it make the code more readable and surface/separate the components
among the rest of the info in the HTML?
-->
<div class=" t-unit t-media ">
<div class=" t-media__img ">
<a href="#">
<img class=" product-img " src="http://example.com" alt="">
</a>
</div>
<form class=" t-media__opt js-action-rate ">
<button class=" product-rating " type="submit">
<div class=" product-rating__panel ">
<span class=" product-rating__points ">
5
</span>
<span class=" product-rating__label ">
upvotes
</span>
</div>
<strong class=" product-rating__action t-btn btn-normal ">
Upvote
</strong>
</button>
</form>
<div class=" t-media__body ">
<h2 class=" h2 ">
<a href="#">Product title</a>
</h2>
<p>[content]</p>
<ul class=" t-uilist--hz ">
<li><a class=" tag " href="#">tag name</a></li>
<li><a class=" tag " href="#">tag name</a></li>
<li><a class=" tag " href="#">tag name</a></li>
</ul>
</div>
</div>
@oskarrough
Copy link

Patterns in class names are great.

Adding more white-space doesn't make it more readable to me and as far as it makes sense, I believe we should try not to go too far away from grammar rules e.g. not " syntax " but "syntax".

@necolas
Copy link
Author

necolas commented Mar 2, 2012

Spaces around class names are perfectly acceptable with the existing grammar. I don't actually use it, because I think something like smarter syntax highlighting could be more effective.

@oskarrough
Copy link

Yea, agree. What I meant was, that I prefer tying coding style to typographical rules or conventions. Guess it's not grammar per se.

@simurai
Copy link

simurai commented Jun 14, 2012

Something that is a little unfortunate: Double-clicking the names to just edit parts of them doesn't work well since underscores are selected together, but dashes not. So for example with this t-template-name__subcomponent-name, if you wanna select "subcomponent-name" by double-clicking, name__subcomponent gets selected or only name.

An option would be the other way around: t_template_name--subcomponent_name. But I also don't like it that much since underscores visually separate the parts more than dashes.

@AndrewHenderson
Copy link

I'm interested in this approach.

One question, doesn't prefixing your classes with the parent classname ignore the benefit of hierarchy? For instance:

.product-rating {}
  .product-rating .panel {}

Insead of:

.product-rating {}
.product-rating__panel {}

Or worse yet:

.product-rating {}
  .product-rating .product-rating__panel {}

The second option could lead to .product-rating_panel being used without any regard for whether the DOM node is actually nested inside .product-rating.

The third option just seems redundant.

@stefsullrew
Copy link

Andrew, I believe the logic is that name spacing your selectors allows devs that come along behind you to understand that .product-rating__panel is meant to be a sub-component of .product-rating. Thus, it should always be used inside that DOM node. It isn't meant to stand alone or outside that node.

The other benefit I see is, avoiding descendent selectors where possible allows you to utilize the cascade more fully. You can modify the .panel rule with another single class rule that comes later in the cascade. You don't have to use another descendent selector or !important to override the original class like you would if you used:

.product-rating .panel {}

Adding the modifier class .foo to the original .panel would not override the original rule unless you wrote the new rule as:

.product-rating .foo {}

Or

.panel.foo {}

(Chaining selectors, as above, can give you a performance hit and so should be avoided where possible.)

If you had used the .product-rating__panel style of naming, a second single class would modify the necessary properties. For example:

.product-rating__panel--foo {}.

Yes, it does get long... and yes, I've struggled with this as well (in fact, I'm reevaluating naming conventions now for an extremely large project). But for keeping the code maintainable on a site with a large number of developers, this seems logical. You'd be left with these rules (all with the same specificity):

.product-rating {}
    .product-rating__panel {}
    .product-rating__panel--foo {}

(Harry Roberts suggests indenting the rules in your CSS, so that they mirror the DOM instead of nesting rules so that specificity is increased.)

@meleyal
Copy link

meleyal commented Feb 1, 2013

For JS hooks you could use data-attributes, e.g.

<form class="t-media__opt" data-behavior="action-rate">

With options:

<form class="t-media__opt" data-behavior="action-rate" data-scale="[0..5]">

As used in Twitter Bootstrap, Rails.

@jackmoore
Copy link

You could use data attributes, but does that make more semantic sense than using a class? My weakly held position is that they are roughly equal. If there isn't a clear benefit to using data attributes then it's worth noting that they carry a querying performance penalty.

@asakurayoh
Copy link

Meleyal as a point... class should be used for styling, not add functionnality. So using "data" parameter should be a better choice.

@bjankord
Copy link

bjankord commented Mar 6, 2013

@asakurayoh Looking at the jsperf tests for querying class selectors vs. data attribute selectors, I lean towards keep JS hooks as classes. It would be nice if querying performance was the same or at least not as big of a diff between the two. It would be nice to keep JS hooks separate from CSS hooks in the HTML markup.

I've started to adopt this BEM like syntax as well and mixing it with my understanding of SMACSS.

.module {...}
.module--modifier {...}
.module__component {...}
.module__component--modifier {...}

I've seen Jonathan Snook talk about using a syntax like:

.module {...}
.module-submodule {...} /* Module Modifier */
.module--subcomponent{...}

.product-rating {...}
.product-rating-modifier {...}
.product-rating--label {...}

I like this naming convention as well, though I like the double dash for modifiers.

Another naming convention I've thought about similar to the one above looks like:

.module {...}
.module--modifier {...}
.module-component {...} /* Single dash instead of underscores */
.module-component--modifier {...} 

.product-rating {...}
.product-rating--modifier {...}
.product-rating-label {...}

The issue I see with this is it is hard to see the difference between modules and components when module names are made up of multiple words like product rating.

product-rating {...} /* Module */
product-rating-label /* Component with single dash. Harder to understand label is component of product rating. */
product-rating__label /* Component with double underscores. Easier to understand */

One way to get around this would be to use camelCase for module names and not use dashes in module names, only for modifiers (double) and components (single)

.productRating {...}
.productRating-label {...}
.productRating--modifier {...}

All of the naming conventions look odd at first. I think it is important to remind others the overall goal of a HTML class naming convention would be to add clarity for developers, I prefer the BEM like syntax the most, though it can take a while for other developers to understand the thinking behind it and go accustom to it .

I've put together a repo with a collection of common class naming patterns I've seen. CSS-Modules-Subcomponents-And-Modifiers-Collection

@suissa
Copy link

suissa commented Jun 10, 2013

@mindplay-dk
Copy link

Here's a pattern I've been experimenting with:

http://jsfiddle.net/mindplay/XmfBa/

I know the leading hyphen looks odd at first glance, but give it a chance - read through the description in the comments and review the example.

I'd love to get your feedback.

@mlarcher
Copy link

We've been playing around with the BEM syntax since late 2012, and we arrived at the following naming convention:

.componentName {...}
.componentName_Element {...}
.componentName_Element.componentName_Element--modifier {...}

  • camelCase is used for names that a single word can't depict (ex: displaySwitcher)
  • A single underscore is used to separate Blocks and Elements (Perhaps we'll get back to __ if we realize that some coders in our team use the _ wrongfully, but for now _ seems enough)
  • We separate modifiers with two dashes to make it stick out and make its role obvious. Also text editors will usually stop the selection on the hyphen when double-clicking, so it makes sense to use it there and not in any other separator.
  • We prefix all modifier classes with the non modified class to ensure the specificity will always be a bit higher, as we've had issues with that (due to modernizr classes iirc).

This convention along with the use of Sass and a semantical file system organisation has boosted our productivity and made maintenance a breathe.

@TheDutchCoder
Copy link

I love the BEM approach in cases where there's a strict hierarchy, but I find it very verbose when working with more global components.

For example:

.header {}
.article .header {}

is much more flexible and reusable than:

.article__header {}

which is very specific to that block.

I use BEM mainly for widgets and cases where I know I really need the namespace, but doing everything the BEM way kind of defeats the Cascading part of CSS in my opinion.

I totally love the states and js hooks though, I think that's great practice as it instantly shows what the component is for.

@rafaelrinaldi
Copy link

Let's say I have a menu and within this menu I have the list of items. What's the correct naming then?

Menu and menu list should be:

.menu {}
.menu--list {}

But what about the items? Should be .menu--list--item or .menu__list--item?

@mykz
Copy link

mykz commented Oct 24, 2013

rafaelrinaldi

.menu {}
.menu--list {}

The item naming would then fall like this if you needed to modify the menu item

.menu__item--list {}

You could call the modifier what you wanted but it makes sense to keep the modifier name the same as the parent modifier. Then you can couple the new menu item modifier with the parent modifier.

@rafaelrinaldi
Copy link

Gotcha! Thanks @mykz.

@Zeokat
Copy link

Zeokat commented Mar 9, 2014

Zeokat interesting code, i have to adjust it a little. Thanks.

@viT-1
Copy link

viT-1 commented Aug 10, 2016

Another variant of writing css-rules is using AMCSS.
For example: http://codepen.io/viT-1/pen/VjXybd

Copy link

ghost commented Jan 1, 2017

Hi nicolas,

What is the different between a Template Components and theming Component? I can see some of your classes start with a t and some don't. I just want to know the reason and the logic please. Thanks

@ldexterldesign
Copy link

ldexterldesign commented Feb 23, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment