Practical CSS Guidelines To Use In All Your Projects

Variables

  1. Sass variables are there only before compiling. If we inspect our styles in the browser we will only see the computed values, not the variable names.
  2. CSS variables are a bit more clunky to use (color: var(--text-gray) vs color: $text-gray).
  3. Unlike Sass variables, CSS variables cannot be used for device breakpoint values, even if setting them up on :root selector - the HTML element (@media screen and (min-width: var(--breakpoint-sm)) won't work!).
  4. CSS variables do cascade, meaning, we can override them on a given selector to what we want, and as such, the element’s children will access changed values. This works really nicely for application-wide theming.
  5. CSS variables can also be used within the calc() function so do not be afraid to use them there.
  6. CSS variables can be used as a tool to create configurable components by utilizing the value fallbacks.
.button {
// Take the value of --color-primary or if not defined, default to another color
background-color: var(--color-primary, #c0ffee);
}
#grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
--columns: 5;
}
var grid = document.querySelector("#grid");// Set the number of columns to 10
grid.style.setProperty("--columns", 10);

Specificity

  1. Avoid nesting selectors for your own styles and if you do, decide upon a reasonable max depth of nesting level (1 or 2 levels deep for example) and try not to go over.
  2. Take advantage of the fact that browsers take later appearance as a priority in the same specificity level selectors. The less specific the styles the earlier they should occur in stylesheets.
  3. Avoid using !important unless necessary or otherwise very hard to avoid.
  4. Do not style id selectors. Keep yourself constrained to classes or elements.
  5. Although it’s opinionated, you might consider avoiding applying styles on element selectors or being very careful about it. While they have low specificity overall, they are very generic and thus it’s easy to apply a tiny bit too many styles which later on need to be overridden on class-specific selectors.

Selectors

Pseudoclasses

Pseudoelements

Sibling selectors

.list-item + .list-item {
margin-top: 20px;
}
// or when using Sass
.list-item {
& + & {
margin-top: 20px;
}
}

Cross-browser support

  1. Do use style normalization like normalize.css. It allows us to set up a common ground to build your project's styles upon by mitigating the difference between different HTML tags.
  2. In case you don’t know it, caniuse is your best friend. You can easily check how supported a given feature is, in what browser versions, and with what caveats.
  3. Do use autoprefixers if possible (i.e. postcss). They allow you to not worry and omit vendor prefixes for your styles (-webkit-, -moz-, -o-, -ms-). You can also configure the specific browser versions you target so it provides those only when needed.
  4. Sometimes a relatively new feature that you would greatly benefit from may not be supported in all the major browsers yet. That does not necessarily mean you cannot use it at all. Consider using polyfills in these cases.
  5. Avoid using browser-specific features if not broadly supported — in those cases always try to use polyfills or provide alternatives solutions for graceful degradation.
  6. Don’t test your interfaces only in one browser, try to mix it up sometimes.

Units

Absolute units

Relative units

Commonly used

  • px
  • % (relative to the parent element)
  • em and rem
  • fr (which represents a fraction of a grid)
  • vw, vh (less so vmin and vmax)

PX vs REM

Mitigating REMs inconveniences

html {
font-size: 62.5%; // 16 * 62.5% = 10; so now 1rem = 10px
}
body {
font-size: 1.6rem; // 16px
}

Responsiveness / RWD

  1. For the most part — be very careful about fixed sizes for your elements (especially width or height).
    One simple change of assumptions is to instead of width: 250px doing max-width: 250px; width: 100% so that if there would be less space the element would still fit.
    This is especially important for images or videos.
  2. Decide upon a reasonable minimal device width (be it 320px or 375px) and assume your application should work on this one and any bigger one.
  3. If things change their order in designs on different devices, you can use order property on your flex/grid container's elements. This way you can avoid repeating HTML content and hiding/displaying on demand just because some things were reordered. In the case of grids, you can also use named areas and just change the grid template.
  4. Decide upon a list of fixed device breakpoints (usually up to 3 or 4 is more than enough) and try to stick to them.
  5. Make sure things are easily clickable on mobile devices. This however doesn’t mean the elements need to be visually bigger. Adding some padding or even an absolutely positioned container to add some area works wonders.
  6. You can check if your device supports hover by a media query @media (hover: hover)!

Styles Scoping

BEM

  1. If you use Sass, you can save yourself plenty of keystrokes by utilizing the &
.button {
&__icon { } //.button--icon
&--primary { } //.button--primary&--primary &__icon { } //.button--primary button__icon
}
// BAD
.foo {
&__bar {
&__baz { }
}
}
// GOOD
.foo {
&__bar { }
&__baz { }
}
// GOOD
.foo {
}
.bar {
&__baz { }
}
.foo {
&__bar { }
&__bar--active { } // .foo__bar--active
}
// VS.foo {
&__bar { }
// Bigger specificity (which may be helpful), shorter class names
&--active &__bar { } // .foo--active .foo__bar
}

CSS in JS solutions

Accessibility / a11y

  1. Avoid removing focus styles unless you provide a suitable alternative.
    Yes, the focus outline might not be very pretty but it’s on us to make focused elements look good. Just hiding the focus styles should be avoided.
  2. Try to respect the user’s preferences.
    Use relative units — we can scale the entire UI appropriately based on preferred font size, or even based on the screen’s width or height.
  3. Make sure your font sizes are sensible so that things are big enough to read comfortably on all devices.
    Browsers did not set 16px as standard font size for no reason. Smaller font sizes should be used carefully.
    As an example — if an input has a smaller font size than 16px set on iOS — the Safari browser will zoom in the screen for the user which is most likely an undesired behavior.
  4. Try to adhere to the WCAG color contrast standards and keep the number of culprits low (or at least avoid them on important, big blocks of text). Your dev tools can help you here, so check in your browser how you can see the contrast ratio for a given selector. Proper use of colors and contrast not only will make the application easier on the eyes, but it will also improve the usability and accessibility at the same time.
  5. Learn the difference between hiding content visually, hiding content from assistive tech, and actually hiding content for everyone. Check the a11y’s project article on the matter

Others

Utilise CSS math functions if you can

Hacks, magic values, on-paper calculations

Avoid generic properties unless necessary or helpful

.foo {
background-color: blue;
background-image: url("https://foo.bar/image.png");
background-repeat: no-repeat;
background-size: cover;
}
.foo--bar {
//this will not only change the color but reset all the other properties above to defaults
background: red;
}

Global styles

Utility classes

  1. They should do one thing, and one thing only.
  2. Their name should specify directly what they do. text-align-center is very obvious and does not require looking it up to understand it.
  3. In case your project has a big stylesheet file it builds from importing everything else, import them at the very end so they have higher specificity by default.
  4. While the dreadful !important is usually a big no-no, utility classes might be a good use-case for it, especially if we want to be extra sure (you could still override them anyway if you really wanted to) they work as intended.
  5. While opinionated, you may like to prefix your utility classes to avoid conflicts with other class names and to distinguish them a little bit (for example: u-text-align-center);

Box sizing

  1. content-box - the default one (usually) may not always be to our liking but has its uses. In this model, the width or height are independent of the element's border or padding, they only depend on the content. So in this model if we wanted to have, say a 40px tall element with a 1px border (and that's how we look at things for the most part, the border being a part of an element), we would actually have to set the height to 38px, which may not be that intuitive. On the other hand - it works superbly with things like wrappers that constrain our width and introduce margin or padding.
  2. padding-box is even different. This time the width or height are including the padding properties so the values of width or height we set are calculated in a way that includes the padding values within them. Sometimes this may prove useful but we still need to scratch our heads with the borders.
  3. border-box - finally, probably the most intuitive of all the models. This time the total height and width include both padding and border-width properties. This resolves the problem of looking at a design and having to calculate the borders or padding values out.
// Make the default model use `border-box`
html {
box-sizing: border-box;
}
// And cascade it to everything else while being open
// to using `content-box` or `padding-box` when needed
*,
*::before,
*::after {
box-sizing: inherit;
}

Final words

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store