Categories
Uncategorized

Learning to query and read CrUX data

I’ve decided to take another look at BigQuery in the context of the Chrome User Experience Report or CrUX.

The idea is that Google, through the Chrome team and tools they make available to developers, has collected real user metrics (RUM) for billions of sites around the world and has grouped them according to country.

Warning

In the BigQuery free tier, users can only query 1TB worth of data per month. Beyond that, the standard rate of $5/TB applies. So when Big Query tells you how many gigabytes of data are processed in each request, pay attention.

This is why I picked a smaller country to experiment with rather than use the US or the full dataset… it can’t get really expensive if you’re not careful.

The first example uses BigQuery to search for all unique origins in Chile as of December 2019.

SELECT
  COUNT(DISTINCT origin)
FROM
  `chrome-ux-report.country_cl.201912`

let’s unpack the query referencing Rick Viscomi’s Using the Chrome UX Report on BigQuery

SELECT COUNT(DISTINCT origin) means querying for the number of unique origins in the table. Roughly speaking, two URLs are part of the same origin if they have the same scheme, host, and port.

FROM `chrome-ux-report.country_cl.201912` specifies the address of the source table, which has three parts:

  • The Cloud project name chrome-UX-report within which all CrUX data is organized
  • The dataset country_cl, representing data from Chile (county code CL)
  • The table 201912, representing December 2019 in YYYYMM format

With slight modifications, we can get a list of the URLs for the unique origins. We remove COUNT and leave the select statement as DISTINCT origin

SELECT
  DISTINCT origin
FROM
  `chrome-ux-report.country_cl.201912`

You can see a detailed preview of the data in BigQuery. Note that BigQuery is moving to the Google Cloud Console and that the URL may change as a result.

Of particular importance are the performance metrics available as part of the report:

The data for each metric is organized as an array of objects that we can capture as [metric].histogram.bin.

The following example will pick up the sum of all First Contentful Paint density values and assign them to the variable fast_fcp.

It will pull the data from the 2019/12 report and will flatten all the values in first_contentful_paint.histogram.bin

The matching conditions are: the origin has to be https://www.vidasecurity.cl/ and the fcp.start value has to be greater than 10000, meaning that the site has to take 10 seconds or more to load.

SELECT
  SUM(fcp.density) AS fcp_density
FROM
  `chrome-ux-report.country_cl.201912`,
  UNNEST(first_contentful_paint.histogram.bin) AS fcp
WHERE
  origin = 'https://www.vidasecurity.cl/' AND
  fcp.start > 10000

The following example, unlike the other ones, selects multiple elements from a specific table to get more fine-grained information from the data.

This example asks the question: in the country_cl.201912 report, how many origins took more than 20 seconds (20000 milliseconds) to load on a phone. List the results by origin

SELECT
  origin,
  fcp
FROM
  `chrome-ux-report.country_cl.201912`,
  UNNEST(first_contentful_paint.histogram.bin) AS fcp
WHERE
  form_factor.name = 'phone' AND
  fcp.start > 20000
ORDER BY
  origin

And this is the equivalent query for desktop connections taking more than 2000 seconds to start.

SELECT
  _TABLE_SUFFIX AS yyyymm,
  AVG(fcp.density) AS fast_fcp
FROM
  `chrome-ux-report.country_cl.*`,
  UNNEST(first_contentful_paint.histogram.bin) AS fcp
WHERE
  form_factor.name = 'desktop' AND
  fcp.start > 20000
GROUP BY
  yyyymm
ORDER BY
  yyyymm

With these two queries, we can start making comparisons and predictions about the data before we jump into more in-depth queries.

Some of the questions I had about the data:

  • How much do numbers change over the years?
  • Is there a significant difference between desktop and mobile values?

Using these questions as a starting point we can dig deeper into the general data or query specific sites for more information. If we want, we can save the data as JSON and use a visualization library like D3 to generate graphical representations of the data or save it as CSV to manipulate on Excel or Google Sheets.

Once you’ve got all the answers you can get out of the CrUX dataset you can move to the HTTP Archive dataset. This dataset is a far more comprehensive both in the breadth of the data it collects and the frequency that it collects the data.

For more information on how to use the HTTP Archive BigQuery dataset see Getting Started Accessing the HTTP Archive with BigQuery by Paul Calvano.

Categories
Uncategorized

WordPress Child Themes: Refresher

Every so often I want to explore new WordPress themes for my blog without having to start from scratch using Underscores. At the same time, I want the flexibility of customizing the theme without losing the changes every time I update the theme.

That’s where child themes come in. They allow developers to customize an existing theme independently of the parent and without losing the changes when we update the parent theme.

Process

Creating a child theme is a four-step process:

  1. Create a folder inside the wp-content/themes directory
  2. Create a style.css stylesheet and add the theme boilerplate
  3. Create a functions.php file and add the code to enqueue parent and child stylesheets
  4. Activate the theme

For this post, we’ll use Twenty Twenty as the parent theme to take advantage of the new Gutenberg editor (whether it works or not is a subject for another post).

Create child theme folder

Depending on how you do development creating the theme folder can be done either through a GUI (Windows Explorer or Finder) or through a terminal (Linux, Mac or WSL for Windows)

I work on Mac’s terminal so, starting on the root of the WordPress installation I run the following command

mkdir -p wp-content/themes/twentytwenty-rivendellweb/

Change to the directory you just created.

cd wp-content/themes/twentytwenty-rivendellweb/

Create a stylesheet: style.css

The core of the child theme is the style.css stylesheet and the comment block that defines the theme.

The example below is what I used for my Twenty Twenty child theme. Individual fields are explained below.

/*
 Theme Name:   Twenty Twenty Rivendellweb
 Theme URI:    https://github.com/caraya/2020-child
 Description:  Twenty Twenty Child Theme for The Publishing Project
 Author:       Carlos Araya
 Author URI:   https://publishing-project.rivendellweb.net/
 Template:     twentytwenty
 Version:      1.0.0
 License:      MIT
 License URI:  https://opensource.org/licenses/MIT
 Tags:         light, dark, two-columns, right-sidebar, responsive-layout, accessibility-ready
 Text Domain:  twentytwentyrivendellweb
*/

Theme name (REQUIRED). This is the name that will show up for your theme in the WordPress admin screen

Theme URI. This points to the website or demonstration page of the theme at hand. This or the author’s URI must be present for the theme to be accepted into the WordPress directory.

Description This description of your theme will show up in the theme menu when you click on “Theme Details”.

Author. This is the author’s name.

Author URI. Author’s website.

Template (REQUIRED). This is the name of the parent theme, meaning its folder name. Be aware that it is case-sensitive, and if you don’t put in the right information, you will receive an error message

Version. This displays the version of your child theme. Using semver is usually a good way to version your theme

License. This is the license of your child theme. WordPress themes in the directory are usually released under a GPL license.

Licensing is something that it’s important to me and needs clarification.

Since I don’t plan on releasing my themes through the directory, I can use whatever license I choose. I don’t think that the GPL is a good license to use due to its viral nature and GPL compatible licenses, like MIT, are fine too, contrary to what Matt says.

However, since WordPress Core is licensed under GPL it may make more sense to have only one license throughout the codebase. It ends up being a matter of preference.

The Free Software Foundation maintains a list of free software licenses that are compatible with the GPL in case that the GPL is too restrictive for your taste.

License URI. This is the address where your theme’s license is explained. The URI must match the license you choose to use.

Tags. The tags help others find your theme in the WordPress directory. Thus, if you include some, make sure they are relevant.

Text domain. This part is used for internationalization and to make themes translatable. This should fit the “slug” of your theme.

Enqueue parent styles

The recommended way of enqueuing the parent theme stylesheet currently is to add a wp_enqueue_scripts action and use wp_enqueue_style() in your child theme’s functions.php.

The following example adds both the parent and the child theme stylesheets.

<?php
function my_theme_enqueue_styles() {

    $parent_style = 'parent-style'; // This is 'twentytwenty-style' for the Twenty Twenty theme.

    wp_enqueue_style( $parent_style, get_template_directory_uri() . '/style.css' );
    wp_enqueue_style( 'child-style',
        get_stylesheet_directory_uri() . '/style.css',
        array( $parent_style ),
        wp_get_theme()->get('Version')
    );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_styles' );

In this example, each theme has a single stylesheet. If a theme has more than one stylesheet you’re responsible to enqueue the styles in the right order so the theme will continue working.

A solution is to concatenate the stylesheets during the build step or using SASS imports.

Activate the new theme

Now we can activate the theme from the Administrator interface. Go to Appearance > Themes, select the theme that we just created and activate it.

If everything worked out OK, there should be no difference between the parent and child themes.

We can now start working with our child theme and change it to our heart’s content and everything should work the same.

Once WordPress detects you’re working with a child theme it will no longer warn you about changing the theme’s CSS but it will still suggest you use the theme customizer.

Adding additional tools and functionality

Now that we have a working child theme we can start looking at more advanced ideas for our theme.

We’ll look at three ways to enhance a WordPress theme:

  • Using third-party scripts and tools
  • Customizing with Gutenberg blocks
  • Using CSS to modify the theme appearance
  • Modifying PHP templates

Third-part scripts

Warning

This example, and any other function that adds scripts and stylesheets to pages of a WordPress installation that convert to AMP, is likely to run afoul of AMP validation.

If you want to use an AMP compliant syntax highlighter, I’d suggest you look at Syntax-highlighting Code Block (with Server-side Rendering) by Weston Router.

We can create multiple wp_enqueue_scripts actions and use wp_enqueue_script() and wp_enqueue_style() to add tools in your child theme’s functions.php in a similar way to how we added the stylesheets for the theme to work.

For example, let’s say that we want too enqueue Prism core and styles for our theme rather than use a plugin.

The file names for the scripts and the stylesheets in the example do not correspond to the real names. In an ideal world, I’d create brand new downloads and organize them inside the theme as needed.

<?php
function rivendellweb_enqueue_prism() {

    wp_enqueue_style( 'prism_style', get_template_directory_uri() . '/prism/prism-solarizedlight.min.css' );
    wp_enqueue_script( 'prism_script',
        get_template_directory_uri() . '/prism/prism-core.js' );
}
add_action( 'wp_enqueue_scripts', 'rivendellweb_enqueue_prism' );

The usual caveats of adding scripts and stylesheets apply. If you need to, use Scripts To Footer and Scripts-To-Footer Exclude AMP to move the scripts to the footer excluding AMP-related scripts.

This should improve performance.

Customizing the theme: Gutenberg Blocks

In addition to customizing the theme itself, we can choose to create custom Gutenberg blocks to fit the type of content we want to create and that we’re too lazy to figure out how to do without blocks.

I would only recommend this for people who are just starting to work with WordPress. Both my use cases use HTML that doesn’t work with Gutenberg without changing the way I write… and I’m not going to change it.

Overriding the parent theme: CSS

The first means to change the parent theme is to use CSS in the child theme to override the parent’s CSS.

One thing that has changed since I last played with child themes is that WordPress is now based on blocks so you can’t just override the specific class.

The following code sets the default content width to 960 pixels.

I had a hard time parsing the first item of the rule. It reads as:

Select any element that has the class entry-content that has any children that do not have any of alignwide, alignfull, alignleft, alignright, or is-style-wide classes

We also pick the elements with class, post-meta-wrapper or post-meta

.entry-content > *:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright):not(.is-style-wide),
.post-meta-wrapper,
.post-meta {
    max-width: 960px;
}

Again, more research is needed, particularly when working with Gutenberg blocks and what additional classes they introduce to the different components of the page.

Overriding the parent theme: PHP

The hardest way to modify the parent theme is to change the PHP templates to create a new structure or create new templates to represent new types of content.

The basic template file gives the new template a name, Project Template and tells WordPress what type of content to use.

<?php
/*
Template Name: Project Template
Template Post Type: post, page
*/
get_template_part( 'singular' );

Once we have the basic theme, we can leverage template partials to further customize the template. Inside the partials, we can write custom HTML or template tags to customize the behavior of the template.

The example below changes the header of all pages that don’t use the project template. If the page uses the project template then the custom header will not be used, we should provide a fallback with the default template so the site will look as it did before we made the changes.

    <?php
        if (! is_page_template(array('templates/template-project.php' ))) {
        ?>
        <header id="site-header" class="header-footer-group" role="banner">
      <p class="blog-title"><strong><a href="<?php bloginfo('url')?>"><?php bloginfo('name')?></a></strong> — <?php bloginfo('description')?></p>
        </header><!-- #site-header -->

            <?php
            // Output the menu modal
            get_template_part( 'template-parts/modal-menu' );
        }
        ?>

Likewise, we customize the footer template to remove all content from the footer so we can fully customize it with Gutenberg blocks or we can edit it and customize it with PHP, HTML, and CSS.

<?php
if ( ! is_page_template( array( 'templates/template-canvas.php' ) ) ) {
?>
    <footer id="site-footer" role="contentinfo" class="header-footer-group">
    <!-- Build footer content here -->
    </footer><!-- #site-footer -->
<?php } ?>
    <?php wp_footer(); ?>

    </body>
</html>

See Template Files in the WordPress Theme Handbook.

You can also see a fully worked version of the code in these examples on Github at https://github.com/caraya/2020-child/

Conclusion

We’ve just looked at the basics of creating and customizing a child theme for WordPress.

Since the release of WordPress 5.0 and the Gutenberg editor, the development process has changed significantly and this post barely scratches the surface of what you can do.

A possible future project is to use WordPress to build a site combining the blog, the project list, and the layout experiments site.

Categories
Uncategorized

Exploring Canvas

In its simplest form <canvas> can be used to draw graphics via scripting (usually JavaScript). This can, for instance, be used to draw graphs, combine photos, or create simple (and not so simple) animations.

This post will only discuss the 2D drawing canvas mode. You can also use the canvas element to host 3D WebGL models, but that’s a whole other set of articles, so we’ll skip it for now.

Getting started

In our HTML document, we need to add a canvas element where we will draw our content. The id attribute is the name that we’ll use in the script to reference the canvas element.

Height and width are not required but it’s always better to write them out

<canvas id="tutorial" width="600" height="600">
  <p>Your browser doesn't support Canvas</p>
</canvas>

We also need a script that will generate whatever we want to do on the canvas. In this example, we’ll generate a square and an arrow. How we generate them is not as important, we’ll discuss it later.

// Procedural Canvas generation
const canvas =
  document.getElementById('tutorial');
if (canvas.getContext) {
  const ctx = canvas.getContext('2d');

// This draws a rectangle
  ctx.strokeRect(100, 100, 125, 125);

  ctx.beginPath();
  ctx.moveTo(200, 50);
  ctx.lineTo(100, 75);
  ctx.lineTo(100, 25);
  ctx.fill();
}

Understanding the Canvas grid

Graphic showing the canvas coordinate system
Canvas coordinates space. Taken from MDN

To better understand the material that we’ll cover next, we need to talk about the canvas coordinate space.

Normally 1 unit in the grid corresponds to 1 pixel on the canvas. The origin of this grid is positioned in the top left corner at coordinate (0, 0) with all elements placed relative to this origin.

So the position of the top left corner of the blue square becomes x pixels from the left and y pixels from the top, at coordinate (x,y).

Canvas provides tools to translate the origin to a different position, rotate the grid and even scale it, but for now, we’ll stick to the default.

Drawing primitives

Canvas supports two primitives for drawing: rectangle and path. All other forms are derived from these two primitives.

Rectangles

Rectangles draw either rectangles or squares,

fillRect(x, y, width, height)
Draws a filled rectangle.
strokeRect(x, y, width, height)
Draws a rectangular outline.
clearRect(x, y, width, height)
Clears the specified rectangular area, making it fully transparent.

Paths

Now let’s look at paths. A path is a list of points, connected by segments of lines that can be of different shapes, curved or not, of different width and of a different color. A path, or even a subpath, can be closed. To make shapes using paths takes some extra steps:

  • First, you create the path
  • Then you use drawing commands to draw into the path
  • Once the path has been created, you can stroke or fill the path to render it.

Here are the functions used to perform these steps:

beginPath()
Creates a new path. Once created, future drawing commands are directed into the path and used to build the path up.
closePath()
Adds a straight line to the path, going to the start of the current sub-path.
stroke()
Draws the shape by stroking its outline.
fill()
Draws a solid shape by filling the path’s content area.

The first step to create a path is to call the beginPath(). Internally, paths are stored as a list of sub-paths (lines, arcs, etc) which together form a shape. Every time this method is called, the list is reset and we can start drawing new shapes.

The second step is calling the methods that actually specify the paths to be drawn. We’ll see these shortly.

The third, and an optional step, is to call closePath(). This method tries to close the shape by drawing a straight line from the current point to the start. If the shape has already been closed or there’s only one point in the list, this function does nothing.

Notes

When the current path is empty, such as immediately after calling beginPath(), or on a newly created canvas, the first path construction command is always treated as a moveTo(), regardless of what it actually is. For that reason, you will almost always want to specifically set your starting position after resetting a path.
Note: When you call fill(), any open shapes are closed automatically, so you don’t have to call closePath(). This is not the case when you call stroke().

Styles and colors

fillStyle = color
Sets the style used when filling shapes
fillStroke = color
Sets the style for shapes’ outlines
Note: When you set the strokeStyle and/or fillStyle property, the new value becomes the default for all shapes being drawn from then on. If you want to use a different color, you must reassign fillStyle or strokeStyle.

You can also control transparency either globally, using globalAlpha which takes a value between 0 and 1 and will affect all shapes drawn after it’s set up, or on per-element basis using strokeStyle and fillStyle with RGBA or another color format that supports transparency.

// Assings default transparency value
ctx.globalAlpha = 0.2;

// Assigning transparent colors
// to stroke and fill style

ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';

Images

Importing images into a canvas is a two-step process:

  1. Get a reference to a supported image type. It is also possible to use images by providing a URL.
  2. Draw the image on the canvas using the drawImage() function.

Supported image types

HTMLImageElement
<img> elements or images made using the Image() constructor, as well as any element
HTMLImageElement
These are images embedded using the <image> SVG element
HTMLVideoElement
Using an HTML <video> element as your image source grabs the current frame from the video and uses it as an image
HTMLCanvasElement
You can use another <canvas> element as your image source

Using an existing image in an external URL, we can use the following HTML

<canvas id='demo'>
  <h2>Your browser doesn't support Canvas</h2>
</canvas>

And the following JavaScript to insert an image into the canvas element.

// Capture the canvas element
const canvas = document.getElementById('demo');
// Sets up a 2d context for the canvas
const ctx = canvas.getContext('2d');

// Image constrouctor and src assignment
const img = new Image();
img.src =
'https://s3-us-west-2.amazonaws.com/0168.JPG';

// Draw the image at the given coordinates
ctx.drawImage(img, 0,0);

We can then do further drawing on top of the image we just added using the primitives discussed earlier.

Idea

Using an image in a canvas element lends itself pretty well to having an outline and then draw on top of the outlines.

Where to next?

There are many other things you can do with canvas. Some ideas are:

  • Image blending and transformations
  • Mouse and pointer interaction
  • Animations
  • Image compositing and clipping

Canvass gives you a huge playground to play in. Play responsibly 🙂

Links, tutorials and resources

Categories
Uncategorized

Figures and Counters

Playing with figures and counters can produce some very interesting bits of presentation for our content.

The post will talk about the figure and figcaption elements, what they do and how to use them.

We will then discuss CSS counters, what they do and how they work when inserted into HTML elements.

Finally, we’ll look at what to do with figures and counters together. I hope you can find your creative uses.

Figure and figcaption

The HTML figure represents self-contained content with an optional caption represented by the figcaption child element. The figure, its caption, and its contents are referenced as a single unit.

I’ve always thought of figures as images with captions but the HTML spec says something different about the element:

The figure element represents some flow content, optionally with a caption, that is self-contained (like a complete sentence) and is typically referenced as a single unit from the main flow of the document.

“Self-contained” in this context does not necessarily mean independent. For example, each sentence in a paragraph is self-contained; an image that is part of a sentence would be inappropriate for figure, but an entire sentence made of images would be fitting.

The element can thus be used to annotate illustrations, diagrams, photos, code listings, etc.

From: https://html.spec.whatwg.org/#the-figure-element

One thing that the specification cautions authors is how to reference figures (any kind) from within the document:

When a figure is referred to from the main content of the document by identifying it by its caption (e.g., by figure number), it enables such content to be easily moved away from that primary content, e.g., to the side of the page, to dedicated pages, or to an appendix, without affecting the flow of the document.

If a figure element is referenced by its relative position, e.g., “in the photograph above” or “as the next figure shows”, then moving the figure would disrupt the page’s meaning. Authors are encouraged to consider using labels to refer to figures, rather than using such relative references so that the page can easily be restyled without affecting the page’s meaning.

From https://html.spec.whatwg.org/#the-figure-element

So, according to the HTML specification, we can do something like this:

<figure>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </ul>
  <figcaption>List 1: Example list</figcaption>
</figure>

This example has a few drawbacks. The main one is that it hardcodes the number of the item in question, in this case, the list number. This means that if we insert a list before the one we labeled number one, we’ll have to manually change the numbers. This is error-prone and brittle if we have too many items of one kind in the document.

We can leave it as Example List but that’s confusing, particularly if you have too many similar items. It may also make it difficult to reference individual items as suggested in the specification.

CSS Counters

CSS Counters address the need for a means to automate the numbering of figure items. The idea is that we use counters and insert them, along with accompanying text, in the place we specify.

The process is as follows:

  1. We initialize the counters by using counter-reset with the name of the counters we want to reset
    1. We can repeat this for multiple counters
    2. The counter-reset function can be placed anywhere in the stylesheet. In this example, we placed the initialization in the root element selector
  2. Wherever we want to increase the value of the counter we use counter-increment
    1. We can repeat this for multiple counters
    2. The counter-increment function can be placed anywhere in the stylesheet. In this example, we placed the increment in the section h2::before selector
  3. Insert the content you want to display using a combination of text and the counter(counter-name) for the counter that you want to use inside a content property

The CSS code example below illustrates how to use counters to automatically set up a counter for chapter numbers and attach these counters to the first h2 element inside a section.

body {
  counter-reset: chapter;
}

section h2::before {
  counter-increment: chapter;
  content: "Chapter " counter(chapter) ": ";
}

The HTML shows one possible way to write HTML that uses the CSS in the previous block to auto number the sections we use as chapter separators.

<h2>Book Title</h2>

<section>
  <h2>Example Chapter</h2>

  <p>Some text for the chapter.</p>
</section>

You can see a working example in this code pen

See Automatic Numbering With Counters in the CSS Lists Module Level 3 specification for more information and additional counter functionality.

Experimenting

The following are some examples of what we can do with counters and how to best leverage counters and figures to make our content more appealing and easier to read.

Multiple counters

The first experiment is how to handle multiple counters in the same document. The example CSS code below works with four different counters: chapters, figures, tables and lists with each of these counters using different elements too trigger counter increment and displaying the content.

I picked these particular types of counters to illustrate different ways to use them.

The initialization works the same as the single-counter example. counter-reset can take more than one value separated by spaces.

:root {
  /* initializing counters */
  counter-reset: chapter figure tables lists;
}

The chapter counter works the same as our previous example.

section {
  counter-increment: chapter;
}
section h2::before {
  content: "Chapter " counter(chapter) ": ";
}

The figure counter works with our default figure setup without adding any additional information.

figure {
  counter-increment: figure;
}
figure figcaption::before {
  content: "Figure " counter(figure) ": ";
}

The table counter works with the HTML table elements and uses the caption child to display the counter in a similar way to what the default figure counter does.

table {
  counter-increment: tables;
}
table caption {
  padding: 10px;
  caption-side: bottom;
}
table caption::before {
  content: "Table " counter(tables) ": ";
}

The lists counter uses a modified version of the figure counter to account for it being a special type of figure.

figure.lists {
  counter-increment: lists;
}
figure.lists figcaption::before {
  content: "List " counter(lists) ": ";
}

Counters reset at the chapter level

The previous example initialized/rest the counters in the root element of the stylesheet. There may be times when we want to reset items further down the style tree.

This example will reset the figure counter on every section, restarting the figure numbering on every section, regardless of how many figures we have on each chapter.

:root {
  counter-reset: chapter;
}

section {
  counter-reset: figure;
  counter-increment: chapter;
}
section h2::before {
  content: "Chapter " counter(chapter) ": ";
}

figure {
  counter-increment: figure;
}
figure figcaption::before {
  content: "Figure " counter(figure) ": ";
}

Using more than one counter

So far all our examples have used a single counter to show the positioning of an item on the page.

There are times where we want to show more specific information about the image, for example, this is the first image of chapter two or the third table in chapter 5.

To do this we have to use more than one counter when writing the counter content.

We’ll take the same code as the previous example and change the content inside figcaption::before to add the chapter counter and a string to separate the chapter counter from the figure counter. The rest of the code is the same.

:root {
  counter-reset: chapter;
}

section {
  counter-reset: figure;
  counter-increment: chapter;
}
section h2::before {
  content: "Chapter " counter(chapter) ": ";
}

figure {
  counter-increment: figure;
}
figure figcaption::before {
  content: "Figure " counter(chapter) '-' counter(figure) ": ";
}

Using counters is not exclusive to figures. It’s just a matter of how creative you are and what you’re trying to accomplish.

Categories
Uncategorized

Testing javascript in the browser without the browser

There are times that we want to test snippets of Javascript code in different browsers to make sure that it works as intended in all our target browsers. I’ve always fired the browsers and pasted the code into DevTools or Web Inspector to check if the results are the same.

Now there is a pair of applications that will automate this for you: jsvu and eshost.

To install these packages run the following command

npm i -g jsvu eshost

JSVU (JavaScript Version Updater) manages the installation and updates for different Javascript engines avoiding the compilation process. Out of the box, it supports the following engines and OS combinations.

Vendor JavaScript engine Binary name mac64 win32 win64 linux32 linux64
Microsoft Chakra chakra or ch
Facebook Hermes hermes & hermes-repl
WebKit/Apple JavaScriptCore javascriptcore or jsc * *
Fabrice Bellard QuickJS quickjs
Mozilla SpiderMonkey spidermonkey or sm
Google V8 v8
Google V8 debug v8-debug
Moddable-OpenSource XS xs (32) (32)

When you first run it it will prompt you to select the JS engines to install. After initial install running the jsvu commannd will update the engines as appropriate.

Once we have the engines that we want to work with we can configure ESHost to run the same command with these multiple js engines. In this example, we’re adding the major browser’s JS engines to work with ESHost.

# Chakra is old Edge's JS engine
eshost --add 'Chakra' ch ~/.jsvu/chakra
# JSCore is Safari's JS engine
eshost --add 'JavaScriptCore' jsc ~/.jsvu/javascriptcore
# Spider Monkey is Firefox JS engine
eshost --add 'SpiderMonkey' jsshell ~/.jsvu/spidermonkey
# V8 is Chrome's JS engine
eshost --add 'V8' d8 ~/.jsvu/v8

Once we have the engines set up, ESHost is ready to go.

We have multiple ways to run scripts in ESHost. We can run a short script in all configured browsers with the -e flag like this:

eshost -e "12*12"

You can also run complete scripts in the configured browsers with the following command:

eshost my-script.js

The only flags I will refer to are -m and -s.

The -m flag will treat the script as a module with the corresponding differences in syntax.

The -s flag will consolidate results when different engines produce the same result. For example, the following command:

eshost -se "console.log(112*12)"

will produce the following result where only JavaScriptCore produces a different result. This will help in researching browser differences that need workarounds

eshost -se "console.log(122*12)"
#### Chakra, SpiderMonkey, V8
1464
undefined

#### JavaScriptCore

TypeError: undefined is not an object (evaluating 'console.log')

For more information use the following command:

eshost --help