Self Contained Styles - SAP/fundamental-styles GitHub Wiki
Background
"Self-contained" is a term we have coined that means both the markup (JS/JSX) and the styles (Sass/CSS) are included in the same package for a given component.
Characteristics
- Prevents element-based styling (usually from a global level) from bleeding into the components
- Removes package dependencies on a style library (the dreaded version mismatch between the component library and the style library)
- Can be added to a blank page with no styles and it will render and style correctly *
- Can be added to any page in an application that has existing styles and it will render and style correctly
- fonts (and icons fonts) are an exception to this. they need to be in place globally.
Naming CSS classes
We are using BEM (Block Element Modifier) naming for our classnames. Each Sass file will have a similar structure as shown below.
NOTE: The &
characters used below along with the nested Sass syntax do not create nested selectors -- they are appending each level to the final CSS classname and will ultimately result in number of unique single-level class selectors.
// Core variables and mixins
@import './new-settings';
$block: fd-sample;
.#{block} {
// block styles
&__element1 {
// element styles
}
&__element2 {
// element styles
&--element-modifier1 {
// element modifier styles
}
}
&--modifier1 {
// modifier styles
}
&--modifier2 {
// modifier styles
}
}
Checklist
When making a component self-contained, below is a list of things to consider.
-
Each React component (JS file) should only import one Sass file. REMEMBER, child components each come with their own self-contained styles.
import './SampleComponent.scss';
-
There should NOT be references to any blocks (the B in BEM) other than the default block in the component's Sass file. If you find yourself needing any, then additional classes will need to be injected into child components in order to access them for styling modifications. Injecting additional classes into child components usually means creating a new element (the E in BEM) class in your Sass file. See example below:
Instead of:
#block: fd-sample; .#{block} { .fd-button { // additional styles or style overrides } }
Use:
#block: fd-sample; .#{block} { &__button { // additional styles or style overrides } }
Or (if nested selectors are necessary):
#block: fd-sample; .#{block} { .#{block}__button { // additional styles or style overrides } }
-
A BEM Sass file should be as "flat" as possible. Most, if not all, selectors should be single-class selectors. In fact, try creating everything as a single-class selector to start and then adjust as necessary. Some nesting is okay, but if you find yourself using three or more descendant layers, there's probably a better way to do it. Use the structured syntax capability of Sass to generate classnames rather than creating multiple-level selectors.
-
Keep each element and modifier class focused. Avoid having a single class do too much styling. For example, if a modifier class exists for sizing, then only size-related styles should be included.
-
Don't be afraid to add multiple classes to each element in the component. It's very common for a given element to have a block or element class and then multiple modifier classes.
-
Try and avoid
*
(catch-all) and>
(direct descendant) in the selectors. The catch-all usually catches too much and it does not increase a selector's specificity. The direct descendant selector tightly couples the markup and the styles and things can easily break with the simplest markup changes. -
Blocks (the B in BEM) and elements (the E in BEM) should be the only classes that call the
fd-reset()
mixin. Modifiers (the M in BEM) should only be used to define small alterations to existing blocks or elements.