├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 André de Sousa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS Best Practices 2 | 3 | This is a guideline of best practices that we can apply to our front-end project. 4 | Some of these tips are for CSS pre-processors such as [Sass](https://sass-lang.com/), [Less](http://lesscss.org/) and [Stylus](https://stylus-lang.com/). 5 | These tips are based on CSS documentation, books, articles and professional experience. 6 | 7 | ## Table of Contents 8 | 9 | 1. [Follow conventions](#follow-conventions) 10 | 2. [Follow a CSS methodology](#follow-a-CSS-methodology) 11 | 3. [Lint the CSS files](#lint-the-css-files) 12 | 4. [Alphabetize CSS properties](#alphabetize-css-properties) 13 | 5. [Cross-browser compatibility](#cross-browser-compatibility) 14 | 6. [Prefer CSS over JavaScript](#prefer-css-over-javascript) 15 | 7. [Comment the CSS](#Comment-the-css) 16 | 8. [Avoid undoing styles](#avoid-undoing-styles) 17 | 9. [Avoid magic numbers](#avoid-magic-numbers) 18 | 10. [Avoid qualified selectors](#avoid-qualified-selectors) 19 | 11. [Avoid hard-coded values](#avoid-hard-coded-values) 20 | 12. [Avoid brute forcing](#avoid-brute-forcing) 21 | 13. [Avoid dangerous selectors](#avoid-dangerous-selectors) 22 | 14. [Avoid extra selectors](#avoid-extra-selectors) 23 | 15. [Avoid reactive !important](#avoid-reactive-important) 24 | 16. [Avoid IDs](#avoid-ids) 25 | 17. [Avoid loose class names](#avoid-loose-class-names) 26 | 18. [Avoid string concatenation for classes](#avoid-string-concatenation-for-classes) 27 | 19. [Avoid duplicated key selectors](#avoid-duplicated-key-selectors) 28 | 20. [Avoid using inline styles](#avoid-using-inline-styles) 29 | 21. [Avoid classes in wrong components](#avoid-classes-in-wrong-components) 30 | 22. [Avoid @mixin everywhere](#avoid-mixin-everywhere) 31 | 23. [Avoid @extend everywhere](#avoid-extend-everywhere) 32 | 24. [Avoid shorthand syntax everywhere](#avoid-shorthand-syntax-everywhere) 33 | 25. [Avoid too many font files](#avoid-too-many-font-files) 34 | 26. [Use multiple classes](#use-multiple-classes) 35 | 27. [Use nested declarations](#use-nested-declarations) 36 | 28. [Use "Margin: 0 auto" to center layouts](#use-margin-0-auto-to-center-layouts) 37 | 29. [Use Hex Code instead of Name Color](#use-hex-code-instead-of-name-color) 38 | 30. [Use a CSS reset](#use-a-css-reset) 39 | 31. [Use a CSS pre-processor](#use-a-css-pre-processor) 40 | 32. [Use a CSS post-processor](#use-a-css-post-processor) 41 | 33. [Use a CSS framework](#use-a-css-framework) 42 | 34. [Use a design system](#use-a-design-system) 43 | 35. [Use relative units](#use-relative-units) 44 | 36. [Use CSS variables](#use-css-variables) 45 | 37. [Write descriptive media queries](#write-descriptive-media-queries) 46 | 38. [Understand Block vs Inline Elements](#understand-block-vs-inline-elements) 47 | 39. [Separate global vs local style](#separate-global-vs-local-style) 48 | 40. [Minimize expensive properties](#minimize-expensive-properties) 49 | 41. [Style to be responsive or at least adaptive](#style-to-be-responsive-or-at-least-adaptive) 50 | 42. [Let the content define the size](#let-the-content-define-the-size) 51 | 43. [Let the parent take care child position](#let-the-parent-take-care-child-position) 52 | 44. [Keep HTML semantics](#keep-html-semantics) 53 | 45. [Create the HTML first](#create-the-html-first) 54 | 46. [Combine elements with same styles](#combine-elements-with-same-styles) 55 | 47. [Modularize the styles](#modularize-the-styles) 56 | 48. [Lazy load stylesheets](#lazy-load-stylesheets) 57 | 49. [Remove unused CSS](#remove-unused-css) 58 | 50. [Minimize the CSS](#minimize-the-CSS) 59 | 60 | ## Follow conventions 61 | 62 | Code conventions are base rules that allow the creation of a uniform code base across an organization. 63 | Following them does not only increase the uniformity and therefore the quality of the code. 64 | [Airbnb CSS/Sass Style Guide](https://github.com/airbnb/css) is very popular and recommended. 65 | We can complete them with [CSS Guidelines](https://cssguidelin.es/) and [Sass Guidelines](https://sass-guidelin.es/). 66 | To make it mandatory, we need a linter, formatter and strong code review. 67 | The code conventions must be dynamic and adaptable for each team and project. It is up to each team to define its convention. Finally, take a few minutes to browse [Awesome Sass](https://github.com/Famolus/awesome-sass). 68 | Awesome Sass is a list of awesome Sass and SCSS frameworks, libraries, style guides, articles, and resources. 69 | 70 | ## Follow a CSS methodology 71 | 72 | CSS methodologies will ensure consistency and future proof our styles. 73 | There are plenty of methodologies out there aiming to reduce the CSS footprint, organize cooperation among programmers and maintain large CSS codebases. 74 | [BEM](http://getbem.com/) (Block, Element, Modifier) was introduced in 2010 as a methodology organized around the idea of dividing the user interface into independent blocks. 75 | Other CSS methodologies are [ITCSS](https://itcss.io/), [OOCSS](http://oocss.org/), [SMACSS](http://smacss.com/), [SUITCSS](https://suitcss.github.io/) and [Atomic CSS](https://acss.io/). 76 | 77 | ## Lint the CSS files 78 | 79 | Linting works by ensuring that we follow the rules that we define for our style and make sure our styles are consistent, well structured, and follow CSS best practices. 80 | We can use [stylelint](https://stylelint.io/) in our CSS projects. 81 | It is very well documented and can be integrated with our favorite IDE. 82 | To ensure all files committed to git don't have any linting or formatting errors, we can use [lint-staged](https://www.npmjs.com/package/lint-staged). 83 | It allows to run linting commands on files that are staged to be committed. 84 | 85 | ## Alphabetize CSS properties 86 | 87 | Anyone has their organization rules. However, what makes sense to you is not for another developer. 88 | Instead, define your rules, use a linter to enforce the same rules for everyone. 89 | [Sass Lint](https://www.npmjs.com/package/sass-lint) and [stylelint](https://www.npmjs.com/package/stylelint) provide rules to organize our CSS. 90 | The specifics of what the linter looks for are up to us, but it can be configured to check everything from syntax errors to making sure our CSS properties are in a strict order. 91 | 92 | ## Cross-browser compatibility 93 | 94 | Cross-browser compatibility is important. CSS browser prefixes are a way for browser makers to add support for new CSS features before those features are fully supported in all browsers. 95 | For example, if we use [PostCSS](https://postcss.org/) with the [Autoprefixer](https://github.com/postcss/autoprefixer) plugin, we can write completely normal CSS without any browser prefixes and let the postprocessor do the rest of the work. 96 | Internally, Autoprefixer relies on a library called [Browserslist](https://github.com/browserslist/browserslist) to figure out which browsers to support with prefixing. 97 | 98 | ## Prefer CSS over JavaScript 99 | 100 | If something we want to do with JavaScript can be done with CSS instead, we use CSS. 101 | Before we try to add JavaScript and even when we add JavaScript, we should consider having CSS making the most of the style and using JavaScript for things like triggers and side effects. 102 | JavaScript is not fault tolerant. 103 | This can be disastrous. 104 | We are much more in control when using JavaScript, but we are also much more responsible. 105 | In general, our applications are faster using CSS instead of JavaScript. 106 | 107 | ## Comment the CSS 108 | 109 | One of the best practices that we can implement for CSS code is by putting a comment for each group of CSS code. 110 | Just like any other language, it's a great idea to comment our code. 111 | Comments are used in CSS to explain a block of code or to make temporary changes during development. 112 | The commented code doesn't execute. 113 | Regarding comments, we have documentation on [how to comment the CSS](https://www.freecodecamp.org/news/comments-in-css/). 114 | 115 | ## Avoid undoing styles 116 | 117 | CSS that unsets styles should start ringing alarm bells right away. 118 | Rulesets should only ever inherit and add to previous ones, never undo. 119 | For more details, see the example below: 120 | 121 | ```css 122 | h2 { 123 | font-size: 2em; 124 | padding-bottom: 0.5em; 125 | border-bottom: 1px solid #ccc; 126 | } 127 | 128 | .no-border { 129 | padding-bottom: 0; 130 | border-bottom: none; 131 | } 132 | ``` 133 | 134 | As we go down a stylesheet, we should only ever be adding styles, not taking away. 135 | If we are having to undo styling as we go down in the document, is because we started adding too soon. 136 | In the example, `padding-bottom` and `border-bottom` should be moved from `h2` to `no-border` class. 137 | 138 | ## Avoid magic numbers 139 | 140 | A magic number is a value that is used because it just works. 141 | Magic numbers have several problems associated with them. 142 | They soon become out of date, they confuse other developers, they cannot be explained, they cannot be trusted. 143 | Never, ever use numbers just because they work. 144 | 145 | ## Avoid qualified selectors 146 | 147 | Qualified selectors are ones like: 148 | 149 | ```css 150 | ul.nav {} 151 | 152 | a.button {} 153 | 154 | div.header {} 155 | ``` 156 | 157 | Basically, selectors who are needlessly prepended by an element. 158 | These are bad news because they totally inhibit reusability on another element, increase specificity and increase browser workload (decreasing performance). 159 | These are all bad traits. 160 | Those selectors can, and should be: 161 | 162 | ```css 163 | .nav {} 164 | 165 | .button {} 166 | 167 | .header {} 168 | ``` 169 | 170 | Which will help us to save actual amounts of code, increase performance, allow greater portability and reduce specificity. 171 | 172 | ## Avoid hard-coded values 173 | 174 | Not unlike magic numbers, hard-coded values are also bad news. 175 | A hard-coded value might be something like this: 176 | 177 | ```css 178 | h2 { 179 | font-size: 24px; 180 | line-height: 32px; 181 | } 182 | ``` 183 | 184 | `line-height: 32px;` here is not cool, it should be `line-height: 1.333;`. 185 | Line heights should always be set relatively to make them more forgiving and flexible. 186 | This may not seem like a massive difference, but on every text element over a large project, this has a big impact. 187 | Hard-coded values are not very future proof, flexible or forgiving, and thus should be avoided. 188 | 189 | ## Avoid brute forcing 190 | 191 | This one is in a similar vein to hard-coded numbers, but a little more specific. 192 | Brute forcing CSS is when we use hard-coded magic numbers and a variety of other techniques to force a layout to work. 193 | 194 | ```css 195 | .foo { 196 | margin-left: -3px; 197 | } 198 | ``` 199 | 200 | This is terrible CSS. 201 | This type of CSS is indicative of either a poorly coded layout that requires this kind of manipulation, a lack of understanding of box-model and layout, or both. 202 | Well coded layouts should never need brute-forcing, and a solid understanding of box model, layout and looking at our computed styles more often should mean that we'd rarely end up in a situation like this. 203 | 204 | ## Avoid dangerous selectors 205 | 206 | A dangerous selector is one with far too broad a reach. 207 | An obvious and simple example of a dangerous selector might be: 208 | 209 | ```css 210 | div { 211 | background-color: #ffc; 212 | } 213 | ``` 214 | 215 | To give such specific styling to such a generic selector is dangerous. 216 | Our styles will leak out into areas they shouldn't as soon as we start trying to use that element again. 217 | We'll need to start undoing styles (adding more code to take styles away) in order to combat this. 218 | 219 | ## Avoid extra selectors 220 | 221 | It's easy to unknowingly add extra selectors to our CSS that clutters the stylesheet. 222 | One common example of adding extra selectors is with lists. 223 | 224 | ```css 225 | body #container .someclass ul li {} 226 | ``` 227 | 228 | In this instance, just the `.someclass li` would have worked just fine. 229 | 230 | ```css 231 | .someclass li {} 232 | ``` 233 | 234 | Adding extra selectors won't bring the end of the world, but they do keep our CSS from being as simple and clean as possible. 235 | 236 | ## Avoid reactive !important 237 | 238 | `!important` is fine and it's a, well, important tool. 239 | However, should only be used in certain circumstances. 240 | `!important` should only ever be used proactively, not reactively. 241 | For example, we will always want errors to be red, so this rule is totally fine. 242 | Where is bad is when it is used reactively, that is, it's used to get someone out of a specificity problem and force things to work. 243 | Using reactively is just a way of circumventing the problems caused by ill-formed CSS. 244 | It doesn't fix any problems, it only fixes the symptoms. 245 | 246 | ## Avoid IDs 247 | 248 | IDs can never be used more than once in a page. 249 | IDs can often have their traits abstracted out into many reusable classes. 250 | An ID is 255 times more specific than one class or infinitely more specific than a class. 251 | They are of no use to anyone and should never be used in CSS. 252 | 253 | ## Avoid loose class names 254 | 255 | A loose class name is one that is not specific enough to define what is its purpose. 256 | For example, imagine a class named `.board`. 257 | Such class name is bad because we can't necessarily glean its purpose based on the class name alone and it is so vague that it might be (accidentally) redefined/reassigned by another developer in a near future. 258 | All this can be avoided by using much stricter class names. 259 | Classes like `.board` and `.user` and suchlike are far too loose, making them hard to quickly understand, and easy to accidentally reuse/override. 260 | [BEM](https://en.bem.info/) helps us in this task. 261 | 262 | ## Avoid string concatenation for classes 263 | 264 | Sass allows us to concatenate strings in our class names with `&`. 265 | The obvious benefit is that we must write our foo namespace only once is certainly very DRY. 266 | 267 | ```css 268 | .foo { 269 | color: red; 270 | &-bar { 271 | font-weight: bold; 272 | } 273 | } 274 | ``` 275 | 276 | One less obvious downside, however, is the fact that the string `foo-bar` now no longer exists in our source code. 277 | Searching our codebase for `foo-bar` will return only results in HTML. 278 | It suddenly became a lot more difficult to locate the source of `.foo-bar's` styles. 279 | 280 | ## Avoid duplicated key selectors 281 | 282 | The key selector is the selector that gets targeted/styled. 283 | It is often, though not always, the selector just before your opening curly brace. 284 | 285 | ```css 286 | header.btn{} 287 | 288 | modal.btn{} 289 | 290 | sidebar.btn{} 291 | ``` 292 | 293 | Aside from the fact that a lot of that is just generally pretty poor CSS, the problem is that `btn` is defined many times. 294 | So, there is no Single Source of Truth telling what `btn` look like and there has been a lot of mutation meaning that the class has many different potential outcomes. 295 | 296 | ## Avoid using inline styles 297 | 298 | [Inline styles](https://www.w3schools.com/css/css_howto.asp) are the style attribute in the HTML tags. 299 | We should avoid using it because it mixes up content and presentation, which can lead to more trouble. 300 | Inline styles are considered as bad practice due to poor scalability and maintainability. 301 | It leads to messy code where each HTML file will need to be updated in the event of a style change, instead of a global change in a single external stylesheet. 302 | As a rule of thumb, define all styles in the CSS files. 303 | 304 | ## Avoid classes in wrong components 305 | 306 | If we need to style something differently because of its context, where should we put that additional CSS? 307 | In the file that styles the thing? 308 | Or in the file that controls that context? 309 | We should do our best to group our styles based on the subject (i.e. the key selector). 310 | For example, it's much more convenient to have the context of all of our buttons in one place. 311 | This makes it much easier to move all of the button styles onto a new project, but more importantly it eases cognitive overhead. 312 | You're familiar with the feeling of having ten files open in your text editor whilst just trying to change one small piece of styling. 313 | As a simple rule of thumb, ask yourself the question am I styling `x` or am I styling `y`? 314 | If the answer is `_`, then your CSS should live in `_`; if the answer is `_`, it should live in `.xx.cssyy.css`. 315 | 316 | ## Avoid @mixin everywhere 317 | 318 | When we don't have to use mixins, just don't do it. 319 | When we use mixins, they have to be well-structured and maintained in a rigorous way. 320 | Using mixins for no good reason is the best way to get lost when the project grows. 321 | They can cause side effects and become hard to update when they are used in many places. 322 | Mixins are here to avoid repeating yourself by keeping a Single Source of Truth. 323 | Also, we don't have to use mixins to prefix CSS properties because we have plugins like [Autoprefixer](https://github.com/postcss/autoprefixer). 324 | 325 | ## Avoid @extend everywhere 326 | 327 | It's not absolutely, always, definitely bad, but it usually is. 328 | The problems with `@extend` are manifold, but to summarize: 329 | 330 | - It's actually worse for performance than mixins are; 331 | - It's greedy. Sass will `@extend` every instance of a class that it finds, giving us crazy-long selector chains; 332 | - It moves things around your codebase. Source order is vital in CSS, so moving selectors around your project should always be avoided; 333 | - It obscures the paper-trail. `@extend` hides a lot of complexity in our Sass. 334 | 335 | ## Avoid shorthand syntax everywhere 336 | 337 | Typically, we would view [shorthand syntax](https://csswizardry.com/2016/12/css-shorthand-syntax-considered-an-anti-pattern/) as a benefit because we have fewer keystrokes, fewer lines of code, less data over the wire. 338 | However, it comes with a rather troublesome side effect. 339 | It often unsets other properties that we never intended to modify. 340 | So, always consider the longhand. 341 | It might be more keystrokes, it might be more repetitive, it might be less DRY, but it's more accurate. 342 | Only write as much as we need and not a single bit more. 343 | 344 | ## Avoid too many font files 345 | 346 | Maybe the designers handed us too many font files which is a red flag. 347 | A website with too many fonts can be chaotic and confusing, so we should only include the necessary fonts for the page. 348 | Fonts can take time to load and be applied and when we have too many fonts. 349 | By default, font requests are delayed until the render tree is constructed, which can result in delayed text rendering. 350 | It is recommended to [optimize WebFont loading and rendering](https://web.dev/optimize-webfont-loading). 351 | 352 | ## Use multiple classes 353 | 354 | Sometimes it's beneficial to add multiple classes to an element. 355 | Let's say that we have a `