This is a handful of Sass mixins that get you everything you need from a CSS grid framework while minimizing dependencies, overrides, and complexity.
Use it if you want, but my point is that many projects don't need anything as complex as Bootstrap or Neat. Don't assume you need a framework at all. Think about what makes sense for your project and maybe just roll your own.
-
Know exactly where a style is coming from
-
Minimize side-effects of changing CSS
-
Know when it's safe to delete styles
-
Know where a rule is coming from
- Style to classes, not tagnames
- Class begins with filename
- e.g. style for
<div class="widget" />
is found inwidget.scss
- e.g. style for
- One module per file
- e.g.
widget.scss
only contains styles for the Widget module
- e.g.
- One element per SCSS block (No nesting elements!)
- e.g. start a new chunk for each sub-module
/* DO THIS */ .widget { padding: 10px; } .widget__label { margin-bottom: 5px; } /* DON'T DO THIS! */ .widget { padding: 10px; .widget__label { margin-bottom: 5px; } }
- Nested subcomponents gain a higher specificity than they need. This will eventualy cause specificity conflicts between modules, and the styles are more likey to break if the markup changes.
- An SCSS block may contain base styles, states, and breakpoints for a single element.
e.g.
.widget { padding: 10px; &:hover { background-color: #eee; } &--active { border-color: #666; color: white; } }
- e.g. start a new chunk for each sub-module
-
Minimize specificity drama
-
Breakpoints
- Media query breakpoints use min-width, never max-width
- One element per breakpoint, keep it near other styles for that element
- For a given element you want to be able to see all styles and states in one place
-
Organize z-index values into layers
-
Browser prefixing
- Use an auto-prefixer, not mixins
-
- Nested selectors
- If you HAVE to use them, use direct descendant selector
>
if possible. The direct descendant selector prevents the rule from applying to deeply nested children, and is said to have a performance benefit. e.g..widget__image { padding: 5px; > img { display: block; } }
- If you HAVE to use them, use direct descendant selector
- Nested selectors
-
General guidlines
- Don't over-specify
Use the least-specific selector you can to get your work done.
e.g.
/* DO THIS */ .widgit { /* ...styles */ } /* DON'T DO THIS */ div.widgit { /* ...styles */ } /* DON'T DO THIS */ .widgit#foo { /* ...styles */ } /* DO THIS */ .widgit { /* ...styles */ } .widgit__label { /* ...styles */ } /* DON'T DO THIS */ .widgit .widget__label { /* ...styles */ } /* DON'T DO THIS */ .widgit { .widget__label { /* ...styles */ } } /* DON'T DO THIS */ .widgit { &__label { /* ...styles */ } }
- Don't over-specify
Use the least-specific selector you can to get your work done.
e.g.
*/
The container mixin limits the maximum width of an element and horizontally centers it once it reaches that width.
$max-width
:
Centers the element horizontally
and constrains width once this width is reached.
@scss
.l-main {
@include container($lg-screen);
}
*/
@mixin container($max-width) {
display: flex;
flex-direction: column;
align-items: center;
width: 100%; /* align-items: center
causes content to shrink horizontally. We want it to grow until it reaches its max-width */
max-width: $max-width;
}
Helper to simplify the media query directive.
$min-width
:
The minimum width at which the contained rules will apply.
@scss
$phone-landscape-width: 600px;
$tablet-width: 768px;
$tablet-landscape-width: 1024px;
$laptop-width: 1200px;
$large-screen-width: 1600px;
h1 {
font-size: 27px;
@include tablet-breakpoint {
font-size: 34px;
}
}
*/
@mixin breakpoint($min-width) { @media screen and (min-width: $min-width) { @content; } }
@mixin tablet-breakpoint { @include breakpoint($tablet-width) { @content; } }