--- tags: COMP-1850 --- <style> .markdown-body h1:first-of-type { margin-top: 24px; } .markdown-body h1 { margin-top: 64px; } .markdown-body h1 + h2 { margin-top: 32px; } .markdown-body h2 { margin-top: 48px; } .markdown-body h3 { color: cornflowerblue; } .exercise { font-size: 150%; font-weight: bold; color: rgb(227,112,183); } .note { color: red; } </style> # COMP 1850 Lesson Seven ## Images and Graphics for the Web HTML provides two elements for adding images to pages: `img` and `picture`. The `img` element has one required attribute: `src`, and one strongly recommended attribute used for accessibility: `alt`. ``` <img src="dog.jpg" alt="A large black dog chasing a ball in a field"> ``` The `src` attribute, similar to `href` in `<a>` elements, contains the path to the file to be displayed. The `alt` attribute contains text that will be read by screen readers (for users with visual disabilities) and/or displayed on the page if the file pointed to by `src` cannot be found or cannot be rendered. ### Formats The img element supports a variety of formats: `.jpg`/`.jpeg`, `.png`, `.gif`, and `.webp` for ==raster== images (images made up of pixels) and `.svg` for ==vector== images (images made up of continuous curves). Rastered formats are typically used for photographic images, and vector images are typically used for smaller decorative elements, e.g. logos. Of the rastered formats mentioned above, `.jpg` and `.png` are the most common and easiest to create, but their file sizes tend to be considerably larger than newer, more optimized formats like `.webp`. Image file size is a hugely important consideration for the web: larger images means a larger overall page download size for the browser, and a larger overall download size means slower load times for your site. ### Responsive Images In addition to file size, the _dimensions_ of an image are also an important consideration. Images must display correctly on a variety of devices, with difference screen sizes. There are several ways we can handle making our images responsive: #### The img element with `srcset` attribute Using image manipulation software like Photoshop, we can prepare several copies of an image at different widths/heights, and allow the browser to choose between them based on the current size of a device. ``` <img srcset="pug-500px.jpg 500w, pug-1000px.jpg 1000w" sizes="(max-width: 800px) 500px, 1000px" src="pug-1000px.jpg" alt="Two baby pugs being carried together"> ``` <img srcset="https://hackmd.io/_uploads/BkNaeE0P2.jpg 500w, https://hackmd.io/_uploads/SJwAlNRP3.jpg 1000w" sizes="(max-width: 300px) 500px, 1000px" src="https://hackmd.io/_uploads/SJwAlNRP3.jpg" alt="Two baby pugs being carried together"> The `srcset` attribut associates a filename (pug-500px.jpg) with an _intrinsic_ size (500w). "Intrinsic" means the actual size of the image, irrespective of how it displays in a browser. The `sizes` attribute allows us to define media queries with associated file sizes - in the example above _if the width is less than or equal to 800px, show the 500px wide version of the image_. The `src` attribute in this case provides a default or fallback image path. <span class="note">NOTE: the images listed above in the 'sizes' attribute are referred to as 'hints', meaning the browser may ignore them and choose to display a different size!</span> How does the browser make a decision about which image size to use? It follows the steps below (paraphrased from [MDN Responsive Images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)): 1. Determine the device width (e.g. 600px) 2. Work out which media query in the sizes list is the first one to be true (e.g. (max-width: 800px)) 3. Look at the size associated with that media query (e.g. (max-width: 800px) 500px) 4. Load the image referenced in the srcset list that has the same size as the associated size or, if there isn't one, the first image that is larger than the associated size. (e.g. pug-500px.jpg 500w) <span class="note">NOTE: one important factor in how the browser determines which size to use is "device pixel ratio", i.e. how many physical pixels will be used to draw a single CSS pixel - more on this here: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio. If necessary you can set the device pixel ratio manually by following the guides for [Firefox](https://firefox-source-docs.mozilla.org/devtools-user/responsive_design_mode/index.html) or [Chrome](https://developer.chrome.com/docs/devtools/device-mode/#dpr) #### The `picture` element If we need to __force__ a particular size to be rendered, rather than leaving the decision up to the browser, we can use the `picture` element. ``` <picture> <source media="(max-width: 800px)" srcset="pug-500px.jpg"> <source media="(min-width: 801px)" srcset="pug-1000px.jpg"> <img src="pug-1000px.jpg" alt="Two baby pugs being carried together"> </picture> ``` In this case the accompanying `source` element declares a media query and associated size, and the `img` element provides a fallback for cases where no media queries apply. Note that without the `img` element nothing will display! ### Image Dimensions and `object-fit` In addition to loading appropriately-sized images as noted above, it is important to ensure that fixed-size images do not overflow the browser's viewport. Below is a common pattern for ensuring fixed-size images will not overflow the viewport: ``` <!-- N.B. Some tags omitted for brevity --> HTML: <html> <head></head> <body> <main> <img src="dog1.jpg" alt="A photo of a dog"> </main> </body> </html> CSS: main { max-width: 100%; } img { width: 100%; height: auto; } ``` The above markup and CSS does three important things: 1. Ensures that the `<main>` element is never wider than the viewport (i.e. the visible portion of the page) 2. Ensures that images fill the entire width of their parent element (in this case, `<main>`) 3. Ensures that the ==aspect ratio== of the image is preserved The term 'aspect ratio' refers to the ratio of width to height. A typical aspect ratio for screens is 4:3 for landscape (images are 1.33... times wider than they are tall) or 3:4 for portrait (images are 1.33... times taller than they are wide). By setting `width: 100%;` and `height: auto`, images can fill the width of their parent element while being allowed to take up as much height as necessary to display correctly. If we want to _change_ the natural aspect ratio of an image, or force it to fit inside a container that does not match the natural aspect ratio, we can use the CSS `object-fit` property: ``` HTML: <img class="square" srcset="https://hackmd.io/_uploads/BkNaeE0P2.jpg 500w, https://hackmd.io/_uploads/SJwAlNRP3.jpg 1000w" sizes="(max-width: 300px) 500px, 1000px" src="https://hackmd.io/_uploads/SJwAlNRP3.jpg" alt="Two baby pugs being carried together"> CSS: .square { width: 400px; height: 400px; object-fit: cover; } ``` Here the value of 'cover' for object-fit takes our original landscape image and crops it inside the given width and height. Without the object-fit property, the image will be stretched or distorted in order to fit the dimensions we have enforced: <style> .square { width: 400px; height: 400px; } .fit-cover { object-fit: cover; } </style> Without `object-fit: cover`: <img class="square" srcset="https://hackmd.io/_uploads/BkNaeE0P2.jpg 500w, https://hackmd.io/_uploads/SJwAlNRP3.jpg 1000w" sizes="(max-width: 300px) 500px, 1000px" src="https://hackmd.io/_uploads/SJwAlNRP3.jpg" alt="Two baby pugs being carried together"> With `object-fit: cover`: <img class="square fit-cover" srcset="https://hackmd.io/_uploads/BkNaeE0P2.jpg 500w, https://hackmd.io/_uploads/SJwAlNRP3.jpg 1000w" sizes="(max-width: 300px) 500px, 1000px" src="https://hackmd.io/_uploads/SJwAlNRP3.jpg" alt="Two baby pugs being carried together"> ## Web Fonts The typefaces used in websites are thankfully not restricted to what is available on an user's device. Fonts can be purchased and self-hosted, or downloaded for free from services like [Google Fonts](https://fonts.google.com/): ``` HTML: <head> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap"> </head> <body> <p class="noto"></p> </body> CSS: p.noto { font-family: 'Noto Sans JP', sans-serif; } ``` In some cases, we may also need to specify the names of our custom fonts in CSS via the `@font-face` rule: ```css @font-face { font-family: "Lobster"; src: url("lobster.woff") format("woff"), url("lobster.otf") format("opentype"); } h1 { font-family: 'Lobster'; } ``` ## CSS Transitions Interactive elements on a web page should be self-evident to users. Links, for example, are often coloured blue and/or underlined to indicate that they are clickable. Buttons may have a common shape, and change ==state== when hovered over: <style> .btn-18501 { padding: 10px 15px; background-color: green; border-radius: 5px; color: white !important; } .btn-18501:hover { background-color: red; cursor: pointer; } </style> <br> <p><a class="btn-18501">Are you sure?</a></p> <br> In the above example, the 'state' instantly changes from green to red when hovered over. Many CSS properties (background-color included) allow their values to be changed gradually rather than immediately, using the `transition` property. A fairly exhaustive list of animatable properties can be found here: https://www.tutorialrepublic.com/css-reference/css-animatable-properties.php Below is an example of the same button markup and styles, with the addition of a `transition` added to the background-color property: ``` HTML: <a class="btn-1850">Are you sure?</a> CSS: a.btn-1850 { padding: 10px 15px; background-color: green; border-radius: 5px; color: white; transition: background-color 2s ease; } a.btn-1850:hover { background-color: red; cursor: pointer; } ``` <style> .markdown-body a:not([href]).btn-1850 { padding: 10px 15px; background-color: green; border-radius: 5px; color: white; transition: background-color 5s ease; } .markdown-body a:not([href]).btn-1850:hover { background-color: red; cursor: pointer; } </style> <br> <p><a class="btn-1850">Are you sure?</a></p> <br> The first value listed after `transition:` is the property we want to transition (background-color). `2s` is the amount of time we want the transition to take (2 seconds), and `ease` is the 'function' used to perform the transition (e.g. does it change evenly over 2 seconds? Does it begin quickly and end slowly?) Here are a few visual examples of the types of animation 'functions' available to use in CSS: ![](https://hackmd.io/_uploads/Hy3x2Yy_h.png) ### Multiple Transitions More than one property can be transitioned via a single rule – simply separate the values with a comma: ``` p { transition: color 2s ease, background-color 4s linear; } ```