├── 01-box-model.md ├── 02-resets.md ├── 03-components-vs-layout.md ├── 04-grids.md ├── README.md └── img ├── grid ├── add-element.png ├── calc-margin.png ├── flex-basis.png ├── flex-grow.png ├── flex-wrap.png ├── flex.png ├── gutter.png ├── html.png ├── mobile.png ├── negative-margin-bg.png └── negative-margin.png ├── layout-1.png ├── twitter-desktop.png └── twitter-mobile.png /01-box-model.md: -------------------------------------------------------------------------------- 1 | # Understanding the Box Model 2 | 3 | Whether styling a single element or a larger layout, the first thing to understand is the "Box Model". 4 | This is how elements decide how much space to occupy and where to place themselves. 5 | 6 | When thinking about the size of an element there are 4 different parts or properties that make up a single element's "Box Model": 7 | 8 | 1. Content (width/height) - This is the size of the text content within an element or the set `width` and `height` properties 9 | 2. Padding - This is the whitespace between the content and the border of an element 10 | 3. Border - This is a indiviually styled and colored wrapper surrounding an element 11 | 4. Margin - This is the whitespace between the current element and the next element on the page 12 | 13 | ## Fixing the Box Model 14 | 15 | By default `width` and `height` only dictate the size of the "Content" area of an element. 16 | This means that if you have an element that is `width: 100%; background: red` and add a `padding: 1rem`, then the default setting is for the element's background to take up 100% of the parent element's width **PLUS** another `2rem` (that's `1rem` for both the left and right sides of the element). 17 | This behavior can be hard to wrap your mind around since most people associate the background of an element with the SIZE of an element. 18 | Luckily this behavior can be fixed by adding one simple rule to your CSS: 19 | 20 | ```css 21 | *, *::before, *::after { 22 | box-sizing: border-box; 23 | } 24 | ``` 25 | 26 | 27 | This means that `width` and `height` on all elements (and pseudo-elements) includes the `padding` and `border` for that element. 28 | 29 | ## How to Use `width` and `height` 30 | 31 | 1. Whenever possible, avoid specifying `width` or `height` - this can cause overflow issues as text resizes or dynamic content is added via an API or CMS (Content Management System) 32 | 2. If you have to specify `width` or `height`... DONT DO IT! 33 | 3. Seriously, you still want to specify `width` or `height`? Can you use `flex-basis` instead? 34 | 4. Did `flex-basis` not work out? Ok, but at least use a `%` measurement 35 | 36 | ## Choosing Between `padding`, `margin`, and `border` 37 | 38 | 1. Does this whitespace need to have a color different from your background? 39 | * Yes: `border` 40 | * No: Keep Going 41 | 2. Do you have a background? 42 | * Yes: Should the whitespace be inside or outside of the background? 43 | - Inside: `padding` 44 | - Outside: `margin` 45 | * No: Keep Going 46 | 3. Will this element EVER *possibly* have it's own background? 47 | * Yes: Should the whitespace be inside or outside of the future background? 48 | - Inside: `padding` 49 | - Outside: `margin` 50 | * No: Go back and ask your designer to make sure, then either look at the previous step or keep going 51 | 4. Did you already set a hard `width`, `height`, or `flex-basis`? 52 | * Yes: Did you use `calc` to account for future margins? 53 | - Yes: `margin` 54 | - No: `padding` 55 | * No: `margin` 56 | 57 | ## How `display` Changes the "Box Model" 58 | 59 | The `display` property can vastly alter the way that an element's box model is created. 60 | Let's look at some features of this (note this assumes the element isn't within a "flex parent" and not `position: absolute` or `fixed`) 61 | 62 | * `display: block` and `display: flex` 63 | * Defaults `width` to `auto` which is `100%` but will automatically account for margins 64 | * Margin and padding on all sides is accounted for when laying out other elements 65 | * Setting hard `width` or `height` alters the size of the element 66 | * Next element will always drop to a new line unless floated or in a flex context 67 | * `display: inline-block` 68 | * Defaults `width` to `auto` which only takes up the size of the text or internal content 69 | * Margin and padding on all sides is accounted for when laying out other elements 70 | * Setting hard `width` or `height` alters the size of the element 71 | * Next `inline`, `inline-block`, or text content will be on the same line if any parent `width` is remaining 72 | * Whitespace between elements will show up 73 | * `display: inline` 74 | * Width and height are completely ignored and only text content is applied 75 | * Margin top and bottom are completely ignored 76 | * Margin left and right are accounted for as usual 77 | * Padding top and bottom changes the rendering of background but does not shift the layout of other elements 78 | * Padding left and right are accounted for as usual 79 | 80 | ## Need Help Deciding Between Display Properties 81 | 82 | 1. Are you trying to modify the behavior of whitespace and child elements within the selected element? 83 | * Yes: `display: flex` 84 | * No: Continue on 85 | 2. Are you trying to modify a text property like `text-decoration`, `font-weight`, `color`, etc within text? 86 | * Yes: `display: inline` - **NOTE** this almost always should be already applied by your element choice `a`, `span`, `strong`, `em` 87 | * No: Continue on 88 | 3. Is this element inline with EXISTING text content? (Note this does not include things like "levels") 89 | * Yes: Should spaces between text and the current element be accounted for? 90 | * Yes: `display: flex` 91 | * No: Possibly make the parent element `display: flex` 92 | * No: `display: block` 93 | 94 | --- 95 | 96 | Let's continue by looking at common CSS resets: [NEXT](02-resets.md) 97 | -------------------------------------------------------------------------------- /02-resets.md: -------------------------------------------------------------------------------- 1 | # Resets 2 | 3 | One common practice from the last few years of web development is to reset everything!!! 4 | While this can be useful to having a clean slate to work from, it often means that work is doubled. 5 | This bloats stylesheets and makes rendering more taxing. 6 | 7 | In contrast to this, look at [this site](http://motherfuckingwebsite.com/) (I apologize for the strong language). 8 | These are the default styles we're working with. 9 | By leveraging these styles along with a few logical improvements, we can get a good launch pad for building beautiful sites. 10 | 11 | ## Body Margin 12 | 13 | How many sites do you know that have absolutely no change in background? 14 | I'm even including the navbar! 15 | 16 | When changing backgrounds, 99.9999999999% of the time you will want the background (I'm including horizontal box-shadows and borders in this too) to span from sea to shining sea! 17 | So 5/5 dentists agree that while the default margin set on the `body` element is nice when our stylesheet fails to load, it makes it really annoying to get full-width backgrounds working. 18 | Fixing this takes one simple CSS rule: 19 | 20 | ```css 21 | body { 22 | margin: 0; 23 | } 24 | ``` 25 | 26 | > **NOTE** this one is really easy to see: it looks like there is an `8px` border around the page, you likely forgot this one little thing. 27 | 28 | > **ALSO NOTE** this body reset is a great place to put your default background and font-family to knock those out in one go. 29 | 30 | ## Lists 31 | 32 | Think of all the places you use `ul` elements: 33 | 34 | * Collections 35 | * Nav Groups 36 | * Tabs 37 | * Button Groups 38 | * Article Lists 39 | * Bulleted Lists 40 | 41 | Except for the last one, none of the "lists" above need bullets! 42 | So why should a bulleted list be your default style for `ul` elements? 43 | Well it's nice when there's absolutely no styling, but for everything else there's a quick and easy reset: 44 | 45 | ```css 46 | ul { 47 | padding-left: 0; 48 | list-style: none; 49 | } 50 | ``` 51 | 52 | ## Making the Stylesheet Responsive Again 53 | 54 | Do you know what it takes to break the amazing nature of the built in, completely responsive browser stylesheet? 55 | One single image that is wider than the device screen-width! 56 | 57 | Since even the original iPhone saved photos at a 1600x1200 resoution, it's really easy to get LARGE images unless you remember to save for web or use an image processing library on your server (like [gm](https://npm.org/packages/gm)). 58 | This means a single user upload could wreck massive damage on a site. 59 | But protecting from this is easy: 60 | 61 | ```css 62 | img { 63 | max-width: 100%; 64 | } 65 | ``` 66 | 67 | ## THAT'S IT 68 | 69 | What more do you want from a reset? 70 | A lot of the browser defaults are fairly sensible and give a good sense of typographic scale. 71 | Sure it's not perfect, but it makes time to initial review REALLY fast. 72 | 73 | ## TLDR; 74 | 75 | So far, our stylesheet looks like this: 76 | 77 | ```css 78 | *, *::before, *::after { 79 | box-sizing: border-box; 80 | } 81 | 82 | body { 83 | margin: 0; 84 | } 85 | 86 | ul { 87 | padding-left: 0; 88 | list-style: none; 89 | } 90 | 91 | img { 92 | max-width: 100%; 93 | } 94 | ``` 95 | 96 | --- 97 | 98 | Let's continue by looking at what makes something a part of layout and what makes something a "Component": [NEXT](03-components-vs-layout.md) 99 | -------------------------------------------------------------------------------- /03-components-vs-layout.md: -------------------------------------------------------------------------------- 1 | # Components vs Layouts 2 | 3 | One concept that can drastically reduce the complexity of your CSS and increase the reusability of your HTML is to separate the concepts of what is part of a "Component" and what is "Layout". 4 | 5 | > Layout is like a bookshelf and then components are like the things that get to move around on the bookshelf - Brad Westfall 6 | 7 | The point of separating Layout from Components makes it easier to move components around. 8 | For instance consider a single Tweet on Twitter. 9 | This would be a component, both on desktop: 10 | 11 | ![tweet-desktop](img/tweet-desktop.png) 12 | 13 | And on mobile: 14 | 15 | ![tweet-mobile](img/tweet-mobile.png) 16 | 17 | But notice that even when this tweet is shown on mobile and takes up the full-width of the page, it's rendering is the same! 18 | This is the definition of a good Component, even as the layout changes it can adapt and fill the space needed. 19 | It is also independent of the grid (or lack of grid) that may contain it. 20 | 21 | This also makes reusability of layout primitives easier when they do not carry the baggage of styling like `borders`, `box-shadows`, `background`, or text properties. 22 | 23 | Consider the following layout: 24 | 25 | ![Layout Example](img/layout-1.png) 26 | 27 | The left side is about half of the page size and then there are two quarter grid items. 28 | At first glance, you might think that the HTML may look like: 29 | 30 | ```html 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | ``` 43 | 44 | But this actually is a tougher way of breaking down this layout since it limits new content that could be added to the right side of the page. 45 | Instead, let's look at how we can reuse the `columns` primitive to make a fluid and dynamic grid: 46 | 47 | ```html 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | ``` 68 | 69 | See this example on [Codepen](http://codepen.io/rtablada/pen/oYYNvN) 70 | 71 | Notice that by breaking down the left and right into halves and then creating a new row of columns in the right side, there is now room for more items on the right to fit into the grid system. 72 | Try adding a `.column+.orange` to the codepen HTML within the `right-x` div to see this in action! 73 | 74 | If the grid items and sizes had borders or any styles not pertaining to layout, then this flexibility would not be possible! 75 | 76 | Also as a note, components should not contain layout style elements, so for instance while it may be tempting to use the `column` classes above to recreate the tweet "Component" picture to text ratio, this would add weight and complexity if the layout classes were to change. 77 | 78 | --- 79 | 80 | Let's continue by how we can use the concepts of layout to create a responsive grid and set of containers for a full responsive site: [NEXT](04-grids.md) 81 | -------------------------------------------------------------------------------- /04-grids.md: -------------------------------------------------------------------------------- 1 | # Grids and Containers 2 | 3 | Grids and containers can be rather intimidating when building layout. 4 | With libraries like Bootstrap packing a mind-boggling 1051 lines of code for just a 12 column responsive grid, it's no wonder that companies think that responsive grids are best left to a framework! 5 | Luckily with a few tricks, you'll be making an even better, more responsive, and more maintainable grid than one you'd find off the shelf. 6 | 7 | let's start with some basic HTML for our grid. 8 | 9 | ```html 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ``` 25 | 26 | ![HTML](img/grid/html.png) 27 | 28 | Here we're using BEM naming conventions to make it easier to see that `grid__item`s belong directly within `grid` blocks. 29 | We'll also be using SCSS to help see how these items belong together. 30 | This will also help to see how `display: flex` changes child elements. 31 | LET'S GET STARTED!!! 32 | 33 | First we need to have our `grid` line up our four `grid__item`s next to each other using flexbox: 34 | 35 | ```scss 36 | .grid { 37 | display: flex; 38 | } 39 | ``` 40 | 41 | ![Add Flex](img/grid/flex.png) 42 | 43 | Next we need our `grid__item` to be 1/4 of the grid width for flexibility later we'll use `flex-basis` instead of `width` (more on this decision later). 44 | 45 | ```scss 46 | .grid { 47 | display: flex; 48 | 49 | &__item { 50 | flex-basis: 25%; 51 | } 52 | } 53 | ``` 54 | 55 | ![Add Flex Basis](img/grid/flex-basis.png) 56 | 57 | Now that we have 1/4s we should get whitespace between our `grid__item` elements using margin: 58 | 59 | ```scss 60 | .grid { 61 | display: flex; 62 | 63 | &__item { 64 | flex-basis: 25%; 65 | margin: 1rem; 66 | } 67 | } 68 | ``` 69 | 70 | ![Add Gutter](img/grid/gutter.png) 71 | 72 | Notice that this did two things: 73 | 74 | * Since `flex-shrink` defaults to `1`, the `grid__item`s have shrunk from their last size. 75 | * Since all of the elements have margin on all sides, the red `grid__item` no longer lines up with the heading that isn't part of our grid. 76 | 77 | Frameworks often complicate this by requiring all elements to be within grid containers, but this adds a lot of unnecessary weight to our HTML! 78 | Instead, we can get around this with a single line of CSS (oh and no `:nth-of-type` selectors that often complicate grids): 79 | 80 | ```scss 81 | .grid { 82 | display: flex; 83 | margin: -1rem; 84 | 85 | &__item { 86 | flex-basis: 25%; 87 | margin: 1rem; 88 | } 89 | } 90 | ``` 91 | 92 | ![Negative Margin](img/grid/negative-margin.png) 93 | 94 | Here the `-1rem` margin stretches the `grid` div actually beyond the grey background of it's parent element. 95 | This can be seen by quickly giving a purple temporary background to our `grid` div: 96 | 97 | ![Negative Margin BG](img/grid/negative-margin-bg.png) 98 | 99 | But what if we add another element to our HTML: 100 | 101 | ```html 102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | ``` 120 | 121 | ![Add Element](img/grid/add-element.png) 122 | 123 | Uh oh!!! 124 | Looks like now we have a 5 element grid even though our `flex-basis` is `25%`? 125 | We can prevent this by adding `flex-wrap` on our `grid`: 126 | 127 | ```scss 128 | .grid { 129 | display: flex; 130 | flex-wrap: wrap; 131 | margin: -1rem; 132 | 133 | &__item { 134 | flex-basis: 25%; 135 | margin: 1rem; 136 | } 137 | } 138 | ``` 139 | 140 | ![Flex Wrap](img/grid/flex-wrap.png) 141 | 142 | But now there's only three elements on each row in our `grid`? 143 | If we recall the ["Box Model"](01-box-model.md), even with `box-sizing: border-box` our `width` doesn't account for `margin`s. 144 | So to fix this we can use the `calc` function to subtract the left and right margins on our `grid__item`s: 145 | 146 | ```scss 147 | .grid { 148 | display: flex; 149 | flex-wrap: wrap; 150 | margin: -1rem; 151 | 152 | &__item { 153 | flex-basis: calc(25% - 1rem - 1rem); 154 | margin: 1rem; 155 | } 156 | } 157 | ``` 158 | 159 | ![Calc](img/grid/calc-margin.png) 160 | 161 | The next step is optional, but in my opinion it makes a really cool grid. 162 | What to do with the `grid__item` that is all by itself. 163 | Here we can leverage flexbox in a few different ways, my personal favorite is to add `flex-grow: 1` to the `grid__item` so that it fills up any remaining space on it's row: 164 | 165 | ```scss 166 | .grid { 167 | display: flex; 168 | flex-wrap: wrap; 169 | margin: -1rem; 170 | 171 | &__item { 172 | flex-basis: calc(25% - 1rem - 1rem); 173 | flex-grow: 1; 174 | margin: 1rem; 175 | } 176 | } 177 | ``` 178 | 179 | ![Flex Grow](img/grid/flex-grow.png) 180 | 181 | The last thing we can do is make sure that our grid stacks vertically on mobile using a media query. 182 | For this we can make our `grid` div `flex-direction: column`. 183 | This will make all of the `grid__item`s stack! 184 | 185 | ```scss 186 | .grid { 187 | display: flex; 188 | flex-wrap: wrap; 189 | margin: -1rem; 190 | 191 | &__item { 192 | flex-basis: calc(25% - 1rem - 1rem); 193 | flex-grow: 1; 194 | margin: 1rem; 195 | } 196 | 197 | @media (max-width: 400px) { 198 | flex-direction: column; 199 | } 200 | } 201 | ``` 202 | 203 | ![Mobile](img/grid/mobile.png) 204 | 205 | > **NOTE** Notice that this grid doesn't use any `min-width` or `max-width` settings. 206 | > This is because it should be the responsibility of components to decide when they break down. 207 | > If a tweet needs `20rem` width to render properly, then the grid shouldn't add this limitation since it could have a button column that only needs `4rem`. 208 | 209 | 210 | Here is a [Codepen playground](http://codepen.io/rtablada/pen/ZBBEJP) to work with grids. 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS Can Be Easy 2 | 3 | This guide is to teach that building responsive sites and applications using CSS can be easier than you might think. 4 | There are a few best practices 5 | 6 | ## Assumptions 7 | 8 | This guide is built around a support stack of IE 11 and modern browsers (evergreen Chrome, Edge, Safari, Firefox, Opera). 9 | IE <11 is no longer supported by Microsoft for even security updates. 10 | This means not only is your team missing out on awesome features like `calc` and flexbox, but continuing to support IE <11 puts your app and more importantly your customers at risk. 11 | To read more about Microsoft's support policy, see: https://www.microsoft.com/en-us/WindowsForBusiness/End-of-IE-support. 12 | 13 | This recommendation guide pretty heavily uses `display: flex`. 14 | Even with the support stack listed above, I would recommend using something like [autoprefixer](https://github.com/postcss/autoprefixer) to fill in gaps in prefixing or experimental features. 15 | 16 | Also, while most everything here should Just Work™, there are a few known flexbox bugs. 17 | To learn more about these, check out the popular [Flexbugs Repo](https://github.com/philipwalton/flexbugs). 18 | 19 | ## Let's Get Started! 20 | 21 | 1. [Box Model](01-box-model.md) 22 | 2. [Resets](02-resets.md) 23 | 2. [Components vs Layout](03-components-vs-layout.md) 24 | 3. [Grids (the Easy Way)](04-grids.md) 25 | -------------------------------------------------------------------------------- /img/grid/add-element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/add-element.png -------------------------------------------------------------------------------- /img/grid/calc-margin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/calc-margin.png -------------------------------------------------------------------------------- /img/grid/flex-basis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/flex-basis.png -------------------------------------------------------------------------------- /img/grid/flex-grow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/flex-grow.png -------------------------------------------------------------------------------- /img/grid/flex-wrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/flex-wrap.png -------------------------------------------------------------------------------- /img/grid/flex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/flex.png -------------------------------------------------------------------------------- /img/grid/gutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/gutter.png -------------------------------------------------------------------------------- /img/grid/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/html.png -------------------------------------------------------------------------------- /img/grid/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/mobile.png -------------------------------------------------------------------------------- /img/grid/negative-margin-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/negative-margin-bg.png -------------------------------------------------------------------------------- /img/grid/negative-margin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/grid/negative-margin.png -------------------------------------------------------------------------------- /img/layout-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/layout-1.png -------------------------------------------------------------------------------- /img/twitter-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/twitter-desktop.png -------------------------------------------------------------------------------- /img/twitter-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtablada/css-can-be-easy/b5ba6246b0e41b7d710ed29e7cf1c08927d67dd6/img/twitter-mobile.png --------------------------------------------------------------------------------