Google Certified Mobile Web Specialist Study Notes

Reference

Basic Website Layout and Styling

Mobile Emulation

Viewport

<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 
    device-width = DIP
    initial-scale: DIP:CSS Pixel = 1:1
-->
  • Mobile assume all sites with a width of 980px unless viewport defined
  • Device Independent Pixels(DIP): relate pixels to real distance
  • Device Pixel Ratio(DPR) = Hardware Pixels / Device Independent Pixels
  • Font boosting: scale up auto-detected primary content

Relative Element Width

disable overflow

img, embed, object, video {
    max-width: 100%;
}
  • Not overflow or Absolutely smaller than any viewport(320px)

Tap/Touch Target Sizes

Minimum size 48px X 48px
Rooms in between 40px:

.list-item a {
    padding: 1.5em inherit
}
.nav-item a {
    padding: 1.5em
}

Design and Prioritising Content

Start Small

Selectively apply CSS

More HTTP requests

<link rel="stylesheet" media="screen and (min-width:500px)" href="over500.css">

Fewer HTTP requests, larger files

@media screen and (min-width: 500px) {
    body {}
}

Expensive method (should be avoid)

@import url("over500.css") only screen and (min-width: 500px);

Based on browser width
min-width: apply when larger than the defined value
max-width: apply when smaller than the defined value

Based on devide width
min-device-width:
max-device-width:

Breakpoints

Content Based Breakpoint Selection: Add a breakpoint when the content start to look weird

Complex Media Queries

Apply when viewpoint is in between 300px and 600px

@media screen and (min-width: 300px) and (max-width: 600px) {
    ...
}

Grids

  • Bootstrap

Flexbox

  • display:flex define flex container
  • flex-wrap:wrap define whether flex container wrap inside items
  • order:n use with flex item for flex order inside a flex container
  • width use with order to achieve a full-width layout with multiple element

Responsive Pattern

Column Drop: (Single Column)Stack elements -> Multiple Columns
Mostly Fluid:
  • More grid-like, More Columns fit in different way
  • Margin in widest viewport
flex-container {
    width: 700px;
    margin-left: auto;
    margin-right: auto;
}
Layout Shifter:
  • Most responsive
  • Require more planning
  • Use the flex item order attribute
Off Canvas
  • Place content off screen
  • Only display if screen large enough
    For example, navigation menu
nav {
  width: 300px;
  background-color: #ffffff;
  z-index: 10;
  position: absolute;
  /* This trasform moves the drawer off canvas. */
  -webkit-transform: translate(-300px, 0);
  transform: translate(-300px, 0);
  /* Optionally, we animate the drawer. */
  transition: transform 0.3s ease;
}
nav.open {
  -webkit-transform: translate(0, 0);
  transform: translate(0, 0);
}

Javescript to toggle the menu

var menu = document.querySelector('#menu');
var main = document.querySelector('main');
var drawer = document.querySelector('.nav');

menu.addEventListener('click', function(e) {
    drawer.classList.toggle('open');
    e.stopPropagation();
});
main.addEventListener('click', function() {
    drawer.classList.remove('open');
});

Responsive Table

  • hide columns
  • position header off screen
  • pull data from content
    • content: attr(data-th);
td { 
    position: relative;
    padding-left: 50%; 
}

td:before { 
    position: absolute;
    left: 6px;
    content: attr(data-th);
    font-weight: bold;
}
Tontained Scrolling Table
div {
    width: 100%;
    overflow-x: auto;
}
Vertical Tables (When Applicable)
  • disable table layout with display:block
  • hide table header thead by push it off screen so that it can still be read
  • place header in front of each data with custom data attribute
  • bold to highlight 'headers'
@media screen and (max-width: 500px) {
    table, thead, tbody, th, td, tr {
      display: block;
    }
    thead tr {
      position: absolute;
      top: -9999px;
      left: -9999px;
    }
    td { 
      position: relative;
      padding-left: 50%; 
    }

    td:before { 
      position: absolute;
      left: 6px;
      content: attr(data-th);
      font-weight: bold;
    }
    td:first-of-type {
      font-weight: bold;
    }
}

Fonts

Base font
font-size: 16px
line-height: 1.2em

Ideal Measure of Lines
  • 45-90 cpl
  • 65 for the web

Minor Breakpoints

Long Text Ellipsis


Reponsive Images

  • max-width:100%;
50% width with 10px space in between
img {
    margin-right: 10px;
    max-width: 426px;
    width: calc((100% - 10px)/2);
}
img:last-of-type {
    margin-right: 0;
}
calc((100% - 20px)/3);
  • A space is required around + and - operators
  • with * and /, spaces are not required
  • the problem is an ambiguity around negation
Landscape and Portrait
  • responsively cover the whole height of the viewport: 100vh => 100% height
  • 100vw => 100% width
  • 100vmin: 100% shorter side of the viewport
  • 100vmax: 100% longer side of the viewport
Raster and Vector
  • PNG v SVG
Automation Tools
  • Grunt: Task Runner
  • ImageMagick: Image Processing Tool
Text & Images
  • Font over images
  • Avoid using images but a decent layout and font
  • Use Images sparingly
CSS Background Techniques
  • background:cover
  • CSS gradient
  • CSS elaborate
  • Conditional image display
@media screen and (max-width: 500px)
div.photo {
    background-image: none;
    height: 0;
    margin: 0;
    width: 0;
}
div.photo {
    background-size: cover;
    float: left;
    margin: 0 2vw 1vw 0;
    height: 50vw;
    position: relative;
    top: 3px;
    transition: all 0.5s;
    width: 50vw;
}
  • Alternative images
@media screen and (max-width: 500px)
div {
    background-image: url(koala_crop.jpg);
}
div {
    background-image: url(koala.jpg);
    background-size: cover;
    height: 50vw;
    transition: background-image 2s;
    width: 50vw;
}
  • image-set(): ideal for icon display
div {
    background-image: url(icon1x.png);
    background-image: -webkit-image-set(url(icon1x.png) 1x, url(icon2x.png) 2x);
    background-image: image-set(url(icon1x.png) 1x, url(icon2x.png) 2x);
    height: 128px;
    width: 128px;
}
Contain & Cover
  • background-size:contain => shorter dimension of the background image might be shorter than the container
  • background-size:cover => longer dimention may be covered by the container
Unicode to replace image
<meta charset="utf-8" >
ICON Fonts
Inline SVG & Data URI
srcset
  • assume images display full viewport width
  • 1x & 2x: Pixel Density Descriptor
  • w: Tells browser about image width dimensions
size Attribute: indicate display size of an image
  • sizes="50vw"
  • sizes="(max-width: 250px) 100vw, 50vw"
picture & source element
  • picture: container for sources
  • source: alternative image sources
  • img: fallback but compulsory, the actual element display the images
  • WebP
<picture>
  <source srcset="kittens.webp" type="image/webp">
  <source srcset="kittens.jpeg" type="image/jpeg">
  <img src="kittens.jpg" alt="">
</picture>
  • source with media attribute
<picture>
  <source media="(min-width: 1000px)" srcset="kookaburra_large_1x.jpg 1x, kookaburra_large_2x.jpg 2x">
  <source media="(min-width: 500px)" srcset="kookaburra_medium_1x.jpg 1x, kookaburra_medium_2x.jpg 2x">
  <img src="kookaburra_small.jpg" alt="The kookaburra: a terrestrial tree kingfisher native to Australia and New Guinea (according to Wikipedia)">
</picture>
Final Code
<figure>
    <picture>
      <source media="(min-width: 750px)" srcset="images/volt-1600_large_2x.jpg 2x, images/volt-800_large_1x.jpg"  />
      <source media="(min-width: 500px)" srcset="images/volt_medium.jpg" />
      <img src="images/volt_small.jpg" alt="Volt sign in old Berlin power station">
    </picture>
    <figcaption>Sign in old Berlin power station</figcaption>
</figure>

Flexbox

Container

  • display
  • flex-direction: row | row-reverse | column | column-reverse;
    • row ltr
    • column top to bottom
  • flex-wrap
  • flex-flow: flex-direction flex-wrap
  • justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
  • align-items: flex-start | flex-end | center | baseline | stretch;
  • align-content: flex-start | flex-end | center | space-between | space-around | stretch;

Items

  • order: ;
  • flex-grow: ;
  • flex-shrink: ;
  • flex-basis: | auto;
  • flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
  • align-self: auto | flex-start | flex-end | center | baseline | stretch;

Autoprefixer

npm install grunt-cli grunt-contrib-watch grunt-autoprefixer

grunt

module.exports = function (grunt) {
    grunt.initConfig({
        autoprefixer: {
            dist: {
                files: {
                    'build/style.css': 'style.css'
                }
            }
        },
        watch: {
            styles: {
                files: ['style.css'],
                tasks: ['autoprefixer']
            }
        }
    });
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-contrib-watch');
};

Media Queries

@media speech and (min-width: 30em) and (orientation: landscape) { ... }
@media (min-height: 680px), screen and (orientation: portrait) { ... }

Media types

  • all
  • print
  • screen
  • speech

Media Features

  • width&height
  • aspect-ratio
  • orientation
  • resolution
  • hover
  • light-level
  • monochrome

Logical Operations

  • and combines multiple media features
  • not [Media Type]
  • only [Media Type]
  • , combines multiple media queries ( || )

not: is evaluated last

@media not all and (monochrome) { ... }

is the same as

@media not (all and (monochrome)) { ... }

Media Query Level 4

@media (width <= 30em) { ... }
@media (30em <= width <= 50em ) { ... }
@media (not(hover)) { ... }

Audio and Video

Video

<video controls>
  <source src="rabbit320.mp4" type="video/mp4">
  <source src="rabbit320.webm" type="video/webm">
  <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
</video>
  • width & height
  • autoplay
  • loop: re-play automatically
  • muted
  • poster: display before the video is played
  • preload: none | auto | metadata

Audio

<audio controls>
  <source src="viper.mp3" type="audio/mp3">
  <source src="viper.ogg" type="audio/ogg">
  <p>Your browser doesn't support HTML5 audio. Here is a <a href="viper.mp3">link to the audio</a> instead.</p>
</audio>

Video Text Tracks

  • Subtitles
  • Captions
  • Timed descriptions
WEBVTT

Chapter 1
00:00:01.000 --> 00:00:02.000
Introduction to HTML5

Chapter 2
00:00:03.001 --> 00:00:04.000
Introduction to Chapter 2
  ...
<video controls>
    <source src="example.mp4" type="video/mp4">
    <source src="example.webm" type="video/webm">
    <track kind="subtitles" src="subtitles_en.vtt" srclang="en">
</video>
srt to vtt

Touch Event

// [concurrent "touch AND mouse/keyboard" event binding](https://w3c.github.io/touch-events/#mouse-events)

// set up event listeners for touch
target.addEventListener('touchend', function(e) {
  // prevent compatibility mouse events and click
  e.preventDefault();
  ...
});
...

// set up event listeners for mouse/keyboard
target.addEventListener('click', ...);
...
  • touchstart
  • Zero or more touchmove
  • touchend
  • touchcancel
  • mousemove
  • mousedown
  • mouseup
  • click

TouchEvent: an event that occurs when the state of touches on the surface changes

Touch: a single point of contact

TouchList: a group of touches


Front End Networking

fetch() & Promises

fetch('./api/some.json')
  .then(
    function(response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
          response.status);
        return;
      }

      // Examine the text in the response
      response.json().then(function(data) {
        console.log(data);
      });
    }
  )
  .catch(function(err) {
    console.log('Fetch Error :-S', err);
  });

Response Types

  • basic: a resource on the same origin
  • cors: a resource on another origin which returns the CORs headers
    • restricts the headers you can view to
      • Cache-Control,
      • Content-Language,
      • Content-Type,
      • Expires,
      • Last-Modified, and
      • Pragma.
  • opaque: a request made for a resource on a different origin that doesn't return CORS headers

Defined fetch request mode

  • same-origin: only same origin request succeeds
  • cors: allow same origin and also other origins with CORs headers
  • cors-with-forced-preflight: always perform a preflight check
  • no-cors: make requests to other origins with no CORS headers in return (Not possible yet July 2, 2018)
fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(function(response) {
    return response.text();
  })
  .then(function(text) {
    console.log('Request successful', text);
  })
  .catch(function(error) {
    log('Request failed', error)
  });

Chaining Promises

benefit: share logic across fetch requests

function status(response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json(response) {
  return response.json()
}

fetch('users.json')
  .then(status)
  .then(json)
  .then(function(data) {
    console.log('Request succeeded with JSON response', data);
  }).catch(function(error) {
    console.log('Request failed', error);
  });

Post Request

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(function (data) {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function (error) {
    console.log('Request failed', error);
  });

Credential

fetch('https://example.com', {
  credentials: 'include'  
})
  • 'same-origin': only send on the same origin
  • 'omit': not to include credentials

Second Parameter, an init object

fetch(url, {
    method: "POST", // *GET, POST, PUT, DELETE, etc.
    mode: "cors", // no-cors, cors, *same-origin
    cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
    credentials: "same-origin", // include, *same-origin, omit
    headers: {
        "Content-Type": "application/json; charset=utf-8",
        // "Content-Type": "application/x-www-form-urlencoded",
    },
    redirect: "follow", // manual, *follow, error
    referrer: "no-referrer", // no-referrer, *client
    body: JSON.stringify(data), // body data type must match "Content-Type" header
    })
    .then(response => response.json()); // parses response to JSON

Uploading JSON data

var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers:{
    'Content-Type': 'application/json'
  }
}).then(res => res.json())
.then(response => console.log('Success:', JSON.stringify(response)))
.catch(error => console.error('Error:', error));

Uploading a file

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', JSON.stringify(response)));
Multiple Files
var formData = new FormData();
var photos = document.querySelector("input[type='file'][multiple]");

formData.append('title', 'My Vegas Vacation');
formData.append('photos', photos.files);

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData
})
.then(response => response.json())
.then(response => console.log('Success:', JSON.stringify(response)))
.catch(error => console.error('Error:', error));

Request Object

var myHeaders = new Headers();

var myInit = { method: 'GET',
               headers: myHeaders,
               mode: 'cors',
               cache: 'default' };

var myRequest = new Request('flowers.jpg', myInit);

fetch(myRequest).then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

Headers interface

  • Content-Type
  • Content-Length
  • X-Custom-Header
  • Set-Cookie
  • Origin
Check Content Type
fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.log(error); });
Guard
  • none
  • request => may not set 'Content-Length'
  • request-no-cors: for request created with Request.mode=no-cors
  • response => not allow to insert 'Set-Cookie'
  • immutable

Response Objects

body
  • Blob
  • BufferSource: a typedef
  • FormData
  • ReadableStream: a readable stream of byte data
  • URLSearchParams
  • USVString: the set of all possible sequences of unicode scalar values
Common Properties
  • Response.status: An integer(default 200)
  • Response.statusText: A string(default 'OK')
  • Response.ok: Boolean (check if status is between 200-299)

Body: an instance of any of the following types

  • ArrayBuffer: represent a generic, fixed-length raw binary data buffer
  • ArrayBUfferView: representing any of a group of JavaScript TypedArray
  • Blob/File: a file-like object of immutable, raw data
  • string
  • URLSearchParams: defines utility methods to work with the query string of a URL
  • FormData: a way to easily construct a set of key/value pairs representing form fields and their values
Extract Methods
  • arrayBuffer()
  • blob()
  • json()
  • text()
  • formData()

Feature Detection

if (self.fetch) {
    // run my fetch request here
} else {
    // do something with XMLHttpRequest?
}

polyfill

Stream


Cross-Origin Resource Sharing (CORS)


Accessibility

Web Fundamentals – Accessibility

Web Content Accessibility

  • Preceivable
  • Operable
  • Understadable
  • Robust
Focus
  • Built-in interactive HTML elements are implicity focusable
    • automatically inserted into the tab order
    • have built-in keyboard event handling
Semantics
  • Assistive technology
  • Affordances
  • Screen readers
    • The element's role or type, if it is specified (it should be).
    • The element's name, if it has one (it should).
    • The element's value, if it has one (it may or may not).
    • The element's state, e.g., whether it is enabled or disabled (if applicable).
Styling
  • styles for focus and various ARIA states
    • Styling focus
    /* At a minimum you can add a focus style that matches your hover style */
    :hover, :focus {
      background: #c0ffee;
    }
    
    -Input modality: show button when input by keyboard
    fake-button {
        display: inline-block;
        padding: 10px;
        border: 1px solid black;
        cursor: pointer;
        user-select: none;
      }
    
      fake-button:focus {
        outline: none;
        background: pink;
      }
    
    • Styling states with ARIA
      .toggle[aria-pressed="true"] { ... }
  • can be zoomed or scaled to accommodate users
    • Multi-device responsive design
      • <meta name="viewport" content="width=device-width, initial-scale=1.0">
      • A minimum recommended touch target size is around 48 device independent pixels
  • Choosing the right colors and contrast