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.
202 lines
47 KiB
202 lines
47 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 } 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: Advanced Layouts with Grid")); |
|
heading.append(addParagraph("Morten Rand-Hendriksen - LinkedIn Learning - Updated April 1st 2019")); |
|
heading.append(addParagraph("Chapter 1 - Core Principles?")); |
|
|
|
main.append(addHeader("TERMINOLOGY")); |
|
main.append(addParagraph("The exercise files for this course includes a handy pdf file covering grid terminology which will act as a useful reference and you can download a copy of that from <a href='/downloads/CSSGrid-reference.pdf'>here</a>.")); |
|
main.append(addSubHeader("Grid Container")); |
|
main.append(addParagraph("A grid container is simply any element that you designate as the grid container, meaning that the element is being split into different cells in order to produce the layout you want. You will often find, at least in my limited experience, that the grid container is the body in HTML or at least some container within body that contains all of the other HTML which would mean that the whole page is essentially one big grid. However, that doesn't have to be the case and you can, in fact, designate multiple elements on a page as being a grid. You can also nest grids so an element within a grid can also be designated as a grid container.")); |
|
main.append(addParagraph("This is added to the CSS for the container by setting the value of the display property to grid.")); |
|
main.append(addSyntax("display: grid;")); |
|
main.append(addSubHeader("Grid Item")); |
|
main.append(addParagraph("A grid item is any element that is a direct descendant of the grid container but it doesn't include any elements that they might contain. To clarify this, let's say that you have designated body as the grid container with a 3X3 grid and within the body you have 9 div elements, each of which is a grid item. Any elements you add to those divs will not be grid items - at least not relative to the 3X3 grid. You could designate one or more of these divs as a grid container in which case, the direct child elements would be grid items within that sub-grid.")); |
|
main.append(addSubHeader("Grid Cells and Grid Lines")); |
|
main.append(addParagraph("Continuing with the same example we used before, let's say we have a 3X3 grid so that has three columns and 3 rows and if they were all square, this would look a little bit like a noughts and crosses (or tic-tac-toe) board. The squares represent cells in the grid and the grid lines are the lines, it's as simple as that but you should remember that this does include the edges. These are numbered from left to right, starting at 1 and from top to bottom, also starting at 1. Note that this is true if the webpage is written in English because the language is written from left to right. However, in the case of languages (such as Hebrew) that are written from right to left, the numbering for the vertical grid lines would start from the right-hand side.")); |
|
main.append(addParagraph("Grid cells can be referenced using the numbered grid lines but you can also give the lines names and use those names to reference them as we shall soon see.")); |
|
main.append(addSubHeader("Grid Track")); |
|
main.append(addParagraph("You might think of grid tracks being exactly the same as grid lines but where grid lines are the lines between cells (or between the rows and columns), the grid track is the area between the rows and columns. You may notice when you create a grid, there is by default, no space between adjacent cells. You can add space with command like")); |
|
main.append(addSyntax("grid-gap: 1em")); |
|
main.append(addParagraph("or")); |
|
main.append(addSyntax("gap: 1em")); |
|
main.append(addParagraph("This will give you a gap of 1 em between both rows and columns. If you provide 2 different measures such as")); |
|
main.append(addSyntax("gap: 1em 2em")); |
|
main.append(addParagraph("the first applies to the gap between rows and the second to the gap between columns. You can also specify these individually like this")); |
|
main.append(addSyntax("row-gap: 1em")); |
|
main.append(addSyntax("column-gap: 2em")); |
|
main.append(addParagraph("The gap is the grid track so this is only going to be visible when you set a gap value. One other distinction between grid lines and grid gap is that if you define a grid area (which we will see next), there may be lines between the cells in that area but there will not be a gap.")); |
|
main.append(addParagraph("If you have a scenario where there is something behind the visible area of grid cells, for example if you have a div with a background of black, for example or with an image and you define that as a grid where the cells have a background colour of white, you will see the black colour or the image between the cells if you set a value for gap. The exception to that is that you will not see the gap between cells that have been grouped into a grid area.")); |
|
main.append(addSubHeader("Grid Area")); |
|
main.append(addParagraph("A grid area is made up of one or more cells grouped together and you do that using the grid-template-areas property. To give an example, let's say that you have a 3X3 grid and want all the cells on the top row to be grouped together to form your header and all of the cells on the bottom row to be grouped together as the footer. In the middle row, you want the first cell to hold a nav menu and the last cell to hold a sidebar with your main content in the middle, you would add the grid-areas property to you grid container like this.")); |
|
main.append(addInsetCodeListing(["grid-template-area:", " \"header header header\"", " \"nav main sidebar\"", " \"footer footer footer\""])); |
|
main.append(addParagraph("You can also put a full stop in one or more of those areas which would effectively create some white space. Once you have set up the grid areas, you can then assign grid items to that area by adding a rule like this to the css for the appropriate element. For example, if you have a div with class \"heading\", you would add this to .heading in your css.")); |
|
main.append(addSyntax("grid-area: header;")); |
|
main.append(addParagraph("Now, if you add elements either directly with HTML or indirectly via JavaScript to the .heading element, this will be rendered in the area designated as header, the top row in this example.")); |
|
main.append(addHeader("DEFINE A GRID")); |
|
main.append(addParagraph("The exercise files contains a sample HTML page which we will use as our starting point in describing how a grid is defined. The yeo images below show the HTML file in the browser (figure 1) and in the code editor (figure 2) so you can see how this looks when displayed but can also see how the HTML is laid out.")); |
|
main.append(addImageWithCaption("./images/basic_grid.png", "Figure 1 - how our basic grid layout looks in the browser.")); |
|
main.append(addImageWithCaption("./images/bg_code.png", "Figure 2 - the HTML code for our simple demo layout.")); |
|
main.append(addParagraph("There are a couple of questions you might ask here. The first is why I am not showing the CSS and the second is why the 'grid' looks as though it is just a number of elements displayed in order. The answer is the same in both cases and that is, since this is a course in CSS and not HTML, we are not really interested in how the HTML is created except in so far as it is designed to work with a grid. For that reason, we will start with a basic HTML page and gradually add CSS rules to produce the grid layout. At the moment, we haven't added any CSS to create the grid so you will see how it changes as we do that.")); |
|
main.append(addParagraph("Although the 'magic' that is going to create a nice layout for us will happen in the CSS, we need to structure the HTML so that it is possible for us to style it appropriately. To start with, we need to have at least a rough idea of what we want to include in our page and how we want that to be laid out so it's a good idea to look over figure 2. If you think about the DOM or the hierarchy of elements on the page, we have the body at the top level. This has five child elements (excluding the link at the start) and these are a header element, a div element, a main element, an aside element and a footer element and they have all been given class names.")); |
|
main.append(addParagraph("One interesting point to note about this is that the body and these five child elements are not visible content. What I mean is that each of the five elements contains some child elements of their own and these are a pre element, an h1 or h2 element and in some cases, a p element. If you look at figure 1, the pre element show the class name in white with a black background. The header element seems to be either giving some description of the purpose of the element (eg Main content or Footer) or some brief description of the page (CSS Grid or Demo Sandbox for Advanced Layouts with CSS Grid). The paragraph elements, where they appear, give brief descriptions of the purpose of the element (for example, for Main content, This is where the main bulk of the document content would go).")); |
|
main.append(addParagraph("So nothing particularly revolutionary there but the content is incidental and is really there to make this look more like a real web page. The important point here is that the content is not, to give an example, the main element, but rather the main element is a vehicle for some of our content as are the other elements.")); |
|
main.append(addParagraph("Putting this in to Grid terms, the HTML body is our grid container. The child elements are grid items which are also containers and these contain the actual visible content, the parts of the web page we want users to look at or read. A couple of things are worth mentioning here. Each of the elements has been given a different background color which makes it easy to see exactly where they are and how they are positioned in relation to other elements and it will also help to show the grid layout when we start to add the CSS. Since there is no grid defined, at the moment, the elements are simply stacking up in the browser in the same order in which they appear in the HTML.")); |
|
main.append(addParagraph("Quick side note, when write CSS for a page, I tend to use at least two stylesheets. One is used for the layout and the other is used for other styles such as colours, font-sizes and so on. I like this approach because it means I can use the same stylesheet for the layouts in any number of pages and if necessary, use different style sheets to give the pages different colour schemes, for example.")); |
|
main.append(addParagraph("I think it can also make things a little simpler because stylesheets can be long and it's easier to troubleshoot an issue with a layout if there isn't too much detail in the stylesheet. Something I haven't done yet is to maintain a library of stylesheets to cover different types of pages which I think would be very useful so I hopefully will start to do that while I work through this course.")); |
|
main.append(addParagraph("For now, we will get started on our first grid layout. The first step is going to be to define a grid container so we will set the display property on the element we want to act as the grid container. That is the HTML body which, remember, we identified with class site and so .site is the selector and the rule is")); |
|
main.append(addSyntax("display: grid;")); |
|
main.append(addParagraph("If you are following along in an environment where you have a live server and can see the website change as the CSS is added, you will notice that making this change does not change the appearance of the website because although we now have a grid container, we still haven't defined a grid. However, if you open up the Web Development tools in Firefox (or Chrome) and click on the Layout tab, you can then select Grid Overlay. This will superimpose a grid on to the page so you can see the grid lines with numbers, the grid areas and if you have given them names, you will also see these. You can enable or disable some of these options but this gives you a good idea of how the grid looks and can be very useful for troubleshooting.")); |
|
main.append(addImageWithCaption("./images/grid_overlay.png", "Figure 3 - the Grid Overlay displayed in the web developer tools (Firefox).")); |
|
main.append(addParagraph("As you can see in figure 3, the Grid panel shows an outline of the grid and the line numbers are visible in the viewport. Note that the 'Display area names' option is enabled but the grid areas don't have names at this point so you won't see them yet!")); |
|
main.append(addHeader("GRID LINES AND UNITS")); |
|
main.append(addParagraph("At the moment, he have what you might call the default grid - in other words it's the grid you get when you haven't defined a grid! Notice in figure 3 that we can see two vertical grid line numbered 1 and 2. We can't see them all in the image but the horizontal grid lines are numbered from 1 to 6. We can set the number and sizes of the columns with a rule like this.")); |
|
main.append(addSyntax("grid-template-columns: 50% 50%;")); |
|
main.append(addParagraph("This will give us a grid with two columns and they should both be the same size and you can see that in figure 4. This is basically a 2 x 3 grid so we have columns and 3 rows Each of the columns occupies 50% of the total width. As you can see in figure 4, the vertical line numbers now run from 1 to 3 and although we can't see all of the horizontal lines, these are numbered from 1 to 4.")); |
|
main.append(addImageWithCaption("./images/2_columns.png", "Figure 4 - a two column grid layout.")); |
|
main.append(addParagraph("The default width for columns or height for rows is auto so you don't need to specify although you can if you want your CSS to be a little bit more explicit. You may also need to specify it if you need to override an inherited property. Remember that a width or height specified as auto means that the size of the element will be determined either by the size of its content or the size of the content in a nearby element. For example, if you have a row with three columns and a separate element in each of the column and you set its height to auto, the height of the row will be determined by the content that gives you the greatest height. In other words, the height will be set so that it is big enough for all three elements.")); |
|
main.append(addParagraph("That might not be completely clear in our example because all of the rows have roughly the same height but if you want to, you can add more content to one of the grid items. You will see that the row increases it's height to accommodate the additional content but if there is another element in the same row, you will also see that element increasing in size to match.")); |
|
main.append(addParagraph("We can use all of the standard units to specify widths and heights for the grid. These can also be mixed so could, for example, specify the column widths like this.")); |
|
main.append(addSyntax("grid-template-columns: 1px 4em 20% 50vw;")); |
|
main.append(addParagraph("This would give us the grid shown in figure 5.")); |
|
main.append(addImageWithCaption("./images/columns1.png", "Figure 5 - a four column layout using different units for the column widths.")); |
|
main.append(addParagraph("It might not be clear in figure 5, but the columns do not occupy the whole width of the viewport because of the way in which the widths have been specified (that is, they have not all been specified as a proportion of the whole width and even if they were, the total specified widths may well be less than the total available). Compare this to the previous version of the grid where we had two columns and each occupied 50% of the available width which meant that we were occupying 100% of the available width.")); |
|
main.append(addParagraph("With grid, there is another unit available which also allows us to occupy the full width and that is fr. This allocates a fraction of the available width (or height) so for example, if we specify the grid with")); |
|
main.append(addSyntax("grid-template-columns: 1fr 1fr 10em;")); |
|
main.append(addParagraph("Here, we are specifying a three column layout and a fixed size of 10em for the third column. The other two columns are allocated a width of 1fr. As there are two columns, that means the total allocated in this way is 2frs and so each column is going to be 50% of the remaining space after allocating 10ems to the third column.")); |
|
main.append(addParagraph("The nice thing about this unit is that it specifies the widths of the columns relative to each other so you don't need to work out what % to allocate to them. For instance, let's assume we want to allocate the same amount of space to both the first and third columns but we want the second column to occupy most of the available space. We can specify the widths like this.")); |
|
main.append(addSyntax("grid-template-columns: 1fr 8fr 1fr;")); |
|
main.append(addImageWithCaption("./images/3_columns.png", "Figure 6 - a three column layout using fr units.")); |
|
main.append(addParagraph("In this case, we have allocated a total of 10 frs to the columns so each of these is effectively one tenth of the available width so this is equivalent to")); |
|
main.append(addSyntax("grid-template-columns: 10% 80% 10%;")); |
|
main.append(addParagraph("In either case, the result is the same and will give you the grid shown in figure 6. It might not be immediately apparent what the difference is between using percentages or fr units but there are two advantages to using fr. Firstly, it may not be immediately obvious what the percentages should be or it may not be so easy to calculate. For example, if we wanted to specify 3 columns where each column occupies the same amount of space, we could specify it like this.")); |
|
main.append(addSyntax("grid-template-columns: 33.33% 33.33% 33.33%;")); |
|
main.append(addParagraph("This gives us almost 100% of the total width (99.99% to be precise) but is a little less tidy than")); |
|
main.append(addSyntax("grid-template-columns: 1fr 1fr 1fr;")); |
|
main.append(addParagraph("You might also argue that it is less clear, particularly if the layout is more complicated. The biggest difference comes when you mix units. For example, we might want the first column to occupy 200 pixels and the rest of the space to be shared evenly by the other two columns. To use percentages, we would need to know percentage of the width is still left but with fr units, we can just specify 1fr for the second and third columns to get the layout we want.")); |
|
main.append(addParagraph("Another useful way in which you can specify column widths in a grid layout is by using the minmax function which allows us to specify the minimum and maximum width. If you consider the grid layout")); |
|
main.append(addSyntax("grid-template-columns: 200px 1fr 1fr;")); |
|
main.append(addParagraph("This type of layout is quite typical when you have a column fulfilling a specific purpose where you really need a minimum amount of space. The problem with that is that it may work very well on a large screen, but you will run in to problems if the page is displayed on a small screen because the column may occupy too much of the screen width.")); |
|
main.append(addParagraph("One possible solution would be to use the minmax function which will allow the column to occupy 200 pixels when the screen is large enough but will shrink if the screen is smaller until it reaches the minimum width you specified. You would use the function like this")); |
|
main.append(addSyntax("grid-template-columns: minmax(100px, 200px) 1fr 1fr;")); |
|
main.append(addParagraph("The size of the first column is flexible but will never be less than 100 pixels on a small screen or larger than 200 pixels on a large screen. This helps to ensure your layout is responsive and can save some trouble by making a media query unnecessary.")); |
|
main.append(addParagraph("If you are specifying a layout where there are a lot of columns with the same width, you can specify these with the repeat function. For example, let's assume that you want a four column layout where all the columns have the same width. The long version of that might look something like")); |
|
main.append(addSyntax("grid-template-columns: 1fr 1fr 1fr 1fr;")); |
|
main.append(addParagraph("If we want to specify the same layout using the repeat function, we could write it like this.")); |
|
main.append(addSyntax("grid-template-columns: repeat(4, 1fr);")); |
|
main.append(addParagraph("Notice that the repeat function takes two arguments. The first of these is an integer representing the number of columns you want and the second being the width for those columns. This doesn't necessarily mean that all of the columns in the layout would have to be the same size but all of the columns specified with repeat will be the same size. For instance, let's say we want a layout with 5 where the first four columns will be 150 pixels wide and the fifth column will take up the remaining space. We could write that as")); |
|
main.append(addSyntax("grid-template-columns: repeat(4, 150px) 1fr;")); |
|
main.append(addHeader("Automatic Grid Item Placement")); |
|
main.append(addParagraph("You might have noticed in the sample grids we have seen so far that we specified the size of the grid and we also set column widths, but we didn't specify where each element would go.")); |
|
main.append(addParagraph("If you look back at the HTML in figure 2, you will see that there are five elements that are children of the body element and therefore there are 5 grid items and these are")); |
|
main.append(addInsetBulletList(["header - class masthead", "div - class page-title", "main - id content, class main-content", "aside - class sidebar", "footer - class footer-content"])); |
|
main.append(addParagraph("Figure 1 shows this page in a browser and you can see that these all follow the normal flow meaning they are simply displayed in sequence.")); |
|
main.append(addParagraph("If you compare this to a 2 dimensional grid such as the one shown in figure 6 where we have three columns, the order is still the same so across the first row we have the header, the div and the main element. The other two element appear in the next row where we have the aside followed by the footer.")); |
|
main.append(addParagraph("The elements have all been placed automatically so where we have rows, the elements will fill that row in the order in which they appear in the HTML and move on to the next row until all the elements have been added to the grid. We didn't specify the number of rows so the browser will give us the number of rows needed for the content we have so that is five rows in figure 2 and 2 rows in figure 6.")); |
|
main.append(addParagraph("This is probably how you would expect the elements to be displayed and it's not really too different but rather than the elements being placed in a single column and in order, they are placed in as many rows as needed but the order is the same. The main significance of this is that it is simply the default behaviour and it is not essential for the elements to be placed in that way. In truth, any element can be placed anywhere in the grid and this is one of the things that makes a CSS grid a powerful layout tool. Next, we will look at how we can define where to position each element.")); |
|
main.append(addHeader("Manual Grid Item Placement")); |
|
main.append(addParagraph("We have seen that our grid items are by default placed in order in the available cells on the grid. However, we can place any item anywhere in the grid. For example, we can do that by specifying which rows and columns that item should occupy. Let's look at an example of that where we start with a 3 x 3 grid which is defined like this")); |
|
main.append(addInsetCodeListing([".site {", " display: grid;", " grid-template-columns: 2fr repeat(2, 1fr);", " grid-template-rows: auto 1fr 3fr;", "}"])); |
|
main.append(addParagraph("The columns are set so that the first column occupies half of the container width and the second and third columns have an even share of the remaining space. The column heights are set to auto for the first rows and the other rows share the remaining space but the third row occupies 3 times as much space as the second. This will give is the layout shown in figure 7.")); |
|
main.append(addImageWithCaption("./images/grid_placement0.png", "Figure 7 - the default layout for our 3 x 3 grid.")); |
|
main.append(addParagraph("Bear in mind that the grid items have not been explicitly positioned yet so they currently occupy their default positions. Also, since we have only five element, only 5 of the available 9 cells currently have content. As a result, we can only see 2 of the 3 rows but we can see that the third row is occupying some of the vertical space.")); |
|
main.append(addParagraph("Now, let's say that we want to position or masthead (the grid item with class masthead) in the second and third columns of the second row. To do that, we need to add a little bit of CSS to that class to specify this and we are basically saying for both rows and columns that we want the item to be between the grid lines that we specify. So we are using the grid line to tell the browser where we want this item to go. Remember, the these lines are counted from 1 so between 1 and 2 would be the first row or column. In this case, we want it to be between 2 and 3 on the horizontal lines (so that's the row specification) and between the second and fourth vertical lines (so that's the column specification). The CSS will look like this.")); |
|
main.append(addInsetCodeListing([".masthead {", " grid-column: 2/4;", " grid-row 2/3;", "}"])); |
|
main.append(addParagraph("To illustrate this, figure 8 shows the layout with the grid lines and their numbers.")); |
|
main.append(addImageWithCaption("./images/grid_placement.png", "Figure 8 - layout with numbered lines.")); |
|
main.append(addParagraph("We would expect that the masthead will now occupy the area that is shaded in figure 8, and as we can see in figure 9, that is the case.")) |
|
main.append(addImageWithCaption("./images/grid_placement1.png", "Figure 9 - the layout with masthead positioned on the second row, columns 2 and 3.")); |
|
main.append(addParagraph("As you can see, the other grid items are moved to take up the space that is now available.")); |
|
main.append(addParagraph("So we can place any of the items anywhere on the grid and they can occupy multiple cells which is why we might want to use a grid with 9 cells to contain 5 items. Let's continue that by placing the page-title on the entire top row so for the CSS for that is ")); |
|
main.append(addInsetCodeListing([".page-title {", " grid-column: 1/4;", " grid-row 1/2;", "}"])); |
|
main.append(addParagraph("The page now looks like figure 10.")); |
|
main.append(addImageWithCaption("./images/grid_placement2.png", "Figure 10 - the layout with page-title positioned on the entire first row.")); |
|
main.append(addParagraph("We also want to modify the position of main-content so that it occupies the same cell it currently occupies but also the cell below. The CSS for that is")); |
|
main.append(addInsetCodeListing([".main-content {", " grid-column: 1/2;", " grid-row 2/4;", "}"])); |
|
main.append(addParagraph("Before we move on, I just want to mention a couple of things. Before we changed the positions for pager-title and main-content, by default, title-page was already between horizontal lines 1 and 2 and main-content was already between vertical lines 1 and 2 so we didn't really need to specify that. For example, for page-title")); |
|
main.append(addInsetCodeListing([".page-title {", " grid-column: 1/4;", "}"])); |
|
main.append(addParagraph("would actually have the same effect in this instance and similarly for main-content, we could have specified the value for grid-columns only. However, it is better to specify both so someone reading your CSS will not have to work out what the default position for an element is in order to work out exactly where it is positioned and it does also show that you did intend to have the item where it is since you have clearly and completely specified the position.")); |
|
main.append(addParagraph("For reference, the layout is shown in figure 11.")); |
|
main.append(addImageWithCaption("./images/grid_placement3.png", "Figure 11 - the layout with main-content occupying two rows, first column only.")); |
|
main.append(addParagraph("The other point I want to make relates to something that is not apparent in figure 11 but we can see it if we reduce the size of the screen as shown in figure 12.")); |
|
main.append(addImageWithCaption("./images/grid_placement4.png", "Figure 12 - the same layout as show in figure 11 with width of the viewport reduced.")); |
|
main.append(addParagraph("Now, remember that the column widths are such that the first column occupies 50% of the available space with the other two columns each occupying 50% of the remaining space (25% of the overall space).")); |
|
main.append(addParagraph("When the width of the viewport is reduced so that it is no longer possible to display all of the content (for example in figure 12, you can see that when padding is taken into account, the heading, .footer-content, is occupying the entire width of the column and to do that, the column now takes up more space than the second column.")); |
|
main.append(addParagraph("So it is important to remember that the browser will change the widths of the columns in order to correctly display the content if it can. This is a case of the CSS grid respecting the minimum width of the content and it can happen if there is something in the HTML that needs more space than you are giving it which can be a long word or an image with a fixed width. This is a quirk of CSS grid and is something you may have to take account of.")); |
|
main.append(addParagraph("There are several possible solutions and the most obvious one is to ensure the content isn't too wide for its container which may involve breaking up any long words or blocks of text without any spaces or other breaks. A less elegant solution would be to turn off overflow so that part of the text gets cut off.")); |
|
main.append(addParagraph("Ideally, you would probably want to avoid such issues by using a more responsive layout or at least, a more considered layout that takes into account how much space is actually required. For example, we could move the footer section down to the next line. We can do that by specifying the following rule for footer-content.")); |
|
main.append(addInsetCodeListing([".footer-content {", " grid-column: 4;", "}"])); |
|
main.append(addParagraph("Note that in this case, we are only specifying the starting row, but you might notice that this is also currently the last vertical line so essentially we are positioning footer on the fourth row of a grid that has three rows. Before we delve into that, the layout with this modification is shown in figure 13.")); |
|
main.append(addImageWithCaption("./images/4_rows.png", "Figure 14 - the layout with footer moved down to the fourth row.")); |
|
main.append(("So we have moved footer onto a row that is outside the grid and the browser is quite happy with that. It generates a new line to accommodate this new row converting our grid from one that has three columns and three rows to one that now has three columns and 4 rows and we could also have added content to a fourth column or a fifth row or column and the grid would automatically be increased.")); |
|
main.append(addParagraph("This is a concept that in CSS is referred to as implicit lines and we can basically keep adding rows as much as we want in this way. The same applies to columns but given that the screen has a fixed width, it seems more likely we would want to be adding more rows. An example, might be a gallery of thumbnail images where the full image is displayed when the user clicks on it. You might want to set this up in such a way that we can add as many rows as we need and can then add more rows as more images are added and we don't need (or want) to update the CSS every time we do that to make sure the grid is the correct size. Basically, this allows the grid to be expanded as needed. I guess this would give you something of a challenge if you have a footer or some other data that you want to position that you want to position after the gallery,")); |
|
main.append(addParagraph("A couple of possible solutions spring to mind. One is that you can place it in a specific column and then then position it so that it is taken out of the normal flow. For example, you can give it a fixed position at the bottom of the screen but there is a danger that when you scroll to the bottom of the gallery, part of the gallery would be obscured behind the footer. I would probably be thinking of margins between the footer and the rest of the content to fix that but I'm not entirely sure if that would work given the footer is no longer in the normal flow!")); |
|
main.append(addParagraph("In the scenario we have here, I think the situation is a little clearer. Although implicit lines can be useful in generating extra rows (or columns) as needed, I think it is something that you would want to avoid unless there is a very good reason for using them such as where you need the number of rows to grow dynamically. Where the number of rows and columns is fixed, a much better solution would be to redefine the grid as a 4 x 3 grid if you decide that your layout needs it.")); |
|
main.append(addParagraph("Note that since we have not specified the additional row, it's height is set to auto which is the default option.")); |
|
main.append(addParagraph("You will also see in figure 13 that footer is in the first column only because we didn't specify otherwise, but if we want it to occupy all three columns, we can specify that with")); |
|
main.append(addSyntax("grid-column: 1/4;")); |
|
main.append(addParagraph("We also have another property with grid that allows us to do the same thing and may be more useful if using implicit lines and that is span. For example, if we want the footer to span two columns, we can specify that with")); |
|
main.append(addSyntax("grid-column: 2 span;")); |
|
main.append(addParagraph("The result of this is that footer will now span two columns and because we didn't specify the starting column, it will occupy the first two columns. If we want it to span the second and third columns, we can add a starting column like this")); |
|
main.append(addSyntax("grid-column: 2/2 span;")); |
|
main.append(addParagraph("We could also use the same syntax to have footer span three columns but if we do that in conjunction with a starting column, for example with")); |
|
main.append(addSyntax("grid-column: 2/3 span;")); |
|
main.append(addParagraph("the browser will create a fourth column because we have asked it to span the footer across columns 2,3 and 4.")); |
|
main.append(addParagraph("We can also use span to specify the number of rows we want our element to span or combine the two to have it span multiple rows and columns. However, I don't think there is ever any good reason to use implicit lines to create extra columns so I personally would avoid doing that but it is handy to know that it is an option. The span property is also convenient, but at the same time it does seem to be simply a shortcut to avoid having to work out what the ending line number should be. Again, though, it is nice to know it exists and can be used to add extra rows and columns by way of implicit lines.")); |
|
main.append(addParagraph("At the moment, I am looking at different ways to specify a layout and particularly in regard to positioning elements precisely on the layout and it does occur to me that it might be easy to do this with grid. Let's say that you define a grid will 100 rows and 100 columns and you give these a width of 1% and a height of 1%. In theory, you should then be able to place any element anywhere in this grid by specifying the line numbers as though you were using cartesian co-ordinates. The only drawback is that you would probably have to be quite specific about the number of rows and columns the element spans but it is something that is interesting enough to warrant a little experimentation before we move on to the next video!")); |
|
main.append(addHeader("Named Lines")); |
|
main.append(addParagraph("Using line numbers can be a little abstract because you have to refer to the grid sometimes to make sure you know what are of the grid they represent. More importantly, when you design a layout, you're not thinking about what line numbers a particular part of the page should lie between. Typically, you will think about what elements you need. If we take this page as an example (and bear in mind that the layout may change so I will add an image of how it currently looks which you can see in figure 15) you can see several obvious layout elements.")); |
|
main.append(addImageWithCaption("./images/figure15.png", "Figure 15 - this page as it looks at time of writing.")); |
|
main.append(addParagraph("The first element is the header which displays some details about this course including title, source and the chapter name for this page. Below that, there is the first of two menus and this is the global menu which allows high-level navigation around the site.")); |
|
main.append(addParagraph("The next three elements are grouped together into the main panel and that includes the second navigation panel and this is the local menu allowing navigation around this specific course. In the centre is the main content which is the most important part of this page. If I click on one of the links in the local menu to go to another page in the same course, aside from the chapter name in the heading, the only thing that will change is the main content. The final element is the sidebar which conventionally would be on the left-hand side but for me, it is on the right hand side. This is really just a bit of extra content to make the page look a little more interesting.")); |
|
main.append(addParagraph("The main point here is that the layout is defined to accommodate these elements, so it makes more sense to be thinking in terms of main-area, header, sidebar and so on rather than thinking in terms of line numbers. There are a couple of ways in which the CSS itself can help with this and we will look at both starting with named lines. To see how this works, let's take a look at the grid dfeinition we used for the sample grid which is")); |
|
main.append(addSyntax("grid-template-columns: 2fr, 1fr, 1fr;")); |
|
main.append(addParagraph("Using the grid lines with their numbers, we specified the position of the page-title element like this")); |
|
main.append(addInsetCodeListing([".page-title {", " grid-column: 1/4;", "}"])); |
|
main.append(addParagraph("The first column will be between lines 1 and 2, the second between lines 2 and 3 and the third between line 3 and 4. If we want to give thse lines names so that we don't have to use line number, we do that by adding the line name in square barckets in the line's actual position. What I mean by that is that the first line is before the first column, the second is between the first and second columns and so on. We might call these lines start-line (line 1), half-way (line 2 because the first column occupies half of the container width), quarter and end-line. This would give us")); |
|
main.append(addSyntax("grid-template-columns: '[start-line] 2fr [half-way] 1fr [quarter] 1fr [end-line];")); |
|
main.append(addParagraph("We could now position page-title using these named lines.")); |
|
main.append(addInsetCodeListing([".page-title {", " grid-column: start-line/end-line;", "}"])); |
|
main.append(addParagraph("So that makes things a little clearer and we can also name the horizontal lines using the same technique. We can also use more descriptive names such as page-start rather than start-line and page-end rather than end-line, but the problem is that there are other elements that start and end on these lines so you are probably going to have conflicts there which will force you to choose which element to use in the line names.")); |
|
main.append(addParagraph("While this is a little bit of an improvement on just using line numbers, it's still not particular clear and the syntax seems a little clumsy so this is not a technique I ever use. However, we will see a better method in the next section that is clearer, particularly once you have set up the layout and it is a technique that I have used with grid layouts.")); |
|
main.append(addParagraph("There is a shorthand that you can use with named lines. Let's say that we have named the lines like this.")); |
|
main.append(addSyntax("grid-template-columns: '[main-start] 2fr [main-end] 1fr [sidebar-start] 1fr [sidebar-end];")); |
|
main.append(addParagraph("We can use the line names to position each of the elements so let's start with the page-title that spans all four columns and we can position it like this")); |
|
main.append(addInsetCodeListing([".page-title {", " grid-column: main-start/sidebar-end;", "}"])); |
|
main.append(addParagraph("So the element is starting from line 1 and is spanning the columns defined for both main and sidebar. In other words, it is going from the start of the main area to the end of the sidebar area and that will become significant in a moment. Just remember that main-start and mani-end are lines 1 and 2 and sidebar-start and sidebar-end are lines 3 and 4.)")); |
|
main.append(addParagraph("For masthead, the position is now")); |
|
main.append(addInsetCodeListing([".masthead {", " grid-column: main-end/sidebar-end;", " grid-row 2/3;", "}"])); |
|
main.append(addParagraph("The masthead is spanning the second and third columns so it starts at main-end and ends at sidebar-end and it does encompass the entire sidebar area which is just the third column.")); |
|
main.append(addParagraph("The last two elements are going to be the simplest because main-content starts at main-start and ends at main-end. The sidebar starts at sidebar-start and sidebar-end")); |
|
main.append(addParagraph("Here, we are not using any shorthand and this is where an element spanning the whole of either main or siderbar becomes significant. Because we have specified a pair of lines for each, a start line and an end line, we can omit these where the element lies (in whole or in part) between these start and end lines. I will look at these in the reverse order now so let's start with sidebar this time which runs from sidebar-start to sidebat-end and can now be specified as just")); |
|
main.append(addInsetCodeListing([".sidebar {", " grid-column: sidebar;", " grid-row 3/4;", "}"])); |
|
main.append(addParagraph("and similarly for main-content")); |
|
main.append(addInsetCodeListing([".main-content {", " grid-column: main;", " grid-row 2/4;", "}"])); |
|
main.append(addParagraph("Moving on to masthead, we don't have any neat way here to encompass the whole of the third column which is the column between main-end and sidebar-start so the starting point is still main-end. But since the end point is sidebar-end meaning as well as the third column, the element also spans the column designated as the sidebar column we can specify the endpoint as just sidebar, giving us")); |
|
main.append(addInsetCodeListing([".masthead {", " grid-column: main-end/sidebar;", " grid-row 2/3;", "}"])); |
|
main.append(addParagraph("To be fair, this is probably the least neatly positioned element using this method but page-title is much neater. Since to covers all of the columns, it's starting point is going to be nain-start and its ending point will be sidebar-end so we can simplify this to")); |
|
main.append(addInsetCodeListing([".page-title {", " grid-column: main/sidebar;", "}"])); |
|
main.append(addParagraph("This is an improvement on just using line numbers butit's still not particular clear and the syntax seems to me to be a little clumsy so this is not a technique I ever use. In particular, I don't like the fact that we have specified an area (in terms of columns) that two of the elements will occupy and then specified the other two elements in terms of how they fit around them. I also think that a major weakness of this method is that we haven't specified names for the horizontal lines which I think would just add tot he confusion. As a result, I have never used this technique.")); |
|
main.append(addParagraph("We will see a better method in the next section that is clearer, particularly once you have set up the layout and it is a technique that I have used with grid in the past. However, there may be times when named lines is the best approach and our example is a fairly simple layout, so it may well be worth looking into them in a little more depth and at the very least, you will want to know how they work in case you see them in someone else's code.")); |
|
main.append(addHeader("Grid Areas")); |
|
main.append("As we have seen, with lines and named-lines, we can specify which cells in a grid each element is going to occupy and it is not too complicated to do that, you just need to specify the area in terms of the lines each element occupies in terms of those lines. That definition is made in the element which might lead ro problems if you get the lines number or name wrong which might lead to you putting two elements into the same area of the grid."); |
|
main.append(addParagraph("With grid areas, that is switched around so you are defining a name for each area of the grid and you are doing that in the CSS for the frid rather than the grid items(the elements) so that removes any chance of putting grid items in to the same area. Actually, it is technically still possible to do that but it's a little harder to make a mistake like that and a lot easier to fix if you do.")); |
|
main.append(addParagraph("In our example, we have a grid that is 3 x 3 so it contains 9 cells or areas and we have five elements. We want our title to occupy the entire first row. On the second row, the first column will be where we put the start of main with header occupying the other two columns. The third row will also have main in the first column with sidebar in the second column and footer in the third.")); |
|
main.append(addParagraph("The property we would use to specify this, and remember, this is specified in the grid rather than the grid items so we only need to do this once for each grid, we would use the property, grid-template-areas. The value we assign to the property is essentially a map of the grid with each cell becoming part of one of our areas or in some cases, the entire area. For example, for the grid I just described, we would specify it like this.")); |
|
main.append(addInsetCodeListing([".site {", " display: grid;", " grid0template-columns: 2fr 1fr 1fr;", " grid-template-rows: auto 1fr 3fr;", " grid-template-areas:", " \"title title title\",", " \"main header header\",", " \"main sidebar footer\";", "}"])); |
|
main.append(addParagraph("Well then!")); |
|
|
|
|
|
|
|
addSidebar("webdev"); |
|
sidebar.append(addParagraph("A link to <a href='https://www.linkedin.com/learning/css-advanced-layouts-with-grid-2017/'>this course</a>.")); |
|
|
|
|
|
|
|
|