import { addBanner, addArticle, addTitle, addHeader, addParagraph, addClassParagraph, addSubHeader, addOrderedList, addUnorderedList, addBlockquote, addQuote, 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 Essential Training")); heading.append(addParagraph("Christina Truong - LinkedIn Learning - October 2019")); heading.append(addParagraph("Chapter 8 - Fluid and Responsive Layouts")); main.append(addSubHeader("Introduction to Responsive Design")); main.append(addParagraph("The term Responsive Design was coined by Ethan Marcotte in a blog post entitled A List Apart in 2010.")); main.append(addQuote("\"Rather than tailoring disconnected designs to each oven other increasing number of web devices, we can treat them as facets of the same experience. We can design for optimal viewing experience, but embed standards based technologies into our designs to make them not only more flexible, but more adaptive to the media that renders them.\"", "Ethan Marcotte from A List Apart, 25 May 2010.")); main.append(addParagraph("Responsive wed design tends to be built into a web design rather than added on as an 'additional feature' as it would have been in the past. In other words, we really want to design our web pages in a way that will allow them to easily adapt to whatever platform a user is accessing the site with so that we can optimise the user experience on any device.")); main.append(addSubHeader("Project: Creating Fluid Layouts")); main.append(addParagraph("When it comes to responsive design, there are three key ingredients.")); main.append(addInsetBulletList(["A fluid layout", "Flexible images", "Media queries"])); main.append(addParagraph("Going back to the resume, we can see how responsive the page currently is by resizing the window and we can see that, at the moment, it is not responsive at all.")); main.append(addParagraph("If we look at the CSS for the content-wrap, we see the following.")); main.append(addInsetCodeListing([".content-wrap {", " width: 800px;", " margin: 0 auto;", " padding: 60px 0;", "}"])); main.append(addParagraph("Bearing in mind the fact that each section is within a container with the class content-wrap, this is essentially styling the whole page. Note that each section therefore has a fixed width of 800 pixels. We could use the max-width property in place of width.")); main.append(addInsetCodeListing([".content-wrap {", " max-width: 800px;", " margin: 0 auto;", " padding: 60px 0;", "}"])); main.append(addParagraph("Where the screen width is greater than 800 pixels, each section will have a width of 800 pixels, but with smaller widths, the sections will be no wider than the screen so that is an improvement. The problem with this is that there is no room around the text when the screen width goes below 800 pixels and this can make it harder to read. However, we will keep this property and add the width property back in again with a value of 85%. Now, if the screen width is high enough (more than about 940 pixels), our sections will be no wider than 800 pixels. If the screen width is smaller than that, the width will be simply 85% of the screen width so we can be sure that there will always be some space on either side of the sections.")); main.append(addInsetCodeListing([".content-wrap {", " max-width: 800px;", " width: 85%;", " margin: 0 auto;", " padding: 60px 0;", "}"])); main.append(addParagraph("The changes can be seen in resume17.html. This works pretty well on smaller screens, but it still does leave a couple of issues. When the screen width is very small, on a smartphone for instance, the text looks quite cramped and the links in the contact info section no longer fit inside the screen width. With regard to the links, you may remember that these were styled with flex properties so using flexwrap may be an option. Alternatively, we could use media queries to change the styles when the screen size gets below a particular width, but we will come back to this again later.")); main.append(addParagraph("As we saw, using a fluid layout, which is what we have done in part at least by apply the changes to content-wrap (making the width of the sections more fluid), it doesn't solve all of the problems we might encounter, particularly with very small screens, but it is a good basis for a site and will minimise those problems if we need to add media queries to achieve a more responsive design.")); main.append(addSubHeader("Flexible Background Images")); main.append(addParagraph("We can add an image to our web site in one of two ways. For images that are part of the content, we would use the usual <img> tag. However, if we are using the image for decorative purposes such as a background, we can apply that with CSS. In this section, we are interested in adding an image as a background, so we do this with CSS using the background-image property.")); main.append(addParagraph("As an example, I have a background image on the home page and subject pages of this site (at least, I do as I write this but that may of course change in future). This is added through CSS.")); main.append(addInsetCodeListing(["#homebody {", " background-image: url(\"/images/DSC_0371-1-3.jpg\");", " background-size: cover;", " background-repeat: no-repeat;", " background-position: center;", "}"])); main.append(addParagraph("Note that I have added a few other properties to control how this image is displayed and we will look at each as we come across them, starting with the back-ground repeat property. By default, if the image is smaller than its window or container, it will repeat along both the vertical and horizontal axis. To prevent this from happening, I have explicitly specified that the image should not repeat along either axis by setting the value of the background-repeat property to no-repeat.")); main.append(addParagraph("If you have both a background image that doesn't repeat and doesn't fill its container and a background colour, the background colour will show in the portion of the container not covered by the image. If you don't specify a position for the image, it will be docked to the top left of the container. You can change this by specifying the background-position property. This can take any length unit as a value such as a 100 pixels.")); main.append(addInsetCodeListing(["#homebody {", " background-position: 100px;", "}"])); main.append(addParagraph("If only one value is specified, this represents the distance from the left hand edge of the container. In that case, the position relative to the top of the container defaults to auto. If you want to specify the distance from the top as well, you can give the property two values.")); main.append(addInsetCodeListing(["#homebody {", " background-position: 100px 100px;", "}"])); main.append(addParagraph("There are also five keywords we can use for the background-position property and these are")); main.append(addInsetBulletList(["top", "bottom", "left", "right", "center"])); main.append(addParagraph("We can stretch the image to fit into its container by specifying the background-size property. This can also take one or two values with the first being the width and the second being the height. If we set both to 100%, the image will stretch to fill the container, but if it doesn't have the same aspect ratio as the container - and bear in mind that the size of the container can change so its aspect ration should not be considered to be constants - the image will be distorted.")); main.append(addParagraph("We can also use keyword values here such as cover, used in the first example. This causes the image to fill the container, but rather than distorting the image, if necessary, the image is clipped so that it fits inside the container. This can cause a problem, particularly if there is a part of the image that you want to ensure is always visible and we can do that with the background-position property. For example, if we set this to top right")); main.append(addInsetCodeListing(["#homebody {", " background-position: top right;", "}"])); main.append(addParagraph("This will ensure that the top of the image is flush with the top of the container and the right side is flush with the right side of the container and we could use other options depending on what portion of the image is the most important.")); main.append(addParagraph("Another option, of course, is to crop the image before applying it to the website. Bear in mind that ideally, you would want the size of the image to match the container which you can match and scale for the best results and then add media queries of any adjustments that might be needed. For example, you may have designed this for a small screen and perhaps you just want the whole image to display on a large screen so you might change the background-size property to cover, as an example, if the screen width is larger than a given size.")); main.append(addParagraph("If you are using the shorthand property for background, the order in which you put the values doesn't matter, for the most part, but if you are using a background-size value, this has to come after the background-position property, separated with a forward slash - /. If you are not specifying a value for background-position, you can still specify background-size, but not in the shorthand.")); main.append(addParagraph("As an example, a shorthand version of the background declaration might look something like this")); main.append(addInsetCodeListing(["selector {", " background: url(../image.jpg) no-repeat center/cover;", "}"])); main.append(addParagraph("Here, we are specifying the image to be used, that it should not repeat, that is is centrally positioned and that it should fill the container as far as possible without being stretched. If we want to apply the same CSS rules without the central position, the size must then be declared in long hand so the CSS would look like this.")); main.append(addInsetCodeListing(["selector {", " background: url(../image.jpg);", " background-size: cover;", "}"])); main.append(addParagraph("The background-size property should also be declared after the shorthand property so that it overrides and default size associated with that shorthand declaration.")); main.append(addParagraph("When you are using shorthand properties like this, there can sometimes be specific rules for the syntax of the shorthand and if you omit a value, it will often default to the initial value. It might be helpful to look at an example here. Let's say we have a declaration setting the background color to red.")); main.append(addInsetCodeListing(["selector {", " background-color: red;", "}"])); main.append(addParagraph("and this is followed by a shorthand declaration.")); main.append(addInsetCodeListing(["selector {", " background: url(../image.jpg) no-repeat;", "}"])); main.append(addParagraph("The first declaration set the background colour to red so if the image doesn't fit the container, we would expect to see a red area behind it. To test this, I have amended grid7.html by giving the first grid item an id of test so that I can style it and be sure that it will override any other background styles since I don't have any other elements with ids on the page. I have applied the following CSS.")); main.append(addInsetCodeListing(["#test {", " background: url(\"thumbnails/DSC_0005.jpg\") no-repeat;", " background-color: red;", "}"])); main.append(addParagraph("The result can be seen in grid8.html. If I switch these round so that the CSS becomes")); main.append(addInsetCodeListing(["#test {", " background-color: red;", " background: url(\"thumbnails/DSC_0005.jpg\") no-repeat;", "}"])); main.append(addParagraph("See what effect this has in grid9.html, in particular notice that the background colour has disappeared. This is because we first declared red as the background colour and followed that with a shorthand background property which set the background image and set it to not repeat. The shorthand declaration did not specify a background-colour property, but since this was not included, the initial value was applied (which is transparent) overriding that red background colour. In this case, it is important to remember that even when we don't specifically set a value when using shorthand, it may be set implicitly so we need to be careful with these.")); main.append(addParagraph("There are other background properties that we didn't mention here, so you may want to check out the documentation in the MDN Web Docs.")); main.append(addSubHeader("Project: Flexible Background Image")); main.append(addParagraph("In the current version of the resume, the education section looks a little bit blank so it might brighten it up if we added some decoration here and a background image might help. There are three images in the images folder which for me, is in the same folder that I keep the resume versions. THese were dowmloaded by Christina from a site called Unsplash (note that images from the site can be used freely and the default filename includes the name of the photographer so you can search for more images by them if you like. The images chosen by Christina are pretty good for background images because they either have large empty areas where our text will go or a blurry appearance so the background won't clash with the text.")); main.append(addParagraph("We will need to decide where exactly we want to place the image. In the HTML, under the education section, we have a container with a class of education and this spans the whole window. The education items themselves are in another container with a maximum width of 800 pixels and this has a class of content-wrap and a class of item-details. The education class is of course unique to these sections, but content-wrap and item-details are used in other classes, so we would probably need a new class for this container if we wanted to use it here. In fact, we will use the outer container so we can use the education class as the selector.")); main.append(addParagraph("To keep things simple, initially, I am going to use longhand syntax for the properties and we will be using the image with the pencils and the yellow backdrop. I will provide the CSS first and then we will look at what each of the lines does.")); main.append(addInsetCodeListing([".education {", " background-image: url(\"/\");", " background-size: cover;", " background-position: top right;", "}"])); main.append(addParagraph("The background-image property obviously specifies the image we want to use and if you look at the page with that iamge being applied, it looks fine and it seems to take up all of the avaialable are so we don't really need the background-size property, at least not when we are viewing the page on a large pc screen. However, we want to be sure that the oimage always fills the container so that's why we added a background-size of cover. This makes our image fluid and responsive!")); main.append(addParagraph("If we refresh the page with this change, we will see that the position of the pencils does shoft slightly. The pencils are the main feature of this image so we want to make sure that they are always visible regardless of the screen size so we have used background-position with a value of top right to effectively anchor the top right corner of the image to the top right corner of the container so they are always visible.")); main.append(addParagraph("This creates another issue with small screen sizes where the text can sometimes clash with the darker parts of the image (the pencils in other words) and you will sometimes get this kind of trade-off when positioning a background image. Adding some padding to the bottom of the education section can help with that but it doesn't completely solve the problem.")); main.append(addParagraph("Depending on the circumstances, you might want to consider reducing the effect of the clash rather than avoiding it either by changing the background image to avoid dark colours or by changing the font colour. In this case, we will reduce the width of the paragraphs. We will use a descendant selector for this so that it only impacts the education section and only the paragraphs in that section.")); main.append(addInsetCodeListing([".education p {", " width: 60%;", "}"])); main.append(addParagraph("Again, this is not a perfect solution but we can see a definite improvement and the text doesn't clash with the text as much except when the screen is really very narrow. Depending on how this looks on the smallest devices, smartphones, we may need to take more drastic action, perhaps even removing the image altogether but that is something we would use a media query for an we will look at those next.")); main.append(addParagraph("This does give us some insight into how we go about designing a site suitable for all devices. We may have to compromise on occassion, but we want to at the very least ensure that the content is always readable and it won't be if it is clashing with an image that is similar in terms of the colour or tone. We can see the revised version of the resume in resume18.html.")); main.append(addSubHeader("Introduction to Media Queries")); main.append(addParagraph("Media queries are used to apply CSS rules in certain situations. Take the following example.")); main.append(addInsetCodeListing(["@media screen and (max-width: 1000px) {"," h1 {", " font-size: 16px;", " }", "}"])); main.append(addParagraph("Basically, this media query checks two things - is there a screen and does it have a width no larger than 1000 pixels. If both these things are true, the CSS rules that follow will be applied. So we could express this by saying, if the screen width is 1000 pixels wide or less, set the font-size for &lu;h1> elements to 16 pixels.")); main.append(addParagraph("Note that the CSS associate with the media query is enclosed in a pair of curly braces, but inside those curly braces is just regular CSS. The media type, in this example screen, is simply a medium that can display web documents and there are currently we can use with media queries.")); main.append(addInsetBulletList(["all - matches to any device.", "print - matches to printers and print-related displays such as a printable version of a web page.", "speech - matches to screen reading devices.", "screen - matches to any device not covered by print or screen but typically this means the screen on the pc, tablet and so on."])); main.append(addParagraph("We will use screen in order to make our web pages responsive, but we can also omit the media type, in which case the media type will default to all. Older versions of CSS provided more media types which have seen been deprecated. It is possible, of course, that this will change again at some point so if you are looking at online documentation, make sure you check the publication date to ensure the information you are seeing is up to date. This is a general rule of thumb that applies every time you look at CSS online documentation, but it is arguably more important in respect to responsive design because this is an area where there has been a lot of changes and this will probably continue to be the case.")); main.append(addParagraph("Media features can be used to test certain aspects of a device (that is, to setup conditions based on the results of some test) and they look a little like CSS properties but the feature is wrapped in parentheses and there is no semi-colon to terminate it. The general syntax is")); main.append(addSyntax("@media (feature name : value)")); main.append(addParagraph("If you are using a media feature with a media type, these are combined using the and keyword. You can also combine some of these features to make more complex queries. When each of the specified conditions is met, the CSS inside the curly braces is applied. It can actually get a little bit more complex than that. There are additional logical operators such as not and or, but in this course, we won't go beyond using and to create a media query.")); main.append(addParagraph("There are also a number of media features including width, height, aspect ratio and so on, but in most cases, width is used for a repsonsive design.")); main.append(addParagraph("By convention, media queries are placed at the end of the CSS stylesheet or in a separate sheet containing just your media queries. You can also put the media query directly below the corresponding CSS rule. For example, if you have a font-size of 32 pixels for <h1> elements and you want to add a media query to reduce this for smaller screens, you will see something like this in the CSS files.")); main.append(addInsetCodeListing(["h1 {", " font-size: 32px;", "}", "", "@media (max-width: 1000px) {", " h1 {", " font-size: 16px;", " }", "}", "", "h2 {", " font-size: 24px;", "}"])); main.append(addParagraph("This type of usage is commonly used when working with a CSS preprocessor such as SASS or LESS. Another option is to place your CSS in a stylesheet and then import it into a CSS sheet. This is done by using an import rule in your CSS that specifies the file you are importing the rule from along with the media type and the media feature, so it will look something like this.")); main.append(addSyntax("@import url(\"mobile.css\") screen and (max-width: 480px);")); main.append(addParagraph("A similar method can also be used to import the media query directly into an HTML page using a <link> tag where the media feature is specified as a media property and the filename as the href property so that would look something like")); main.append(addSyntax("<link rel=\"stylesheet\" media=\"screen and (max-width: 480px)\" href=\"mobile.css\">")); main.append(addParagraph("The impression I get is that media queries are becoming less common since responsive designs can largely be facilitated via a fluid layout with media queries being used to tweak the CSS if we can't achieve the desired effect otherwise - the previous example where we applied the background image to the Education section of the resume is a good example of that. You may therefore find that a couple of media queries stuck at the end of a CSS file is perfectly sufficient, but it is still good to be aware of these other ways of adding media queries because you may see them in legacy code.")); main.append(addParagraph("More information on media queries and media features can be found in the MDN Web Docs.")); main.append(addSubHeader("Media Queries, Widths and Breakpoints")); main.append(addParagraph("Typically, when we are looking at media queries for a responsive design, this is in relation to screen widths. That is, we are designing queries that allow us to style our web pages for a type of screen width. It is normal to take between 2 and 4 screen widths and set breakpoints for those widths. What I mean by this is, for example, in the media queries we saw earlier, we were setting CSS rules that would be applied when the screen width was no larger than 1000 pixels, so that is our breakpoint.")); main.append(addParagraph("It is important to remember that there is now a wider variety of screen sizes available to people browsing the web than there would have been in the early days of the Internet, so it can be difficult to know where to set the breakpoints. For example, gs.statcounter.com provides a breakdown of screen resolutions typically used for browsing and there is a wide range of these with screen widths ranging from 360 pixels right up to 1920 pixels. Just as an aside, there is also some other interesting statistical data there such as browser usage.")); main.append(addParagraph("With such a large number of possible devices, it doesn't really make sense to try to target specific devices with media queries. A more sensible approach would be to test your web page on as many devices as possible and use media queries when it is clear that the CSS is breaking or needs to be adapted.")); main.append(addParagraph("When you use a media query based on width, this is the width of the viewport. Width is generally a range feature so you can specify a minimum or maximum width. You can also specify a width, but thus probably wouldn't be very useful since the CSS rules attached would only apply if the screen width matched the specified width exactly.")); main.append(addParagraph("Putting these things together, you are probably going to use several media queries specifying a number of different widths and apply the CSS appropriate for each. There are also two possible approaches to media queries which I will mention first. One is Desktop First, the other is Mobile First. These are the two extremes in terms of screen sizes, so essentially this means you design your page for the largest possible screen and then add media queries when required to adapt the design for smaller screens (Desktop First) or you design it to be viewed on a mobile device and then adapt the design for larger screens.")); main.append(addParagraph("If you take the first approach, your media queries are going to be based on a maximum width. So, esesntially, you write a media query for, let's say, a maximum width of 800 pixels and another for a maximum width of 400 pixels.")); main.append(addInsetCodeListing(["/* optimised for larger screens */", "body { ... }", "", "/* adapted for smaller screens */,", "@media (max-width: 800px) {", " body { ... }", "}", "", "@media (max-width: 400px) {", " body { ... }", "}"])); main.append(addParagraph("Conversely, if you adopt a Mobile First approach, you will use minimum widths in the media queries.")); main.append(addInsetCodeListing(["/* optimised for smaller screens */", "body { ... }", "", "/* adapted for larger screens */", "@media (min-width: 401px) {", " body { ... }", "}", "", "@media (min-width: 801px) {", " body { ... }", "}"])); main.append(addParagraph("Both the minimum and maximum widths may be combined to create a range of widths. For instance, you may want to have some CSS applied when a screen width is at least 400 pixels and at most 800 pixels.")); main.append(addInsetCodeListing(["/* Apply for screen widths from 400 pixels to 800 pixels */", "@media (min-width: 400px) and (max-width: 800px) {", " body { ... }", "}"])); main.append(addParagraph("You may need to be careful with these sizes, both in terms of a range and in terms of a single specified width. For example, let's say you have designed a site for screen widths up to 360 pixels and you want to add a media query for larger widths. You would use a min-width feature that looks something like this")); main.append(addInsetCodeListing(["/* Apply for screen widths larger than 360 pixels */", "@media (min-width: 360px) {", " body { ... }", "}"])); main.append(addParagraph("This might be something that is easy to miss, the media query looks okay, but it will apply these styles when a screen width is 360 pixels or bigger. That's not really what we wanted, we only want them to apply when the screen width is larger than 360 pixels, but not when it equals 360 pixels. To avoid this, you may simply want to select the largest size where you don't want the styles to apply and then add 1.")); main.append(addInsetCodeListing(["/* Apply for screen widths larger than 360 pixels */", "@media (min-width: 361px) {", " body { ... }", "}"])); main.append(addParagraph("Similarly, when specifying more than one range, you might accidentally specify the same width in two separate ranges. For example, let's say we design for a maximum screen width of 360 pixels, we want to apply one set of modifications for larger screens up to 800 pixels wide and another for screen widths larger than 800 pixels.")); main.append(addInsetCodeListing(["/* Optimise for screen widths up to 360 pixels */", " body { ... }", "", "/* Adapt for larger screens up to 800 pixels */", "@media (min-width: 360px) and (max-width: 800px) {", " body { ... }", "}", "", "/* Adapt for large screens */", "@media (min-width: 800px) {", " body { ... }", "}"])); main.append(addParagraph("Here, again, we have the problem that we have specified a screen width of 360 pixels in the first media query. Since we want this to apply for screen widths up to 800 pixels, the second part of the range (the upper limit) is fine. But in the second media query, we have included a screen width of 800 pixels.")); main.append(addParagraph("I would say that when you are specifying a minimum width, you are probably thinking of the largest size of screen you want to exclude (as in, up to 360 pixels) so you probably want to add a 1 there. When you are thinking in terms of a maximum width, you are most likely thinking about the largest size to include so you probably don't want to change that. So this would mean our media queries should be written like this.")); main.append(addInsetCodeListing(["/* Optimise for screen widths up to 360 pixels */", " body { ... }", "", "/* Adapt for larger screens up to 800 pixels */", "@media (min-width: 361px) and (max-width: 800px) {", " body { ... }", "}", "", "/* Adapt for large screens */", "@media (min-width: 801px) {", " body { ... }", "}"])); main.append(addParagraph("I would say that the second most important thing here is to be consistent in how you apply these media queries. THe only thing that is more important than that is ensuring that the media queries are targeting the right screen widths. Ultimately, the best way to determine that, even if the media queries aren't exactly targeting the precise widths you expect them to, is to see how the site looks on a variety of different screen sizes. If you expect to see a certain set of styles on a particular size of screen but you are seeing styles more suited to a smaller (or larger) screen, you probably want to check what screen sizes you are targeting with the media queries.")); main.append(addSubHeader("Testing Responsive Layouts")); main.append(addParagraph("Ideally, you would want to test your web design on as many different devices as possible, but the reality is that you may not have access to a huge number of these. To help with this, there are online servies, such as browserstack.com. This allows you to test a web site on a variety of devices to see how it looks. However, use of the site is limited if you are not a subscriber (for example, when I tested it, I was checking how my website looks on a Galsaxy S9 but I could only view it for a minute.")); main.append(addParagraph("Another option is to use the browser developer tools. I normally have these in a separate window so I have the site on one monitor and the developer tools on another monitor, but for this to work, you will need to esnure they are docked to the left, right or bottom of your browser window. To the right is probably the best option for smaller devices so that you have as much of the screen vertically as possible. A handy feature here is the box model diagram (in Firefox) which gives you an indication of the width of the viewport. If you have an element that spans the whole width of the screen, then the width of that element is the screen width. Do bear in mind that in this context, you need to take both padding and margins into account.")); main.append(addParagraph("Christina uses a similar trick in the course video, but she sees the dimensions of the elemnt at the top of her screen in a little box. Like me, she is using Firefox although hers is on a Mac. I do see something like that in Chrome though, where the dimensions are shown for whatever element you select or hover over in the element tree.")); main.append(addParagraph("This can be a big help in determining where to set your breakpoints.")); main.append(addParagraph("Most browser developer tools also have a device emulation option. You can switch it on by clicking on its icon which, in Firefox, looks like a phone next to a tablet. From here, you can select from a few devices listed in the dropdown menu, you can change the orientation or you can manually input screen dimensions.")); main.append(addParagraph("One other requirement before you add media queries is the viewport meta tag, which looks something like this.")); main.append(addSyntax("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">")); main.append(addParagraph("This is added to the head section of the HTML and is used to control the viewport's size and scale. The content property, here, specifies that the page should be rendered at the same width as the device width initial scale sets the zoom level when the page is loaded so a value of 1.0 means that you are not initially going to be displaying the page zoomed either in or out.")); main.append(addParagraph("Using it in this way is the standard convention for any responsive web design, so it's really only being mentioned in passing here, but you can read more about it in a post entitled Understanding the viewport meta tag which is on the sharingsolutions.net website.")); main.append(addSubHeader("Project: Media Queries and Responsive Layout")); main.append(addParagraph("For this exercise, we are going to use the browser developer tools in otder to determine where to add breakpoints and by doing that, we can see that it starts to look squished in parts when the screen width is below around 750 pixels, so that seems like an ideal place to put a breakpoint. Apart from that, there is no other point where it gets worse so this is going to be our only breakpoint.")); main.append(addParagraph("For the CSS, we are going to add the media query to the bottom of the existing CSS fit. We need to decide whether to use a min-width or a max-width for the media query and this depends on the existing structure, so let's take a look at that.")); main.append(addParagraph("One of the places in the resume where things start to squish up below a certain size is the projects section which has a two column layout and on a very small screen, we probably want to switch back to a single column. In our CSS, we have a float being applied to the image to give us the two columns. One of the advantages of working with the broswer developer tools is that we can test whether the changes we want to make are going to be successful. For instance, we can reduce the width of the window so that we see the squished up appearance in the projects section. We believe that removing the float from the image will give us the desired result, so we can easily uncheck it here to immediately see whether it will behave as expected.")); main.append(addInsetCodeListing([".project-item img {", " float: left;", " margin-right: 20px;", "}"])); main.append(addParagraph("One approach we can take is to use a max-width media query and override these styles. However, it is generally better not to override styles when you don't need to because this is increasing the possibilities for conflicts and errors. Since we want the style to be applied when the screen width is larger than 750 pixels, but we don't want it to apply for smaller screens, it makes more sense to remove these styles from the products section in the CSS and place them into the media query. This means that the media query will apply these styles when they are needed, but won't apply them when they are not. So, our media query becomes")); main.append(addInsetCodeListing(["/* Media Queries", "------------------------------------*/", "", "@media screen and (min-width: 800px) {", "", " .project-item img {", " float: left;", " margin-right: 20px;", " }", "}"])); main.append(addParagraph("We can check the results in a browser to confirm that the two column layout in the projects section becomes a one column layout when the width of the screen is small. Now that we have a breakpoint established, we may as well check the page at this smaller size to see if there are any other changes we might make and we can see that there is a similar problem in the work experience section. In this case, the two column layout was created with a simple grid layout.")); main.append(addInsetCodeListing([".job-item {", " display: grid;", " grid-template-columns: 1fr 2fr;", " column-gap: 20px;", "}"])); main.append(addParagraph("Again, we want to remove these styles and put them into the media query so that the grid layout is only created when the screen width is 750 pixels or bigger and as we did previously, we can uncheck these styles in the browser developer tools to ensure that we see the desired result.")); main.append(addParagraph("Once these changes have been made, we can revisit the page to see if there are any other changes that should be made. Sometimes, making changes like this can alter the overall look of the page and make other elements look as though they don't quite fit in as well. This might apply to the header section. We previously centre aligned the text for that and this doesn't work so well with the changes we made so we want to remove that for the smaller sizes. This style comes from the cSS for the header and footer and we want to remove it from both so we will remove it from both these sections and comibne the two as the selector to add the style back in under the media query.")); main.append(addInsetCodeListing(["header, footer {", " text-align: center;", "}"])); main.append(addParagraph("There are some other changes we want to make to the heading. The font-size seems to be a little too large for a smaller screen and the spacing could be improved. The font size was set to 100 pixels in the header section, but this is a slightly different situation from the stlyes we added to the media query. This time, we want the font-size to be set in both the main CSS and in the media query, but to different values for each. Because of this, we will leave the styling as it is and create a new media query. This time, we want to set the font-size for smaller screens when the width is less than 750 pixels so we will specify a max-width of 749 pixels. We will set the font-size to 75 pixels.")); main.append(addInsetCodeListing(["@media screen and (max-width: 749px) {", " h1 {", " font-size: 75px;", " }", "}"])); main.append(addParagraph("At the smaller width, the headings are spread over two lines and there seems to be a little bit too much space between them. Previously, we had set the line spacing to 1.5 so we will reduce this in the media query by setting the value to 1. This also applies to the <h2> header so we can use a combination selector to add this to both types at the same time.")); main.append(addInsetCodeListing(["h1, h2 {", " line-height: 1;", "}"])); main.append(addParagraph("Refreshing the page again, we see there is no space between the two lines of the <h2> element but we still have some space between the lines of the <h1> element. Ordinarily, setting a line height of 1 would mean that each line is the same height as the text in that line, but this can vary depending on the font since some fonts don't actually use the full height. This means that we will need an even smaller value for the<h1> so we will amend our combination selector to use h2 as a type selector. We already have the font-size using h1 as a type selector, so we will also add the line-height property there and give it a value of 0.9.")); main.append(addParagraph("THese two headings now look as though they are a little bit too close together, so we will add some space between them by giving the <h1> element a bottom margin of 20 pixels.")); main.append(addParagraph("Moving on to the footer, as I had noted earlier, the items under contact info no longer fit across the width of the screen when the screen size is very small, they are actually being cut off so they are only partially visible so we want to do something about that. The items in contact info were styled as a flex container. The styling for this container is")); main.append(addInsetCodeListing([".contact-list {", " list-style-type: none;", " padding: 0;", " display: flex;", " justify-content: center;", "}"])); main.append(addParagraph("We want to keep some of these styles regardless of screen width, so ew won't move the whole grouping of the selector and declarations over to the media query. It is the last two that create the flexbox, so we will use the same selector in our first media query (the one spcifying a min-width of 750 pixels) and move just those two rules over to it, leaving the rest where they are.")); main.append(addParagraph("When we view the amended page, we can see that the flexbox applies for larger screens but when the screen width is reduced far enough, the list of contacts no longer appear as a flex box and are now just displayed as a list (still without the bullet points because the declaration to change the list styling to none was left in the main part of the CSS so it is not affected by the media query).")); main.append(addParagraph("The spacing doesn't look right so we will want to do something about that. This space is coming from the margin property we set for the class contact-list. It currently has a value of 15 pixels sl we want to reduce this to 5 pixels. As this styling is for the smaller screens which will almost certainly be a touch screen, we want to leave a little bit of space around this beacuse you may remember that this additional space was being used to increase the size of the clickable area and that would be helpful here.")); main.append(addParagraph("As with the font sizes, this is a style where we want to keep the original value for larger screen widths but use a smaller value for our smaller screens. So, we want to copy the rule over, using the same selector")); main.append(addInsetCodeListing([".contact-list a {", " padding: 15px;", " display: inline-block;", "}"])); main.append(addParagraph("WE have checked the styling by varying the width of the viewport in the browser developer tools, but once we are happy with the design, including the media querties, we should test the layout on some real devices. We could do that with the emulated devices available in the browser developer tools or osme other similar tool. For instance, Android Studio also provides mobile device emulation for testing purposes. Of course, where possible, you should also test the design on real devices.")); main.append(addParagraph("One thing that I notice hasn't been resolved is the fact that the background image in the Education section still clashes with the text when the screen width is low enough. With a little bit of testing, I have determined that the maxumum size where the pencils don't clash with the text or come too close is at aroung 550 pixels, so I'm going to add an additional media query with a new breakpoint so that I can change that syling for screens with a width of less than 550 pixels. This will mean a maximum width of 549 pixels. As a reminder, the current styling for this background has a class selector of education.")); main.append(addInsetCodeListing([".education {", " background-image: url(\"../images/joanna-kosinska-unsplash.jpg\");", " background-size: cover;", " background-position: top right;", "}"])); main.append(addParagraph("I don't want to remove the image altogether, but you may recall that the background-position property was introduced to ensure that the pencils were always visible in the image and by testing this property in the developer tools, it seems that changing the value of the property to top left actually seems to keep the pencils visible without clashing with the text so that should solve the problem.")); main.append(addInsetCodeListing(["@media screen and (max-width: 550px) {", " .education {", " background-position: top left;", " }", "}"])); main.append(addParagraph("This seems to work pretty well and it concludes the resume exercise. The final version is resume9.html.")); main.append(addParagraph("I think that this while exercise is a good demonstration of a typical process for developing a responsive design with media queries. You make some changes, see how it looks on the sort of screen you are targeting with those changes and what effect it has on other elements and then repeat the process until you have a design that you are happy with and that works on all of the different types of device that you want it to work on.")); addSidebar("webdev");