--- tags: COMP-1850 --- <style> .markdown-body h1:first-of-type { margin-top: 24px; } .markdown-body h1 { margin-top: 64px; } .markdown-body h1 + h2 { margin-top: 32px; } .markdown-body h2 { margin-top: 48px; } .markdown-body h3 { color: cornflowerblue; } .exercise { font-size: 150%; font-weight: bold; color: rgb(227,112,183); } .note { color: red; } </style> ## Advanced Page Layout: CSS Flexbox CSS Flexbox is a ==one-dimensional== (either horizontal or vertical) layout method that can help us quickly create layouts otherwise difficult to achieve with previous techniques (e.g. positioning, floats) discussed in this course. First and foremost, Flexbox requires any elements you want to lay out to have a ==parent== element set to `display: flex`. This parent element could be a div, containing several images; it could be the body, containing the header, main, and footer elements; or any other case where you would like to lay out elements in a row or column. Below is an example of three divs, nested inside a parent div, laid out using the browser's _default normal flow_: ``` CSS: .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <div style="background-color: #aaa;"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> <br> Simply by adding `display: flex` to the parent div, we now get the following layout: ``` CSS: .flex { display: flex; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div class="flex"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .flex-wert { display: flex; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-wert"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> <br> By default, the direction is ==row==, meaning all child elements will be laid out horizonally, but this behaviour can be changed with the `flex-direction` property: ``` CSS: .flex { display: flex; background-color: #aaa; } .col { flex-direction: column; } .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div class="flex col"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <div style="background-color: #aaa;"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> <br> Now we are back to where we started, so why in this case would you use Flexbox? The answer becomes clear when we add some additional space to the parent element. Below is an example with the parent element set to twice the height of the combined child elements: ``` CSS: .flex { display: flex; height: 600px; background-color: #ddd; } .col { flex-direction: column; } .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div class="flex col"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .flex-1850 { display: flex; flex-direction: column; height: 600px; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> <br> Now that we have additional space, what if we wanted to: - center the child elements vertically and horizontally inside the parent element? - group the three child elements together at the bottom of the parent element? - evenly (and responsively) space the elements out along the column? All three of the above would be difficult using positioning, but become trivial with CSS Flexbox. By adding various values of `justify-content` and `align-items` to the parent element we can achieve all of the above with very little code. ### `justify-content` `justify-content` determines how child elements are laid out along the direction set by `flex-direction`. Below are examples of common values for the `justify-content` property: `justify-content: flex-start;` - (default) arrange items at the beginning of the main axis/direction <style> .flex-1850-s { display: flex; flex-direction: column; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-s"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> `justify-content: center;` - arrange items in the center of the main axis/direction <style> .flex-1850-scaifs { display: flex; flex-direction: column; justify-content: center; align-items: flex-start; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-scaifs"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> `justify-content: flex-end;` - arrange items at the end of the main axis/direction <style> .flex-1850-sfe { display: flex; flex-direction: column; justify-content: flex-end; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-sfe"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> `justify-content: space-between;` - arrange items to have maximum space _between_ them <style> .flex-1850-sb { display: flex; flex-direction: column; justify-content: space-between; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-sb"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> `justify-content: space-around;` - add as much space between items as possible, with an additional half space at either end of the axis <style> .flex-1850-sa { display: flex; flex-direction: column; justify-content: space-around; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-sa"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> `justify-content: space-evenly;` - add an even amount of space around all items <style> .flex-1850-se { display: flex; flex-direction: column; justify-content: space-evenly; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-se"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> ### `align-items` The `align-items` property works similarly to `justify-content`, and shares the same possible values, but positions items along the ==opposite== axis. For example, if we want to center items vertically and horizontally inside a flex parent, we can use the following combination of properties and values: ``` justify-content: center; align-items: center; ``` <style> .flex-1850-sc { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-sc"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> ### `flex-wrap` All of the above layouts work well when the length/height of the child elements do not exceed the length/height of the parent flex container, but in cases where child elements are too large, we may need to control how they ==wrap==. Below is an example of child elements that are too wide for their parent container: ``` <style> .flex-1850-fw { display: flex; width: 300px; height: 300px; background-color: #ddd; } .square-s { width: 150px; height: 150px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .flex-1850-fwn { display: flex; flex-wrap: nowrap; width: 300px; height: 300px; background-color: #aaa; } .square-sw { width: 150px; height: 150px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-fwn"> <div class="square-sw bg-cyan"></div> <div class="square-sw bg-magenta"></div> <div class="square-sw bg-yellow"></div> </div> <br> In the above case, the default value of `flex-wrap: nowrap` is forcing the child elements to render at less than their declared 150px width. This responsive behaviour can be desirable, but in any instance where the widths must be respected (e.g. for images), we can set a value of `wrap` or `wrap-reverse` to control how items will break onto new lines: `flex-wrap: wrap;` <style> .flex-1850-fw { display: flex; flex-wrap: wrap; width: 400px; height: 300px; background-color: #aaa; } .square-sw { width: 150px; height: 150px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-fw"> <div class="square-sw bg-cyan"></div> <div class="square-sw bg-magenta"></div> <div class="square-sw bg-yellow"></div> </div> <br> <br> `flex-wrap: wrap-reverse;` <style> .flex-1850-fwr { display: flex; flex-wrap: wrap-reverse; width: 400px; height: 300px; background-color: #aaa; } .square-sw { width: 150px; height: 150px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-fwr"> <div class="square-sw bg-cyan"></div> <div class="square-sw bg-magenta"></div> <div class="square-sw bg-yellow"></div> </div> <br> ### `align-content` In any case where `flex-wrap` is causing us to have multiple _lines_ of items, we can use `align-content` to control how the _lines_ are laid out along the axis opposite the main direction. `align-content: center;` <style> .flex-1850-fwrac { display: flex; flex-wrap: wrap-reverse; align-content: center; width: 400px; height: 400px; background-color: #aaa; } .square-sw { width: 150px; height: 150px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-fwrac"> <div class="square-sw bg-cyan"></div> <div class="square-sw bg-magenta"></div> <div class="square-sw bg-yellow"></div> </div> <br> ### `gap` Last but not least, we can add a gap between rows and/or columns of elements using the shorthand `gap` property: <style> .flex-1850-scg { display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 10px 0; /* row column */ width: 300px; height: 300px; background-color: #aaa; } .square-s { width: 50px; height: 50px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } </style> <div class="flex-1850-scg"> <div class="square-s bg-cyan"></div> <div class="square-s bg-magenta"></div> <div class="square-s bg-yellow"></div> </div> <br> ## Flexbox Child Properties All of the above-mentioned properties are added to the parent flex element, but CSS also provides properties that are applied only to ==child== elements inside a flex parent. Flexbox properties for child elements allow us to: - position items on a per-child basis - let child elements grow (or shrink) to fill available space in the parent - re-order the children regardless of how they appear in our HTML markup ### `align-self` The `align-self` allows us to override the default value of `align-items`: ``` CSS: .flex { display: flex; justify-content: center; align-items: center; height: 200px; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan-asdf { background-color: cyan; align-self: flex-start; } .bg-magenta { background-color: magenta; /* not overriden, so uses value of align-items */ } .bg-yellow-asdf { background-color: yellow; align-self: flex-end; } HTML: <div class="flex"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .flex-asdf { display: flex; align-items: center; justify-content: center; height: 200px; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan-asdf { background-color: cyan; align-self: flex-start; } .bg-magenta { background-color: magenta; } .bg-yellow-asdf { background-color: yellow; align-self: flex-end; } </style> <div class="flex-asdf"> <div class="square bg-cyan-asdf"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow-asdf"></div> </div> <br> ### `flex-grow` The `flex-grow` property allows all (or individual) elements to grow to fill any available space inside a flex parent. #### Using `flex-grow` to create child elements of equal width ``` CSS: .flex { display: flex; background-color: #aaa; } .square { flex-grow: 1; /* allow all items to grow */ height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div class="flex"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .flex-fga { display: flex; background-color: #aaa; } .square-fga { height: 100px; flex-grow: 1; } </style> <div class="flex-fga"> <div class="square-fga bg-cyan-asdf"></div> <div class="square-fga bg-magenta"></div> <div class="square-fga bg-yellow-asdf"></div> </div> <br> #### Using `flex-grow` to allow *individual* child elements to grow ``` CSS: .flex { display: flex; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { flex-grow: 1; background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div class="flex"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .bg-magenta-fgi { flex-grow: 1; background-color: magenta; } </style> <div class="flex-fga"> <div class="square bg-cyan-asdf"></div> <div class="square bg-magenta-fgi"></div> <div class="square bg-yellow-asdf"></div> </div> <br> ### `flex-shrink` The `flex-shrink` property allows individual elements to shrink inside a flex parent, creating more room for other items. ``` CSS: .flex { display: flex; width: 600px; background-color: #aaa; } .square { width: 400px; height: 100px; } .bg-cyan { background-color: cyan; } .bg-magenta { flex-shrink: 2; background-color: magenta; } .bg-yellow { background-color: yellow; } HTML: <div class="flex"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .flex-fga { display: flex; width: 600px; background-color: #aaa; } .square-fgfs { height: 100px; width: 400px; } .square-fgfs.bg-magenta { flex-shrink: 2; } </style> <div class="flex-fga"> <div class="square-fgfs bg-cyan-asdf"></div> <div class="square-fgfs bg-magenta"></div> <div class="square-fgfs bg-yellow-asdf"></div> </div> <br> ### `order` The `order` property allows us to arbitrarily re-order items regardless of their position in the HTML markup ``` CSS: .flex { display: flex; background-color: #aaa; } .square { width: 100px; height: 100px; } .bg-cyan { order: 3; background-color: cyan; } .bg-magenta { order: 1; background-color: magenta; } .bg-yellow { order: 2; background-color: yellow; } HTML: <div class="flex"> <div class="square bg-cyan"></div> <div class="square bg-magenta"></div> <div class="square bg-yellow"></div> </div> ``` <style> .bg-cyan-fgo { order: 3; background-color: cyan; } .bg-magenta-fgo { order: 1; background-color: magenta; } .bg-yellow-fgo { order: 2; background-color: yellow; } </style> <div class="flex-fga"> <div class="square bg-cyan-fgo"></div> <div class="square bg-magenta-fgo"></div> <div class="square bg-yellow-fgo"></div> </div> <br> <!-- https://mastery.games/post/the-difference-between-width-and-flex-basis/ --> --- <span class="exercise" id="a5a">Assignment 5a: Page Layout using CSS Flexbox</span> For assignment 5a you must create a valid HTML5 page that fulfills the following criteria: * Uses two web fonts from Google Fonts – one for headings (serif) and one for body text (sans-serif) * Contains all the text content provided in assignment5a-content.txt, marked up appropriately by you * Contains a grid of 9 portrait or landscape animal photos chosen by you, displayed as squares using the object-fit property. __Note:__ the photos you choose must not be square to begin with! They must be a non-square aspect ratio, made to appear as square via CSS * Serves smaller versions of the above-mentioned photos on screens below 800px, using either the 'srcset' and 'sizes' attributes, or via the 'picture' element (see lecture notes for examples) * Magnifies the images slightly on hover, using a CSS transition and the transform property described here: https://developer.mozilla.org/en-US/docs/Web/CSS/transform * Displays all content correctly at any screen size, i.e. images and text must not overflow the viewport ## Optional Reading Before Next Class - [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout) - [A Complete Guide To CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) ## Tasks To Complete Before Next Class - [ ] Complete [assignment 5a](#a5a) - [ ] (Optionally) complete the [readings](#Optional-Reading-Before-Next-Class) ---