Here lies the foundation of the styling system. We reset default browser styles to give a clean slate upon which we build up the styles. If you find yourself defining a ruleset that fights against base styling, it should probably go here. Definitions of nice defaults for bare HTML elements should also be here.
-
-
Save ryngonzalez/c2a8aba799d9aa51b645 to your computer and use it in GitHub Desktop.
Every conceptual bit of interface that can be reused should be encapsulated in a component. You have a thing that triggers an action when clicked? Wrap it up into the concept of a button
. A thing that displays a list of people allowed to view a space? Call it a memberList
.
You get the point. When you have things that need to be used repeatedly and represent some behavior or functionality, wrap it up as a component. Build an Angular directive, react component, polymer element, etc. The style and presentation counterparts for these components live here.
So it was a hard fought battle between doing what I like and doing the thing that makes reading selectors easy and decision making when writing selectors simple. I went with the latter. Much of what follows is primarily a blatant copy and paste of @fat's excellent CSS guidelines for Medium, though some extra bits of commentary and a few minor changes (@extend
and component modifiers, naming recommendations, etc.) are added in from me. Also SUIT CSS from @necolas and Twitter, though things are modified a bit from that. Anyway:
The component's name must be written in camel case. Prefer using single word names for components if possible (to mimic the functionally limited but aesthetically preferred CSS convention of dash-spacing words).
.myBanner { /* … */ }
.banner { /* … */ }
<div class="myBanner">
…
</div>
<div class="banner">
…
</div>
A component modifier is a class that modifies the presentation of the base component in some form. Modifier names must be written in camel case and be separated from the component name by two hyphens. To define the relationship between a component and its modifier, use SASS's @extend
directive. Using @extend
means that a modifier inherits the rules of a class it's extending. It allows for simpler markup, moves the definition of inheritance relationships into CSS rather than HTML.
/* Core button */
.button { /* … */ }
/* Primary button style */
.button--primary {
@extend .button;
/* … */
}
When defining rules for modifiers, make sure to check the order of definitions (hint: check your _index.scss
file for your component). Component modifiers must be defined after the base component they modify.
<button class="button--primary">…</button>
A component descendant is a class that is attached to a descendant node of a component. It's responsible for applying presentation directly to the descendant on behalf of a particular component. Descendant names must be written in camel case.
We also call these subcomponents.
<div class="post--new">
<header class="post-metadata">
<img class="post-avatar">
…
</header>
<div class="post-body">
…
</div>
</div>
Use is-stateName
for state-based modifications of components. The state name must be Camel case. Never style these classes directly; they should always be used as an adjoining class.
JS can add/remove these classes. This means that the same state names can be used in multiple contexts, but every component must define its own styles for the state (as they are scoped to the component).
.post { /* … */ }
.post.is-selected { /* … */ }
<li class="post is-selected">
…
</li>
so ends the naming section. from your friends @ryngonzalez, @fat, and @necolas. fin.
<componentName>[--modifierName|-descendantName]
- Base component:
.button
- Component modifier:
.button--primary
- Component descendent:
.button-text
<a class="button--home" href="/">
<span class="button-text">Home</span>
</a>
===
The styling of a component should not effect unrelated components. From the SUIT CSS design principles doc:
The implementation of a component should not be exposed to other components. For example: your component should not leak styles into the HTML tree fragments of other components; a component's HTML should not be directly included in the HTML for another component.
Complexity is a significant problem for large, adaptive applications. The more you can reduce the entanglement of your components, the easier it is to reason about the system.
If your components are large and have subcomponents, consider nesting the main styling and subcomponent styling in a subfolder.
components
|- _index.scss
|- myOtherComponent.scss
|
|- my-component
|- _index.scss
|- myComponent.scss
|- myComponent-subcomponentOne.scss
|- myComponent-subcomponentTwo.scss
If you find the need to share styling specific to the current application between components, create a shared placeholder (%placeholder-class
) style in ../styles
and @extend
that base where needed in your components.
If you see the need to add styling that can be applied in a more general sense across applications, and serves a functional selector (layout helpers, style resetting, etc.) expand or create new styling utilities in ..\utilities
.
If you find yourself with a set of rules that manifests in many parts of a component and its subcomponents, define those rules up front (in the equivalent myComponent.scss
file of your component folder) and @extend
a placeholder or an existing class. If you find yourself doing this, consider whether the ruleset can be further pushed up the stack (to intra-component sharing).
You want to define all re-usable variables in the style system in this folder. Colors, z-index levels, custom easings, etc. should all live here.
Since these files hold variables and configuration to be used elsewhere, you should prefix them with an underscore, so that the SASS compiler doesn't compile anything contained in these files unless imported by another file that is slated for compilation. Names should reflect the variable map being defined, and should be in the plural form.
For example: _z-indexes.scss
, _font-weights.scss
, etc.
When defining a new map value, make sure the key is a string.
$colors: (
'red': #E46766
);
Colors should be aliased to a descriptive name (this site should be helpful) and any names describing their use in the style system. All color variables should be added to the $colors
map. Space out groups of the same color with newlines, and keep the values of each group vertically aligned. You can either use the $colors
map to access the color, or access via the corresponding map helper function. For example:
// Definition
$colors: (
'red': #E46766,
'error': #E46766,
'denial': #E46766,
'blue': #2A7BBC,
'green': #72B456,
'confirm': #72B456,
'success': #72B456
);
// Usage
.my-thing {
color: map-get($colors, 'red');
}
.my-other-thing {
color: colors('red');
}
.error {
color: colors('error');
}
Easing follows the same model as colors, with a global map and helper function as well. Use descriptive naming pls.
The point of having a z-index scale is to allow for z-index issues to be fixed by modifying only one file. All z-index problems can be resolved here. If you have a new component that needs a z-index, add the appropriate z-index into the $levels
map with the appropriate z-index value. Only use the bare, numeric z-index values when prototyping or in the process of building out components. Only use set of z-index values already defined (600
is preferred over an arbitrary level like 666
or 100000
). Sometimes, variables will need to reordered, with the numeric values lowered. That's fine.
// Definition
$levels: (
…
'6': 600,
'hovering': 600
);
.my-hovering-thing {
z-index: levels('hovering');
}
The SASS documentation is your friend. Know your SASS functions too. Get familiar with those two links, keep them close at hand.
Table of Contents
Like CSS itself, we use a cascading organization to make sure rules are defined in the correct and expected order.
main.scss
|
|__@import 'libraries/index';
|
|__@import 'utilities/index';
|
|__@import 'config/index';
|
|__@import 'base/index';
|
|__@import 'styles/index';
|
|__@import 'components/index';
|
|__@import 'pages/index';
Our folder hiearchy goes from external, non-specific, to highly-specified and application-specific. If you want to know where to put things, read the README
s available in each folder.
If you need to add a new subfolder (anywhere), make sure to use an _index.scss
and follow the pattern shown elsewhere.
How do you name a thing? How does a name represent the thing it describes, and how does one systematically determine that mapping given either a name or a thing? The semantic rules below describe how names map to the concept of a thing.
Functional selectors describe the behavior or an aspect of a thing. Conceptual selectors describe what a thing is.
Given some markup:
<div class="lightbox is-visible">
…
</div>
…and some styling:
.lightbox {
background: white;
border: 1px solid grey;
box-shadow: 0px 1px 4px rgba(0,0,0,0.3);
&.is-visible {
display: block;
}
}
.lightbox
describes the concept of a lightbox ascribed to a <div>
. .is-visible
describes the current behavior and state of the element.
For stateful, functional selectors, always prefix them with .is-
. When trying to determine the behavior and name of a functional selector, always determine what the default state of the item it's trying to modify is. For example, I have a button that's only visible when some user interaction has occurred:
<button class="button--confirm">…</button>
So I determine the base state to be display: none;
, and I create a functional selector that modifies the state to display: block;
when appropriate:
.button--confirm {
…
display: none;
&.is-visible {
display: block;
}
}
Use two-space indenting.
CSS rules should be comma separated but live on new lines:
Right:
.content,
.content-edit {
…
}
Wrong:
.content, .content-edit {
…
}
CSS blocks should be separated by two newlines.
Right:
.content {
…
}
.content-edit {
…
}
Wrong:
.content {
…
}
.content-edit {
…
}
Quotes are optional in CSS and SCSS. We use single quotes as it is visually clearer that the string is not a selector or a style property. Use double quotes only when you need to do SASS string interpolation.
Right:
background-image: url('/img/you.jpg');
font-family: 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial;
Wrong:
background-image: url(/img/you.jpg);
font-family: Helvetica Neue Light, Helvetica Neue, Helvetica, Arial;
Use SASS's nesting ability with caution. Nest when you need to add a functional selector definition to styling. Also use it for the equivalent built-in CSS pseudo-selectors. Break out named subcomponents into un-nested rulesets. If you have general, non-named descendants that need to be styled, that's a fine place to nest. Just consider adding subcomponent classes to descendants and breaking them out in the future. If you have a modifier class that modifies an existing component, and needs to modify and existing component's subcomponents, that's also a good place to nest.
.button--confirm {
…
display: none;
&.is-visible {
display: block;
}
&:active {
…
}
.button-content {
color: green;
}
span {
display: inline;
}
}
When writing rulesets, organize the rules within them as follows:
.button {
@extend …;
@include …;
// Layout
…
// Appearance
…
// Typography
…
// Modifiers
…
// Children
…
}
Only split things up if there are enough rules in the ruleset such that there are multiple sections. Put @extend
s and @include
s at the top of the ruleset.
ul.user-list li span a:hover { color: red; }
Styles are resolved during the renderer's layout pass. Selectors are resolved from right to left, resolving when it has been detected the selector does not match. Therefore, in the example above, every <a>
tag has to be inspected to see if it resides inside a span and a list. As you can imagine this requires a lot of DOM walking and and for large documents can cause a significant increase in the layout time. For further reading checkout: https://developers.google.com/speed/docs/best-practices/rendering#UseEfficientCSSSelectors
If we know we want to give all <a>
elements inside the .user-list
red text on hover we can simplify this style to:
.user-list > a:hover {
color: red;
}
If we want to only style specific a
elements inside .user-list
we can give them a specific class:
.user-list > .link-primary:hover {
color: red;
}
- Specifity Calculator, given a selector: http://specificity.keegan.st/
-
Selector: A sequence of class names, id names, etc. that defines the elements effected by the ruleset subsequently defined
-
Ruleset: a set of CSS declarations that are defined on a selector or group of selectors
-
SASS: the name of the CSS preprocessor and the name of the language that is processed into CSS. Also the name of a deprecated syntax that the language can be written in. The file extension for SASS syntax is
.sass
-
SCSS: the name of the current and preferred syntax to write SASS language code. The file extension for SCSS syntax is
.scss
===
The goal here is to make writing the best CSS in the world easy, through well-defined rules and smart system design. Hopefully this is helpful, and does its job well. Don't be afraid to make changes and modify the rules after finding out a better way of operating.
So, go forth and make more kickass CSS.
Pages should include the rulesets that define view and state-specific styling — if you can (or do) map a state to a URL, and it has the need to modify components styling in that context or define custom styles, you should probably add a page style.
Use PascalCase (uppercase all words). For example:
-
Members.scss
-
PostView.scss
-
Members.scss
Treat descendent elements of pages as components, with the same naming scheme used elsewhere (camelCase).
Ideally, you'll want to define styles in way that components can be created and reused, and then applied in a page with minimal modification. If you find yourself writing more and more page code, you probably have a code smell. Reconsider whether the code can be refactored into components. Also consider if the design being implemented lends itself to using existing components; if you find yourself building out styles that are unnecessarily divergent from existing stlyles, you have a design smell. Talk to the designer (even if it's yourself) and work through why this new page doesn't use existing components when it could. This will lead to better, more modular code and design, and allow for flexible creation of new components & pages later.
Styles are rulesets that are used between different components, and are only useful within this particular application. Visual styles, common layout patterns, and other reusable, application-specific code should go here.
If you're going to add a style, you're probably going to want to use a placeholder class: define the actual classes that utilize this ruleset elsewhere in your pages and components, and @extend
this placeholder class from there.
// _inset.scss
%inset {
…
}
// components/input.scss
.input {
@extend %inset;
…
}
// components/textarea.scss
.textarea {
@extend %inset;
…
}