This is Atom's naming convention for creating UI in Atom and Atom packages. It's close to BEM/SUIT, but slightly customized for Atom's use case.
Below the commit box as a possible example:
<div class='github-CommitBox'>
<div class='github-CommitBox-editor'></div>
<footer class='github-CommitBox-footer'>
<button class='github-CommitBox-button'>Commit to sm-branch</button>
<div class='github-CommitBox-counter is-warning'>50</div>
</footer>
</div>
And when styled in Less:
.github {
&-CommitBox {
&-editor {}
&-footer {}
&-button {}
&-counter {
background: red;
&.is-warning {
color: black;
}
}
}
}
Here another example:
<button class='
github-CommitBox-commitButton
github-CommitBox-commitButton--primary
is-disabled
'>
And now let's break it down into all the different parts.
<button class='
namespace-ComponentName-childElement
namespace-ComponentName-childElement--modifier
is-state
'>
github
-CommitBox
Every class starts with a namespace, in Atom's case it's the package name. Since packages are unique (well, at least for all the packages published on atom.io), it avoids style conflicts. Using a namespace like this makes sure that no styles leak out. And even more importantly that no styles leak in.
The namespace for Atom core (atom/atom) is core
-ComponentName.
Atom's UI library is the only exception that doesn't use a namespace. All components start with the ComponentName
. For example Button
, Checkbox
.
Note: If multiple words are needed, camelCase is used:
myPackage
-Component.
github-CommitBox
Components are building blocks of a package. They can be small or large. Components can also contain other components. It's more about seeing what belongs together.
Components can also share the same elements. A pattern often found is that a new Component starts where a childElement ends.
<ul class="github-List">
<li class="github-List-item github-Commit">
<label class="github-Commit-message"></label>
<span class="github-Commit-time"></span>
</li>
</ul>
In this example, github-List-item
is responsible for the "container" and layout styles. github-Commit
is responsible for the "content" inside.
Note: Components use PascalCase. This makes it easy to spot them in the markup. For example in
settings-List-item
, it's easy to see that the component isList
and notsettings
oritem
.
github-CommitBox-commitButton
Elements that are part of a component are appended to the component's class name with a single dash (-
).
Note: If multiple words are needed, camelCase is used: github-CommitBox
-commitButton
.
github-CommitBox-commitButton--primary
Modifiers are used if a component is very similar to the default component but varies slightly. Like has a different color. Modifiers use a double dash --
to distinguish them from components and child elements.
Note: If multiple words are needed, camelCase is used: github-CommitBox-commitButton
--primaryColor
.
github-CommitBox-commitButton is-disabled
States are prefixed with a short verb, like is-
or has-
. Since these class names probably get used in many different places, it should never be styled stand-alone and always be a chained selector.
.is-disabled {
// nope
}
.github-CommitBox-commitButton.is-disabled {
// yep
}
Note: If multiple words are needed, camelCase is used:
has-collapsedItems
.
- Styling elements (like
div
) should be avoided. This makes it easier to switch elements, like from a<button>
to<a>
. - No utility classes. Themes and user styles can only override CSS but not change the markup. Therefore having utility classes doesn't make as much sense once you override them.
- Avoid using existing CSS classes to reference elements in the DOM. That way when the markup changes, functionality and specs will less likely break. Instead use
ref
attributes (ref="commitButton"
). - Avoid changing modifiers at runtime. If you're in need, consider turning the modifier class into a state. For example if you often want to switch a default button to a primary button, change the
github-CommitBox-commitButton--primary
modifier class into ais-primary
state instead. Since state classes are decoupled from components, it's easier to reuse that state class even if the component changes later.
- Just by looking at a class in the DevTools you already get a lot of information about where the source can be found (what package, what component). What relationship each element has (parent/child). What class names are states and might be changed/removed. There should be less "what does this class do" moments.
- Reduces specificity. Mostly there is just a single class. Two when using states. This reduces the specificity war when trying to override styles in packages and
styles.less
. - Using a single class makes it easier to change the markup later. Once a selector like
.class > div > .class
is in the wild, removing thediv
later would break the styling. - Easier to refactor/move code around because you can see what class belongs to what component.
Only in the DOM. During authoring in Less class names can be split into different parts and glued together with &
.
.github {
&-CommitBox {
// styles
&-editor {
// styles
}
}
}
will output as
.github-CommitBox {
// styles
}
.github-CommitBox-editor {
// styles
}
Child elements might also be styled as components. Especially in smaller packages or when components should be shared inside a package. For example github-CommitBox-commitButton
could just be github-Button
if that button doesn't need any special styles even tough it's part of the the CommitBox
component.
The whole selector can of course also be written without nesting. One benefit for not nesting selectors is that they can be easier searched for.
.github-CommitBox { /* styles */ }
.github-CommitBox-editor { /* styles */ }
.github-CommitBox-footer { /* styles */ }