You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

207 lines
51 KiB

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';
import { local_menu_2 } from '/scripts/webdev.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 Essential Training"));
heading.append(addParagraph("Christina Truong - LinkedIn Learning - October 2019"));
heading.append(addParagraph("Chapter 6 - Layouts: Flexbox and Grid"));
main.append(addSubHeader("Introduction to Grid and Flexbox"));
main.append(addParagraph("The main difference between flexbox and grid is that flexbox is used to arrange elements along a single axis, horizontal or vertical and so is a one-dimensional layout tool. By comparison, grid can be used to create a layout with both rows and columns so it is a two-dimensional layout tool."));
main.append(addParagraph("Both are complete modules with their own sets of properties so they are more complex than either float or position and both are currently (as of 16 August 2021) candidate drafts and both are supported by all the major browsers."));
main.append(addParagraph("Although we are going to be looking at both of these in this course, it would still be worth checking out some of the other LI courses on layouts and at least one of these can be accessed via the <a href=\"/webdevelopment/webdevelopmentlinks/webdevelopmentlinks.html\">Web Development Links</a> page."));
main.append(addSubHeader("Introduction to Flexbox"));
main.append(addParagraph("We will start out by looking at some of the terminology associated with flexbox."));
main.append(addInsetBulletList(["flex-container - this is the parent element, so elements are arrange within a flex-container.", "flex-item - these are the elements inside the flexbox container that are being laid-out.", "main-axis - this is the axis along which the elements can be arranged and it can be horizontal or vertical, horizontal is the default but this can be changed to vertical using the direction property.", "cross-axis - this is the axis that runs perpendicular to the main axis.", "main-start, main-end - the start and end points of the main axis." , "cross-start, cross-end - the start and end points of the cross axis."]));
main.append(addParagraph("There is a fairly comprehensive guide called, appropriately enough, <a href=\"https://css-tricks.com/snippets/css/a-guide-to-flexbox/\">A Complete Guide to Flexbox</a> on css-tricks.com and this is by Chris Coyier. The guide was first posted in 2013, but like everything else I have seen on css-tricks, it is kept very much up to date with the last update as I write this being two weeks ago (2nd August 2021)."));
main.append(addParagraph("To set up a flex layout, we will define a flex container and then add some flex-items to it, like this."));
main.append(addInsetCodeListing(["&lt;div class=\"container\"&gt;", " &lt;div&gt;flex item&lt;/div&gt;", " &lt;div&gt;flex item&lt;/div&gt;", " &lt;div&gt;flex item&lt;/div&gt;", " &lt;div&gt;flex item&lt;/div&gt;", "&lt;/div&gt;"]));
main.append(addParagraph("Bear in mind that whilst we might call these flex-items or we might have called the container, flex-container, this is still just standard HTML and none of this defines flex elements. We do that in the CSS using the display property. You may recall that previously, we used the display property with values such as block or inline-block to define how elements would fit into the normal flow. With flex, there are two possible values."));
main.append(addInsetCodeListing([".container {", " display: flex;", "}"]));
main.append(addParagraph("or"));
main.append(addInsetCodeListing([".container {", " display: inline-flex;", "}"]));
main.append(addParagraph("In both cases, the flex items (and remember, that means any elements inside a flex-container) are displayed in a row and their size is determined by the content. This applies which ever of the two values you give to the display property. When the value is flex, regardless of the size of the flex elements, the flex-container will expand to fill the width of its container. When the value is inline-flex, the flex container will only be as wide as its content and will display inline to other flex-containers. These are defaults and you can, of course, specify a height and width. Note that the height of each of the containers within a flex container is determined by the height of the tallest element which is a really useful feature if you want a layout with different columns all having the same height. So that can give you a nice and simple columnar layout."));
main.append(addSubHeader("Flexbox: Orientation and Ordering"));
main.append(addParagraph("The direction of flow in a flex-container is set with the flex-direction property, and this has four possible values."));
main.append(addInsetBulletList(["row - this is the default option and it follows the normal flow, depending on the language you are working with. If this is a left to right language, like English, the flow will be left-to right.", "row-reverse - this reverses the normal flow so, again assuming a left-to-right language, the flow will be left to right.", "column - flow is from top to bottom.", "column-reverse -flow is from bottom to top."]));
main.append(addParagraph("Where main-start and main end is will depend on the flow so for row, main-start will normally be the left-side of the container and flow-end will be the right side. For flow-reverse, main-start will normally be the right side of the container and flow-end will be the left side."));
main.append(addParagraph("This assumes a left-to-right language and these would be reversed for a right-to-left language."));
main.append(addParagraph("If you actually do need to reverse the order of the elements within a flex-container, using the flex-direction property may not be the best way to do that. It doesn't change the HTML so you may find that it is actually better to change the order in your code. The flex-direction property only changes the order in which the elements appear on the screen, it doesn't change the HTML so it is generally better to have the correct order in your HTML than to use flex properties to change it."));
main.append(addParagraph("The flex-wrap property determines what happens if the elements are larger (in total) than the container and the default value is nowrap. This means that if the elements are too big to fit inside the container, they will shrink so that they can fit. If the elements are smaller than the container, you will just have some unused space."));
main.append(addParagraph("If we use a value of wrap for flex-wrap, rather than elements shrinking to fit inside the container, they will wrap onto the next line. We can also use a value of wrap-reverse which is similar but if necessary, elements will wrap on to the previous line. Note that in this case, the cross start and end are reversed but not the main start and end so any given line, the elements are not reversed. The wrap-reverse value is only reversing the flow of the wrap. For instance, let's say that you have four elements in a container labelled, in order, 1, 2, 3 and 4. If they all fit in a single line, the order will be 1, 2, 3 and 4. Now, let's assume that the container is only wide enough to hold the first three elements. With flex-wrap: wrap, the order will then be 1, 2 and 3 with 4 dropping down to the next line under the 1. With flex-wrap: wrap-reverse, 4 will be on the first line and we will have the line with 1, 2 and 3 (in that order) below it. So the order on the main axis is not changed."));
main.append(addParagraph("There is a shorthand version allowing you to set values for both the direction and the wrap with a single declaration using the flex-flow property. As an example, lets say that we have the following snippet of CSS."));
main.append(addInsetCodeListing([".some_container {", " flex-direction: column;", " flex-wrap: wrap;", "}"]));
main.append(addParagraph("The shorthand version of this would be"));
main.append(addInsetCodeListing([".some-container {", " flex-flow: column wrap;", "}"]));
main.append(addSubHeader("Flexible Sizing"));
main.append(addParagraph("When it comes to setting the size of flex-items, there are three properties that you may find useful and a shorthand notation to combine all three. These properties are"));
main.append(addInsetBulletList(["flex-grow - this is a Boolean value with a default value of 0. If set (that is, if we give it a value of 1), the flex-items will grow to fill the container if there is unused room and they will all grow by the same amount.", "flex-shrink - this is also a Boolean value but the default value is 1. If set, the elements will shrink by the same amount in order to fit the container if the container is not large enough to hold all of the elements.", "flex-basis - this sets the initial size for the flex items. The size can be expressed as link values, percentages or keywords. The default value is auto meaning that the browser will calculate the sizes."]));
main.append(addParagraph("The order in which these are listed is the same as the order used in the shorthand. So, let's assume that we want the flex-items to expand to fill the container if necessary but not to shrink and to have an initial value of auto. The CSS for this is"));
main.append(addInsetCodeListing([".some-container {", " flex: 1 0 auto;", "}"]));
main.append(addParagraph("If we want the flex-items to not expand but to shrink if necessary and to have an initial size set at 100 pixels, this becomes"));
main.append(addInsetCodeListing([".some-container {", " flex: 0 1 100px;", "}"]));
main.append(addParagraph("A couple of notes on this, if you set flex-shrink to 0, there are two possibilities. If you have set a flex-wrap property to wrap or wrap-reverse, then the contents of your container will wrap accordingly. Otherwise, the contents of the container will overflow and whether this means that they will overflow on to another line or they will overflow onto the end of the same line depends on what space is available, taking into account the surrounding elements."));
main.append(addParagraph("Setting the size, you probably want to be thinking about what the ideal size of the elements is, taking into account the size of the container. For instance, if the container has a width of 400 pixels and you have 4 elements, maybe you want to give them an initial size of 100 pixels and you can then decide if they should shrink or grow, depending on the actual size of the container as well as other factors such as margins."));
main.append(addParagraph("It is important to remember that the sizing properties must be added to the flex-items rather than the flex-container, so if you want all of the elements in the container to have the same settings, you would want to create a selector for them all such as a class that you can use to style them all with one declaration. If you don't want them all to be the same size, you can do that. AS an example, let's say that you have 4 flex-items in a container and you want one to be on the top with the others being arranged underneath it. The HTML for that might look something like"));
main.append(addInsetCodeListing(["&lt;div class=\"flex-container\"&gt;", " &lt;div class=\"flex-item-first\"&gt;1&lt;/div&gt;", " &lt;div class=\"flex-item\"&gt;2&lt;/div&gt;", " &lt;div class=\"flex-item\"&gt;3&lt;/div&gt;", " &lt;div class=\"flex-item\"&gt;4&lt;/div&gt;", "&lt;/div&gt;"]));
main.append(addParagraph("So, in this example, the first item is a special case and has a different class-name to reflect this, it allows us to style the element in a way that is different from the others."));
main.append(addInsetCodeListing([".flex-container {", " display: flex;", "}", "", ".flex-item-first {", " flex: 0 0 100%;", "}", "", "flex-item {", " flex: 1 1 auto;", "}"]));
main.append(addParagraph("The first item will span the width of the container and will neither grow nor shrink. The other three items will sit below it and will be sized to fit the width of the container and this includes expanding or shrinking to fit as required."));
main.append(addParagraph("This covers the basics of the flex-property, but there is quite a bit more to it. Once you have mastered these basics, you will probably want to delve into the property in more detail and the online documentation can be found in the <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/flex\">MDN Web Docs</a>."));
main.append(addSubHeader("Flexbox Exercise"));
main.append(addParagraph("This is based on an exercise on <a href=\"https://codepen.io/christinatruong/pen/qwgjJo?editors=1100\">codepen.io</a>. The HTML for this is"));
main.append(addInsetCodeListing(["&lt;div class=\"flex-container\"&gt;", " &lt;div class=\"flex-item\"&gt;1&lt;/div&gt;", "&lt;div class=\"flex-item\"&gt;2&lt;/div&gt;", " &lt;div class=\"flex-item\"&gt;3&lt;/div&gt;", "&lt;div class=\"flex-item\"&gt;4&lt;", " &lt;p&gt;Short block of text&lt;/p&gt;", " &lt;/div&gt;", " &lt;div class=\"flex-item\"&gt;5&lt;", " &lt;p&gt;Longer block of text. Boxes. More boxes. Even more boxes!!&lt;/p&gt;", " &lt;/div&gt;", "&lt;/div&gt;"]));
main.append(addParagraph("As a starting point, the CSS for this is"));
main.append(addInsetCodeListing([".flex-container {", " background: lightblue;", " margin-bottom: 10px;", "}", ".flex-item {", " box-sizing: border-box;", " border: 1px solid midnightblue;", " padding: 10px;", "}"]));
main.append(addParagraph("We gave a &lt;div&gt; container that contains 5 other &lt;div&gt; containers. Respectively, these will be our flex container, but to start with, we aren't using any flexbox properties. As a result, the elements within the container are displayed in block mode so they fill the width of the container and are stacked vertically in sequence. Each of the child elements contains a number and the last two contain a paragraph with a little bit of text. The HTML can be seen <a href=\"samples/flexbox1.html\" target=\"_blank\">here</a>."));
main.append(addParagraph("If we add a property to the parent div to make it a flex container, we can see an immediate change."));
main.append(addInsetCodeListing([".flex-container {", " display: flex;", "}"]));
main.append(addParagraph("This is shown in <a href=\"samples/flexbox2.html\" target=\"_blank\">flexbox2.html</a>. Note that the parent container still spans the width of its container (which is body, so it is filling the width of the viewport). The child items are now flex-items because of the display property on the parent and the size of each of these containers is determined by the content. Since they don't fill the parent container, there is some additional unused space to the right."));
main.append(addParagraph("Next, we will add a width property to the parent container and we will give it a value of 500 pixels. You can see the result in <a href=\"samples/flexbox3.html\" target=\"_blank\">flexbox3.html</a>. Notice that the child items don't fit inside this container width and as a result, each child element has been shrunk to fit and the text in the paragraphs is wrapping down to the next line."));
main.append(addParagraph("We now want to size these child elements using the flex property, and remember, this must be applied directly to the child elements. Since the overall size of the container is 500 pixels, we will specify a width of 100 pixels for the flex-items and set it to grow or shrink as required."));
main.append(addInsetCodeListing([".flex-item {", " flex: 1 1 100px;", "}"]));
main.append(addParagraph("The result is shown in <a href=\"samples/flexbox4.html\" target=\"_blank\">flexbox4.html</a>. Now, the flex-items all have the same width and notice that the height is governed by the last flex-item. It has the most content and so it is the largest of the flex-items and the others all have the same height. So we have, in effect, created a very simple and straightforward layout although it would probably look more like a fully fledged layout if we added a header and a footer, but I will come back to that."));
main.append(addParagraph("For now, let's increase the width of the container to 700 pixels and see the effect this has in <a href=\"samples/flexbox5.html\" target=\"_blank\">flexbox5.html</a>. Bear in mind that the value we gave to flex means that the flex-items will grow or shrink to fill the container if needed and since the container is now 100 pixels greater than the total width of the five flex-items (500 pixels), the flex-items will grow to 140 pixels width each even though we specified a width of 100 pixels."));
main.append(addParagraph("I'll change the first value in the flex property to 0 and this will prevent the flex items from growing to fill the extra space in the container. We can see this in <a href=\"samples/flexbox6.html\" target=\"_blank\">flexbox6.html</a>, where we have some unused space to the right end of the container."));
main.append(addParagraph("If I now change the size of the container to 400 pixels, the flex-items will now shrink to fit inside the container as we can see in <a href=\"samples/flexbox7.html\" target=\"_blank\">flexbox7.html</a>."));
main.append(addParagraph("However, if we change this again so that the flex-items don't shrink (setting the second value in flex to zero), we see that the flex items are now 100 pixels wide and are, in total, larger than the container. This can be seen in <a href=\"samples/flexbox8.html\" target=\"_blank\">flexbox8.html</a> where the blue colour (which is the background colour for the container) can only be seen in the first four elements and the fifth was effectively been pushed outside of that container."));
main.append(addParagraph("All of the versions of the flex demo page we have seen so far have displayed the child elements on a single line. If we add a flex-wrap property to the container"));
main.append(addInsetCodeListing([".flex-container {", " flex-wrap: wrap;", "}"]));
main.append(addParagraph("the last flex-item wraps over to the next line and the container expands to the appropriate height to contain a second row - see <a href=\"samples/flexbox9.html\" target=\"_blank\">flexbox9.html</a>. The width of the child-items is still 100 pixels, and that includes the item in the second row."));
main.append(addParagraph("As a final change, we want to set the grow property of the child elements to 1, as a reminder that is"));
main.append(addInsetCodeListing([".flex-item{", " flex : <strong>1</strong> 0 100px;", "}"]));
main.append(addParagraph("This doesn't change the items in the first row since they fit neatly inside the first row of the container, but now the item on the second row grows to fill the width of the container and we can see that in <a href=\"samples/flexbox10.html\" target=\"_blank\">flexbox10.html</a>."));
main.append(addParagraph("I mentioned earlier, that this simple layout can be made to look more like a page layout. In <a href=\"samples/flexbox11.html\" target=\"_blank\">flexbox11.html</a>, I have removed the flex-wrap property from the container so that all the flex-items occupy the same row and I have set the flex property to ensure they will grow or shrink as required to fit inside the container. This gives you a basic layout, although perhaps with too many columns in the main part, but obviously it could quite easily be adapted to form the basis of a reasonably well laid out web page, albeit very basic!"));
main.append(addSubHeader("Flexbox: Alignment"));
main.append(addParagraph("We've used flexbox to align items in a horizontal row and this was demonstrated in the sample HTML pages, but this was fairly easy to do before flexbox. Getting elements to align horizontally, however, has traditionally been more difficult, requiring some hacks. For example, if you wanted to have an element displayed in the centre of the page, centering it along the horizontal axis has always been quite easy to do, but centering it along the vertical axis isn't so easy to do - until we get to flexbox. Flexbox introduces two properties to help with this."));
main.append(addInsetBulletList(["justify-content - this aligns items along the main axis.", "align-items - this aligns items along the cross axis."]));
main.append(addParagraph("As well as aligning items, these properties can also be used to distribute space around items. There is a handy demo in the online <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content\">MDN Web Docs</a> which should make things clearer. This is looking at the justify-content property. On this page, we have a little demo 'page' containing three elements labelled one, two and three. To the right of these is a list of possible values for the justify-content and if you select one, that value will be applied to the three boxes. These values are"));
main.append(addInsetBulletList(["start - items are packed from the start", "center - items are packed around the centre", "end - items are packed from the end", "space-between - spaces the items so that they are pushed to the edges with space in between them", "space-around - spaces the items so that there is an even amount of space around them - sort of - essentially, you can think of this as each item having the same amount of margin all around but the margins don't overlap so the spaces between items will be larger than the spaces between the items and the edges", "space-evenly - spaces the items so that there is an even amount of space around them - in this case, if we make a similar analogy as we did for space-around - this time the margins does overlap so the gap between items is the same as the gap between items and the nearest edge"]));
main.append(addParagraph("There are a lot of other possible values and these are listed in the MDN Web Docs. In contrast to the flex-grow and flex-shrink properties, these alignment styles are not changing the size of the flex-items, just the space around them."));
main.append(addParagraph("There is a similar demo for the align-items property and this is also in the <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/align-items\">MDN Web Docs</a>. The demo is pretty similar to the one for justify-content, so we have three similar boxes. It should be noted that in both of these demos, the main axis is horizontal and the cross axis is vertical."));
main.append(addParagraph("Again, there are a lot of possible values, but we'll just look at a few of them here."));
main.append(addInsetBulletList(["start - items are packed from the start", "center - items are packed around the centre", "end - items are packed from the end", "stretch - items are 'stretched' to fill the area - actually, what is happening is that items are packed from both the start and the end."]));
main.append(addSubHeader("Project: Flexbox Alignment"));
main.append(addParagraph("For this part of the project, we are going to style the footer which contains the contact details. These are currently in a list so we will start by removing the list styles. To do that, we want to be able to apply rules to the whole list so we will add a class, in the HTML to the &lt;ul&gt; container. We will call that contact-list."));
main.append(addParagraph("In the CSS, we will remove the styles by adding these rules in the contact info section of the stylesheet, below footer."));
main.append(addInsetCodeListing([".contact-list {", " list-style-type: none;", " padding: 0;", "}"]));
main.append(addParagraph("The first rule removes the styling and the second removes the default padding. This removes the bullet points from the list although we do otherwise have the various items looking a little list-like! We want to introduce some flexbox properties so we will also add the flex property to contact-list."));
main.append(addInsetCodeListing([".contact-list {", " display: flex;", "}"]));
main.append(addParagraph("This makes all of the elements, which remember are mainly &lt;li&gt; elements, flex items and they are aligned along the main axis (the horizontal axis) so they are arranged in a row. We want to use the justify-content property to space the items out and one value which would do this is space-between."));
main.append(addInsetCodeListing([".contact-list {", " justify-content: space-between;", "}"]));
main.append(addParagraph("This spaces the elements out evenly, but we actually want them to be more centralised so another option would be center."));
main.append(addInsetCodeListing([".contact-list {", " justify-content: center;", "}"]));
main.append(addParagraph("This does centre the elements, but there is no space between them but we will keep this property and use another method to provide the spacing. That could be margin or padding and we will choose the padding option because this gives us better accessibility. We want this to be applied to all of the links in the contact info section, so we will use a descendant selector that only targets links and only these links."));
main.append(addInsetCodeListing([".contact-list a {", " padding: 15px;", " display: inline-block;", "}"]));
main.append(addParagraph("So, we have added 15 pixels all round for the padding, but since links are inline elements, they will ignore the top and bottom padding. To fix this, we have added a display property with a value of inline-block. This gives us an overall effect of centralised elements but with some surrounding space. The reason we chose the padding option is because this means that the area around each element will form part of the link. This means that if a user clicks slightly off the link, it will still activate provided they are not off by more than the size of the padding. If we used margins instead, this space would not be clickable. This is probably not going to make much, if any difference on a PC screen, but it may make it easier for a user with a small touchscreen (a tablet or even a smartphone) to use the links. If we use margins instead of padding, the area around the link would not be clickable, so the margins are making that clickable area a little bigger."));
main.append(addParagraph("We also want to centre the heading in the contact info section so that it lines up with the links below it. We could use flex properties to do that, but as it is a single element, it is easier just to add a property to that element."));
main.append(addParagraph("The heading is an &lt;h2&gt; element and that is a block element so we can apply the text-align property to the element itself, or to the container so that it is inherited by other elements inside that container. We will add it to the container, which is footer, and give it a value of center."));
main.append(addInsetCodeListing(["footer {", " text-align: center;", "}"]));
main.append(addParagraph("This won't affect the other elements in the contact info section which we have centred using flex properties, but it does mean that if we add an additional element, such as a copyright notice, it will automatically be centred so we won't have to change the CSS to achieve that. Essentially, we are centering the non-flex elements so this means the heading and any other non-flex elements that are added later."));
main.append(addParagraph("To give the page a more symmetrical feel, we want to also centre the content in the header section and we will use a similar method - that is, we will add the text-align property to the container, which in this case is header."));
main.append(addInsetCodeListing(["header {", " text-align: center;", "}"]));
main.append(addParagraph("Since we don't have any flex items in the header section, the text-align property is inherited by the block elements (that is, the paragraphs) in that section so we have all of the text now being centred. The revised resume can be seen <a href=\"resumes/resume13.html\">here</a>."));
main.append(addParagraph("The flex properties can be really useful for centering content, amongst other things, but it is important to remember that there will sometimes be an easier way to accomplish the same thing, so one of the important skills for a web developer is knowing which techniques work best in a given situation."));
main.append(addParagraph("Just a slight digression here, but I also created an experimental page to try out the flex techniques. This is a simple box element (400x400 pixels), centred in a yellow background. I won't describe the techniques used in detail here, but essentially, the page uses a flex container with the box being a child of that element (so it is a flex-item) and it uses both the justify-content and align-items properties to centre the box in the page. This can be viewed <a href=\"samples/flexbox12.html\">here</a>."));
main.append(addSubHeader("Introduction to CSS Grid"));
main.append(addParagraph("There are some similarities between grid and flexbox. As with flexbox, the parent element is the grid container and any elements within that container become block elements. Note that only direct child elements of the grid container are grid items, those grid may contain elements as well, but these would not be grid-items."));
main.append(addParagraph("We can designate a container as a grid container with display and one of two values."));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", "}"]));
main.append(addParagraph("or"));
main.append(addInsetCodeListing([".grid-container {", " display: grid-inline;", "}"]));
main.append(addParagraph("Either option would display its child elements as grid-items in a single column. The difference is that within that column, a value of grid means that the grid-items are displayed as block items where as with grid-inline, the elements are displayed as inline element."));
main.append(addParagraph("In the following example, we have a &lt;div&gt; element which will act as a grid container and this contains 5 child &lt;div&gt; elements. The CSS for these is"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", "}", "", ".grid-container div {", " background: lightblue;", " text-align: center;", " font-size: 36px;", " padding: 36px;", " border: solid black 1px;", "}"]));
main.append(addParagraph("The page can be viewed <a href=\"samples/grid1.html\" target=\"_blank\">here</a>. To make the positioning of the elements clearer, I have added a light blue background, a border and some padding. Since these are block-elements, they span the width of the container which is, in this example, the viewport."));
main.append(addParagraph("In the second example, which can be seen <a href=\"samples/grid2.html\" target=\"_blank\">here</a>, I have added a display property to the grid items."));
main.append(addInsetCodeListing([".grid-container div {", " display: inline;", "}"]));
main.append(addParagraph("In this example, the grid items are sized dependent on their contents, since they are being displayed as inline rather than block elements."));
main.append(addParagraph("The grid itself will display as a block element so it will stack up along with other block elements when we use a value of grid for the display property. If we use a value of grid-inline, it will display as an inline element, so it will derive its width from its content (the grid items)."));
main.append(addParagraph("Grid lines are the lines that separate the rows and the columns in a 2D grid and they are also used to determine the positions of items within that grid. They can be referenced using a numerical index which counts from the top for rows and the left for columns and starts at 1 (see the image below)."));
main.append(addImageWithCaption("./images/gridlinesindex.png", "Using numerical index values to reference the grid lines"));
main.append(addParagraph("We can also reference grid lines from the bottom or right edges by specifying a negative number so where 1 references the top line, -1 would reference the bottom line and so on."));
main.append(addParagraph("In addition to using a numerical index value, we can also use custom names for the grid lines."));
main.append(addParagraph("Let's assume we have a grid with 3 horizontal lines and 5 vertical, like the grids shown in the image. This describes a 2 x 4 grid, that is 2 rows of 4 columns. Each area spanning a single row and a single column is referred to as a grid cell. The space between the grid lines is referred to as the grid track so each column and each row is a grid track. If there is a space (or margin) between the grid tracks, this is referred to as the grid gutter. Defining these grid tracks and therefore defining the numbers of rows and columns in the grid can be done either explicitly or implicitly and we will see how to do that shortly."));
main.append(addSubHeader("The Explicit Grid"));
main.append(addParagraph("To define an explicit grid, we specify the number of columns with the grid-template-columns property and the number of rows with the grid-template-rows property. For each of these, the number of columns or rows are expressed as a list separated by spaces specifying the width of each row or column. These can be expressed using a variety of units, including a couple that were introduced with the grid module. Let's say that we have a container with a class of grid-container and we want to create an explicit grid with 3 columns, each having a width of 100 pixels and 2 rows, each having a height of 100 pixels. The CSS would look like this."));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 100px 100px 100px;", " grid-template-rows: 100px 100px;", "}"]));
main.append(addParagraph("If you want to specify the column and row sizes as a proportion of the available space using the fr unit. This doesn't represent a specific size or even fraction of the available size. For example, let's say that we wanted to specify the same explicit grid as shown above, but we want the rows and columns to use all the available space evenly, so they are all the same size. We would do that with"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 1fr 1fr 1fr;", " grid-template-rows: 1fr 1fr;", "}"]));
main.append(addParagraph("For the columns, we have three with a size of 1fr each so each fr represents a third of the available width. For the rows, we have two with a size of 1fr each so each fr represents one half of the available height."));
main.append(addParagraph("If we wanted to specify the grid so that the first row and first column were twice as large as the others, we could do that with"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 2fr 1fr 1fr;", " grid-template-rows: 2fr 1fr;", "}"]));
main.append(addParagraph("One really nice advantage we have with this unit is that since it is specifying each size as a proportion of the available size, it will fit neatly into the available space. But also, if the available size changes, for example if you switch from landscape to portrait on a mobile device, the available space changes and the grid resizes to accommodate this so it is responsive."));
main.append(addParagraph("To demonstrate this, I have created a page called <a href=\"samples/grid3.html\" target=\"_blank\">grid3.html</a> and you can see that the rows and columns have the specified proportions. We can resize the browser and the grid changes size as required."));
main.append(addParagraph("We can use a shorthand to specify multiple rows or columns with the same size and this is repeat followed by the number of tracks and then the size (in parentheses). For example, if we wanted to specify x rows or columns with a size of y fr, this would be"));
main.append(addSyntax("repeat (x, yfr)"));
main.append(addParagraph("To give a concrete example, we can respecify the grid for grid3.html like this"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 2fr repeat(2, 1fr);", " grid-template-rows: 2fr 1fr;", "}"]));
main.append(addParagraph("We can mix units so let's say we want to specify the first column with a width of 200px and we want the remaining columns to share the remaining space and we just want the rows to share the available space. This can be specified as"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 200px repeat(2, 1fr);", " grid-template-rows: repeat(2, 1fr);", "}"]));
main.append(addParagraph("We can see these changes in <a href=\"samples/grid4.html\" target=\"_blank\">grid4.html</a>. If you look at this full size on a PC monitor, you will probably see that the first column is much narrower than the other columns. However, if you resize the window, the first column doesn't change size, but the other columns do and if the container becomes small enough, you will get to a point where the first column is larger than the other columns."));
main.append(addParagraph("We can use the gap property to add gutters between the columns and rows and there are several ways to specify the size of the gutters. For example, let's say we want a gutter size of 10 pixels between both the rows and the columns, we can use a shorthand like this."));
main.append(addSyntax("gap: 10px;"));
main.append(addParagraph("If we want to specify the rows and columns separately, we provide two values with the first specifying the row gutter size and the second specifying the column gutter size."));
main.append(addSyntax("gap: 10px 10px;"));
main.append(addParagraph("Of course, these don't have to be the same size so we could have, for instance, a gap of 10 pixels between the rows and 20 pixels between the columns."));
main.append(addSyntax("gap: 10px 20px;"));
main.append(addParagraph("We can use pretty much any distance unit for specifying the gap, so this might be pixels as shown above or a percentage. We can also use the calc function to specify the size and this will allow us to specify it as a percentage value with a pixel offset, such as 10% plus 20 pixels, like this."));
main.append(addSyntax("gap: calc(10% + 20px);"));
main.append(addParagraph("However, we can't use units such as fr."));
main.append(addParagraph("We can also specify the gap size for just rows or columns by using either the row-gap or the row-column properties. If we wanted to, say, specify a gap size of 10 pixels for rows and 20 pixels for columns, we could do it like this"));
main.append(addInsetCodeListing(["row-gap: 10px;", "column-gap: 20px;"]));
main.append(addParagraph("It is interesting to note that the original CSS specification for grid layout were more explicit in specifying these as grid properties so the shorthand version was grid-gap rather than gap and similarly, the longhand versions were grid-row-gap and grid-column-gap rather than row-gap and column-gap. These properties can now be used to specify gap sizes for flexbox containers as well. When the course was recorded, Firefox was the only browser supporting gap for flexbox. As of today, coverage is much better and most of the major browsers, including Chrome, Safari and even Edge do now support it."));
main.append(addParagraph("There is a <a href=\"https://codepen.io/christinatruong/pen/YbRWzy?editors=1100\">codepen</a> demo for a grid layout. Most of the points covered in the demo have been covered here already with one exception which I will look at now."));
main.append(addParagraph("In <a href=\"samples/grid4.html\" target=\"_blank\">grid4.html</a>, we have three columns and two rows and the CSS to define this grid is"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 200px repeat(2, 1fr);", " grid-template-rows: repeat(2, 1fr);", "}"]));
main.append(addParagraph("We have specified 3 column widths and two row heights. If we amend this so that we have only one row height as follows"));
main.append(addInsetCodeListing([".grid-container {", " display: grid;", " grid-template-columns: 200px repeat(2, 1fr);", " grid-template-rows: 100px;", "}"]));
main.append(addParagraph("This gives us the result shown in <a href=\"samples/grid5.html\" target=\"_blank\">grid5.html</a>. It doesn't look very different from grid4.html, but the first row has the specified height of 100 pixels and the second row has a height determined by the content."));
main.append(addParagraph("If you look at the CSS, you may notice that the grid specified is 3 columns by one row, whereas in grid5.html. we are still seeing three rows and two columns. This is because the first row of three columns is an explicit grid. That is, it is the single row of three columns specified by our CSS. The second row in an implicit grid and we will look at that next."));
main.append(addSubHeader("The Implicit Grid"));
main.append(addParagraph("The point of an implicit grid is that you will not always know in advance how many grid items you will need to display. For example, you may have content that is being created dynamically such as search results or comments."));
main.append(addParagraph("With an explicit grid, as we saw in the last example, if the grid has been filled up completely, the grid container will generate additional tracks if there is more content. If you haven't specified any explicit grid, it will just generate tracks as they are needed."));
main.append(addParagraph("If you create an explicit grid, like the one in our example, with a single row of three columns, but you don't have enough content to fill the grid, you will still get the grid of the specified size but some of the grid items will be empty."));
main.append(addParagraph("In this sort of situation, where you don't know what size of grid is going to be required, you can create the whole grid as an implicit grid. Alternatively, if you have a value for the minimum number of grid items, you can use the same method we used earlier to create an explicit grid with additional rows (or columns) being auto-generated as required. For this exercise, I am going to try to create a page that would be useful as a template for this site and that is a page that displays thumbnails of photographs which later will become a photo-gallery. Our starting point is the grid we finished with in grid5.html. As a reminder, that is the grid with 2 rows of 3 columns where the first row is defined by an explicit grid and the second by an implicit grid. We will keep that pattern and I will define a height of 200 pixels for the rows and 320 pixels for the columns."));
main.append(addParagraph("For the implicit rows, they will size themselves based on the content, but I need these to be the same height as the first row so I will specify this using the grid-rows-auto property. The CSS for this page is"));
main.append(addInsetCodeListing(["img {", " align-content: center;", " height: 160px;", "}", "", ".thumbnails {", " display: grid;", " grid-template-columns: repeat(6, 1fr);", " grid-template-rows: 200px;", " grid-rows-auto: 200px;", "}", "", ".thumbnails div {", " background: lightgreen;", " text-align: center;", " font-size: 36px;", " border: solid black 1px;", "}", "", ".thumbnails p {", " text-size: 16px;", "}"]));
main.append(addParagraph("This page can be seen <a href=\"samples/grid6.html\" target=\"_blank\">here</a>. The page is currently displaying 20 thumbnails, so there are three full rows of 6 and 2 on the fourth row. Since only the first row has been declared as an explicit grid, we would still see 6 spaces even if there were fewer than 6 thumbnails but any more going into new rows in the implicit grid. This is why we have the additional three rows. It also means that I can continue to add more thumbnails and the implicit grid will continue to grow as needed."));
main.append(addSubHeader("Grid Placement Properties"));
main.append(addParagraph("In all the examples w have seen so far, grid items are simply elements within the grid, each occupying a single grid cell and their position within the grid is simply determined by the order of the elements in the HTML. However, we also have the option of positioning items in a specific place in the grid and these can occupy more than one cell. To do that, we will need to specify which columns and which rows the item will occupy."));
main.append(addParagraph("For columns, the properties required to specify the columns to be occupied are grid-column-start and grid-column-end. For example, if the item is to occupy columns 3 and 4, we can specify that with"));
main.append(addInsetCodeListing(["grid-column-start: 3;", "grid-column-end: 5;"]));
main.append(addParagraph("Remember that we start counting from 1 rather than 0 and notice that when specifying the end column."));
main.append(addParagraph("We can also use a shorthand of grid-column followed by the two values separated by a /."));
main.append(addSyntax("grid-column: 3 / 5;"));
main.append(addParagraph("The properties for specifying the rows are similar."));
main.append(addInsetCodeListing(["grid-row-start: 1;", "grid-row-end: 3;"]));
main.append(addParagraph("There is also a shorthand version."));
main.append(addSyntax("grid-row: 1 / 3;"));
main.append(addParagraph("To demonstrate this, I have created an additional HTML element with the class caption and this contains one &lt;h1&gt; heading. I have positioned this on the grid as follows."));
main.append(addInsetCodeListing([" .caption {", " align-content: center;", " grid-column: 3 / 5;", " grid-row: 1 / 3", "}"]));
main.append(addParagraph("The result, which can be seen in <a href=\"samples/grid7.html\" target=\"_blank\">grid7.html</a>, is that the text in the caption now occupies the middle two cells in the first two rows."));
main.append(addParagraph("Like flexbox, grid is a new and relatively complex CSS module so it is worth looking at the documentation in the <a href=\"https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Grids\">MDN Web Docs</a>."));
main.append(addSubHeader("Project: Grid Columns and Firefox Grid Inspector"));
main.append(addParagraph("Now that we have seen how easy it is to create a layout with multiple columns using a grid layout, we want to apply this to the employment section of the resume. At the moment, the employment section has a little hedaer area followed by a number of jobs. Each job is in a section and each section contains some job details and a brief summary and each section looks something like this."));
main.append(addInsetCodeListing(["&lt;!-- Job 1 --&gt;", "&lt;section&gt;", " &lt;div&gt;", " &lt;h3&gt;Front-end Developer &amp; Educator&lt;/h3&gt;", " &lt;p&gt;Freelance&lt;/p&gt;", " &lt;p&gt;January 2012-Present&lt;/p&gt;", " &lt;/div&gt;", " &lt;div&gt;", " &lt;p&gt;Provides various front-end related services ranging from development work, speaking engagements, instructor training, workshops, and curriculum development. See more at &lt;a href=\"http://christinatruong.com\"&gt;christinatruong.com&lt;/a&gt;.&lt;/p&gt;", " &lt;/div&gt;", "&lt;/section&gt;"]));
main.append(addParagraph("The aim is to create a two column grid with the job details in the first column and the job summary in the second. Since everything is contained within a &lt;section&gt; element, we will make this our grid container and the two child &lt;div&gt; elements will become the grid items. To allow us to apply the appropriate styles, we will add a class name of job-item to the section and job-details and job-summary to the two divs. We also add the same class names to the other job items."));
main.append(addParagraph("To set up the grid, we will add the display property to our job-item class like this (this will go in the work experience section of our CSS)."));
main.append(addInsetCodeListing([".job-item {", " display: grid;", " grid-template-columns: 1fr 2fr;", "}"]));
main.append(addParagraph("We have also used the grid-template-columns property set up two columns and notice that the second is twice the width of the first. The revised resume can be viewed as <a href=\"resumes/resume14.html\" target=\"_blank\">resume14.html</a>. Notice that we don't need to do anything to the grid items. You might also notice that there is a bit of space between the job details and the job summary, but this was more by chance than anything else. It just so happens that the various elements in each div do happen to fit together quite nicely, but if we had a little bit more text in one of the job details divs, it is possible that this would run right up against the job summary, so we want to add a gap to ensure that there will always be some separation. We can add that wth"));
main.append(addInsetCodeListing([".job-item {", " column-gap: 20px;", "}"]));
main.append(addParagraph("It's worth noting that it is quite common to use fixed sizes like this for the gap because you don't usually want these to be relative to the page or container size, in most cases you will want there to always be a gap of the specified size."));
main.append(addParagraph("Note that I haven't created a new version of the resume for such a small change so the link to <a href=\"resumes/resume14.html\" target=\"_blank\">resume14.html</a> displays a version of the resume with the gap already added."));
main.append(addParagraph("It is also worth noting here that in Firefox Developer Tools, there is a grid inspector which allows you to check out the properties of the grid. This is shown in the image below."));
main.append(addImageWithCaption("./images/firefox-developer-tools.jpg", "Firefox Developer Tools"));
main.append(addParagraph("In the right hand column of the Firefox Develope Tools (see image), each of the grid items has a grid label next to it - note that we created one grid layout for each of the job items. The middle column, you may recall, is our custom CSS so you can see the grid properties there along with the rest of the CSS."));
main.append(addParagraph("In the right column we can see the grid and we can configure that to display any of the grid layouts or all three and we can also see grid lines appearing over the page in the browser, depending on which are selected. There are also some options to select the grid line numbrers, which will add the column and row numbers so this can be helpful for troubleshooting. We can also display are names which we are not using in the resume and extend lines which can make the grid lines a little easier to see, especially when the grid layout isn't occupying the full width of the screen."));
main.append(addParagraph("Christina also mentioned that this is only available in Firefox, but remember that the course was published in 2019. If you use the browser developer tools for Chrome, this actually does have similar features now and I guess that you might also see these in other browsers now."));
main.append(addParagraph("One final point to make since we have been devling into the developer tools, is that like any other CSS, we can alter the properties for the grid in the browser window and see immediately what effect this has. Of course, this doesn't change the CSS file so if the browser is refereshed any changes will be lost unless you physically add them into the CSS files. This can be handy for experimenting with things like the gap or the relative sizes of the columns to see which values work the best and if you find a value that seems to work well, you can go ahead and add it into your CSS."));
addSidebar("webdev");