Some key quotes:
SMACSS has two core goals: 1. Increase the semantic value of a section of HTML and content. 2. Decrease the expectation of a specific HTML structure.
SMACSS is about identifying the patterns in your design and codifying them.
- Base
- the default style
- single-element selectors (body{})
- Layout
- divide the page into sections
- not reusable on a single page
- start with ".l-"
- Module
- reusable, more modular
- State
- for changeable things
- collapsible / expandable navs
- favorite / not a favorite
- how does this item look at this moment?
- Theme
- might not be necessary
- skins and other changes based on some special condition
(merge this with notes from before; starting on p. 13)
Laying out major sections is different from laying out minor sections of the page. Major sections are Layout elements; Minor sections are Modules.
One way to think about them is to use IDs for major sections, since they're only coming up once per page:
- #header
- #article
- #footer
(Arguably, since those are all HTML elements, might be better just to go with the raw element. NBD, though.)
Sometimes a major section might need to be modified in some way, like an #article section that might need to be full-width on some pages: .l-no-sidebar #article
Note the presence of the .l- to indicate that it's NOT a module, but, rather, a layout block.
He gives an example layout, from CNN. Showing the grid of "featured" stories. I can see how he approached it, but I'm not yet convinced his naming scheme is necessary.
More discrete component of the page.
- navigation bars
- search boxes
- login forms
Each should be designed to exist as a standalone obejct.
Will often have a child selector or hierarchy to scope it.
It's a good idea to avoid element selectors, like ".fld span" since you might need spans for other things down the road
You're overriding a base class with a more specific sub-class.
So normally, you'd have "class='pod '" class, now you might have "class='pod pod-constrained' "
Again, this is describing the thing that the class is being applied to, not the location on the page.
In the CSS file, you can make sure you're scoping it by writing the CSS like this:
.pod{}
.pod.pod-constrained{}
Not mentioned in the text, but worth noting: With Sass, you can move that to the CSS file, and have the following: .pod { } .pod-constrained { @extend .pod }
States override generic styles. They're used in cases where the visual state of a component on the page can change, often based on a user's activity.
For example, a navigation tree that can expand out might be in a default "is-collapsed" state or in an "is-expanded" state. It's probably simpler to simply use a default and an alternate. That is, the default, collapsed state, is the normal setup, and if there's an "is-expanded" class, that gives additional rules.
Or a form input might have an "is-error" state.
A few distinctive elements about State rules:
- They indicate a JavaScript dependency.
- They can apply to either layout classes or module classes.
Unlike most CSS scenarios, you'll often need to use "!important" in your CSS rules to override other, existing classes with a greater specificity. Don't use it unless you need to, but if you need to, use it.
If a state needs to be scoped to a specific module, include that module's name in the state's classname setup. Example:
#nav {}
#nav.is-expanded{}
That way, you won't have a collision with something else, like:
.article-meta-info{}
.article-meta-info.is-expanded{}
Themes are uncommon, but need to override defaults. Think: seasonal branding, special announcements, etc.
As Snook explains it, the theme files simply have the same class names, but you define the specific colors and other changes in the theme file, not in the core module file. I think it might be easier to keep the default color set in the core file, but to have an override in the theme file, using some parent class. So, for example, you could have a class of "holiday" or whatever that you apply to the body element.
// in sidebar.css
.callout {
border: 4px solid #333
}
// in theme-holiday.css
.holiday .callout {
border-color: #fcc
}
Fonts will affect base, module, and state styles, but since fonts aren't a part of layout, they shouldn't be present in layout styles. Snook implies that typography would be defined within those (base, module, state), but NOT in an external typography style file. The typography file would be for when you have internationalization or other unusual situations to handle.
You should not have more than 3 - 6 font sizes in your project. More than 6 and your users aren't going to notice and you're just making your site harder to maintain.
State changes are handled in one of three ways:
- class name - change happens with JavaScript
- pseudo-class - change
- media query - change happens based on what kind of device / dimensions the page loads on
Snook advocates for state changes where the interacted-with element (say, the dropdown item or button) is the thing that takes the ".is-active" class, and its sibling is the thing that then gets modified. So, for example, instead of adding "is-active" to a parent element, you'd apply it to the button. And instead of a descendent CSS control, you'd have it set on a sibling.
.btn.is-active + .menu {display: block}
Snook discusses the possibility of using data- attributes to define state.
.btn[data-state=default]{opacity: .85}
.btn[data-state=disabled]{opacity: .5;pointer-events: none}
.btn[data-state=pressed]{opacity: 1}
I like that, as it doesn't cloud up the element with classes.
With SMACSS, the goal is to keep styles that pertain to a module together, whether they're for desktop or for mobile, or for anything else. So instead of having one "mobile" stylesheet where you duplicate classes, you'd have media queries peppered throughout, with each module getting its own media query.
It's important to minimize the number of classes you invoke in each CSS rule, for two reasons:
- If the HTML changes, the CSS breaks
- You can't re-use components as easily, so you duplicate your CSS.
(Nothing super-new in here.)
Use Google Page Speed. The style of an element is evaluated on element creation. CSS gets evaluated from right to left.
Key: The evaluation of any more than a single element to determine styling is inefficient.
That isn't always practical. We don't want to end up with a class applied to every paragraph tag. (Right?)
Snook has three rules:
- Use child selectors.
- Avoid tag selectors for common elements.
- Use class names as the right-most selector.
At the same time, Steve Souders, who focuses on performance, notes that CSS isn't a high-value target for optimization, so don't waste too much time on it.
SMACSS has two core goals:
- Increase the semantic value of a section of HTML and content.
- Decrease the expectation of a specific HTML structure.
HTML5 plays very nicely with those goals.
Instead of framing a set of list items as .nav-primary li {display: inline-block} or .nav-secondary li {display: block} Set them as .l-inline-list li {display: inline-block} .l-stacked-list li {display: block}
SMACSS is about identifying the patterns in your design and codifying them. So are prototypes.
- show states
- review localization
- isolate dependencies
At Yahoo! they built a prototype engine to dynamically generate prototypes.
Maybe a prototype isn't necessary, but an easy-to-review format is really helpful. Perhaps just a document showing the various styles used for the site, and the code required for each module.
The good: variables, functions, extend directives. The bad: too-deep nesting, @extending across modules, overused mixins.
Handle extensions at the HTML level, not the CSS level. Define multiple classes in the HTML.
<a class="btn btn-default">button</a>
Snook encourages putting common thematic elements into their own classes that you can call, stacking classes in the HTML. So, for example, .theme-border{}
As in the IFTTT code, Snook recommends splitting media queries throughout, and nesting them under their parent elements. His example:
.nav > li {
width: 100%;
@media screen and (non-width: 320px){
width: 100px;
float: left;
}
@media screen and (min-width: 1200px) {
width: 250px;
}
}
- Place all Base rules into their own file.
- Major Layouts can either go into one big file, or several smaller files.
- Each module should go in its own file.
- Sub-modules should go in their own files.
- Global styles should go in their own files.
- Layout and Module States (including media queries that affect those layouts and modules) should go into the layout and module files.
You'll also need a master file that collects the other files.
A few elements should be defined at the base-level.
It's tempting to just declare a single table style, on the bare element. Don't do this.
Each table is a module. Treat it as such.
Specify child selectors to keep the impact as small as possible.
Buttons and Inputs can often suffer the same fate.
The litmus: Does this style serve a particular purpose? If so, create a specific module.
Classic approach with sprites: .menu{} .menu-home{} .menu-mail{} etc.
Problematic in a few ways.
- over-reliance on li{}
- sprites had to be redefined in other modules to be used elsewhere
- bumping up font size could break the layout as more sprites came into view
Now icons are their own modules" <> Inbox</>
Why three different classes? Each has a slightly different purpose.
/* turn the <i> into an inline-block element */
.ico {
display: inline-block;
background: url(/img/sprite.png)
no-repeat;
line-height: 0;
vertical-align: bottom;
}
/* set the height and width */
.ico-16 {
height: 16px;
width: 16px;
}
/* define which icon shows through */
.ico-inbox {
background-position: 20px 20px;
}
.ico-drafts {
background-position: 20px 40px;
}
So as we think about Channel icons for IFTTT, one way to display them would be to have: channel-icon channel-icon-40 channel-icon-facebook
table/calendar example
Takeaway: inheritance can wreak some havoc on our well-laid plans' there isn't always a perfect solution.
Not sure why he's relying on "is-selected" as a tr class instead of a more descriptive "current-week".
Snook's approach:
- single-line per rule; this can get nasty with lots of CSS vendor prefixes; also tough with a large team
- space after the colon
- four-space indent
- properties grouped by type
- opening bracket on same line as rule set
- colors use short form when possible
Organizes in the flowing order:
- Box
- display, float, position, left, top, height, width, etc.
- Border
- border, border-image, border-radius
- Background
- background, background-color, background-image, background-size, etc.
- can get pretty large, esp. w/ vendor prefixes, gradients, etc.
- Text
- font-family, font-size, text-transform, letter-spacing, etc.
- Other
- everything else
3-digit or 6-digit hex codes, not text names. Also, rgba or hsla are options.
(Doesn't factor in Sass.)
Naming: How would you file this in a folder?
- .team-bios
.team-member
.team-member__photo
.team-member__name
.team-member__twitter-username
.article
.article__head
.article-headline
.article-date
.article-byline
.article__body
.article-copy
Criticisms of SMACSS
-
naming convention should have element built-in .comparison{} should be .table-comparison{}
-
doesn't leverage the power of Sass, namely @extend
- so much could be simpler