import { addBanner, addArticle, addTitle, addHeader, addParagraph, addClassParagraph, addSubHeader, addOrderedList, addUnorderedList, addBlockquote, addInset, addInsetList, addInsetCodeListing, addInsetBulletList, addImageWithCaption, addButtonGroup, addSidebar, addSyntax, menu, global_menu } from '/scripts/import.js'; const heading = document.querySelector(".heading"); const global = document.querySelector(".global_menu"); const local = document.querySelector(".local_menu"); const sidebar = document.querySelector(".sidebar"); const main = document.querySelector(".main_content"); heading.append(addTitle("CSS Layouts from Float to Flexbox and Grid")); heading.append(addParagraph("Christina Truong - LinkedIn Learning - September 2019")); heading.append(addParagraph("Chapter 2 - Flexbox")); main.append(addHeader("Getting Started with Flexbox")); main.append(addParagraph("We saw some of the limitations associated with using floats to create a layout such as the fact that we had columns with different heights. With flexbox, there are ways to do the following:")); main.append(addInsetBulletList(["Align content.", "Order items.", "Implement flexible sizing."])); main.append(addParagraph("Flexbox was introduced as a complete layout module so this means that it introduces a lot of new properties along with its own terminology. It is often referred to as a one-dimensional layout because it lays out elements along a single axis and that can be either horizontal or vertical.")); main.append(addParagraph("You can define a container as being a flexbox using the display property and any child elements of such a container are flex-items but child elements of these flex-items are not themselves flex-items. In other words, let's say that you have a container called main-content and you have three divs inside that with elements such as headers or paragraphs within those divs. If we define main-content as a flexbox or a flex container, the three divs would become flex-items but the headers and paragraphs wouldn't be flex-items.")); main.append(addParagraph("However, we can nest flex containers so we might, for example, define one of the divs as also being a flex container and if we did that, the elements that it contains would be flex-items. So an element can be both a flex-item and a flex container.")); main.append(addParagraph("You might recall that we had previously used the display property to define elements as block or inline so this was changing the way in they appear in the normal flow. It doesn't remove them from the normal flow, but it changes the flow. That's really what we are doing with flex so it seems appropriate to use it for defining a flex-box which we can do with one of two values, flex or inline-flex.")); main.append(addParagraph("Let's look at an example in codepen.io. We have a div with class container and within that, we have four other divs.")); main.append(addImageWithCaption("./images/flex1.jpg", "A basic layout following the normal flow.")); main.append(addParagraph("In this case, we are not applying any CSS that changes the layout so this is just four items displayed one after the other and with each element spanning the width of the container. In other words, these are being displayed as block elements.")); main.append(addParagraph("If we apply a display property with a value of flex to container, we can immediately see a difference.")); main.append(addImageWithCaption("./images/flex2.jpg", "The same layout in a flex container.")); main.append(addParagraph("Now, the elements inside the container are being displayed inline and so the width of these elements is determined by the content. I also applied a background colour to the container itself so you can see that it is still spanning the whole container width. In essence, flex-items are being displayed inline but the flexbox itself is displayed as a block element. Although we are not going in to this in any detail just yet, we can see the effect of using a value of inline-flex for the display property.")); main.append(addImageWithCaption("./images/flex3.jpg", "The same layout in an inline-flex container.")); main.append(addParagraph("Now, the container itself is also displayed inline and we can tell that because of the fact that the width of the container is now determined by the content. It might be a little clearer if we added a second identical container but we will give it a background colour of yellow this time.")); main.append(addImageWithCaption("./images/flex4.jpg", "The same layout in an inline-flex container.")); main.append(addParagraph("As expected, we now have two flex containers and since both containers are defined as online-flex, the containers themselves are displayed as inline elements.")); main.append(addParagraph("Let's look at one final example for this section.")); main.append(addImageWithCaption("./images/flex5.jpg", "A simple layout using float.")); main.append(addParagraph("This is a very simple layout created using float. I have applied float left to both the elements in this layout, the sidebar and the main content area. I haven't bothered to implement clearfix or any other fix which I would need if I were using this in a real world layout where I may need to add other elements. I set the widths of both the elements to ensure they could be displayed side by side and this illustrates the fact that we have elements within this layout with different heights.")); main.append(addParagraph("We'll take out those widths and remove the floats and instead, we will apply a display property with a value of flex to the container.")); main.append(addImageWithCaption("./images/flex6.jpg", "The same layout using flex.")); main.append(addParagraph("The flex layout looks better. The height of the columns is the same and we did this with just one line of CSS - the line where we set the display property to flex for container. We don't need to do anything with the child elements and I didn't have to try different widths for sidebar and main in order to get them to display side by side. So this is giving us the flexible sizing we mentioned earlier and we can see this if we make the width of the viewport much smaller.")); main.append(addImageWithCaption("./images/flex7.jpg", "Resizing the flex layout.")); main.append(addParagraph("This is a pretty small width and the sort of width you would see on a phone screen but it still looks okay.")); main.append(addParagraph("Of course, there is a little more to flex than that and it has other properties you can use to customise a layout as we will see shortly, but the main thing to take from this introduction is that flex, unlike float, is specifically designed for the purpose of creating flexible layouts which can be used for an entire page and we can already see that the most basic flex layout is easy to implement compared to float as well as looking better.")); main.append(addHeader("Orientation with flex-direction and flex-wrap")); main.append(addParagraph("Although we can align items either vertically or horizontally using the display property with inline or by using the float property, we have seen that these have their own little quirks which can make them difficult to use and can involve additional hacks and fixes, but we can do this very easily with flex.")); main.append(addParagraph("Alignment of items in a flexbox is based on two axes. The axis that runs in the direction you are aligning the elements is the main axis. So, for example, if you are aligning items horizontally, the main axis runs horizontally across the page. The second axis runs perpendicular to this and is the cross-axis. Each axis has a start and end point known as the main start, main end, cross start and cross end.")); main.append(addParagraph("The flex-direction property sets the direction and can be set to row or column. With row, the items are aligned horizontally so the main axis is horizontal and with column, the items are aligned vertically so the main axis also runs vertically.")); main.append(addImageWithCaption("./images/flex-direction1.jpg", "Screenshot from the course illustrating a flex-direction of row. Notice the direction of the start and end points for both axis.")); main.append(addImageWithCaption("./images/flex-direction1.jpg", "Screenshot from the course illustrating a flex-direction of column. The axis have switched around.")); main.append(addParagraph("We can also set values of row reverse and column reverse for flex-direction and this will switch the positions of the end and start points for the axis. For instance, lets say we set it to row reverse, items will be aligned horizontally within their container but the main start point will be on the right side of the screen rather than left and the items will be aligned from right to left. If you look at the course screenshots shown above, reversing the flow-direction swaps the start and end points around.")); main.append(addParagraph("You should bear in mind the fact that this reversal of the order of the elements is purely visual and it shouldn't be used as a reversal of the HTML elements. For example, let's say items are not reversed in a column and you put the most important items first for reasons of accessibility. If you then reverse the order of the items as they are displayed, your most important content is on the right hand side but it is still first in the HTML. Conversely, if you think that the order of these items is wrong, let's say that your most important content is on the right hand side, reversing the direction will make it appear first in the browser, but it won't make it appear first in the HTML. As such, you should consider the order of the elements in your HTML and for accessibility reasons, you will probably want to put your most important content first but reverse column or reverse row will not have any effect on this.")); main.append(addParagraph("It should be noted that I am assuming your content is in English or at least some language that is written from left to right starting at the top of the page. I don't know if there are any languages that are written from bottom to top but there are certainly some that are written from right to left or from top to bottom and this will affect the position of the start and end points of the main axis which will run from right to left by default and from left to right when reversed.")); main.append(addParagraph("With flexbox, if the elements are in a row, all of the items will appear in a single row even if the flex-items overflow but we can change this with the flex-wrap property. The default value for this is nowrap which means that items will fit themselves into a single line. If we give it the value wrap, items will be aligned over multiple lines if this is required. As such, you can actually produce a grid effect with one flex container so you effectively have a one dimensional layout being displayed in two dimensions.")); main.append(addImageWithCaption("./images/flex-wrap1.jpg", "Demonstrating flex-wrap.")); main.append(addImageWithCaption("./images/flex-wrap2.jpg", "With wrap-reverse, the order on the main access is still the same, but the cross axis has been reversed.")); main.append(addParagraph("We can also change the wrap direction using the value wrap-reverse which switches the cross start and cross end points so this doesn't change the order of the elements along the main axis.")); main.append(addParagraph("Flex-wrap and flex decoration can also be applied with the shorthand property, flex-flow.")); main.append(addHeader("Flexible Sizing")); main.append(addParagraph("One of the nice features of flex is that elements are flexible in terms of both size and how they wrap. Let's look at a couple of quick examples of that.")); main.append(addImageWithCaption("./images/flex-sizing1.jpg", "Changing the content can affect both the size of the element and the overall layout.")); main.append(addParagraph("In this example, I have removed the width property from the flex items so that their size is determined by the content. This is not something that is specific to flex, but it does cause the wrap to occur differently based on the fact that the first div is now large enough to span the width of the container pushing the other divs on to the next line.")); main.append(addImageWithCaption("./images/flex-sizing2.jpg", "Resizing the screen will not change the size of the elements (unless you have set a width using percentage values) but it does affect the layout.")); main.append(addParagraph("I have removed the additional text from the first div and reduced the width of the screen and here, we now have the first two elements on the first line with the other two wrapping around to the next line.")); main.append(addImageWithCaption("./images/flex-sizing3.jpg", "If the screen size is small enough, our elements which are laid out with flex-row will adapt to become a column layout.")); main.append(addParagraph("This feature renders a lot of media queries redundant since you don't have to change the layout manually for a smaller screen - it happens automatically. However, flex has three properties that allow you to control the sizing and these are:")); main.append(addInsetList(["flex-basis: sets the initial size for flex-items", "flex-grow: how items will expand if the container has extra space", "flex-shrink: how items will shrink if the container doesn't have enough space"])); main.append(addParagraph("There is a property, flex, that combines all three into one shorthand property and its syntax is:")); main.append(addSyntax("flex: flex-grow flex-shrink flex-basis")); main.append(addParagraph("Both the flex-grow and flex-shrink properties will have integer values which determine how much a flex-item will shrink or grow in comparison to the elements around it. Flex basis will have a size value using any of the normal elements so it can be, for example, a value in pixels or a percentage or it can be a keyword value such as auto. Some examples of this are:")); main.append(addInsetList(["flex: 0 1 100px;", "flex: 1 0 40%", "flex: 2 2 auto;"])); main.append(addParagraph("To demonstrate this, we will look at an example on codepen.io. Here, we have a flex container with 6 child elements but the display property isn't set yet so this is a flex container in name only and it looks like this.")); main.append(addImageWithCaption("./images/flex-sizing4.jpg", "Demonstrating the flex syntax - before the display: flex property is applied.")); main.append(addParagraph("Since the container is not a flex container, the elements inside it are displayed according to the normal flow so they are stacked vertically and span the width of the container (which has been set to 600 pixels). Let's set the display property of the container to flex.")); main.append(addImageWithCaption("./images/flex-sizing5.jpg", "Demonstrating the flex syntax - with the display: flex property now applied.")); main.append(addParagraph("We do still have what we can consider to be a normal flow but it is horizontal. It only applies to the flex-items - that is the child elements of the flex container - but they are aligned along the horizontal axis (the main axis). The biggest visible difference is that the items are all sized according to the content so they take up as much of the available space that they need. Since the container has a border, we can see that it is much larger than the total amount of space that these flex-items are occupying so it is mostly empty.")); main.append(addParagraph("If we add a flex property with values of 0 for growth, 1 for shrink and auto for basis")); main.append(addSyntax("flex: 0 1 auto;")); main.append(addParagraph("you will see that this makes no difference at all and this is probably what you would expect since this means that elements will not grow (so they won't take up that unused space), they will shrink if there is not enough space to display them and sizing is auto so it is governed by the content. However, these are also the default values so adding then will make no difference anyway.")); main.append(addParagraph("If we change the size of the flex-items to 100 pixels, remember that there are six of them and the container width is 600 pixels")); main.append(addImageWithCaption("./images/flex-sizing6.jpg", "Demonstrating the flex syntax - with a width of 600 pixels for the container and 100 pixels for each of the six flex-items.")); main.append(addParagraph("Not surprisingly, the flex-items now occupy all of the available space in the container. If we now reduce the containers width to 400 pixels, there is no longer enough space to contain 6 elements, each with a width of 100 pixels.")); main.append(addImageWithCaption("./images/flex-sizing7.jpg", "Demonstrating the flex syntax - with a width of 400 pixels for the container and 100 pixels for each of the six flex-items.")); main.append(addParagraph("Now the shrink property takes effect and each element is reduced in width to fit inside the smaller space. The space is removed evenly from all 6 elements, more or less. The width of each flex-item is still being determined by the content to some degree so if we add more content to one of the elements, it will occupy more space.")); main.append(addImageWithCaption("./images/flex-sizing8.jpg", "Demonstrating the flex syntax - with a width of 400 pixels for the container and 100 pixels for each of the six flex-items but also with additional content added to the first element.")); main.append(addParagraph("The content we now have in the first div is")); main.append(addSyntax("1101101133214324287868586656")); main.append(addParagraph("The last visible digit in the first div is a 1 and you might notice it seems to be clipped so its not completely visible and all of the digits that come after that are also hidden. You might be surprised by this, but remember that we have a width of 100 pixels on the flex-items so it won't occupy more than that and the other 5 divs all shrink to the same width to occupy the remaining space.")); main.append(addParagraph("We want our sizing to be flexible if possible so we would like to be able to better accommodate a situation like this where content is being cropped. We can do that by removing the width property from the flex-items and adding to the flex property we had applied to the container in place of auto.")); main.append(addSyntax("flex: 1 0 100px;")); main.append(addParagraph("Essentially, we are saying that 100 pixels is the ideal width so flex will try to get the width of the elements as close to that value as it can. If we reduce the width of the container far enough, we will see that the width of each flex item never shrinks so much that it hides the content so it effectively has a minimum size. If we increase it, we will see that the width of the first element which is already more than 100 pixels doesn't change. The other elements will grow evenly if there is enough space for them to do that but only up to a maximum size of 100 pixels or greater if more space is needed.")); main.append(addImageWithCaption("./images/flex-sizing9.jpg", "Demonstrating the flex syntax - this is much the same as the previous image but this time we are adding the size property to flex rather than to the elements so that we can really take advantage of flexible sizing.")); main.append(addParagraph("Now, the first element shows all of its content so it is taking up as much space as it needs for its content and the space that is left over is evenly distributed amongst the remaining 5 elements.")); main.append(addParagraph("This raises an important point. Although we have set the value for flex-grow to zero, this isn't really absolute. The elements will still grow to accommodate more content. In addition, if the elements shrink because there is limited space available, they will also grow up to the width set in flex-basis when more space becomes available.")); main.append(addParagraph("The size that we set with flex-basis is sometime referred to as the initial size because it is the size that will be set for the flex items if there is enough space but if there isn't, these will either shrink or grow depending on both the amount of available space and the values you have set for the flex-grow and flex-shrink properties.")); main.append(addParagraph("Let's look at another example where the width of the container is set to 400 pixels and we have amended the content of the first div so it has a single digit, just like other five divs. if we have set a value of 1 for flex-shrink, the elements will simply reduce their size evenly to fit the container, but we will set that to zero so that the flex items don't shrink.")); main.append(addImageWithCaption("./images/flex-sizing10.jpg", "Demonstrating the flex syntax - the container has a width of 400 pixels and we have set flex-shrink to 0.")); main.append(addParagraph("As you can see, the flex items are now occupying more than the space available in the corner so we have some overflow on the left-hand side. If we set a flex-wrap property set to wrap, these will move down to the next line.")); main.append(addImageWithCaption("./images/flex-sizing11.jpg", "Demonstrating the flex syntax - we have added a flex-wrap so that element wrap down to the next line.")); main.append(addParagraph("Remember that this is with the elements set to not-shrink. If we set them to shrink again, you might expect that they will revert to a single line since the elements could now fit into a single line, but that doesn't happen. The flex-wrap property allows the elements to keep the size set in flex-basis by using the wrap function, so in essence, keeping the flex-items to the width set in flex-basis is taking priority over keeping them on a single line. Let's set flex-grow to 1 and see what happens!")); main.append(addImageWithCaption("./images/flex-sizing12.jpg", "Demonstrating the flex syntax - we have added a flex-wrap so that elements wrap down to the next line and set flex-grow to 1.")); main.append(addParagraph("This is pretty similar to the previous example but notice that since there are only two elements in that second line, they do now have some room to grow so they are displayed with twice the width of the elements on the first row.")); main.append(addParagraph("We can also add a flex-direction property with a value of column.")); main.append(addImageWithCaption("./images/flex-sizing13.jpg", "Demonstrating the flex syntax - adding a flex-direction property with a value of column.")); main.append(addParagraph("This has the effect of aligning the flex items in a column rather than a row but it also means that the flex-basis is now going to apply to the height of the elements rather than the width. The width now spans the width of the container and this is 400 pixels because that's what we set its width to. If we didn't set a width for the container, it would span the width of its container and the flex items would do the same. You might also see that the elements shrink according to the value set for the flex-shrink property, but you will only see this if you restrict the height of the container which is effectively unlimited in a browser window.")); main.append(addParagraph("Similarly, you may see elements grow if there is space to grow into but bear something in mind. If we don't see any sizing for the flex container - in other words, no width or height - the container will span the width of its container which is the viewport in this example. The hight, however, doesn't span the height of the container if the container is the viewport. The reason should be obvious. A browser window is designed to be, in effect, a window with a fixed width but no fixed height so the width is essentially set by the size of your screen (or less if you don't use the full screen) but it is also designed to accommodate as much content as you want to include.")); main.append(addParagraph("To give you an example of that, let's say you run a search in Google and you get a lot of results back, you will usually have to scroll down to see all of the available results, but you don't have to scroll to the right. This is pretty intuitive and most of us will be used to a scrolling screen like this (think of the opening to Star Wars or a vidi-printer like the one in Good Morning Vietnam).")); main.append(addSubHeader("Sizing Multiple Flex Items")); main.append(addParagraph("We have used flex-sizing on the container so far which essentially means that we are sizing the flex items in that container collectively. In other words, whatever settings we apply will apply equally to each element. There may well be times, however, when you want to distribute the space unevenly. You may have wondered why we have used 1 as the value for flex-grow and whether there are any other values, other than 0, that we might apply and the answer to that question will explain how we can size flex-items differently. The value is actually a ratio which means that if we apply a flex-grow value of 1 to an element and a value of 2 to another element, these will no longer grow evenly. The second element will grow twice as much as the first.")); main.append(addParagraph("In this example, we have three flex items in a container. The first and third elements have a flex property of:")); main.append(addSyntax("flex: 1 1 150px;")); main.append(addParagraph("The second flex item has a flex property of:")); main.append(addSyntax("flex: 2 1 150px;")); main.append(addParagraph("In both cases, our flex-basis is 150px and it is important to remember that the different values for flex-grow do not mean that the elements are not the same size or that the second element will be twice as wide as the other two. The container starts with a width of 450 pixels and this means that all three elements are the same width.")); main.append(addImageWithCaption("./images/flex-sizing14.jpg", "Demonstrating the flex syntax - different elements have different values for flex-grow.")); main.append(addParagraph("Remember that flex-grow doesn't set the size of the items, it sets the amount by which they will grow if there is more space available. An example of that is provided in the course video.")); main.append(addImageWithCaption("./images/flex-sizing15.jpg", "Demonstrating the flex syntax - sizing elements differently - screenshot from the course video.")); main.append(addParagraph("We have two flex items in the flex container, but we are applying different values for flex-grow to each, remember that flex-grow is the first parameter for the flex property. For the first item, we have")); main.append(addSyntax("flex: 1 1 150px;")); main.append(addParagraph("and for the second we have")); main.append(addSyntax("flex: 2 1 150px;")); main.append(addParagraph("Since they both have the same flex-basis (150 pixels) if the flex container is 300 pixels wide, both items will have the same width and since they both shrink with the same flex-shrink value, this will also be true if the width is less than 300 pixels. For example, with a flex container width of only 200 pixels, both of these items will shrink to a width of 100 pixels.")); main.append(addParagraph("Let's say that we increase the width of the container to 900 pixels so we have 600 pixels of extra space. This is where the different flex-grow values come into play. We can split the extra 200 pixels in to three separate spaces, each with a width of 200 pixels. One of these is added to flex-item-one giving it a total width of 350 pixels. The other two are added to flex-item-two giving it a total width of 750 pixels.")); main.append(addParagraph("We can do this in a more flexible and consistent way by using a flex-basis of 0, all of the elements will have an initial size based on their content and all of the remaining space will be distributed amongst the elements based on the flex-grow values you have given them.")); main.append(addParagraph("With flex-grow, you don't actually have to use integer values, you can use any numeric value. For example, if you want the second flex item to grow 50% more than the first, you can use flex-grow values of 1 and 1.5 but it is probably clearer if you use integers. In this case, you could use 2 and 3 as the values for flex-grow but a better option is just to multiply both values by 100 so you have flex-grow values of 100 and 150.")); main.append(addHeader("The order Property")); main.append(addParagraph("With the order property, we can change the order in which our flex-items are stacked without changing the order in the HTML. This allows us to order our elements in the HTML in a way which makes more sense for screen-readers, for example (putting our most important content first) but displays in a way which makes more sense when viewing the page (putting our navigation links first).")); main.append(addParagraph("All elements have a default value of 0 for order so the order in which you put them in your HTML is the order in which they are displayed. This will also be the case if they have the same value regardless of what the value is so for example, if you added an order property with a value of 100 to each of your elements, the effect would be the same as if you didn't add the property at all or if you added it with a value of 0 to each element. Where the elements have different numbers, they are displayed within their container from the item with the lowest number to the item with the highest number.")); main.append(addParagraph("We can also use negative numbers which can be handy if we want to give one element a value that is lower than everything else. Rather than give other elements a number that is higher than the element that you want to appear first, you can just give that element a negative number.")); main.append(addParagraph("As an example, let's say that you have a flex container with 5 flex items and you want them to appear roughly in the same order as they appear in the HTML, but you want to flip the first two elements so it is the second element that is displayed first. We are assuming that nothing already has the order property applied so if we leave it as it is, the elements will all have a value of zero and will appear in order.")); main.append(addParagraph("We could add the order property to all of the elements and give the lowest value to the element we want to appear first and we could apply different values to the other elements so they reflect the order we want or the same if we are happy with the order they are already in. On the other hand, we could just apply a negative value to one element and that will appear first without us having to add anything to the other elements.")); main.append(addParagraph("Remember that when we were working on the example with the float layout, to achieve the same effect, we had to change the order of the elements in the HTML. The order property allows us to order the HTML in a way that makes sense whilst having the order of display different if that makes more sense.")); main.append(addHeader("Nesting flex Containers")); main.append(addParagraph("We have seen that although flex is essentially a one dimensional layout, it is possible to create more complex two dimensional layouts by using the flex-wrap property. We can also create more complex layouts by nesting flex containers and a nested container doesn't have to be aligned in the same way as its parent so you might have, for example, a flex container with three flex items aligned horizontally. One (or more) of these might also be a flex container with several flex items aligned vertically.")); main.append(addParagraph("Let's look at an example of that on codepen.io. We have two elements called Aside and Main and the main element contains three additional elements.")); main.append(addImageWithCaption("./images/nesting1.jpg", "Demonstrating nesting flex containers - starting point.")); main.append(addParagraph("At this stage, we haven't added any flex properties so everything is stacked vertically. We'll start by making page-wrap a flex container. This contains the other elements so that will make Aside and Main flex-items.")); main.append(addImageWithCaption("./images/nesting2.jpg", "Demonstrating nesting flex containers - the page-wrap element is now a flex container.")); main.append(addParagraph("Aside and Main are now aligned horizontally in the flex container and the elements inside Main are aligned vertically. These are not flex items (yet) so they are following the normal flow.")); main.append(addParagraph("Next, we will add flex properties to both Aside and Main. We are adding them to these elements rather than the parent container because we want them to be sized differently. Aside will have a flex-grow property with a value of 1 and for Main, this will be a value of 3. Both have a flex-shrink value of 1 and we will set the flex-basis at 0 so that the actual size of the element is not taken into account when determining how much relative space each will take.")); main.append(addImageWithCaption("./images/nesting3.jpg", "Demonstrating nesting flex containers - each of the flex-items has been sized differently with Main growing three times as much as Aside to fill the available space inside its container.")); main.append(addParagraph("If we make Main a flex container, it's child element become flex items and are aligned along the horizontal axis.")); main.append(addImageWithCaption("./images/nesting4.jpg", "Demonstrating nesting flex containers - Main is now a flex container so as you can see, the elements are now aligned along the horizontal axis.")); main.append(addParagraph("We don't want the child elements in Main to stack horizontally, we want to keep the vertical stacking so we will also add a flex-direction property to Main and give it a value of column.")); main.append(addImageWithCaption("./images/nesting5.jpg", "Demonstrating nesting flex containers - the flex-direction property which we added to Main with a value of column gives us the vertical stacking for its elements.")); main.append(addParagraph("At this point, there is no sizing on the flex items within Main so they are sized according to the content only. We will add a flex property with values of 1 for both flex-grow and flex-shrink and bear in mind that since we are stacking the elements in a column, these properties dictate how the height will change. We have specified auto for our flex-basis so the height of each element will be based on the content with the extra space divided equally between the elements. We can see this more clearly if we increase the amount of content in the first paragraph.")); main.append(addImageWithCaption("./images/nesting6.jpg", "Demonstrating nesting flex containers - the flex-direction property which we added to Main with a value of column gives us the vertical stacking for its elements.")); main.append(addParagraph("I will remove that extra content from he first paragraph.")); main.append(addImageWithCaption("./images/nesting7.jpg", "Demonstrating nesting flex containers - reverting the content in the first paragraph and we can see the height of the flex-items is similar for each.")); main.append(addParagraph("This shows a little more clearly that the height of the elements is growing evenly, bearing in mind that the additional content in paragraph 1 means that it's starting height is a little bigger than that of the other elements.")); main.append(addParagraph("Remember that we are using auto as the flex-basis for the elements in this column and that is why the content is taken into account when determining the relative sizes. Essentially, the height of each element is determined by both the content and then the degree to which it subsequently grows. If we change this to zero, the flex items will all be the same height because this largely ignores the content when determining the height of the elements.")); main.append(addImageWithCaption("./images/nesting8.jpg", "Demonstrating nesting flex containers - with the flex-basis set to zero, content is being ignored when calculating the height of each element and so they all have the same height.")); main.append(addParagraph("Note, however, that with the elements having the same height, there is still enough room in each to accommodate the content. If that wasn't the case, then the content would be taken into account when sizing. We can demonstrate that by reducing the height of the container, which is currently 600 pixels, to 350 pixels.")); main.append(addImageWithCaption("./images/nesting9.jpg", "Demonstrating nesting flex containers - with the container height set to 350 pixels, it is no longer possible to have the same height for each element so the first element has a slightly greater height.")); main.append(addParagraph("So content is still taken into account when calculating height with a flex-basis of 0 but in effect, the amount of content is imposing a limit on how much the item can shrink by.")); main.append(addParagraph("In this final example, I have reduced the height of the page-wrap container (the outer flex container) to 150 pixels which means that it is too small to accommodate the elements even before they shrink so the elements are over-flowing and their size is determined by the content only, including the padding.")); main.append(addImageWithCaption("./images/nesting10.jpg", "Demonstrating nesting flex containers - with the container height set to 150 pixels, the elements don't shrink or grow but since they no longer fit inside their container, they do overflow.")); main.append(addParagraph("It is worth remembering that while we can create the appearance of a two dimensional layout, flex is nonetheless a one-dimensional layout so what we are actually seeing, in this instance is a column layout nested inside a row layout.")); main.append(addHeader("Exercise: Build a Layout with Flexbox")); main.append(addParagraph("We will be using the same HTML file as we did for the float layout exercise and you can see the page before an CSS has been applied by clicking here.")); main.append(addParagraph("Although we are using the same HTML for all three of our project layouts, you might recall that for the float layout, we changed the order of the HTML elements. This was to allow the navigation menu to come first. For this exercise, as well as the grid layout, we are using the file without these changes so the main content will be first and this is better for users with screen readers.")); main.append(addParagraph("We want everything in the page to be included in the flex container so we will set our display property with a value of flex on the outer container - which has the class container. We will also add a flex-wrap property and set it to wrap.")); main.append(addImageWithCaption("./images/flex-exercise1.jpg", "Flex Exercise - the outer container is now a flex element.")); main.append(addParagraph("The result is that all of the elements now align themselves along the horizontal axis and we can see that since we haven't added any other flex properties, they are displayed in order so we have the main content before the nav bar. If you recall from the float exercise, ultimately we are trying to produce what is essentially a two-dimensional layout rather than have everything on the same line. In particular. both the header and footer should each be on a line by themselves and should scan the entire width of the container. It might be worth noting here that each of the flex items has a semantic HTML tag so we have <header>, <main>, <nav>, <aside> and <footer> which is useful in that it tells us the purpose of each element but it also gives us a method of styling each element individually.")); main.append(addParagraph("For both the header and footer, we will give them a width of 100% by adding a flex property.")); main.append(addSyntax("flex: 1 0 100%;")); main.append(addParagraph("I'm not really sure if either the flex-grow or flex-shrink properties will have any effect here since the flex-basis is 100% so this may be equivalent to adding a flex-basis property with 100% as the value. It does make it a little clearer that the intent is for the element to always grow to fill the container and to never shrink to below that full width. In any case, the layout now looks like this.")); main.append(addImageWithCaption("./images/flex-exercise2.jpg", "Flex Exercise - the header and footer now both span the entire width of the container.")); main.append(addParagraph("Now we want to look at the order of the elements, in particular putting the nav bar before the main content. We could do that by giving the nav bar an order property of -1 (remember that by default, the elements all have an order value of 0) so this would mean the nav bat would go before the main content but it would also put it above the header in our layout. This would have worked if we had created a separate flex container nested inside the outer flex container, but it won't give the desired effect.")); main.append(addParagraph("We could give the header an even lower order value but a simpler way and one that makes the code a little easier to be understand is to give each of the five elements an order property and a value from 1 to 5 that reflects its position so someone who reads the code will see that the order of all of the flex items is explicit and deliberate.")); main.append(addImageWithCaption("./images/flex-exercise3.jpg", "Flex Exercise - adding the order property allows us to put the nav bar before the main content in the web page without changing the order of these elements in the HTML.")); main.append(addParagraph("The layout is starting to look a little bit better now but we still need to style the three elements between the header and footer. We want to have them aligned along the horizontal access which is a different approach to the one we took with float. This time, we are thinking about how the layout will look on a large screen but at the same time, styling it in a way that will also make it look good on a smaller screen and the flexible sizing you get with the flex layout makes that possible.")); main.append(addParagraph("We want the nav bars and the sidebar to be the same size and to appear either side of the main content. We want the main content to be larger if there is enough space, but we also want to make sure that the nav bar and sidebar don't shrink too much so that we keep the three column layout when the screen is too small. This is where, in the past, we would design a single column layout suitable for a smartphone and then add media queries to get the layout we want when the screen is large enough to support it - the 'mobile-first' approach.")); main.append(addParagraph("We'll start by looking at the nav bar and we will give it a flex property of")); main.append(addSyntax("flex: 1 0 200px;")); main.append(addParagraph("The flex-basis has been set at 200 pixels because we want it to be at least that. For the same reason, we have a zero for flex-shrink but we do want it to grow if there is more space available so flex-grow is set to 1 and we will use the same values when adding the flex property to the sidebar.")); main.append(addParagraph("For the main content, we will use these settings.")); main.append(addSyntax("flex: 1 1 500px;")); main.append(addParagraph("Again, we want main content to grow when there is more space available and we want it to grow evenly so we are giving flex-grow a value of 1. This means that when there is additional space available, it will be distributed evenly amongst the three elements. We want it to shrink if space is limited so we have a 1 for flex-shrink. Although we want all of the elements to grow evenly, we don't want them all to be the same size so we are giving it a larger value for the flex-basis.")); main.append(addParagraph("Where the width of the container is 900 pixels, we will see a width of 500 pixels for main content and 200 pixels each for both the nav bar and sidebar. For a larger width, the extra space will be distributed evenly across the three elements. Where the width is less than 900 pixels, only the main content will shrink with the other two elements remaining at 200 pixels width. Let's see how that looks with these different scenarios. First of all, this is what it will look like with a width of 900 pixels.")); main.append(addImageWithCaption("./images/flex-exercise4.jpg", "Flex Exercise - container width 900 pixels.")); main.append(addParagraph("As you can see, I have opened up the browser's Web Developer tools and selected the header. Since it spans the whole width of the container, its width is also the containers width. The width of the three elements in that central panel are exactly as specified in their respective flex properties.")); main.append(addImageWithCaption("./images/flex-exercise4.jpg", "Flex Exercise - container width 1880 pixels.")); main.append(addParagraph("There is now more than enough space to contain the elements and also allow them to grow which they have done evenly although the main content should still be larger than the other two panels since it has a larger flex-basis setting. Actually, you could calculate how much space is added to each element given that we know the width of the container and the starting points for the three elements and this works out at a little under 340 pixels. If you select one of these items in the browsers Wed Developer tools, as well as the width of the element, you can also see the amount of width that has been added due to flex-grow.")); main.append(addImageWithCaption("./images/flex-exercise6.jpg", "Flex Exercise - the flex items have grown by 340 pixels.")); main.append(addParagraph("This shows as the flexibility and notice the plus sign which shows that the element has grown. Our final example shows a screen width of 300 pixels.")); main.append(addImageWithCaption("./images/flex-exercise7.jpg", "Flex Exercise - container width 300 pixels.")); main.append(addParagraph("You may expect that with such a small width, the elements will not grow but that is actually not the case. Bear in mind the fact that our flex basis is 200 pixels for the nav bar, 500 pixels for main content and 200 pixels for the sidebar. The only element that can shrink (because of the flex-shrink setting we applied) is main content so the only question is whether it can shrink enough to fit side-by-side with the nav bar and as you can see, it can't so it wraps around to the next line. Similarly, it is going to be too wide to leave 200 pixels for the sidebar so that also wraps around to the next line with the result that you now have a single column layout. As a matter of interest, you can see in the web developer tools that there is a positive value for flexibility on both the nav bar and the sidebar with a negative value.")); main.append(addParagraph("So we are seeing that the nav bar and the sidebar will increase in width if we have, for example, a container width of 300 pixels and this is due to the fact that it is no longer possible to display any two of the three elements on the same line which is why we have the effect of a single column layout. Each of the elements will be 300 pixels wide because that is the space that is available to them with the result that both the nav bar and sidebar will grow to fit the column (remember they have a flex-grow property of 1) while the main content which is too wide for the column will shrink to fit inside it. If we hadn't set its flex-shrink value to 1, we would be seeing some overflow.")); main.append(addParagraph("As a general rule, I think that playing about with these settings is a really good way of learning. As a matter of interest, you can set the flex-shrink value to 0 so that it will not shrink to fit our single column layout and you will see that it does overflow and in fact, based on the scroll bar, you might reasonably estimate that the amount of overflow is a little less than 50% of the width of the element (500 pixels width, 300 pixels available, 200 pixels overflow).")); main.append(addParagraph("So this shows how powerful flexible sizing is. It has provided us with a responsive layout without the need for us to use media queries and the key to doing that lies in two of our flex properties. First is flex-basis which determines how wide an element will be if there is enough space to accommodate it and the second is really two, the flex-grow and flex-shrink properties which allow us to decide what will happen to an element if there is too much or too little space for it.")); main.append(addParagraph("For me, there is a temptation to always set a flex-shrink property of 1 which will mean that the element will always shrink when the width of the container is too small to contain it meaning that items will never overflow.")); main.append(addParagraph("In this example, it seems reasonable to assume this isn't necessary for the nav bar or the sidebar since it is not really likely that you will ever encounter a screen width of less than 100 pixels which, ironically, you will never see on a very small screen like a phone where apps typically take over the whole screen. Having said that, I don't really see that there is a downside to setting this value to 1.")); main.append(addParagraph("Ultimately, I guess that this is as much a philosophical as it is a technical question but to be pragmatic about it, whatever design choices you make, a key principle is that you really do need to test it on a range of screen sizes from the smallest to the largest.")); main.append(addParagraph("One other point that might be worth mentioning here is that in all of these examples, nav bar, main content and sidebar all of have the same height and this is one of the benefits of flexible sizing and something we had problems with in the float exercise.")); main.append(addParagraph("In the float exercise, we also centred our content by setting a maximum width of 80% for the flex container and we also set a value of auto for the left and right sides. This means that if there is spare room, it will be used to grow the elements in accordance with the flex-grow settings but the total width will always be 80% of the actual container width. We didn't do a similar thing in the flex exercise but we will look at that next.")); main.append(addHeader("Exercise: Build a Layout with Flexbox")); main.append(addParagraph("The aim here is to add a max-width property to our entire layout so that as in the float exercise, it doesn't span the entire page but is aligned centrally. As this is a flex exercise, we want to achieve this using flex properties where possible. Flexbox does allow us to align items and justify the content but if we add a property to the outer container (remember, that's the element with class container) it would apply that to the elements inside it but not to the container itself. Essentially, when we apply the property to a container, it will align the elements inside it rather than the container itself. We don't want to do that, we want this to apply to the container so we need to add this to its container.")); main.append(addParagraph("In this case, the container is an element that contains all of the other elements but it is inside the HTML body element so we will need to convert body into a flex container.")); main.append(addParagraph("Don't forget, we only want to centre the layout for a large screen, we still want it to use the whole width on a smaller screen, especially something as small as a phone. So, we will still need to set a maximum width property to the container and we will set that to 1000 pixels.")); main.append(addParagraph("We will add a display property with a value of flex to the body element and then a justify-content property with a value of flex. That completes the exercise and you can see the finished version by clicking here.")); main.append(addParagraph("I will also check this at different screen sizes so if we reduce the container width to around 1000 pixels, we should see that the layout spans the entire container width.")); main.append(addImageWithCaption("./images/flex-exercise8.jpg", "Flex Exercise - the layout spans the entire container width if that width is 1000 pixels or less.")); main.append(addParagraph("Actually, this is slightly less than 1000 pixels but the content is no longer being centred - in other words, there is no unused width on either side. If we reduce the width to 300 pixels we should still see our responsive design.")); main.append(addImageWithCaption("./images/flex-exercise9.jpg", "Flex Exercise - we also see on a smaller screen that the layout spans the entire width of its container. So by setting the maximum width of the layout to 1000 pixels, we are ensuring that it won't fill the entire container and the justify-content property ensures that it is centred, but only when the screen width is large enough for that to make sense. If it is less than 1000 pixels, and this is particularly important for a very small screen, we don't see any effect. Strictly speaking, the content is still being centred but since it fills the entire container width, that doesn't have any effect.")); addSidebar("webdev");