While at CSSDevConf 2015, I also took a workshop from Jonathan Snook. He helped lead the Yahoo! Mail redesign and works at Shopify as a lead front-end engineer.
He developed the SMACSS philosophy and has an e-book on the topic.
Intro
SMACSS is:
- Categorization
- Naming Convention
- Decoupling CSS from HTML (minimize side effects of CSS we write)
- State-based Design
Categorization
Base
These HTML elements will always get this style
Maybe fonts, default paddings
Not a fan of CSS reset
- Only write the CSS when I need it (end up with smaller code base)
- Grid systems: most of it is not used
- Normalize: normalizes video, html5 elements, many others
- Don't just throw stuff in
Layout
Usually simple, keep it at the high level (header, content, sidebar, footer)
Module
90 to 95% of your project
Logos, navigation, modals, media objects
- Contain content
- Each module is an interface that your users have to learn
- Each module is a code that has to be written, delivered and maintained
If you have too many modules, your site is too complex
Users have to re-learn new things
Module Variations (sub modules)
E.g. Buttons
Start with a common base
Maybe same rounded corners, same font, size
Make variations from there
BG color, FG color
Sometimes removing borders or increasing size/padding
Module child elements (sub components)
E.g. a Modal is the parent, and the child elements are the title, content inside
How deep should a module be?
If we go too deep, should refactor and make into other components
Goals of Modules
- Isolate modules from each other
- Prevent styles from bleeding out, others from bleeding in
Recognize similar patterns across different places. Good example at Shopify:
date picker in reports
filter UI
contact cards on hover
They all had a popover/dropdown with rounded corners, shadow and little divet at the top. So the above all share this one module, and contain other module(s) inside them
CSSCSS (redundancy checker tool)
State
Can effect layout changes (a hamburger sidebar menu)
Open, closed, active, disabled, default, shown, hidden
- Like a module variation, but indicates a JavaScript dependency
Theme
Many folks do not have to worry about theming
Simplified down to 6 colors per theme
primary
darker
darkest
lighter
lightest
disabled
- Only for on-the-fly style changes
- Usually aren't needed (preprocessors can help)
CSS preprocessors can help with colors, for example if I want to have 50 shades of grey for my project...
- Categorization allows for identifying patterns
Naming Conventions
Base: no names, just use semantic DOM elements
Layout: always use layout prefix
.layout--header {}
.layout--content {}
.layout--sidebar {}
Use class over ID always. CSS will style all things by that ID (doesn't enforce or care if there is only one)
Go with .link-danger
rather than .link-cancel
(to allow re-use)
Module: no prefix of "module", since its 95% of code
In modules: does not include hyphens (listview, tab, tabpanel)
Module Variations: use the module prefix, and no hypens in variation name (.btn, .btn-large, .btn-small)
Sub Modules: use the module prefix and no hyphens (.modal, .modal-body, .modal-footer)
State: use an is prefix, indicates js dependency (.is-btn-active, .is-btn-disable)
new naming he prefers, put it after module (.btn-is-active, .btn-is-disable)
- Indicates togglable state
- States specific to a module should include module name
Global states:
/*able to be applied to multiple modules*/
.is-selected {}
Themes:
.theme-header {}
.theme-border {}
.theme-background {}
Text:
.text-xl { font-size: 140%; }
.text-l { font-size: 120%; }
.text {}
.text-s { font-size: 90%; }
.text-sx { font-size: 80%; }
Not covered in the book:
Prefix for versioning or packaging
Sometimes need to add a prefix to all styles using post-processing
Yahoo made PureCSS (out of SmacCSS philosophy) (everything is prefixed with "post-")
During redesign process
Two modules in same project
.next-btn {}
.next-btn-is-active {}
.next-btn-is-disabled {}
CSS in JavaScript/React
Naming conventions are the important thing there too, regardless of what your team comes up with
Hard to minify CSS (cant just change the classes, since you need to control all the HTML/JS)
Recap naming
.modulename
.modulename-submodule
.modulename-subcomponent
Variations in naming (BEM style):
.module-name
.module-name--sub-module
.module-name__sub-component
Using camel case:
.moduleName
.moduleName-subModule
.moduleName--subComponent
- Layouts specify widths and margins
- Modules expand to fill layouts
- states are !important (only)
Questions & Answers
Try to mitigate change
- Describe things for what they do, not their context
- Ask the designer why it's highlighted, or why there's extra padding, or why the font is bigger, etc
When is it Base?
// Some comment mistakes:
<button>
<table>
<input>
- Don't over-styleize the above when it should just be a module
- Base file should be very minimal
- If you have to unstyle things, you're doing things in the wrong place (bootstrap!!)
More on specificity
- Goal is 1 selector
- One selector and a descendant selector, okay too
.comment-author {}
.comment-number > a {}
State
When having 3+ states (not just boolean) and they can only be in one state at a time (for example you don't want to support pressed + disabled):
.button[data-state=default] {}
.button[data-state=pressed] {}
.button[data-state=disabled] {}
Include media queries with the file they effect (whether layout or modules)
Preprocessors
CSS Panic
Prototyping
- Build and test individual components
- Allow for front-end development independing of engineering team/cycle
doc / styleguide generators:
dexy.it
snk.ms/23 - collection of style guides
===
Exercises
5by5.tv
Exercise 1
Identify layout sections (and class names)
Layout
- Header
- Hero (Highlight Zone)
- Content
- Sidebar
- Footer
- Layout-constrained (applied to each section since fixed width)
Grid system supported 4 & 5 columns
Exercise 2
5 possible modules, and their class names
NavBar
ListHeading
List
ShowCover
SocialIcon
what do you call the container w/ body
when you already have boxes, you could call pod
Exercise 3
engagdge nav structure
SASS:
/* nav.scss */
.nav {}
.nav > li {}
.nav > li > a {}
.nav > li > a.is-active {}
/* menu.scss */
.menu {}
.menu-horizontal {}
/* card.scss */
.card {}
.card-image {}
.card-title {}
/* rating.scss */
.rating {}
/* button.scss */
.button {}
.button-with-more {}
.button-is-active {}
HTML:
<ul class="primarynav">
<li>
<a href=""></a>
<div class="primarynav-canexpand">
<div class="megamenu">
<ul>
<li>
<a href="#" class="reviewcard">
<span class="rating">92</span>
<span class="reviewcard-title"></span>
</a>
</ul>
<ul>
<li>
<a href="#" class="chiclet"></a>
</li>
</ul>
</div>
</div>
</li>
<li>...</li>
<li>...</li>
<li>
<a href=""></a>
<div class="...">
<input />
</div>
</li>
<li>
<a href=""></a>
<ul>
<li></li>
<li></li>
</u>
One solution to reduce dependencies:
.primarynav-has-dropdown {}
OR
.primarynav-canexpand {}
Always keep modules in separate DOM elements (even if not needed technically at the moment - single responsibility principle, separate of concerns)