$30 off During Our Annual Pro Sale. View Details »

New Adventures in Responsive Web Design

New Adventures in Responsive Web Design

We all are still trying to figure out just the right strategy for designing and buildings responsive websites just in time. We want to use all of these technologies, but how can we use them efficiently, and how do we achieve it within a reasonable amount of time?

In this talk, Vitaly Friedman, editor-in-chief of Smashing Magazine, will be looking into some practical strategies for crafting fast, resilient and flexible responsive design systems by utilizing all of those wonderful shiny web technologies we have available today. We’ll also talk about dealing with legacy browsers and will cover a few dirty little techniques that might ensure that your responsive websites will stay relevant, flexible and accessible in the years to come.

Vitaly Friedman

April 25, 2017
Tweet

More Decks by Vitaly Friedman

Other Decks in Design

Transcript

  1. New Adventures In
    Responsive Web Design
    Vitaly Friedman (illustrations by Simon C Page, Nelson Cash)
    April 24, 2017 • Toronto, Canada

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. It’s your lucky day. You grow, and your
    company expands to foreign markets. Your
    site has to support 7 languages. How do
    you architect CSS/JS to support it?

    View Slide

  6. View Slide

  7. View Slide

  8. The crucial asset of longevity is
    building “neutral”, configurable
    components which can be easily
    extended and adjusted.

    View Slide

  9. // english.json

    {

    serviceName: 'english';

    language: 'en';

    textDirection: 'ltr';

    socialMediaButtons: ['twitter', 'facebook', 'reddit'];

    }


    // russian.json

    {

    serviceName: 'russian';

    language: 'ru';

    textDirection: 'ltr';

    textLength: 'verbose';

    socialMediaButtons: ['twitter', 'facebook', 'vk'];

    }

    View Slide

  10. config/english.json

    /russian.json

    css/english.css

    /russian.css


    sass/english.scss

    /russian.scss

    /mixins/_textDirection.scss

    /mixins/_textLength.scss

    /mixins/_socialMediaButtons.scss


    index.en.html

    index.ru.html

    View Slide

  11. // english.scss

    $english = true;

    $script = 'latin';


    // russian.scss

    $russian = true;

    $script = 'cyrillic';


    @if $russian {

    // apply styling only to Russian version

    }
    With a templating language, we can then
    plug data from config files and hence
    customize HTML output for every language.

    View Slide

  12. // english.scss

    $english = true;

    $script = 'latin';

    $direction = 'left';

    @include(mixins/directions);

    @include(mainstyles);


    // arabic.scss

    $arabic = true;

    $script = 'arabic';

    $direction = 'right';

    @include(mixins/directions);

    @include(mainstyles);


    @if $arabic {

    // apply styling only to Arabic version

    }

    View Slide

  13. // directions.scss

    $margin-left: margin-left;

    if $direction == 'right' {

    $margin-left: margin-right;

    }


    $padding-left: padding-left;

    if $direction == 'right' {

    $padding-left: padding-right;

    }


    $left: left;

    if $direction == 'right' {

    $left: right;

    }

    View Slide

  14. // directions.scss

    $margin-left: margin-left;

    if $direction == 'right' {

    $margin-left: margin-right;

    }


    $padding-left: padding-left;

    if $direction == 'right' {

    $padding-left: padding-right;

    }


    $left: left;

    if $direction == 'right' {

    $left: right;

    }
    $margin-right: margin-right;

    if $direction == 'right' {

    $margin-right: margin-left;

    }
    $padding-right: padding-right;

    if $direction == 'right' {

    $padding-right: padding-left;

    }
    $right: right;

    if $direction == 'right' {

    $right: left;

    }

    View Slide

  15. // global.scss

    .nav-element {

    #{$margin-left}: 10px;

    #{$padding-right}: 10px;

    #{$left}: 10px;

    }


    // english.css

    .nav-element {

    margin-left: 10px;

    padding-right: 10px;

    left: 10px;

    }


    // arabic.css

    .nav-element {

    margin-right: 10px;

    padding-left: 10px;

    right: 10px;

    }

    .nav-element {

    float: flip(left, right);

    padding: flip(10px 10px 0 0,

    10px 0 0 10px);

    line-height: get-script-value

    (latin 1.3, arabic 1.6);

    }

    View Slide

  16. // global.scss

    .nav-element {

    float: flip(left, right);

    padding: flip(10px 10px 0 0, 10px 0 0 10px);

    line-height: get-script-value(latin 1.3, arabic 1.6);

    }


    // english.css

    .nav-element {

    float: left;

    padding: 10px 10px 0 0;

    line-height: 1.3em;

    }


    // arabic.css

    .nav-element {

    float: right;

    padding: 10px 0 0 10px;

    line-height: 1.6em;

    }


    View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. You have to build in fluid, flexible type,
    and designers want you to implement
    perfect modular scale. The proportions
    have to stay consistent across screens.

    View Slide

  24. View Slide

  25. View Slide

  26. “How do you efficiently scale up /
    down any UI component (e.g. a
    slider or calendar) and keep all the
    proportions intact—without
    fiddling with width, height or
    border-radius manually?

    — @simurai

    View Slide

  27. View Slide

  28. View Slide

  29. “By sneaking a Trojan horse into
    your components. We use rem for
    components “root” and em for sub-
    parts of the components. Then, by
    adjusting the font-size of the root,
    we adjust all size-related CSS
    properties of a component at once.

    — @simurai

    View Slide

  30. View Slide

  31. View Slide

  32. View Slide

  33. With media queries, we can
    target specific screen width
    ranges and adjust type by just
    manipulating the font-size rem
    value of the article’s container.

    View Slide

  34. CSS Architecture
    • Main CSS contains default type styles:
    /* CSS Reset of your choice */

    body { font-size: 100%; line-height: 1.45em; }
    /* 2:3 Perfect Fifth: 7.111, 10.667, 16 (i), 24, 36, 54 */

    h1 { font-size: 3.375rem }

    h2 { font-size: 2.25rem }

    h3 { font-size: 1.5rem }

    h4 { font-size: 1rem }

    caption { font-size: 0.667rem }

    small { font-size: 0.444rem }

    View Slide

  35. CSS Architecture
    /* CSS Reset of your choice */

    body { font-size: 100%; line-height: 1.45em; }
    /* 2:3 Perfect Fifth: 7.111, 10.667, 16 (i), 24, 36, 54 */

    h1 { font-size: 3.375rem }

    h2 { font-size: 2.25rem }

    h3 { font-size: 1.5rem }

    h4 { font-size: 1rem }

    caption { font-size: 0.667rem }

    small { font-size: 0.444rem }
    /* Ideal line length: 66 ch; => max-width: 33em */

    article { max-width: 33em; }

    :lang(de) article { max-width: 40em; }

    p, ul, ol, dl, table { margin-bottom: 1.45rem; }

    View Slide

  36. CSS Architecture
    /* CSS Reset of your choice */

    body { font-size: 100%; line-height: 1.45em; }
    /* 2:3 Perfect Fifth: 7.111, 10.667, 16 (i), 24, 36, 54 */

    h1 { font-size: 54px; font-size: 3.375rem }

    h2 { font-size: 36px; font-size: 2.25rem }

    h3 { font-size: 16px; font-size: 1rem; }

    h4 { font-size: 24px; font-size: 1.5rem }

    caption { font-size: 7px; font-size: 0.667rem }

    small { font-size: 11px; font-size: 0.444rem }
    /* Ideal line length: 66 ch; => max-width: 33em */

    article { max-width: 33em; }

    :lang(de) article { max-width: 40em; }

    p, ul, ol, dl, table { margin-bottom: 1.45rem; }

    View Slide

  37. “To achieve fluid typography, we can
    combine the calc( ) function in CSS
    with viewport units (vw/vh/vmin/
    vmax). But what if you want to
    apply a modular scale to font sizes?

    View Slide

  38. We can get perfectly fluid type with

    html { font-size: calc(1em + 1vw); }
    but it gives us little control over the
    rate at which viewport units change.
    Media queries? Well, with them
    usually there is an annoying “visual”
    jump between fixed and fluid values.

    — Mike Riethmuller

    View Slide

  39. View Slide

  40. …E.g. if we wanted to choose a font-
    size of 16px at a screen resolution of
    400px and then transition to 24px at
    a resolution of 800px, we couldn’t do
    it without a breakpoint.

    — Mike Riethmuller

    View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. View Slide

  45. You choose the min and max font-
    size and the screen sizes, over which
    the font should scale and plug them
    into the equation. You can use any
    unit type including ems, rems or px.

    — Mike Riethmuller

    View Slide

  46. View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. View Slide

  51. View Slide

  52. View Slide

  53. View Slide

  54. View Slide

  55. View Slide

  56. View Slide

  57. View Slide

  58. View Slide

  59. You’ve designed sophisticated hover-
    effects (e.g. on images) but what if hover
    isn’t available? Is there a way to reliably
    check for interaction feature support and
    adjust the experience accordingly?

    View Slide

  60. View Slide

  61. In the past, to handle hover-based
    interactions, we relied on JavaScript
    workarounds to detect hover or tap.
    With CSS Level 4 Media Queries, we
    can detect if hover is available.

    View Slide

  62. (Mouse isn’t the only device input
    that can “hover” (game consoles can
    “hover”, too). Also, a device might
    have multiple inputs, but the media
    query looks up the primary input.)

    View Slide

  63. — Create 2 illustrations: low-fi SVG, high-fi SVG.

    — Wrap a link around embedded low-fi SVG first,

    — Set the high-fi SVG as background image on that link,

    — By default → set opacity: 0 on low-fi SVG,

    — Hover supported → set opacity: 1 instead, animate to 0 on hover.

    View Slide

  64. • CSS:

    .source__image {

    opacity: .75;

    transition: opacity 400ms;

    background: url("high-fidelity.svg");

    }


    .source__image:hover {

    opacity: 1;

    }


    .source__image svg {

    opacity: 0;

    }
    • CSS:

    @media (hover) {

    .source__image svg {

    opacity: 1;

    transition: opacity 400ms;

    } 

    .source__image:hover svg {

    opacity: 0;

    } 

    }

    View Slide

  65. View Slide

  66. View Slide

  67. • CSS:

    .page-wrap::after {

    display: block;

    content: '';

    position: fixed;

    bottom: 0;

    left: 0;

    width: 100%;

    height: 10em;

    background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1));

    }

    View Slide

  68. Beware: any content that falls
    underneath the shadow we just
    created will not be selectable or
    otherwise available for interaction.

    View Slide

  69. We don’t want any user interaction,
    and we don’t want to block interactivity
    of elements falling beneath the fade
    pseudo element. Meet pointer-events.

    View Slide

  70. • CSS:

    .page-wrap::after {

    display: block;

    content: '';

    position: fixed;

    bottom: 0;

    left: 0;

    width: 100%;

    height: 10em;

    background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1));

    }

    View Slide

  71. • CSS:

    @supports (pointer-events: none)

    .page-wrap::after {

    display: block;

    content: '';

    position: fixed;

    bottom: 0;

    left: 0;

    width: 100%;

    height: 10em;

    background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1));

    pointer-events: none;

    }

    }

    View Slide

  72. View Slide

  73. View Slide

  74. View Slide

  75. View Slide

  76. View Slide

  77. What if you’ve designed a rich visual
    experience for your website or app, but it
    makes the website hardly usable on low
    battery? How do you make sure that your
    websiter remains “responsive” then?

    View Slide

  78. View Slide

  79. The Battery Status API talks to the
    device’s hardware and provides
    accurate data about the device’s
    charging state. Accessible via
    navigator.getBattery().

    View Slide

  80. • JavaScript:

    if (navigator.getBattery) {

    // Battery API available.

    }

    else {

    // No Battery API support.

    // Handle error accordingly.

    }
    • JavaScript:

    navigator.getBattery()

    .then(function(batteryManager) {

    // Get current charge in percentages.

    var level = batteryManager.level * 100;

    })


    .catch(function(e) {

    console.error(e);

    });

    View Slide

  81. getBattery() returns a promise
    and resolves with a batteryManager
    object containing information about
    the current status of the hardware.

    View Slide

  82. — batteryManager.level returns the current
    charge, a float between 0 and 1.

    — batteryManager.charging returns if the
    device is on power supply (true/false).

    — batteryManager.chargingTime returns
    remaining time in sec till fully charged.

    — batteryManager.dischargingTime returns
    remaining time until battery is dead.

    View Slide

  83. — batteryManager.onlevelchange

    — batteryManager.onchargingchange

    — batteryManager.onchargingtimechange

    — batteryManager.ondischargingtimechange
    The Battery Status API also
    provides events that can be used to
    monitor changes in above properties.

    View Slide

  84. getBattery() returns a promise
    and resolves with a batteryManager
    object containing information about
    the current status of the hardware.

    View Slide

  85. • JavaScript:

    navigator.getBattery()

    .then(function(battery) {

    // Switch to Power Save for non-charging, low battery.

    battery.onlevelchange = function() {

    if (battery.level < 0.3 && !battery.charging) {

    powerSavingMode = true;

    // Remove parallax, web fonts, map embeds, video, JS

    // Save user’s data, inform them about low battery

    }

    }

    }

    .catch(function(e) {

    console.error(e);

    });

    View Slide

  86. View Slide

  87. View Slide

  88. View Slide

  89. Often we want to target specific children

    in the DOM, but the parent might have
    unknown number of children. So usually
    we would style all children first and then
    overwrite the styles.

    View Slide

  90. View Slide

  91. “What if you want all links to have an
    underline except the ones you
    specify? Or you want all ’s in the
    navigation to have a right border,
    except the last one. Normally you
    would use :last-child (or extra class)
    to overwrite a default CSS rule.

    — Ire Aderinokun

    View Slide

  92. View Slide

  93. View Slide

  94. View Slide

  95. “What if you want a tidy grid with
    fine and consistent line endings?
    Sometimes you might end up with
    not enough space to display all
    content blocks in a row, or not
    enough items to properly fill a row.

    — Patrick Clancey

    View Slide

  96. View Slide

  97. View Slide

  98. A quantity selector is a CSS
    selector that allows styles to be
    applied to elements based on the
    number of siblings.

    View Slide

  99. View Slide

  100. General sibling selector ~
    separates two selectors and
    matches the second element only
    if it is preceded by the first, and
    both share a common parent.

    View Slide

  101. View Slide

  102. View Slide

  103. • CSS:

    li:nth-last-child(6):first-child,

    li:nth-last-child(6):first-child ~ li {

    color: green;

    }

    View Slide

  104. • CSS:

    li:nth-child(n+6) {

    color: green;

    }

    View Slide

  105. li:nth-last-child(n+6) {

    color: green;

    }

    View Slide

  106. li:nth-last-child(n+6):first-child,

    li:nth-last-child(n+6):first-child ~ li {

    color: green;

    }

    View Slide

  107. View Slide

  108. To create a perfect grid, we’ll
    need to define layout for any
    number of items with specific
    quantity selectors within media
    queries.

    View Slide

  109. View Slide

  110. • “Mod query selector” in CSS:

    li:nth-last-child(3n):first-child,

    li:nth-last-child(3n):first-child ~ li {

    /* … styles for list items in a list divisible by 3 … */

    }

    View Slide

  111. li:nth-last-child(3n):first-child,

    li:nth-last-child(3n):first-child ~ li {

    /* … styles for list items in a list divisible by 3 … */

    }
    — Select all following sibllings (~ li) which follow after

    — The first child (first li in the list here), (:first-child) that also is

    — Divisible by 3, starting from the end (:nth-last-child(3n)).

    View Slide

  112. — Select all the items up to and including the fifth item, then

    — Select all the items from the third item onwards.
    • “Range selector” in CSS:

    li:nth-child(n+3):nth-child(-n+5) {

    /* … styles for list items from 3 to 5 … */

    }

    View Slide

  113. View Slide

  114. We use a mod query to check
    if the number of items is
    divisible by 3. Then we use a
    range selector to style items
    differently, e.g. apply one
    styling to first three, another
    styling to the fourth through
    ninth, and another to 10th
    onwards. Voilà!

    View Slide

  115. • “Mod query selector” in CSS:

    li:nth-last-child(3n):first-child /* mod query */

    ~ li:nth-child(n+4):nth-child(-n+6) { /* range selector */

    /* … styles for 4th to 6th elements, in a list divisible by 3 … */

    }

    View Slide

  116. View Slide

  117. View Slide

  118. View Slide

  119. View Slide

  120. What if you wanted the color of the SVG
    icon to inherit the color property of a
    button in which it resides? Can we use
    CSS alone (no SASS/LESS) to establish
    this relationship?

    View Slide

  121. View Slide

  122. View Slide

  123. View Slide

  124. View Slide

  125. View Slide

  126. View Slide

  127. View Slide

  128. View Slide

  129. View Slide

  130. View Slide

  131. View Slide

  132. What if you want to use a full-width
    element in a fixed-width container? E.g.
    when you want some content to extend
    beyond the boundaries of the container?

    View Slide

  133. View Slide

  134. • HTML:


    ...

    ...


    • CSS:

    .u—containProse {

    margin: 0 auto;

    max-width: 40em;

    }

    View Slide

  135. • HTML:


    ...






    ...


    • CSS:

    .u—containProse {

    margin: 0 auto;

    max-width: 40em;

    }

    View Slide

  136. To release our child element from its
    container, we need to know how much
    space there is between the container
    edge and the viewport edge.

    View Slide

  137. What’s this space exactly? Well, we
    just need to subtract half the container
    width from half the viewport width.
    calc() to the rescue!

    View Slide

  138. • HTML:


    ...


    ...


    • CSS:

    .u—release {

    margin-left: calc(-50vw + 50%);

    margin-right: calc(-50vw + 50%);

    }

    View Slide

  139. View Slide

  140. When the height or width of the
    initial containing block is changed,
    they are scaled accordingly. Note that
    the initial containing block’s size is
    affected by the presence of scrollbars
    on the viewport.

    View Slide

  141. • HTML:


    ...


    ...


    • CSS:

    .u—release {

    margin-left: calc(-50vw + 50%);

    margin-right: calc(-50vw + 50%);

    }

    View Slide

  142. • HTML:


    ...


    ...


    • CSS:

    .u—release {

    margin-left: calc(-50vw + 50%);

    margin-right: calc(-50vw + 50%);

    }
    html, body {

    overflow-x: hidden;

    }

    View Slide

  143. View Slide

  144. • CSS:

    .u—release {

    width: 100vw;

    position: relative;

    left: 50%;

    right: 50%;

    margin-left: -50vw;

    margin-right: -50vw;

    }
    We push the container to the exact
    middle of the browser window with
    left: 50%, then pull it back to the left
    edge with -50vw margin

    (h/t Sven Wolfermann).

    View Slide

  145. View Slide

  146. View Slide

  147. Images make up a large portion of
    bandwidth payload. Is there any way to
    optimize images beyond good ol’ image
    optimization? What if a hero image has to
    render fast, e.g. on landing pages?

    View Slide

  148. “...Given two identical images that
    are displayed at the same size on a
    website, one can be dramatically
    smaller than the other in file size
    if it’s highly compressed and
    dramatically larger in dimensions
    than it is displayed in.
    — Daan Jobsis

    View Slide

  149. 600×400px file, 0% JPEG quality,
    displayed in 600×400 (file size 7 Kb)

    View Slide

  150. 600×400px file, 0% JPEG quality,
    displayed in 300×200 (file size 7 Kb)

    View Slide

  151. 600×400px file (7 Kb)

    ________________________________0
    % JPEG quality

    displayed in 300×200
    300×200px file (21 Kb)

    _________________________________

    80% JPEG quality

    displayed in 300×200

    View Slide

  152. View Slide

  153. View Slide

  154. Aftonbladet’s Images Strategy
    • Design specification defined main requirements:
    • Optimization of the mobile version, 

    • The pages should be easy to cache,

    • Solution: Loading images with JavaScript after
    HTML and CSS have fully loaded.
    • A single HTML file to be served to all users,
    • All images on a content delivery network (CDN),
    • No complexity in the image-serving logic,
    • Serving different image versions to different devices.

    View Slide

  155. • Editors can select compression rates, but aggressive
    compression is a default.
    • 30% JPEG quality: bright-red areas don’t compress well.

    View Slide

  156. • On average, the “large” screen has 650 Kb,

    “medium” — 570 Kb, “small” — 450 Kb.
    • The homepage on a mobile device has 40 images.

    View Slide

  157. • The original photo has 1600px width, 971 Kb.
    Quality 60 brings the size down to 213 Kb.

    View Slide

  158. • Blurring unimportant parts of the photo brings
    the size down to 147 Kb.

    View Slide

  159. Sequential JPEG Progressive JPEG
    Images taken from http://www.pixelstech.net/article/1374757887-Use-progressive-JPEG-to-improve-user-experience 13 / 44

    View Slide

  160. Scans
    14 / 44

    View Slide

  161. Default Scan Levels
    Thanks to Frédéric Kayser for creating 'jsk': http://encode.ru/threads/1800-JSK-JPEG-Scan-Killer-progressive-JPEG-explained-in-slowmo 15 / 44

    View Slide

  162. 16 / 44

    View Slide

  163. 17 / 44

    View Slide

  164. 18 / 44

    View Slide

  165. 1st Scan Layer Has Small Byte Size
    Ships Fast
    &
    Shows Soon
    19 / 44

    View Slide

  166. 31 / 44

    View Slide

  167. 1
    32 / 44

    View Slide

  168. 2
    33 / 44

    View Slide

  169. 3
    34 / 44

    View Slide

  170. 4
    35 / 44

    View Slide

  171. 5
    36 / 44

    View Slide

  172. 37 / 44

    View Slide

  173. View Slide

  174. View Slide

  175. “What if you have a large photo that
    requires a transparent shadow?
    PNG is way too large in file size,
    and JPEG isn’t good enough in
    quality because of the gradient in
    the background. What do you do?

    View Slide

  176. View Slide

  177. View Slide






  178. xlink:href="can-top.jpg">
    • hero-image.svg:

    xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 560 1388">



    View Slide

  179. • HTML/CSS:

    , background: url("hero-image.svg")





    xlink:href="can-top.jpg">
    • hero-image.svg:

    xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 560 1388">



    View Slide

  180. View Slide

  181. View Slide

  182. Text compression matters. What’s the
    best strategy to compress assets/content
    these days? Essentially, we want to
    minimize bandwidth + speed up delivery.

    View Slide

  183. gzip is the most common compression
    format on the web; its most common
    implementation is zlib, and it uses a
    combination of LZ77 and Huffman
    encoding algorithms (called deflate).

    View Slide

  184. Each compression library (like zlib)
    has preset quality settings, ranging
    from fast compression (levels 1–3) to
    slow compression (levels 4–9).

    View Slide

  185. As developers, we care about the
    transferred file size and compression /
    decompression speed — for both static
    and dynamic web content.

    View Slide

  186. “ Zopfli can be thought of as a way to do
    a “very good, but slow, deflate or zlib
    compression”. High compression ratio
    at the cost of a higher overhead for
    compressing. Backwarts-compatible
    for browsers that support only gzip.
    — Cody Ray Hoeft

    https://www.quora.com/What-is-Brotli-How-is-it-different-from-Zopfli

    View Slide

  187. “ Brotli is a whole new compression and
    decompression format. For Brotli,
    browser support has to be built into
    the browser. Future-compatible with
    the next generation of browsers.
    — Cody Ray Hoeft

    https://www.quora.com/What-is-Brotli-How-is-it-different-from-Zopfli

    View Slide

  188. View Slide

  189. “ Brotli is a whole new lossless
    compression and decompression
    format. For Brotli, browser support
    has to be built into the browser.
    Future-compatible with the next
    generation of browsers.
    — Cody Ray Hoeft

    https://www.quora.com/What-is-Brotli-How-is-it-different-from-Zopfli

    View Slide

  190. Brotli and Zopfli
    • Compared to gzip, Brotli is significantly slower at
    compressing data, but provides much better savings.
    — Brotli is an open-sourced, lossless compression format,

    — Brotli shows significant improvements for static content,

    — Brotli’s decompression is fast: comparable to zlib,

    — Brotli has an advantage for large fiels on slow connections,

    — Expect 14-39% file savings on text-based assets (level 4),

    — Ideal for HTML, CSS, JavaScript, SVG — anything text-based.

    — Brotli support is restricted to HTTPS connections.

    View Slide

  191. View Slide

  192. View Slide

  193. View Slide

  194. Brotli and Zopfli
    • Compared to gzip, Brotli is significantly slower at
    compressing data, but provides much better savings.
    — Browsers advertise support via Accept-Encoding request header:

    Accept-Encoding: gzip, deflate, sdch, br

    — Servers can choose to use Brotli and serve Content-Encoding: br

    — You might need to recompile your server to include a Brotli

    module (available for Apache, Nginx, IIS).

    — Zopfli often not applicable for on-the-fly compression, but a good 

    alternative for one-time compression of static content.

    View Slide

  195. View Slide

  196. View Slide

  197. View Slide

  198. View Slide

  199. Brotli/Zopfli Compression
    Strategy
    • Compared to gzip, Brotli is significantly slower at
    compressing data, but provides much better savings.
    — Pre-compress static assets with Brotli+Gzip at the highest level,

    — Compress (dynamic) HTML on the fly with Brotli at level 1–4.

    — Check for Brotli support on CDNs (KeyCDN, CDN77, Fastly).

    — Server handles content negotiation for Brotli or gzip.

    — Use Zopfli if you can’t install/maintain Brotli on the server.
    “Results of experimenting with Brotli for dynamic web content”, https://blog.cloudflare.com/results-experimenting-brotli/,

    Tim Kadlec, “Understanding Brotli's Potential”, https://blogs.akamai.com/2016/02/understanding-brotlis-potential.html,

    “Static site implosion with Brotli and Gzip”, https://www.voorhoede.nl/en/blog/static-site-implosion-with-brotli-and-gzip/

    “Current state of Brotli compression”, https://samsaffron.com/archive/2016/06/15/the-current-state-of-brotli-compression

    View Slide

  200. Perceived performance matters.

    The more invisible the loading of assets is,
    the faster the overall experience is. How
    can we speed up delivery effortlessly?

    View Slide

  201. Resource hints allow developers to
    provide some hints to the browser to
    prompt the download of assets, or
    rendering, silently in the background.

    View Slide

  202. — 


    tells browsers to fetch a resource that will
    probably be needed for the next navigation
    (low priority).
    Resource hints allow developers to
    provide some hints to the browser to
    prompt the download of assets, or
    rendering, silently in the background.

    View Slide

  203. — 


    tells browsers to fetch a resource that will
    probably be needed for the next navigation
    (low priority).
    — 


    tells browsers to render the specified page in
    the background (low priority).

    View Slide

  204. — 


    tells browsers to render the specified page in
    the background (low priority).
    — 


    gives a hint to the browser to perform a DNS
    lookup in the background (low priority).

    View Slide

  205. — 


    gives a hint to the browser to perform a DNS
    lookup in the background (low priority).
    — 


    gives a hint to the browser to initiate the
    connection handshake (DNS, TCP, TLS) in
    the background (low priority).

    View Slide

  206. — 


    gives a hint to the browser to initiate the
    connection handshake (DNS, TCP, TLS) in
    the background (low priority).
    — 


    gives a hint to the browser to prefetch resources and
    set the right resource priority for loading assets.

    View Slide

  207. The basic use case for preload is
    loading of late-discovered critical
    resources. If we omit the as attribute,
    it’s just an XHR request, fetching
    with a fairly low priority.

    View Slide

  208. href="late-discovered.js" 

    as="script">




    The as attribute tells the browser what it is
    downloading. E.g. audio, font, image,
    script, style, track, video, document.

    View Slide

  209. href="font.woff2” 

    type="font/woff2” 

    crossorigin 

    as="font">




    E.g. you could include preload directives
    for web fonts that you know you’ll need
    for rendering of the page.

    View Slide

  210. var preload = document.createElement("link");

    link.href= "myscript.js"

    link.rel= "preload";

    link.as= "script";

    document.head.appendChild(link);
    E.g. you could request the fetching of a
    resource because you know you’ll need it, but
    you don’t want to execute it yet.

    View Slide

  211. var script = document.createElement("script");

    script.src= "myscript.js"

    document.body.appendChild(script);
    E.g. you could request the fetching of a
    resource because you know you’ll need it, but
    you don’t want to execute it yet.

    View Slide

  212. href="map.png" media="(max-width: 600px)">

    href="map.js" media="(min-width: 601px)">
    E.g. you could load assets conditionally

    (e.g. a static map on smaller screens, and an
    interactive map on large screens).

    View Slide

  213. View Slide

  214. View Slide

  215. View Slide

  216. View Slide

  217. View Slide

  218. View Slide