Skip to content

Figures Guide

Barry Pollard edited this page Sep 24, 2022 · 77 revisions
Clone this wiki locally

The Web Almanac is about combining the raw stats and trends of the HTTP Archive with the expertise of the web community. As such presenting the data in the chapters in various figures is critical to the aims and success of the project.

Example Images from Web Almanac

We want well-presented figures that are consistent throughout the chapters and are as accessible as possible across a wide variety of devices, while also enabling additional features to analyze the data both now and in the future.

Table of contents

Why use figures?

Without figures, we would simply be discussing stats directly in text. This can still get the point across to readers, but it's not very engaging. Figures serve a few purposes:

  • Figures call attention to the data points worth discussing
  • Figures break up monotonous blocks of text and make chapters easier to read
  • Readers can more easily grasp the magnitude of the data and compare data points based on size

Figures are also treated as landmarks within the chapter. Each figure has an ID that can be deep-linked and referenced later in the chapter, for example "Recall from [Figure 21](#fig-21)".

Figures are treated as portals into our methodology. We annotate every figure with metadata to enable readers to dive into different layers of the analysis. Readers can drill into a figure to see how the data was queried and view the raw results in the spreadsheet. This kind of access is important to the openness and transparency of the Web Almanac methodology, and something that sets us apart from other "state of the web" research.



Note: these deeper layers of the methodology should be available to readers who are explicitly looking for it, but should not typically be treated as top-level parts of our content. That is to say that readers should give some indication that they want to dig deeper before we present them with links to queries or spreadsheets. The standard way readers do this is by clicking into the three-dot menu in the corner of the figure. At the bottom of each chapter we also provide "View results" and "View queries" buttons in the context of other chapter-level meta actions. We should avoid linking directly to specific queries or spreadsheets in the text. If the results are worth discussing, use a figure!

Given their visual nature, figures also have accessibility challenges. We've created primitives to address some of these challenges and help chapter authors ensure that the results conveyed visually are also described textually. Figures have a written description that can be toggled in the UI as well as alt and aria attributes for users with assistive technologies and even SEO. The written descriptions are also particularly useful for people who may not necessarily have trouble seeing the figures, but they do have trouble understanding the figures. We consider accessibility to be an unresolved issue and we're constantly looking for ways to improve it.

Who is responsible for creating figures?

In short - we all are!

  • Analysts should create the data from SQL queries of the HTTP Archive and CrUX datasets.
  • Authors should then review the data and pick which ones they would like to include in the chapter and provide some guidance on what format they want this presented in (chart, tables...etc.)
  • Authors should also select any external data sources (images, videos...etc.) from their experience as experts in the given topic. Images should be copied locally to the chapter directory in git and so authors should ensure they have permission to use any external images.
  • Analysts should then create the charts in Google Sheets, export the fallback images and add to Git, and provide the basic markup to the Authors as described below.
  • Authors should then add/review Captions and Descriptions as they are the writers not the Analysts.
  • Once a chapter is ready for review, Reviewers should review the figures and ensure they are technically correct and back up what is being stated in the text.
  • Editors should review figures to ensure they are marked up correctly, are descriptive, easily understood by non-experts, and follow a consistent style across chapters.
  • Developers should add new functionality to make adding figures easier.

Types of visuals in the Web Almanac

We've a few different options for embedding figures (and there are code samples for these below), but they fall into these categories:

Google Sheets

As of this writing we use Google Sheets to gather the results of our analysis. We then make iframes embeds of the graphs it produces to give interactive visuals on hover (example). These are supplemented by fallback images where Sheets graphs are not loaded depending on network and device capabilities (e.g. on mobile we use the fallback images). Additional information can be provided in tables (see below).

Analysts are encouraged to present the data in the most readable and interesting way and a Data Visualization Template Sheet has been created with various examples in the Web Almanac style (note annoyingly you need Edit access to this sheet to be able to copy these template layouts into your own Sheet - please request this if needed). On top of this @rviscomi has created a video to show you how to apply these to your data (about the 1min 20 seconds mark for chart example). See also examples from the 2019 Web Almanac for column bar charts, pie charts, scatter charts, percentage stacked bar chart and more complex bar charts with lots of data.

Tips for Google Sheets

See Tips and advice at the bottom of this Wiki for lots of advice on making the best, most consistent, figures in Web Almanac style.

Color Scheme

This is the color scheme uses in the Web Almanac:

  • Pale green - #a8caba - this is first colour used for desktop stats
  • Dark grey - #62718b - this is the second colour used for mobile stats. I think of it more as our primary colour over the green, despite - it being the second colour in our charts as we normally highlight mobile more than desktop.
  • Brown - #4a3244 - this one is quite dark so tend not to use it
  • Pale yellow - #ffe599
  • Pale blue - #9fc5e8
  • Pale orange - #f6b26b
  • Pale pink - #ea9999
  • Yellow - #ffd966
  • Blue - #6d9eeb
  • Green - #93c47d
  • Orange - #e69138
  • Red - #e67c73
  • Yellow - #fee48c
  • Amber - #f6b26b

Some figures (e.g. CrUX) use slightly different colors in line with those brands but in general figures should be using the above colours.


Raw table data is also an option, either as an alternative to a graph, or as an additional figure to provide ease of access to the underlying statistics that make up a graph.

Note: We do not insist on tables for all graphs (as some publications might for accessibility reasons), as the data sheets themselves are made available and linked from the chapters. Instead, accessibility is handled with descriptive alt attributes, ARIA labels and ARIA descriptions which are given in the chapters in the figure markup.

Images / Video

Other images or videos which are not made of our data, or which are created outside of Google Sheets (e.g. complex graphs that sheets can’t handle or other HTTP archive stats), may also be included. Images should be added to the chapter directory, so they are hosted on our website. Credit and sources should be given, and authors should ensure they have permission to use the images. Videos should be hosted from YouTube where possible and a fallback image should be created for our ebook.

Call-out figures

Call-out figures allow us to highlight key stats in each chapter and also help add a visual break to text. These are typically large text with a label (example large stats), but can also be smaller text for longer stats (example smaller text).

More interactive visuals

The Google Sheet embeds only give us limited interactivity (basically highlighting data on hover) and in 2020 we are looking at more interactive visualizations. This is being tracked in issue 896. Still deciding exactly what that will look like so please comment on that issue if you've a good candidate or any ideas on this!

Writing good captions and descriptions

We include two types of descriptions of figures:

  • Caption
  • Description

These are important for general readability, accessibility and SEO issues so authors should ensure these are written well.

Writing good figure Captions

The caption is the title of the figure, and also is used as the alt text (though it's possible to have different alt text too). The caption should explain what the figure is (e.g. "HTTP version usage for home pages."). It can include HTML or Markdown for <code> elements or source links (in which case a plain-text alt attribute should be included too as there's some question as to whether alt attributes should include HTML so we choose not to). Captions should be short - a sentence or two.

Writing good figure Descriptions

The description is a much more detailed piece of text, typically several sentences long. It is only required for images, videos and charts and not for tables. It should describe the contents of the figure for visually impaired users. Given that, try to break up really long sentences and use punctuation to give the listener/reader a chance to digest the description!

You should describe what the chart is showing and, where possible, include the actual statistics. You do not need to include the phrases "Picture of" or "Image of" as these will be announced by screen readers, but it can be helpful to explain the graph type (e.g. bar chart).

For example:

Bar chart showing the adoption of HSL, HSLA, RGB, RGBA, and hex color formats. Hex is used on 93% of desktop pages, RGBA on 83%, RGB on 22%, HSLA 19%, and HSL 1%. Desktop and mobile adoption is similar for all color formats except HSL, for which mobile adoption is 9% (9 times higher).

For more detailed graphs where listing all the data points is not feasible (or even desired if it was!), the main points apparent from viewing the chart should be described. For example:

Distribution of 1,000 desktop websites' fast, moderate, and slow FCP. The distribution of fast FCP appears to be linear from 100% to 0% with a slight bulge in the middle.

Pro tip, for larger charts, you can use the following regexs in Visual Studio code to quick convert a table of data rows into roughly what you need:


^([a-zA-Z0-9 ():_.-]+)\t([0-9.]+%?)\t([0-9.]+%?)

Replace with:

`$1` on $2 and $3, 

And then tweak after.

For example this:

name desktop mobile
data-toggle 23% 22%
data-src 20% 20%
data-target 20% 20%
data-id 18% 19%
data-type 15% 15%
data-href 9% 10%
data-fbcssmodules 10% 10%
data-slick-index 10% 9%
data-google-container-id 10% 9%
data-load-complete 10% 9%

Will then become this:

`data-toggle` on 23% and 22%, 
`data-toggle` on 23% and 22%, 
`data-src` on 20% and 20%, 
`data-target` on 20% and 20%, 
`data-id` on 18% and 19%, 
`data-type` on 15% and 15%, 
`data-href` on 9% and 10%, 
`data-fbcssmodules` on 10% and 10%, 
`data-slick-index` on 10% and 9%, 
`data-google-container-id` on 10% and 9%, 
`data-load-complete` on 10% and 9%, 

Which you can then manually tweak into this:

Bar chart showing data-toggle is used on 23% of desktop and 22% of mobile pages, data-src on 20% and 20% respectively, data-target on 20% and 20%, data-id on 18% and 19%, data-type on 15% and 15%, data-href on 9% and 10%, data-fbcssmodules on 10% and 10%, data-slick-index on 10% and 9%, data-google-container-id on 10% and 9%, and finally data-load-complete is used on on 10% of mobile and 9% of desktop pages.

My suggested tweaks—as shown in that example—are (assuming bar chart, device and pages of course):

  • some beginning text ("Bar chart showing ")
  • enhancing the first one ("is used on X of desktop and Y of mobile")
  • adding "respectively" to the second one
  • enhancing the last one ("and finally X is used on X of mobile and Y of desktop pages.").

How to embed figures in the chapters

Jinja2 macros have been created to make it as easy as possible to embed figures in a consistent fashion, while avoiding complex markup or repetitive coding. This will also automatically handle figure numbering, creating anchor links and adding "Show Description" buttons. In future we hope to expand on this with direct links to query data and SQL so are asking for that information to be included in the markup.

Publishing interactive charts

Charts embedded in chapters may be made interactive by referencing a URL provided by Google Sheets. This URL is referenced by the figure attribute named chart_url (more info on these attributes in the section below). Each chart must be manually "published" to generate the corresponding interactive URL:

  1. Navigate to the results sheet containing the chart
  2. Click on the three-dot menu in the top right corner of the chart
  3. Select "Publish chart"
  4. Click "Publish"
  5. Use the URL in the "Link" tab as the value of the chart_url attribute

Screenshot of a published chart in Sheets

You will need edit access to the sheet to be able to publish a chart. Request edit access and ping @rviscomi if you need urgent access.

Charts, Images and Callout Figures examples

These can be created with the figure_markup macro (see examples below), which has the following options available:

Attribute Default value Notes
image The filename of the fallback image in the chapter directory. A fallback image for any Google Embeds must be provided for mobile.
link image Where to go when the image is clicked - by default this will be the image itself, but it is possible to override if you want to link somewhere else. See example of why you might do this below.
video The full URL of any video (in which case the image becomes the fallback image for print).
caption A short caption for the figure explaining the key points (see above).
alt caption By default this uses the caption value, but an explicit alt attribute can be provided to remove HTML tags for example.
description A short paragraph detailing the entire contents of the figure (see above). This should supplement the caption with additional information and ideally explain most of the stats.
chart_url The URL to the Google Sheets embed for this chart.
sheets_gid New for 2020! This will be used in future to link directly to the datasheet tab for this figure.
sql_file New for 2020! This will be used in future to link directly to the SQL query for this figure.
width 600 The width of the image - this will default to 600 if not provided so must be provided if different.
height 371 The height of the image - this will default to 371 if not provided so must be provided if different.
video_width width The width of the video - defaults to same as image width.
video_height height The height of the video - defaults to same as image height.
data_width width The width of the chart - defaults to same as image width.
data_height height The height of the chart - defaults to same as image height.
content The content for call-out figures - not required for Google Sheet images, videos or images.
classes Additional classes to be provided to the callout figure (e.g. big-number or really-big-number) - not required for Google Sheet images, videos or images.

Be mindful to escape quotes (or switch between single and double quotes so you don't have to do this).

Embedded Google Sheets examples

Standard size embed making use of defaults example

See how this looks in the 2019 CSS chapter

{{ figure_markup(
  caption="Popularity of direction values.",
  description="Bar chart showing the popularity of direction values ltr and rtl. ltr is used by 32% of desktop pages and 40% of mobile pages. rtl is used by 32% of desktop pages and 36% of mobile pages.",
Non-standard size embed example

If your images are not 600 by 371 then you need to provide height and width attributes. However as much as possible try to keep to this standard size, for consistency, and to ensure the chart is readable on mobile, and in the ebook. Some charts are taller and that’s OK but then should stick with the 600 width, and in general even then, this should be the exception.

Google Sheets doesn't always have the best formatting options but increasing the size should be considered a last resort and try instead to drop the secondary label (the greeny colour isn't that easy to read in text anyway), or reducing the decimal precision, or lowering the font-size - but not too much!).

By using the standard size you'll easily see if this makes the chart difficult to read at that size, which can often be hidden by blowing it up to a larger size, without realising they will be a lot smaller in some formats.

Anyway if you have to go with non standard sizes, then it is possible.

See how this looks in the 2019 CSS chapter

{{ figure_markup(
  caption="Top named colors.",
  description="Pie chart showing the most popular named colors. White is the most popular at 40%, then black at 22%, red 11%, and blue 5%.",

Tip: To help ensure that your images are all the standard dimensions, try installing the Resize Charts add-on for Sheets. You could also copy a standard chart from the data viz gallery and snap the dimensions of your chart to a standard one.

Images and Video examples

Image with explicit alt attribute example

See how this looks in the 2019 JavaScript chapter

{{ figure_markup(
  alt="JavaScript processing times for",
  caption="JavaScript processing times for From [The cost of JavaScript in 2019](",
  description="Bar chart showing 3 different devices: at the top a Pixel 3 has small amount on both the main thread and the worker thread of less than 400ms. For a Moto G4 it is approximately 900 ms on main thread and a further 300 ms on worker thread. And the final bar is an Alcatel 1X 5059D with over 2,000 ms on the main thread and over 500 ms on worker thread.",

Note the use of single quotes in the caption value to allow use of double quotes in the text, and note the shorter alt attribute without the link to the source.

Image with different link example

This allows you to link to a different tool, rather than just display the image in a larger format when clicked.

See how this looks in the 2019 Markup chapter

{{ figure_markup(
  caption="Element popularity categorized by standardization.",
  description="Scatter graph showing HTML, SVG, and Math ML use relatively few tags while non-standard elements (split into \"in global ns\", \"dasherized\" and \"colon\") are much more spread out.",

Again note the use of escaped double quotes for the description in this example.

YouTube embed example

See how this looks in the 2019 Mobile Web chapter

{{ figure_markup(
  caption="Example of how painful of an experience waiting for JS to load can be.",
  description="Video showing two web pages loading and each page has a figure tapping repeatedly on a button throughout the video, to no effect. There is a clock ticking up from 0 seconds at the top, and an initial happy emoji face for each website, that starts to turn less happy as clock passes 6 seconds, wide-eyed at 8 seconds, angry at 10 seconds, really angry at 13 seconds and crying at 19 seconds shortly after which the video ends.",

Note the different widths and heights for the video and the image.

Call out figure examples

Big Number example

See how this looks in the 2019 CSS chapter

{{ figure_markup(
  caption="Percent of desktop and mobile pages that include logical properties.",
Really Big Number example

See how this looks in the 2019 CSS chapter

{{ figure_markup(
  caption="The largest known <code>z-index</code> value.",
  content="999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 !important",

Table examples

Tables should be written in HTML rather than Markdown if containing numbers and the cells should be given class="numeric" attributes to right align them.

The easiest way to generate the correct HTML is to copy from Sheets, paste into a GitHub comment (without submitting it!), which will convert to Markdown for you. Then cut and paste into to the chapter markdown and generate the chapter locally. Copy the HTML from DevTools console. Replace the Markdown in the chapter and add the appropriate class="numeric" attributes.

Complex tables should be written in HTML with appropriate colspan, rowspan and scope attributes, as shown below.

The figure_link macro should be used to create figures names and other links in the caption.

Simple Markdown Table example

As per above Markdown tables should only be used for non-numeric data, but may be initially used before converting to HTML later.

<figure markdown>
| Protocol | Desktop | Mobile | Both   |
| -------- | ------- | ------ | ------ |
|          |  0.09%  |  0.08% |  0.08% |
| HTTP/1.0 |  0.09%  |  0.08% |  0.09% |
| HTTP/1.1 | 62.36%  | 63.92% | 63.22% |
| HTTP/2   | 37.46%  | 35.92% | 36.61% |

<figcaption>{{ figure_link(caption="HTTP version usage for home pages.", sheets_gid="1839783729", sql_file="20_01.sql") }}</figcaption>

Note there needs to be a blank line between the table markdown and the figcaption (so that the markdown convertor knows the table has finished). This blank line is not needed for HTML tables.

Simple HTML Table example

See how this looks in the 2019 HTTP/2 chapter

        <td class="numeric">0.09%</td>
        <td class="numeric">0.08%</td>
        <td class="numeric">0.08%</td>
        <td class="numeric">0.09%</td>
        <td class="numeric">0.08%</td>
        <td class="numeric">0.09%</td>
        <td class="numeric">62.36%</td>
        <td class="numeric">63.92%</td>
        <td class="numeric">63.22%</td>
        <td class="numeric">37.46%</td>
        <td class="numeric">35.92%</td>
        <td class="numeric">36.61%</td>
  <figcaption>{{ figure_link(caption="HTTP version usage for home pages.") }}</figcaption>

Complex HTML Table example

Headings should use <th> elements (except when empty when <td></td> should be used as <th></th> is invalid HTML) and should be given scope="colgroup" or scope="rowgroup"> attributes. colspan and rowspan can be used as appropriate.

Again the figure_link macro should be used to create figures names and other links in the caption.

See how this looks in the 2019 Security chapter

        <th scope="colgroup" colspan="2" >No. of Home Pages</th>
        <th scope="colgroup" colspan="2" >% of Home Pages</th>
        <th scope="col">Prefix value</th>
        <th scope="col">Desktop</th>
        <th scope="col">Mobile</th>
        <th scope="col">Desktop</th>
        <th scope="col">Mobile</th>
        <td class="numeric" >0.01%</td>
        <td class="numeric" >0.01%</td>
        <td class="numeric">0.00%</td>
        <td class="numeric" >0.00%</td>
<figcaption>{{ figure_link(caption="Cookie prefix usage.", sheets_gid="1184654637", sql_file="08_38.sql") }}</figcaption>

Note there does not need to be a blank line between the table and the figcaption for complex tables.

How to take screenshots of Google Sheet graphics

We need still graphics of all the Google Sheets for devices where the embeds will not show (e.g. Mobile Devices).

We've created a Puppeteer script to automate this once the markdown is created in the chapter markdown file. This is the recommended way to generate the images as it's quicker, and ensures consistency of image size and resolution.

The old method to generate images


I find the easiest way is to use Capture node Screenshot functionality in Chrome Dev Tools (Chrome 86 at least needed). Only issue with this is that Sheets disables right click so you have to hunt for the chart in Element tab of Dev tools - searching for the chart title works. You want the inner element with a width and height style:

Chrome Dev Tools Google Sheets Graphic Element

Alternatively you can also use any browser extension that takes element screenshots like HTML Elements Screenshot.

Do not take screenshots manually as they typically are lower resolution and the dimensions will be off. Resolution is also poor in Google Sheets download options unfortunately, so it should also not be used.

Please run all screenshots through (which handles PNG and JPEG despite its name). We have a Calibre GitHub Action to automatically compress images raised in a Pull Request, but I find TinyPNG does a better compression job and images can be dragged on to it and downloaded in bulk to make this easy.

Images should be given description names, and not fig1.png (note this was used for some chapters last year, but trying to give more descriptive names this year). We used chapterslug-descriptive-name.png file names using dashes rather than underscores (e.g seo-canonical-implementation-method.png or http2-h2-usage.png)

Tips and advice

Here's some tips and advice to make the most of your figures:

  • Use the Figures Template Sheet, rather than trying to create your own charts for the standard ones. They are made with the Web Almanac style, fonts and colours. Of course if you think you can come up with a different visual then we love to see that (see the image at the top of this wiki) - but take guidance from the "standard" charts in terms of style and colour.
  • The first colour is the pale green colour, but we normally concentrate on mobile, which is usually shown in the darker grey colour (it's the second colour as most stats are ordered and desktop shown before mobile). Beyond these two colours there are a few more but they are used less.
  • Be aware that the green has colour contrast issues. We're aware of this and aim to fix it in the future. There are lots of mitigations so we still believe our figures are accessible, but it's not ideal. As I say, we will look to address in the future.
  • Given that, when using the green colour only, ensure the labels are switch to the dark grey colour to be readable. When using green and grey leave the green labels to avoid confusion with grey bars (despite the colour contrast issues).
  • Drop labels on green bars if too squashed. There is lots of precedent with this in Web Almanac (plus given the colour contrast issue, I kind of look for an excuse to drop them, truth be told).
  • Drop both labels if labels would be too small (example).
  • There are other colours (Red, Yellow, Green) for Distribution Charts. In many (but not all!) places in the world they hold special meaning so try to avoid giving Red to good (e.g. latest TLS) and Green to bad as that will confuse some readers. It is true this is far from universal, but is the case in many case and is also what CrUX uses.
  • Titles, legends and axis titles should be in sentence case, except for proper nouns (e.g. "Distribution of desktop FCP performance")
  • Make it clear if desktop is desktop or mobile if it doesn't include both. We like to put "(mobile)" after the chapter name subtitle (example).
  • Include both desktop and mobile if no reason not to, and fits on same chart. At same reason no need to include two charts, if it doesn't fit on one chart and it's near identical data (many chapters like to have a comment at the top saying that for that chapter there is little difference for most data so, unless otherwise noted only the mobile data is shown - but even then the charts should be labelled as such in case the chart is presented outside of the chapter).
  • CrUX data gets it's own subtitle too (example).
  • Avoid using too many decimal places. In general, when deciding how many decimal places to use data should have two "real figures". So 56% is good, but 56.45% is overly detailed. For some topics (or stats within a chapter) with more niche numbers, decimal places may be required (e.g. 4.5% or 0.00012%).
  • Similarly try to use appropriate units. i.e. use KB—or even MB!—rather than bytes appropriately.
  • Always explain what you are showing: pages? websites? requests? origins? Pages make sense when dealing with on page things that might differ across a site (e.g. Number of <h1> elements). Sites makes sense when dealing with something likely to be site wide (CMS's or HTTP/2 usage). Requests makes sense when it's likely to differ (e.g. Third-party requests, or HTTPS usage). Origin is used for CrUX data as it collects stats based on the whole origin and also differentiates between versus, for example.
  • Use percentages not absolute numbers. "45,000 sites use X" is meaningless without context compared to "5.2% of sites us Y". Also the number of websites in our data changes each year (each month actually!) so percentages allows year on year comparison.
  • Try to avoid going smaller than the default font size of 16. If have to do that, then that raises the question of whether you are showing too much data in chart. Should you limit number of data point shown to the top 5 most used stats? Or flip to horizontal where you can fit more by increasing the height? Or just drop the labels completely as per previous point above?
  • Do not increase the width beyond 600px where at all possible. Wider charts might look fine on your big desktop monitor, but will struggle on mobile. Again, if you need to increase the width, then it suggests you may be trying to squeeze too much information in. You can (and should!) increase the height, but be aware any non-default heights beyond 371 needs additional markup to be displayed correctly.
  • Vary the way you present you data. Use Bar charts, Big Numbers, Tables...etc. to keep the reader engaged rather than just the one type.
  • I try to avoid charts with just two figures. They aren't very interesting and seem to be using a chart for a chart sake. Often it's better to move that to a big number for one figure (mobile), and explain both that and the other (desktop) in the text rather than use a chart.