<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Patrick Brosset</title>
  <subtitle>Articles about web technologies</subtitle>
  <link href="https://patrickbrosset.com/feed.xml" rel="self"/>
  <link href="https://patrickbrosset.com/"/>
  <updated>2026-05-13T00:00:00Z</updated>
  <id>https://patrickbrosset.com/</id>
  <author>
    <name>Patrick Brosset</name>
    <email>patrickbrosset@gmail.com</email>
  </author>
  
  
  <entry>
    <title>Le mystère des marges CSS</title>
    <link href="https://patrickbrosset.com/articles/2005-12-13-le-mystere-des-marges-css/"/>
    <updated>2005-12-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2005-12-13-le-mystere-des-marges-css/</id>
    <content type="html">
      
        &lt;p&gt;Eric Meyer, figure du CSS, sur son site &lt;a href=&quot;http://www.complexspiral.com/publications/uncollapsing-margins&quot;&gt;complexspiral&lt;/a&gt; reprend tous les détails techniques du comportement des marges en CSS. Sujet souvent obscur qui après cet article devient limpide!&lt;/p&gt;
&lt;p&gt;À lire absolument pour tous ceux qui ne comprennent pas pourquoi les marges disparaissent subitement...&lt;/p&gt;
&lt;p&gt;Bon, puis il va falloir que je commence à poster d&#39;autres choses que des liens hein... merde c&#39;est un blog quoi...&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>MyDBO, Generateur de code PHP</title>
    <link href="https://patrickbrosset.com/articles/2005-12-13-mydbo-generateur-de-code-php/"/>
    <updated>2005-12-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2005-12-13-mydbo-generateur-de-code-php/</id>
    <content type="html">
      
        &lt;p&gt;&lt;a href=&quot;https://www.phpclasses.org/package/2722-PHP-Generate-classes-to-access-MySQL-from-templates.html&quot;&gt;MyDBO&lt;/a&gt; est pour tous les développeurs PHP/MySQL qui en ont marre de faire et refaire les mêmes fonctions ajout/édition/liste/... sur leurs bases de données pour chaque nouveau site ou application web.&lt;/p&gt;
&lt;p&gt;Cette application a été créée en PHP, est gratuite, et s&#39;installe très facilement.&lt;/p&gt;
&lt;p&gt;Une fois installée et le wizard lancé, vos tables MySQL seront analysées et un code complet orienté objet sera généré pour vous!&lt;/p&gt;
&lt;p&gt;Une fois ce code sous la main, les opérations typiques de gestion de base de données deviennent beaucoup plus faciles et rapides, et surtout, on peut aborder son projet d&#39;un point de vue business et non par requêtes SQL.&lt;/p&gt;
&lt;p&gt;De plus, car ce n&#39;est pas tout, MyDBO est basé sur des templates (modèles), ce qui veut dire que l&#39;on peut créer autant de modèles que l&#39;on veut et les rendre adaptés à ce dont on a besoin de faire.&lt;/p&gt;
&lt;p&gt;Avec MyDBO est aussi inclus un plug-in qui génère une section d&#39;administration complète. Pour ceux qui recherchent un moyen rapide de lister, ajouter, éditer ou effacer des entrées de tables MySQL, MyDBO est pour vous.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Faire des photos macro avec une boite de Pringles</title>
    <link href="https://patrickbrosset.com/articles/2005-12-13-faire-des-photos-macro-avec-une-boite-de-pringles/"/>
    <updated>2005-12-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2005-12-13-faire-des-photos-macro-avec-une-boite-de-pringles/</id>
    <content type="html">
      
        &lt;p&gt;Toujours voulu faire des photos macros? Jamais eu l&#39;argent pour acheter l&#39;objectif?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.photocritic.org/macro.html&quot;&gt;Cette entrée blog&lt;/a&gt; donne une recette DIY (do it yourself) economo-pringlo-dangereuse-mais-bien-coolos pour arriver à de superbes resultats.
L&#39;idée est bien trouvée en tout cas! A voir!&lt;/p&gt;
&lt;p&gt;La photo ci-dessus donne une idee du résultat obtenu avec un simple objectif bas de gamme Canon, une boite de pringles vide et du bricolage.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Handling large files while avoiding memory shortages in PHP</title>
    <link href="https://patrickbrosset.com/articles/2005-12-22-handling-large-files-while-avoiding-memory-shortages-in-php/"/>
    <updated>2005-12-22T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2005-12-22-handling-large-files-while-avoiding-memory-shortages-in-php/</id>
    <content type="html">
      
        &lt;p&gt;I&#39;ve recently had to create a generic text data file uploader in PHP (namely to import tab-delimited fields into a database). After experiencing a few problems related to memory issues, I came up with a solution that might be of interest to some people. Here it is.&lt;/p&gt;
&lt;p&gt;The idea is to parse the file chunk by chunk.
For example, let&#39;s say I have a 10 000 lines file that I&#39;d like to parse. For each file I&#39;m calling some database-related functions or so. As a result, each line can take quite some time to be processed and the whole loop can eventualy fail to finish due to limited memory or timeout.&lt;/p&gt;
&lt;p&gt;So, what I do is I create a set of delimiters: m and n, m will be the line number to start from and n the maximum number of lines to read.&lt;/p&gt;
&lt;p&gt;For example, if n=500 the first run would read from line m=0 to line m+n = 0+500.
Now 500 lines should be quite easy for PHP to parse even though each line needs quite a lot of processing.&lt;/p&gt;
&lt;p&gt;Great, we&#39;ve got our first 500 lines done, how do we process the rest now? Well, that&#39;s the main idea here, we need PHP to free its memory and reset its timeout, so what we do is simply reloading the page!!
header(&amp;quot;Refresh: myscript.php?new_m_value=500&amp;amp;new_n_value=500&amp;quot;);&lt;/p&gt;
&lt;p&gt;Just call the same script again with different m and n values and you&#39;re on!
Of course, we&#39;ll need some variables to be carried accross, for that we have PHP sessions.
Now, enough text, let&#39;s see a little example of how I use this method:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Number of lines to parse in this run&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$maxline&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;max&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;max&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Offset line to start parsing from&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$startline&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;start&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;start&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Number of already parsed lines&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$parsedlines&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;aparsed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;aparsed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Is the parsing finished ?&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$parsingdone&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// PHP session to pass variables from step to step&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;session_start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;dataFileReader&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;dataFileReader&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// This is a bit brutal, but it&#39;s an example. just carry any variable you need ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// File to work on&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$temp_filename&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;dataFileReader&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;tempFileName&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$temp_filename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Opening the data file&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handleReader&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$temp_filename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Counting the rows&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$parsed_count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Reading the file line by line with fgets function&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;feof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handleReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$buffer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fgets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handleReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// Reading one line&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;explode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&#92;t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;        &lt;span class=&quot;token comment&quot;&gt;// Line is tab-delimited&lt;/span&gt;

            &lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           
            &lt;span class=&quot;token comment&quot;&gt;// This is where I know which chunk of lines to process&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// This can be seen pretty much like the SQL clause: LIMIT m,n&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$startline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$maxline&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$startline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$k&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                     &lt;span class=&quot;token comment&quot;&gt;/* DO SOMETHING WITH THE FIELDS HERE */&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token variable&quot;&gt;$parsed_count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Closing the file&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handleReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// Here, we redirect or end the process&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parsed_count&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Redirecting to process the next part of the file&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Refresh: 1; script.php?start=&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$startline&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$maxline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&amp;amp;max=&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$maxline&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&amp;amp;aparsed=&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parsedlines&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$parsed_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// the parsing is done&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$parsingdone&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Displaying number of lines parsed&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parsingdone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parsedlines&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$parsed_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot; lines parsed so far ... continuing ...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parsingdone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// End message&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Data file parsed (&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parsedlines&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$parsed_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot; lines)!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// killing the session&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;dataFileReader&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;session_destroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;ERROR: Couldn&#39;t open the data file - Nothing was uploaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;ERROR: Couldn&#39;t find the temporary data file - Nothing was uploaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>DevTools for creative people</title>
    <link href="https://patrickbrosset.com/articles/2015-02-12-devtools-for-creative-people/"/>
    <updated>2015-02-12T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-02-12-devtools-for-creative-people/</id>
    <content type="html">
      
        &lt;p&gt;Browser DevTools are pretty complex. There are reasons for this though, they&#39;re not just complex to annoy people.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They contain within one tiny UI (often docked in the browser) various tools, often having very little in common with each other. All of these tools are needed, but having all of them together at all times in one application makes for a complex UI to grasp.&lt;/li&gt;
&lt;li&gt;These tools are sometimes very low level. They need to be low level, users need to understand how things work or, most often, why they don&#39;t, and that means explaining how it was interpreted by the browser, at the lower level.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add to this the number of new APIs and libs that appear constantly in the web platform, it&#39;s easy for devtools to become more and more bloated and harder to use for creative people, people that are perhaps used to a more visual set of tools.&lt;/p&gt;
&lt;p&gt;Modifying a page&#39;s structure and style in devtools right now requires knowledge of HTML and CSS. This may sound obvious, but I&#39;m convinced this could be made a lot simpler, or let&#39;s say more visual than having to write code. Why would you want to write a color hex code when you can pick it from a color wheel or an existing palette? Why would you tweak an element&#39;s position pixel by pixel when you can grab and move the element instead, and have it snap to a grid?&lt;/p&gt;
&lt;h3&gt;Bridging the gap&lt;/h3&gt;
&lt;p&gt;I think there&#39;s work to do if we want to make the devtools be more accessible for everybody, and in particular be more efficient for creative people.&lt;/p&gt;
&lt;p&gt;Here are examples of features I think would help, some of them are already present.&lt;/p&gt;
&lt;h3&gt;Animations&lt;/h3&gt;
&lt;p&gt;An animation panel where you can play, pause, loop, seek or slow-down animations and tweak each keyframe&#39;s properties makes adjusting animations in the browser a lot simpler and more natural.&lt;/p&gt;
&lt;p&gt;In time, I think a flash-like timeline to author animations from scratch is inevitable.&lt;/p&gt;
&lt;p&gt;Below is the animation inspector in Firefox 37:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/animation-inspector.png&quot; alt=&quot;Animation inspector in Firefox 37&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Similarly, editing animation timing functions (the rate at which the object moves during the animation duration) feels a lot more natural when you can change the cubic-bezier curve and have visual feedback rather than having to write numbers in a matrix.&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=LemdYmcRrb0&lt;/p&gt;
&lt;h3&gt;Transforms&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;transform&lt;/code&gt; is one of the most often animated CSS property, and that&#39;s because it offers a lot of flexibility to move, reshape or resize an element in a very high performance way. But writing a transform function is often hard and again, much better solved visually.&lt;/p&gt;
&lt;p&gt;Since Firefox 33, you can visualize the effect of a transform in the page:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=OC3ZNbnDHt0&lt;/p&gt;
&lt;p&gt;Being able to visualize how a given transform function affects an element comparing its original and transformed shape and position is a step in the right direction, but ultimately, being able to &lt;a href=&quot;http://codepen.io/fta/full/ifnqH/&quot;&gt;transform elements by freely distorting them in the page&lt;/a&gt; is what devtools should aim for.&lt;/p&gt;
&lt;h3&gt;Colors&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/eyedropper.png&quot; alt=&quot;The eyedropper picks colors from the page (since Firefox 31).&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is one of the more obvious examples, but it&#39;s still worth noting how much better if feels to be able to tweak colors on a web page by choosing from a wheel or picking from the page rather than remembering color names or trying to guess the hex code.&lt;/p&gt;
&lt;h3&gt;Images&lt;/h3&gt;
&lt;p&gt;Right now browser devtools don&#39;t offer much when it comes to image assets management on a site. However &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1108288&quot;&gt;positioning sprite backgrounds&lt;/a&gt; or &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1130761&quot;&gt;assembling multiple css background images&lt;/a&gt; are not always easy problems and they are typically better solved visually than by going through code.
I think devtools should be helping more here.&lt;/p&gt;
&lt;h3&gt;Layout&lt;/h3&gt;
&lt;p&gt;This seems to me like it&#39;s the area that lacks devtools support the most.&lt;/p&gt;
&lt;p&gt;Some people use CSS floats for their layouts, but I don&#39;t see any tool that helps with understanding the notions of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context&quot;&gt;block formatting context&lt;/a&gt; or how to clear floats. It seems like it would be useful to at least have a mode that highlights contexts, floated and clearing elements, to at least visualize the global structure of the layout.&lt;/p&gt;
&lt;p&gt;Some sites start to make use of flexbox, which is awesome, but flexbox isn&#39;t totally straightforward. Here again, devtools could help represent the structure of the layout in the page and allow modifications to be made visually (if that sounds like something you could help implement in FirefoxDevTools then &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1114973&quot;&gt;check out this bug&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/3d-view-firefox.png&quot; alt=&quot;FirefoxDevTools 3D view&quot; /&gt;&lt;/p&gt;
&lt;p&gt;What about z-index? Here again there are some technical details that make the concept not always so easy to grasp, and it&#39;s easy to fall into common traps.&lt;/p&gt;
&lt;p&gt;Seeing your page in 3D, with elements ordered by z-index, can definitely help sometimes.
But what about a mode where you&#39;d clearly &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=948364&quot;&gt;see the stacking context elements and the various z-indices marked&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/highlight-css-selectors.png&quot; alt=&quot;The  command in the Firefox Developer Toolbar can highlight nodes based on a CSS selector (Firefox 33).&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finally, layout is all about sizes, grids and aligning things.&lt;/p&gt;
&lt;p&gt;So the least the devtools can do to help is provide tools to verify that things are aligned correctly.
Drawing guides around elements that match a given selector is one way.
Adding rulers and guides is another nice option (check out &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1089240&quot;&gt;this bug&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Element size and position&lt;/h3&gt;
&lt;p&gt;The last topic I&#39;d like to touch on is positioning. Obviously if you&#39;re not going to position anything in the page and rely on the default flow, then you&#39;re not too likely to need any special help from your favorite devtools, but as soon as you start absolutely positioning elements, or using fixed or even sticky positioning, then some visual tools would be nice.&lt;/p&gt;
&lt;p&gt;Work-in-progress in Firefox 38: the element geometry editor:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=zsNhGllr2-g&lt;/p&gt;
&lt;p&gt;What if the devtools provided a way to fine tune the position or size of an element by moving or resizing it in the page? Wouldn&#39;t that make more sense?
Obviously there are lot&#39;s of cases where typing a number is going to be more efficient than moving something with the mouse, but I&#39;m convinced the opposite is also true. Ever tried to resize an element to the size of its background image? Ever tried to find the perfect position for a logo?&lt;/p&gt;
&lt;p&gt;In conclusion, I&#39;d say I&#39;m pretty hopeful with the direction the various browser devtools are heading right now and I&#39;m certain we can bridge the gaps in many places to help creative people get more out of their experiences using their browsers as a creative tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;After all, if the content is going to be consumed in the browser in the end, why not create it there in the first place?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But there&#39;s still a lot of ground to cover so, get involved, &lt;a href=&quot;http://ffdevtools.uservoice.com/&quot;&gt;submit your ideas&lt;/a&gt;, or &lt;a href=&quot;https://wiki.mozilla.org/DevTools/GetInvolved&quot;&gt;start hacking&lt;/a&gt;!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Breaking-down CSS animation demos with Firefox DevTools</title>
    <link href="https://patrickbrosset.com/articles/2015-02-13-breaking-down-css-animation-demos-with-firefox-devtools/"/>
    <updated>2015-02-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-02-13-breaking-down-css-animation-demos-with-firefox-devtools/</id>
    <content type="html">
      
        &lt;p&gt;I have recently started this series of videos with the idea of showing how various features of the FirefoxDevTools could be used to break-down and understand how seemingly complex CSS animations were done.&lt;/p&gt;
&lt;p&gt;Multiple box-shadows and animated &lt;code&gt;::before&lt;/code&gt; and &lt;code&gt;::after&lt;/code&gt; pseudo-elements:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=eYYTKEIS6zw&lt;/p&gt;
&lt;p&gt;Preview transforms, and highlight selectors:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=lyQINNjv-Fo&lt;/p&gt;
&lt;p&gt;Work with iframes and pause animations:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=pfIrK_2nRRc&lt;/p&gt;
&lt;p&gt;That’s it for now. More to come.&lt;/p&gt;
&lt;p&gt;Follow &lt;a href=&quot;https://www.youtube.com/c/PatrickBrosset&quot;&gt;my channel on YouTube&lt;/a&gt;!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>DevTools for designers, again!</title>
    <link href="https://patrickbrosset.com/articles/2015-02-25-devtools-for-designers-again/"/>
    <updated>2015-02-25T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-02-25-devtools-for-designers-again/</id>
    <content type="html">
      
        &lt;p&gt;I wrote a couple of weeks ago about &lt;a href=&quot;https://patrickbrosset.com/articles/2015-02-25-devtools-for-designers-again/2015-02-12-devtools-for-creative-people&quot;&gt;how browser devtools should be thinking about addressing the needs of designers&lt;/a&gt; and it looks like that post was right on time!&lt;/p&gt;
&lt;p&gt;Indeed, it seems that the ChromeDevTools team is starting to invest in this area more and more. This is brilliant!&lt;/p&gt;
&lt;p&gt;And on &lt;a href=&quot;https://trello.com/b/GKotpgkR/devtools-for-designers&quot;&gt;this trello page&lt;/a&gt; the ChromeDevTools team is looking for people to submit and comment on ideas on this area.&lt;/p&gt;
&lt;p&gt;Here are some screenshots from that page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/chromedevtools-colorpicker-mockup.png&quot; alt=&quot;Mockups of a new color picker for chrome devtools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/chromedevtools-colorpicker-mockup2.png&quot; alt=&quot;Mockups of a new color picker for chrome devtools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Knowing that on the Firefox side, we’ve set ourselves goals to ship creative tools this year too, designers who work for the web should be in a better place really soon!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Understanding the CSS box model for inline elements</title>
    <link href="https://patrickbrosset.com/articles/2015-03-24-understanding-the-css-box-model-for-inline-elements/"/>
    <updated>2015-03-24T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-03-24-understanding-the-css-box-model-for-inline-elements/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://hacks.mozilla.org/2015/03/understanding-inline-box-model/">https://hacks.mozilla.org/2015/03/understanding-inline-box-model/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Do you really understand CSS linear-gradients?</title>
    <link href="https://patrickbrosset.com/articles/2015-03-27-do-you-really-understand-css-linear-gradients/"/>
    <updated>2015-03-27T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-03-27-do-you-really-understand-css-linear-gradients/</id>
    <content type="html">
      
        &lt;p&gt;Want a nice gradient background on your site?&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;BOOM, done!&lt;/p&gt;
&lt;p&gt;Ok, it’s a little boring. So if you want more, I recommend this &lt;a href=&quot;https://css-tricks.com/css3-gradients/&quot;&gt;css-tricks article&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient&quot;&gt;MDN page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Still here? Alright, let’s go and have a look at some of the details of how linear-gradients actually work.&lt;/p&gt;
&lt;p&gt;First of all, let’s remind ourselves of the syntax allowed in the linear-gradient function:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;[&amp;lt;angle&gt; | to &amp;lt;side-or-corner&gt;]? &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &amp;lt;color-stop-list&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function accepts an optional first argument which determines the angle of the gradient, which can be expressed either as an angle with a unit (&lt;code&gt;deg&lt;/code&gt;, &lt;code&gt;rad&lt;/code&gt;, &lt;code&gt;grad&lt;/code&gt;, &lt;code&gt;turn&lt;/code&gt;) or as a side-or-corner keyword.&lt;/p&gt;
&lt;p&gt;The function then accepts a list of color stops.&lt;/p&gt;
&lt;h3&gt;The gradient box&lt;/h3&gt;
&lt;p&gt;A gradient image has not dimensions, it is infinite, unlike traditional background images. What gives a visible dimension to the gradient is its gradient box i.e. the area where it is displayed.&lt;/p&gt;
&lt;p&gt;Typically, when you apply a linear-gradient background-image to a DOM element, that area is the border-box of the element (which is the same area a background-color would be displayed, or where an image identified by a URL would).&lt;/p&gt;
&lt;p&gt;However, if you also use the CSS property &lt;code&gt;background-size&lt;/code&gt; and set it to, say, 200px * 200px, then the gradient box will have this size and will be positioned at the top left corner of the DOM element by default, unless you also set &lt;code&gt;background-position&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, just remember when reading through the next sections that the gradient box isn’t always positioned and sized like the DOM element the gradient applies to.&lt;/p&gt;
&lt;h3&gt;The gradient line&lt;/h3&gt;
&lt;p&gt;In the gradient box, the line that passes through the center and along which color stops are distributed is called the gradient line. This line is more easily explained when talking about the gradient angle, so more about this in the next section.&lt;/p&gt;
&lt;h3&gt;The gradient angle&lt;/h3&gt;
&lt;p&gt;Quite obviously, the angle of a linear-gradient is used to determine in which direction is the gradient going. But let’s go into more details.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/T-sks3Xr3aiLYyWZtTPnXA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;C&lt;/code&gt; is the center point of the gradient box, then &lt;code&gt;A&lt;/code&gt; is the angle between the vertical line that passes through &lt;code&gt;C&lt;/code&gt;, and the &lt;em&gt;gradient line&lt;/em&gt; that also passes through &lt;code&gt;C&lt;/code&gt; and along which the gradient color stops are distributed.&lt;/p&gt;
&lt;p&gt;You can define this angle in 2 ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;by using one of the keywords: &lt;code&gt;to top&lt;/code&gt;, &lt;code&gt;to bottom&lt;/code&gt;, &lt;code&gt;to left&lt;/code&gt;, &lt;code&gt;to right&lt;/code&gt;, &lt;code&gt;to top right&lt;/code&gt;, &lt;code&gt;to top left&lt;/code&gt;, &lt;code&gt;to bottom right&lt;/code&gt;, &lt;code&gt;to bottom left&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;or by defining the angle with a number and a unit, e.g. &lt;code&gt;45deg, 1turn, …&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the angle is omitted, it defaults to &lt;code&gt;to bottom&lt;/code&gt; (which is 180deg or 0.5turn):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/6NnROTVFpbcaa9OKwsF80g.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this picture and the following ones, the gradient box is the bordered rectangle, and the gradient line is the thick grey line that passes in the center and has the color stops displayed along.&lt;/p&gt;
&lt;p&gt;In the example above, the angle is missing, so the white-to-red gradient goes from top to bottom, which is the same as using the &lt;code&gt;to bottom&lt;/code&gt; keyword, as shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/UXz2TNjXRLIQx0RttwA74A.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And, as shown in the following 2 pictures, &lt;code&gt;to top&lt;/code&gt; is equivalent to an angle of zero degrees:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pc3MBOxi6dwo1T0ktlyWsw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/Ij-2NzrZd32FhkP9AiDvBw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another important point when using angle keywords is that &lt;code&gt;to top right&lt;/code&gt; for example (or any of the other corner keywords) depend on the &lt;strong&gt;dimensions&lt;/strong&gt; of the gradient box.&lt;/p&gt;
&lt;p&gt;The logic is that if you wanted a red to blue gradient towards the top right corner of an element, then that element should be exactly blue at the top right corner, and the violet color in the middle of the gradient should form around a line that passes through the top left and bottom right corners.&lt;/p&gt;
&lt;p&gt;Let’s look at this with a picture:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/4FAmAuJE8_5wD2JphAf-bA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So, &lt;code&gt;to top right&lt;/code&gt; doesn’t mean that the gradient line passes through the top-right corner and it doesn’t mean that the gradient angle is 45 degrees either!&lt;/p&gt;
&lt;p&gt;Let’s look at how the gradient line moves when the angle changes in the following animation:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/6QjmQlmwLqgSnWdAm4UuLw.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this animation, the angle is incremented from 0 to 360 degrees, by steps of 10 degrees. The low-resolution of the GIF even allows to better see how the various colors are displayed as “lines” always perpendicular to the gradient-line.&lt;/p&gt;
&lt;p&gt;Let’s recap on gradient angles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The angle is measured between the gradient line and a line that starts at the center of the gradient box and extends towards the top.&lt;/li&gt;
&lt;li&gt;0 degrees therefore means &lt;code&gt;to top.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The default value for an angle, when missing, is &lt;code&gt;to bottom&lt;/code&gt;, which is the same than 180 degrees.&lt;/li&gt;
&lt;li&gt;Corner keywords depend on the gradient box dimensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The gradient line length&lt;/h3&gt;
&lt;p&gt;Before we can take a look at how color stops are distributed along the gradient line we need to explain one thing.&lt;/p&gt;
&lt;p&gt;You may have noticed in the previous animation that the colors stops are sometimes positioned outside of the gradient box, which may sound weird at first but is logical once you know the reason.&lt;/p&gt;
&lt;p&gt;Consider this example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/E0EISyDpcHsBBruyKTvZpA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We want a gradient from red to blue, at 45 degrees and because of the aspect ratio of the gradient box, the gradient line cannot pass through the top right corner.&lt;/p&gt;
&lt;p&gt;But what the browser wants to do here (well, what the specification tells it to do) is make the top right corner exactly blue.&lt;/p&gt;
&lt;p&gt;If we had made the gradient line only start and end at the edges of the box, then the color blue would have covered a greater area of the box, and the gradient wouldn’t have been spread out as much.&lt;/p&gt;
&lt;p&gt;Therefore, in order to do this, the gradient line sometimes has to extend out of the gradient box.&lt;/p&gt;
&lt;p&gt;It’s easy to know where it starts and ends though. Just draw a line that passes through the nearest corner and that is perpendicular to the gradient line. The point where that line crosses the gradient line is the start/end position (this
is explained quite nicely in &lt;a href=&quot;http://dev.w3.org/csswg/css-images/#linear-gradient-syntax&quot;&gt;the specification&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;In fact, if you let &lt;code&gt;W&lt;/code&gt; be the width of the gradient box, &lt;code&gt;H&lt;/code&gt; its height, and &lt;code&gt;A&lt;/code&gt; the gradient angle, then the length of the gradient line is:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;H&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Color stops&lt;/h3&gt;
&lt;p&gt;Color stops are organized in a coma-separated list where each item can be defined as:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&amp;lt;color&gt; [&amp;lt;percentage&gt; | &amp;lt;length&gt;]?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is therefore not mandatory to specify where the color should be placed on the gradient line. For instance, this is ok:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/9zTKP3Z1JJpChFWzlMYVEA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;None of the color stops have positions, so the browser determine positions for them.&lt;/p&gt;
&lt;p&gt;In the simplest case, with just 2 colors, color 1 would be placed at 0% (the start of the gradient line), and color 2 at 100% (the end of the gradient line).&lt;/p&gt;
&lt;p&gt;Then if you add a third color, you’d have color 1 still at 0%, color 2 at 50% and color 3 at 100%, and so on.&lt;/p&gt;
&lt;p&gt;In the example above, 5 color stops have been defined, and the browser has calculated their respective positions as 0%, 25%, 50%, 75%, 100%. The reason it does this is to distribute the colors evenly along the gradient box.&lt;/p&gt;
&lt;p&gt;Now, of course, stops can have custom positions. A position can be expressed either with a percentage (relative to the size of the gradient line), or a CSS length (where any CSS unit works).&lt;/p&gt;
&lt;p&gt;Consider this example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/uqI9Et1xhNhua8gXPXNuog.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, each of the 5 color stops have positions, in pixels. These positions are calculated from the start of the gradient line.&lt;/p&gt;
&lt;p&gt;Using these positions, you can come up with all sorts of nice effects. One thing you can do for instance is use a gradient to not draw a gradient at all, but have multiple colors:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/mNZN571ymIswk91aTk1SxQ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In the above picture, there are 7 stops, and they are organized in a way that the next color starts exactly at the same position as the last one which means that the browser doesn’t have to fill the space between 2 stops with a gradient of colors.&lt;/p&gt;
&lt;p&gt;But of course this is all nice and fun, but what happens if you mix positioned stops with un-positioned stops? Then you make the browser work some more and ask it to distribute the stops for which you omitted a position automatically:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/Lolk7Z-HlExs4E6CxNml7w.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In the simple example above, the 2nd stop (orange) has no position, and so the browser makes up for it and finds the right place between the previous positioned stop and next positioned stop. Here it’s very easy because the immediate neighbors of the 2nd stop have positions, but it gets more complex if only a few stops have positions, or if there are no previous or next positioned stops.&lt;/p&gt;
&lt;p&gt;Let’s take a look at a few examples:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/4X9Dp_-Qv1yT0o37fxxJqQ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Above, only the 3rd stop (yellow) is positioned at 30%. So to nicely distribute the rest, it puts the first one at 0%, the last at 100%, and distribute the stops in between at mid-positions (so the orange ends up right between 0% and 30%, and the red ends up between 30% and 100%).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/8CBMzJNENSiJC8tda2E6Gw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Above, the first and last stops are positioned, so the rest is distributed evenly in between those two.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/GwwdL9exDZ6L5jtBaoDSww.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Of course it would be too easy if 0% and 100% was a hard range you couldn’t go out of. But as seen in the previous example, the last stop is positioned at 120%, and so all other stops have to be distributed according to this position (the default start position remains 0% in this case).&lt;/p&gt;
&lt;p&gt;And if you want to make your browser work even more, why not specify out of order positions?&lt;/p&gt;
&lt;p&gt;Indeed, color stops are expected to be ordered, but nothing prevents you from not doing it, and nothing horrible is going to happen if you don’t. Your browser is simply going to correct you, as seen below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/S3ifssTNSHwGqrwiEM9h2A.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let’s start from the first stop (red), positioned at 30% which is fine, then the second stop is positioned at 10%, which is wrong because, as said above, stops are supposed to be given in increasing positions. So here the browser corrects the second stop position and sets it to the same position as the preceding positioned stop (30%). Then comes the 3rd stop (yellow), at 60%, which is fine, but it is followed by the 4th stop (blue) at 40%. So there again the position is corrected and set as the one of the preceding positioned stop.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/lC-Ls-9-swqvBT0KF6ea8Q.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finally, in the above example, the last stop (blue) is incorrectly positioned and so the browser sets its position to be the same as the preceding positioned stop, which in this case is not its immediate neighbor (yellow), nor the stop before that (orange), so it has to go all the way back to the 1st stop (red). Therefore, all stops between red and blue are also given the position 30% and are therefore not visible.&lt;/p&gt;
&lt;h3&gt;Tooling&lt;/h3&gt;
&lt;p&gt;All the screenshots in this article are from &lt;a href=&quot;http://codepen.io/captainbrosset/pen/ByqRMB&quot;&gt;a simple tool I made on codepen&lt;/a&gt; which lets you input a linear-gradient function and see the gradient box, gradient line, angle and color stops information as an overlay on top of the element.&lt;/p&gt;
&lt;p&gt;There’s all sorts of bugs and limitations in the tool (see the TODO comments in the javascript), so don’t expect too much ☺&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/KmUoZholZRlgAMo0yi2l8g.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>The future of layout with CSS Grid Layouts</title>
    <link href="https://patrickbrosset.com/articles/2015-08-26-the-future-of-layout-with-CSS--Grid-Layouts/"/>
    <updated>2015-08-26T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-08-26-the-future-of-layout-with-CSS--Grid-Layouts/</id>
    <content type="html">
      
        &lt;p&gt;In this article we&#39;ll take a look at the wonderful world of the CSS Grid Layout, a relatively new &lt;a href=&quot;http://dev.w3.org/csswg/css-grid-1/&quot;&gt;W3C specification&lt;/a&gt; that has partially started to see the light in some browsers.&lt;/p&gt;
&lt;p&gt;But before we dive into what this new CSS technique is all about and how to use it, let&#39;s quickly review grid theory.&lt;/p&gt;
&lt;h3&gt;My really short introduction to grid theory&lt;/h3&gt;
&lt;p&gt;I am not a designer nor did I know much about grid theory before stumbling upon the CSS Grid Layout specification, so don&#39;t take my word for it and go look it up yourself, but if you don&#39;t care to, here&#39;s my pitch about it:&lt;/p&gt;
&lt;p&gt;In essence, a grid is a set of invisible vertical and horizontal lines that the various design and content pieces of a web page, magazine or newspaper are positioned on.
The goal of a grid is to serve as a base for placing the various pieces of content and to make sure these pieces are aligned and spaced out evenly.
A grid, even if not visible, provides the viewer with a way to navigate content visually.&lt;/p&gt;
&lt;h3&gt;So what has CSS got to do with it?&lt;/h3&gt;
&lt;p&gt;CSS is getting a set of entirely new properties that, when used together, allow you to define grids for your web content to go onto.
These properties are defined in the &lt;a href=&quot;http://dev.w3.org/csswg/css-grid-1/&quot;&gt;CSS Grid Layout specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;CSS hasn’t particularly been known for its ability to handle complex layouts. Although this may not have been a problem when CSS first came out, it has evolved to be a pretty common one over the years as all sorts of complex layouts couldn’t be easily implemented.
People have been very creative at getting around the lack of support by using floats or absolute positioning and a variety of CSS layout frameworks emerged.&lt;/p&gt;
&lt;p&gt;So it’s about time CSS got some proper layout solutions to support today&#39;s use cases.&lt;/p&gt;
&lt;h3&gt;Can I use it?&lt;/h3&gt;
&lt;p&gt;Yes you can start using it today, but only Chrome has implemented the specification far enough to play with grids, and you need to turn the experimental Web Platform features flag on.&lt;/p&gt;
&lt;p&gt;IE has an early implementation (dating back to IE10) too but the spec has evolved since it was added and so it’s not entirely compatible with it anymore.&lt;/p&gt;
&lt;p&gt;Firefox has &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=616605&quot;&gt;started implementing it&lt;/a&gt; too so it shouldn’t be too long before you can use it in this browser.&lt;/p&gt;
&lt;p&gt;Finally, &lt;a href=&quot;https://github.com/FremyCompany/css-grid-polyfill&quot;&gt;a polyfill&lt;/a&gt; exists, so you have no excuses not to start experimenting (and I strongly encourage you do)!&lt;/p&gt;
&lt;h3&gt;So what&#39;s a CSS Grid Layout?&lt;/h3&gt;
&lt;p&gt;To its core, a grid layout in CSS is a set of vertical and horizontal lines that define cells that content can be arbitrarily positioned on.
So it looks like a table in a way, except for a few key differences we’ll see later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid1.png&quot; alt=&quot;The main building blocks of a grid, lines and cells&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The previous figure shows the building blocks of a grid:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lines&lt;/strong&gt;: in this case there are 4 vertical lines and 3 horizontal lines. Lines are given numbers starting from 1. Vertical lines are shown from left to right, but this depends on the writing direction.
Lines can optionally be given names, which helps with referencing them in CSS as we’ll see later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tracks&lt;/strong&gt;: a track is simply the space between 2 parallel lines. So in the above example, there are 3 vertical tracks and 2 horizontal tracks. Lines are useful to say where content starts and stops, but tracks are ultimately where content goes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cells&lt;/strong&gt;: a cell is where a horizontal and a vertical track meet. In the figure above, only one cell has been highlighted, but there are 6 cells in the grid.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Areas&lt;/strong&gt;: an area is a rectangular shape that can span an arbitrary number of cells. Areas, like lines, can be named. In the above grid, we could for example define areas A, B and C as shown below:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid2.png&quot; alt=&quot;A 3x2 grid with 3 grid areas: A covering the first 2 vertical cells, and B and C covering 2 cells each, horizontally&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now that we know these simple definitions, let’s take a look at what makes grids so powerful.&lt;/p&gt;
&lt;h3&gt;One key advantage of CSS grids is that they enforce real separation of layout and markup.&lt;/h3&gt;
&lt;p&gt;Indeed, the grid itself is completely defined purely in CSS. This means that apart from the parent HTML element the grid is applied to, there&#39;s no need for defining any extra elements for the columns, rows, cells or areas.&lt;/p&gt;
&lt;p&gt;When you think of it, that’s a really interesting property. One aspect of it is that the visual order of elements on the page is decoupled from the order of elements in the markup. That’s important because on a page the source order is used for things like speech and tab navigation, so CSS Grids allow you to optimize the markup for accessibility without compromising your ability to manipulate the visual result. One other point is that the markup will be somewhat lighter and easier to understand, and therefore easier to maintain.&lt;/p&gt;
&lt;p&gt;But more importantly, this gives a very powerful tool for separating the content from the layout, effectively decoupling them in a way that making a change to any of them doesn’t impact and otherwise break the other.
As a designer, you could easily experiment with new layouts without having to change anything else than CSS, as long as your new layouts provide the expected lines and areas the content uses.
And as a developer, you would simply need to use the numbered or named lines and areas to position your content on the grid.&lt;/p&gt;
&lt;p&gt;Imagine the simple following grid layout:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid3.png&quot; alt=&quot;A 3x5 grid with various areas positioned, header, sidebar, footer, title and 3 articles, each spanning a few cells&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this layout, named areas have been defined, allowing content to be positioned in them simply by referencing these names. This means that not only can we change this layout relatively easily in the future, as long as we maintain the named regions (here the named regions act as the layout’s public API in a way), but media-queries can also be used to change this layout dynamically too. Remember, the whole layout is defined in CSS, so media-queries play with grid layouts very well.&lt;/p&gt;
&lt;p&gt;For instance, using a media query, the previous layout could be switched to something like this on smaller screens:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid4.png&quot; alt=&quot;A 7x1 grid, containing the same elements as the one before, but re-arranged on 1 column&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Enough with the theory, let’s see how grid layouts are actually defined using CSS.&lt;/p&gt;
&lt;h3&gt;Creating grids with CSS&lt;/h3&gt;
&lt;p&gt;Defining a grid requires only one HTML element to be present: the &lt;strong&gt;grid container&lt;/strong&gt;. That’s the element the layout is going to be applied to.
Any HTML element that’s a child of the grid container is a &lt;strong&gt;grid item&lt;/strong&gt;. Grid items are what go onto the grid.&lt;/p&gt;
&lt;p&gt;Setting an element as being a grid container is as simple as:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just doing this though isn’t nearly enough, we also need to define what this grid looks like, how many columns and rows it has, and how big they are.
This can be done by using grid templates as shown in the following example:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we’re explicitly defining a grid that has 2 rows and 4 columns. Note how the &lt;code&gt;repeat()&lt;/code&gt; function is used here to avoid repeating a fragment of the tracks 4 times, this is equivalent to &lt;code&gt;100px 100px 100px 100px&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Each of the defined tracks is given a size, and so we end up with a grid structure that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid5.png&quot; alt=&quot;The resulting grid, from the code snippet above, 4 columns, 2 rows&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that grids can also be implicitly defined like in:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, the browser will keep on adding rows as needed to fit the content.&lt;/p&gt;
&lt;p&gt;Let’s go back to our simple, explicit, 4 by 2 grid and let’s add some content. As we said previously, we only need 2 levels of elements, the container and the items, so let’s use this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;item 1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;item 2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;item 3&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;item 4&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;item 5&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which, without any specific CSS code to position the items, leads to the following arrangement:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid6.png&quot; alt=&quot;The grid from before, with 5 HTML elements inside it&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see above, since we didn’t define where these items should sit on the grid, the browser has positioned them automatically by putting one item per cell until the first row got filled in, at which point it decided to put the rest of the items on the next row.&lt;/p&gt;
&lt;p&gt;The spec defines an algorithm for automatically placing items on the grid if they haven’t been given a position and that is sometimes useful when, for example, you have either many items or a dynamic number of items and don’t want to or can’t define the position for them all.&lt;/p&gt;
&lt;p&gt;Let’s see how our items can be positioned on the grid.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.item1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.item2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’ve defined the position of the 2 first items, which means that the other ones will still be automatically positioned by the browser, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid7.png&quot; alt=&quot;The grid from before, with the first 2 items each spanning 2 columns&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The above example uses &lt;strong&gt;line-based placement&lt;/strong&gt; in that it uses numeric indexes of lines to place items.
Items 1 and 2 are defined to be positioned between horizontal lines 1 and 2, and between vertical lines 1 and 3 and 3 and 5.&lt;/p&gt;
&lt;p&gt;What happens now if we keep on adding items, but there aren’t any grid cells defined to receive them?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid7.png&quot; alt=&quot;The same grid again, with 6 more elements in it, stacked at the end, with new implicit cells getting created&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The grid keeps on adding rows and columns as needed. Note that these new tracks will have their sizes depend on the content size.&lt;/p&gt;
&lt;p&gt;So that’s all well and good, but one thing that’s really useful when it comes to CSS Grids is grid areas as we saw earlier. Let’s take a look at how grid areas can be used in CSS.&lt;/p&gt;
&lt;p&gt;First of all you’ll need to define the grid container and its tracks and areas:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px auto 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;header  header&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;sidebar content&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;footer  footer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we’ve defined 3 rows, the top and bottom ones being 100px high, while the middle one will just adapt to its content. We’ve also defined 2 columns, the left one 100px wide, and the right one adapting to its content.&lt;/p&gt;
&lt;p&gt;We’ve also introduced the &lt;code&gt;grid-template-areas&lt;/code&gt; property that may look strange at first, but it’s really simple when you realize it’s only a representation of the grid with names on it. Let’s explain this.&lt;/p&gt;
&lt;p&gt;We said we had 3 rows and 2 columns right? Let’s represent them in ascii-art like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;+---+---+
| . | . |
+---+---+
| . | . |
+---+---+
| . | . |
+---+---+&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each dot is a cell where areas can be defined. So let’s define areas for a typical website layout:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;+--------+--------+
| header | header |
+--------+--------+
| sidebar| content|
+--------+--------+
| footer | footer |
+--------+--------+&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want the header and footer to span the whole width, so we’ve repeated them in the 2 columns.
Now, if we just remove all the useless ascii-art style borders and include each line in double-quotes, we get something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;header  header&quot;&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&quot;sidebar content&quot;&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&quot;footer  footer&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which is exactly how grid areas are defined in CSS using the &lt;code&gt;grid-template-areas&lt;/code&gt; property. It helps thinking about it as a 2D representation of the grid in ascii-art and aligning area names.&lt;/p&gt;
&lt;p&gt;You can also use dots ‘.’ when a cell isn’t covered by an area. Here’s an example of this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;. . . . .&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;. . . . .&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;. . a . .&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;. . . . .&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;. . . . .&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, there are 25 cells and 1 area that is defined to occupy only the center cell.&lt;/p&gt;
&lt;p&gt;Now, going back to the previous header, sidebar, content, footer example, let’s add some markup to it:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;header&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sidebar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;sidebar&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;footer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;footer&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Lorem ipsum dolor sit amet...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
 &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing that needs to be done now is position each grid item in the right named areas (we’ve conveniently used class names that correspond to area names here):&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; header&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.sidebar&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sidebar&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.footer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; footer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which would produce something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid9.png&quot; alt=&quot;A page layout with the header spanning across the top, a sidebar and main areas side by side next, and at the bottom, spanning horizontally, a footer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now, if we wanted to change this layout at any point, or make it adapt to variable screen sizes thanks to media queries, we would only have to change the grid declaration. Maybe to something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;header&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;sidebar&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;content&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;footer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which, without any other changes to the CSS or markup, would produce this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/grid10.png&quot; alt=&quot;The same as before, buth with header, sidebar, main and footer stacked vertically, from top to bottom, in one column&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We could even completely reshuffle the order of items in the grid without having to change the order in the markup.&lt;/p&gt;
&lt;h3&gt;CSS Grids in the real world&lt;/h3&gt;
&lt;p&gt;So far we’ve only seen simplistic examples of grids, but websites making use of grid systems often have much more complex grid structures. It’s not uncommon for such websites to be based on 12 columns, each of them separated by gutters (narrower columns used to space things out).&lt;/p&gt;
&lt;p&gt;Take &lt;a href=&quot;https://dribbble.com/&quot;&gt;dribbble.com&lt;/a&gt; for example, this design showcase website makes use of a grid to display the designs uploaded by their users. Here the grid is very easily visible because each design occupies exactly one cell (excluding gutter rows and columns):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/dribble.gif&quot; alt=&quot;Dribbble&#39;s interface, with the grid system being highlighted&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Dribbble’s grid layout is also responsive, so if the viewport size changes, the size and number of columns changes gradually, as shown in the following animation:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/dribble2.gif&quot; alt=&quot;Dribbble&#39;s interface, re-arranging its number of columns depending on the available space&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Using the new properties that come with the CSS Grid Layout implementation, we can try and create a dribbble-like layout. For an added twist, let’s try and insert a piece of text content on the second row, and make that content span the whole row.&lt;/p&gt;
&lt;p&gt;First of all, our markup will be something like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dribbbles&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dribbble&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dribbble&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dribbble&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;advert&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Some text ....&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where the &lt;code&gt;.dribbbles&lt;/code&gt; element is the main container for all items, and each &lt;code&gt;.dribbble&lt;/code&gt; element represents one of the designs showcased on the page. The last item, &lt;code&gt;.advert&lt;/code&gt;, is the piece of textual content we want to show on the second row.&lt;/p&gt;
&lt;p&gt;Now, let’s make a grid for it.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.dribbbles&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* Let&#39;s assume the default design has a fixed width */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 880px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 220px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;token string&quot;&gt;&quot;.      .      .      .&quot;&lt;/span&gt;
   &lt;span class=&quot;token string&quot;&gt;&quot;advert advert advert advert&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;justify-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.advert&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;                                          
  &lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; advert&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                                         
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’ve defined 4 columns in our grid. We haven’t said anything about rows, so the browser will automatically create rows as needed.
We did, however, define a couple of rows using the &lt;code&gt;grid-template-areas&lt;/code&gt; property and that’s because we need to place our advert text content on the second row. So we’ve just said that we want a first row with empty cells that are going to be filled in automatically with grid items, as they come, and then a second row that defines an advert area that spans all 4 columns.&lt;/p&gt;
&lt;p&gt;Finally, the second rule in there uses the &lt;code&gt;grid-area&lt;/code&gt; property to place the grid item that has the class &lt;code&gt;advert&lt;/code&gt; inside the &lt;code&gt;advert&lt;/code&gt; area.&lt;/p&gt;
&lt;p&gt;Given the markup we created earlier, this should end up looking something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/dribble3.png&quot; alt=&quot;Result of the code above, a grid of pictures, 4 columns wide, with a piece of text content spanning the entire width on the second row&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Pretty simple, right? Of course this requires some styling code to make the items look nice and all, but the layout CSS code is really short and there’s no extra markup required.&lt;/p&gt;
&lt;p&gt;Now, we can make this responsive too. Let’s say we want to jump to a 3-columns layout for smaller screens:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 880px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 660px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.dribbbles&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 660px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 220px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;.      .      .&quot;&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;advert advert advert&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And so on, for all the screen sizes we want to support. And so, just adding a couple more short media queries, we could end up with something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid/dribble4.gif&quot; alt=&quot;Same site as just before, as an animation, resizing the page, to force the layout to swatch from 4 to 3 to 2 columns&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sites like Dribbble don’t use the CSS Grid Layout yet, but there are well known CSS libraries that provide grid systems.&lt;/p&gt;
&lt;p&gt;A good example of a grid system in CSS is &lt;a href=&quot;http://getbootstrap.com/css/#grid&quot;&gt;the Bootstrap CSS library&lt;/a&gt; but there are others like &lt;a href=&quot;http://purecss.io/grids/&quot;&gt;PureCSS&lt;/a&gt;, &lt;a href=&quot;http://960.gs/&quot;&gt;960 Grid System&lt;/a&gt;, &lt;a href=&quot;http://www.responsivegridsystem.com/&quot;&gt;Responsive Grid System&lt;/a&gt;, &lt;a href=&quot;http://materializecss.com/grid.html&quot;&gt;Materialize&lt;/a&gt;, and more.&lt;/p&gt;
&lt;p&gt;These grid systems unfortunately have to use arrangements of carefully sized and floated DIVs, and need extra DIVs for columns and rows, as well as pseudo-elements for clearing floats.
It’s not that it’s all that complex, after all the tricks that these libraries rely on have been known for years, but they’re just this, tricks, and it’s about time we get a proper built-in solution and get rid of them.
Don’t get me wrong, these libraries are awesome in all sorts of way, and most importantly, they are the ones which made it clear we needed a new CSS feature for defining grids in web layouts, and were therefore the ones which ultimately made the CSS Grid Layout specification possible.&lt;/p&gt;
&lt;h3&gt;Closing words and references&lt;/h3&gt;
&lt;p&gt;As we saw, CSS grids are a pretty good tool for defining layouts that have nice characteristics like authoring simplicity, maintainability, separation of content and layout and that play well with responsive design.
But we’ve only barely scratched the surface of what is possible with CSS grids. The various properties and syntax possibilities allow for a lot of really cool things to be done, and I hope this article made you want to dig further by yourself.&lt;/p&gt;
&lt;p&gt;Here are some interesting references if you wish to know more:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The spec: &lt;a href=&quot;http://dev.w3.org/csswg/css-grid-1/&quot;&gt;http://dev.w3.org/csswg/css-grid-1/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Some grid examples by Rachel Andrew: &lt;a href=&quot;http://gridbyexample.com/&quot;&gt;http://gridbyexample.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Bug to follow for the implementation in Firefox: &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=616605&quot;&gt;https://bugzilla.mozilla.org/show_bug.cgi?id=616605&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;How to use it in IE: &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/hh673533%28v=vs.85%29.aspx&quot;&gt;https://msdn.microsoft.com/en-us/library/hh673533%28v=vs.85%29.aspx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If there’s one talk you need to watch, it should be this one by Rachel Andrew:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;https://www.youtube.com/watch?v=GRexIOtGhBU&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;And Manuel Rego’s talk is also very useful:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;https://www.youtube.com/watch?v=9js_5MjiGFo&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Contribute to Firefox DevTools, become a better web developer</title>
    <link href="https://patrickbrosset.com/articles/2015-09-03-contribute-to-Firefox-DevTools--become-a-better-web-developer/"/>
    <updated>2015-09-03T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-09-03-contribute-to-Firefox-DevTools--become-a-better-web-developer/</id>
    <content type="html">
      
        &lt;p&gt;If you’re a web developer and you’re interested in open source projects and you commonly use browser devtools in your activities then you might want to read on.&lt;/p&gt;
&lt;p&gt;In this article, I’ll go over some of the things that make the Firefox DevTools open source project special, and how those things can make you better at web development.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(If you’re already convinced and just want to get started right away, scroll to the bottom for the list of links)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;It feels like Web Development anyway&lt;/h3&gt;
&lt;p&gt;The whole thing is written as a client-server application. It has a front-end part that’s all written in HTML, CSS and JavaScript, and it connects to a server that’s written in JavaScript too.&lt;/p&gt;
&lt;p&gt;Those two parts communicate via a TCP protocol but from JavaScript, it’s encapsulated in a way that you just end up calling asynchronous functions.&lt;/p&gt;
&lt;p&gt;The client-server separation comes from the fact that DevTools is not only able to debug your local browser tab, but also a tab on a remote device (maybe you have a FirefoxOS phone you want to test an app on, or need to fix a bug on a site on Firefox for Android, etc.), and so the front-end isn’t guaranteed to have access to the page it’s inspecting.&lt;/p&gt;
&lt;p&gt;Reasoning about this is simple if you think of it this way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the front-end is the toolbox UI: the panels, the things you click one, where you see previews of DOM nodes, and script breakpoints, etc.&lt;/li&gt;
&lt;li&gt;the back-end however is what runs on the page/device/tab you are targeting. It’s a series of JavaScript objects that live next to the content page you are inspecting, so it has access to DOM nodes, stylesheets, scripts that are running, can listen in on network requests, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I said earlier, the whole code is written using the languages of the web, so you’ll feel right at home.&lt;/p&gt;
&lt;p&gt;Fun fact: we actually use DevTools to debug DevTools! In fact you can use DevTools to debug the whole Firefox UI, because that’s written in HTML, JavaScript, CSS too!&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=nxB1hj3UC4w&lt;/p&gt;
&lt;p&gt;That’s a really cool way to test your own work. Because DevTools is an app for developers, you’re not such a bad user to test it on!
Knowing the complexity and volume of the Firefox UI, if the tool you work on is up to the task when debugging it, then that’s a pretty good field test too.&lt;/p&gt;
&lt;p&gt;In terms of code management, whether you’ve used Git or Mercurial in the past, that’s no problem, the project can be contributed to using any of them, and tools and procedures exist for both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Here are &lt;a href=&quot;https://mozilla-version-control-tools.readthedocs.org/en/latest/hgmozilla/index.html&quot;&gt;the docs for using Mercurial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mozilla has a long history of using the Mercurial MQ extension. While it’s useful, it’s not recommended anymore and instead &lt;a href=&quot;http://gregoryszorc.com/blog/2014/06/23/please-stop-using-mq/&quot;&gt;you should use the much more git-like workflow&lt;/a&gt; with branching, bookmarks, rebases and history rewriting.&lt;/li&gt;
&lt;li&gt;If you want to use git, there’s a &lt;a href=&quot;https://github.com/mozilla/gecko-dev/&quot;&gt;mirror repository on GitHub&lt;/a&gt; and hear that &lt;a href=&quot;https://github.com/glandium/git-cinnabar&quot;&gt;the git-cinnabar tool&lt;/a&gt; is really useful for working with Mercurial repositories directly from Git.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Live on the edge — Learn the newest technologies&lt;/h3&gt;
&lt;p&gt;One of the great parts about working on a piece of the browser is that you can take full advantage of that browser’s functionalities. DevTools run in a sort of a privileged mode that makes it possible to use all the new and shiny web platform features.&lt;/p&gt;
&lt;p&gt;If you’ve wanted to try new implementations of the EcmaScript standard, know that the DevTools code makes use of pretty much all the new syntaxes introduced in ES2015 supported in Firefox. Just looking at the code is a great opportunity to learn.&lt;/p&gt;
&lt;p&gt;The project makes extensive use of things like modules (with a common-JS like module loader), promises (as well as tasks which is an awesome way to write asynchronous code using &lt;code&gt;yield&lt;/code&gt; and generator functions). Of course things like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let&quot;&gt;&lt;code&gt;let&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const&quot;&gt;&lt;code&gt;const&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot;&gt;arrow functions&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map&quot;&gt;&lt;code&gt;Map&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set&quot;&gt;&lt;code&gt;Set&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator&quot;&gt;spread operator&lt;/a&gt;, and more, get used a lot too.&lt;/p&gt;
&lt;p&gt;DevTools is also uniquely place to make use of all the latest CSS features that are implemented in Firefox. Some of these are CSS variables (extensively used for theming the app), Flexbox layout, CSS filters, animations, transforms, etc.&lt;/p&gt;
&lt;p&gt;And if you’ve wanted to learn about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG&quot;&gt;SVG&lt;/a&gt;, DevTools also makes use of those for pretty much all the icons in the app now, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial&quot;&gt;Canvas&lt;/a&gt; is used quite a lot for drawing graphs in places like the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Performance&quot;&gt;performance tools&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Go over the edge — Learn how browsers work&lt;/h3&gt;
&lt;p&gt;Why stop there when you can go even further?
Working on DevTools is a unique opportunity to learn the web &lt;em&gt;from the inside&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The web platform evolves rapidly, new APIs and features are specified and implemented on a regular basis, and most of them need some sort of tooling support. Something that helps users debug problems when using these APIs or features for the first time.
And so, working on DevTools means that you not only get to learn about these new things early, but also get to know more about how they work. Three examples come to mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;DOM Promises&lt;/a&gt; are new, and they’re not particularly easy to debug, so there’s some work going on right now about adding a new promise debugging panel.
The people working on this panel not only have the chance to learn more about promises, but also get to work with the very people that are implementing them in Firefox (in C++). A promise debugging panel needs some special platform APIs to list promises and their status, which is a sure way to dive into interesting implementation discussions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/web-animations/&quot;&gt;Web animations are getting a whole new API&lt;/a&gt; specified and implemented at the moment that will change the way animations are created, sequenced, dynamically changed and provide a much more compelling way to animate things on the web.
Similarly to the previous example, working on a tool to list, inspect and edit animations means that you get to use this new API, but also get into low-level discussions about how it’s implemented and how specific platform APIs can be added to allow the tool to dig in more useful information about them for developers.&lt;/li&gt;
&lt;li&gt;Working on the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Performance&quot;&gt;performance tool&lt;/a&gt; is also a great way to learn how browsers work. Especially what the rendering pipeline of a browser is like, and this has direct consequences on the way you’ll approach web development in the future (also note that browsers share an almost identical rendering pipeline, so you wouldn’t be learning things that only apply to Firefox).
This tool shows the internal operations the browser executes when rendering a page, and so you get to learn about things like restyles, reflows, compositing and painting and what takes time, as well as what can be optimized.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally, working on DevTools means that you’re writing code that’s at the frontier between the content code (the code that runs in the page) and the browser C++ APIs that are exposed to JavaScript.&lt;/p&gt;
&lt;p&gt;Maybe C++ is already your thing, maybe it’s not but you’d like to find out more. Well, at the very least you’d come upon C++ APIs you’ll need to use, which will give you gradually more knowledge on how browsers work which will, in turn, make you a better web developer.
And if you want more, there are always DevTools features that require some sort of C++ platform support which you can get mentored to hack on yourself.&lt;/p&gt;
&lt;h3&gt;Get involved in an awesome community&lt;/h3&gt;
&lt;p&gt;Mozilla is known for its really big and active community of Mozillians, and DevTools, being a Mozilla project, is no exception.&lt;/p&gt;
&lt;p&gt;This means you get to work with both Mozilla staff and volunteers, distributed all over the world, reporting and fixing bugs and landing awesome features in DevTools. And you can get in touch in various ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Come and join the #devtools channel on &lt;a href=&quot;https://wiki.mozilla.org/IRC&quot;&gt;IRC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Join the conversations happening on the &lt;a href=&quot;https://lists.mozilla.org/listinfo/dev-developer-tools&quot;&gt;mailing list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tweet at &lt;a href=&quot;https://twitter.com/FirefoxDevTools&quot;&gt;@FirefoxDevTools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Report ideas and vote for existing ideas on the &lt;a href=&quot;https://ffdevtools.uservoice.com/&quot;&gt;uservoice site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tell the world about your cool new feature on the &lt;a href=&quot;https://hacks.mozilla.org/&quot;&gt;Mozilla Hacks blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Or even meet some of the DevTools people in person by getting invited to Mozilla events (there are 2 bigs ones per year, and an important number of volunteers get invited).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re not too sure yet but feel like looking to get an idea, I highly suggest joining IRC and/or the mailing list and just getting a feel for the types of conversations that happen there.&lt;/p&gt;
&lt;p&gt;Remember, it’s open source which means everything is done in the open, you can tell the world what you’re working on, share early demos and get quick feedback. That is a powerful motivation tool right there.&lt;/p&gt;
&lt;h3&gt;How hard is it to get started?&lt;/h3&gt;
&lt;p&gt;It’s not that hard at all. It comes down to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reading some docs&lt;/li&gt;
&lt;li&gt;checking out a repository&lt;/li&gt;
&lt;li&gt;building Firefox&lt;/li&gt;
&lt;li&gt;making changes to DevTools&lt;/li&gt;
&lt;li&gt;posting patches on bugzilla&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course I’m not going to lie, like any reasonably sized project that has been going on for a few years, there are complexities attached to that. Docs may sometimes be scattered around, the first build may take a long while, posting patches requires some preliminary configurations, etc. But it’s not rocket science either, and there are always people on the mailing list or #devtools or #introduction IRC channels that can help.&lt;/p&gt;
&lt;p&gt;Also, there’s an ongoing project aiming at removing even more barriers to entry for new contributors. In the end, getting the code, building and making DevTools changes that can be reloaded in Firefox will be even quicker and simpler.&lt;/p&gt;
&lt;h3&gt;Alright, I’m convinced, where do I start?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;First things first, go through our (short) &lt;a href=&quot;https://wiki.mozilla.org/DevTools/GetInvolved&quot;&gt;wiki page&lt;/a&gt;, especially take a look at our &lt;a href=&quot;https://wiki.mozilla.org/DevTools/Hacking&quot;&gt;hacking guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Then &lt;a href=&quot;http://firefox-dev.tools/&quot;&gt;find a bug to work on&lt;/a&gt; (or ask on IRC)&lt;/li&gt;
&lt;li&gt;Ping &lt;a href=&quot;https://wiki.mozilla.org/DevTools/GetInvolved#Communication&quot;&gt;people&lt;/a&gt; on bugzilla if you need mentoring or just pointers to get started&lt;/li&gt;
&lt;li&gt;Learn &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/How_to_Submit_a_Patch&quot;&gt;how to submit a patch&lt;/a&gt; for it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s it! Welcome on board!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Inspecting animations in Firefox</title>
    <link href="https://patrickbrosset.com/articles/2015-09-18-inspecting-animations-in-firefox/"/>
    <updated>2015-09-18T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-09-18-inspecting-animations-in-firefox/</id>
    <content type="html">
      
        &lt;p&gt;CSS animations and transitions are awesome, but they’re also hard to get right.
You want your animations to be sequenced a certain way, to last the right amount of time, and to progress at the right pace.&lt;/p&gt;
&lt;p&gt;That’s where tooling comes in. What if you could slow down, rewind, or pause your animations anywhere, so that you could inspect the animated elements more easily?&lt;/p&gt;
&lt;p&gt;That’s what we started to work on a while back in the Firefox DevTools.
In fact, &lt;a href=&quot;https://patrickbrosset.com/articles/2015-09-18-inspecting-animations-in-firefox/2015-02-12-devtools-for-creative-people&quot;&gt;I already mentioned an earlier version of the animation inspector tool previously&lt;/a&gt; (by the way, this article also mentions some of the other ways you can inspect and edit animations in Firefox DevTools, like editing keyframe rules, or visually changing the cubic-bezier timing function, so go check that out).&lt;/p&gt;
&lt;p&gt;I now want to introduce a new version of the tool that’s going to see the light in Firefox 43:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=T2jykykN3yc&lt;/p&gt;
&lt;p&gt;The main features of this version are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all currently animated nodes are displayed automatically, and when new animations are created or removed, that’s reflected too,&lt;/li&gt;
&lt;li&gt;animations are displayed in a timeline user interface which is the obvious choice for representing animations,&lt;/li&gt;
&lt;li&gt;while animations are running, a scrubber is displayed and moves with time, you can also drag and drop the scrubber to any point in time,&lt;/li&gt;
&lt;li&gt;animations are color-coded so you know if they are CSS animations or CSS transitions,&lt;/li&gt;
&lt;li&gt;animation delays and iterations are also displayed in the timeline,&lt;/li&gt;
&lt;li&gt;there’s also a tooltip that provides more details about the animations,&lt;/li&gt;
&lt;li&gt;finally, each animation is displayed along with its target DOM node which you can use to highlight the corresponding element in the page, or select it in the inspector tool.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There’s so much more to do however, this is still an early version of the tool. Don’t hesitate to &lt;a href=&quot;https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&amp;amp;component=Developer%20Tools%3A%20Inspector&quot;&gt;file bugs if you see anything weird&lt;/a&gt;.
Here are some of the things we’d like to add:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the ability to edit or even create animations,&lt;/li&gt;
&lt;li&gt;add the ability to change the playback rate of all animations,&lt;/li&gt;
&lt;li&gt;display and edit the keyframes and timing-function (with the cubic-bezier editor),&lt;/li&gt;
&lt;li&gt;display and edit the animated properties,&lt;/li&gt;
&lt;li&gt;support more animation types (&lt;a href=&quot;https://w3c.github.io/web-animations/&quot;&gt;the Web Animations API&lt;/a&gt; will soon allow web developers to create animations from script, and the tool should support those too).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to follow the development, or even contribute, &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=985861&quot;&gt;here is the main bug&lt;/a&gt;.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Revisiting the Firefox DevTools</title>
    <link href="https://patrickbrosset.com/articles/2015-12-30-revisiting-the-firefox-devtools/"/>
    <updated>2015-12-30T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2015-12-30-revisiting-the-firefox-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2015/12/revisiting-firefox-devtools/">https://www.smashingmagazine.com/2015/12/revisiting-firefox-devtools/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Le positionnement dans les Grilles CSS</title>
    <link href="https://patrickbrosset.com/articles/2016-02-23-le-positionnement-dans-les-grilles-css/"/>
    <updated>2016-02-23T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2016-02-23-le-positionnement-dans-les-grilles-css/</id>
    <content type="html">
      
        &lt;hr /&gt;
&lt;p&gt;This is the French translation of Manuel Rego’s (&lt;a href=&quot;https://twitter.com/regocas&quot;&gt;@regocas&lt;/a&gt;) post “&lt;a href=&quot;http://blogs.igalia.com/mrego/2016/02/01/deep-dive-into-grid-layout-placement/&quot;&gt;Deep Dive into Grid Layout Placement&lt;/a&gt;”, published on February 1st, 2016. All images and code samples come from the original article, with the author’s permission.
Translation: Patrick Brosset (&lt;a href=&quot;https://twitter.com/patrickbrosset&quot;&gt;@patrickbrosset&lt;/a&gt;).&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Ceci est la traduction française de l’article “&lt;a href=&quot;http://blogs.igalia.com/mrego/2016/02/01/deep-dive-into-grid-layout-placement/&quot;&gt;Deep Dive into Grid Layout Placement&lt;/a&gt;” par Manuel Rego (&lt;a href=&quot;https://twitter.com/regocas&quot;&gt;@regocas&lt;/a&gt;), publié le 1er Février 2016. Toutes les images et extraits de code viennent de l’article original avec la permission de l’auteur.
Traduction: Patrick Brosset (&lt;a href=&quot;https://twitter.com/patrickbrosset&quot;&gt;@patrickbrosset&lt;/a&gt;).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Une revue complète des différentes méthodes de positionnement offertes par les grilles CSS&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Manuel Rego&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;1er Février 2016&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ces derniers mois, dans le cadre de mon travail à &lt;a href=&quot;http://www.igalia.com/&quot;&gt;Igalia&lt;/a&gt;, je me penche sur les derniers détails &lt;strong&gt;d’implementation du positionnement des éléments au sein d’un layout de type CSS Grid dans Blink&lt;/strong&gt;.
En résumé, il s’agit principalement de travailler sur ces 2 choses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supporter &lt;em&gt;d’abord les grilles implicites avant les grilles explicites&lt;/em&gt;. Cela afin qu’une grille puisse ajouter des pistes si besoin, pas seulement dans la direction où la grille grandit (en général vers la droite et le bas), mais aussi dans la direction opposée.&lt;/li&gt;
&lt;li&gt;Faire fonctionner la &lt;em&gt;résolution des lignes inconnues de la grille&lt;/em&gt;. Ceci peut arriver lorsqu’un élément est placé sur une ligne nommée “foo”, mais que cette ligne n’existe pas au sein de la grille.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;À première vue ces tâches peuvent paraître simples, mais elles ont demandé une refonte assez importante de l’implémentation interne. Elles m’ont même finalement poussé à remanier tout le code de résolution du positionnement dans une grille.
Pour l’occasion j’ai même écrit &lt;a href=&quot;https://docs.google.com/document/d/1D9x8IUZxirhc3RUma16L6p0yJGv8h199GMnmBA6zLAI/edit&quot;&gt;un document expliquant tout le plan&lt;/a&gt; et les détails techniques. Vous y trouverez les liens vers tous les changements effectués.&lt;/p&gt;
&lt;p&gt;Maintenant que le gros du travail est terminé, il est temps d’expliquer comment placer des éléments dans une grille. Bien que mon collègue &lt;a href=&quot;http://blogs.igalia.com/svillar/2014/03/31/adventures-in-the-grid/&quot;&gt;Sergio ait déjà publié un article à ce sujet en 2014&lt;/a&gt;, la spécification ayant changé depuis, je pense qu’il est bon d’expliquer à nouveau les mécanismes de placement au sein d’une
grille. Cet article est en quelque sorte un résumé (avec des exemples) de la partie &lt;a href=&quot;https://drafts.csswg.org/css-grid/#placement&quot;&gt;“Placing Grid Items” de la spécification CSS Grid Layout&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Les lignes d’une grille&lt;/h3&gt;
&lt;p&gt;Probablement l’un des concepts les plus importants de la spécification. &lt;strong&gt;Les lignes d’une grille&lt;/strong&gt; sont celles qui la séparent horizontalement et verticalement. Ces lignes sont &lt;strong&gt;numérotées&lt;/strong&gt;,
la première étant numéro 1.&lt;/p&gt;
&lt;p&gt;Utilisons une grille de taille 3x2 afin d’expliquer comment cela fonctionne :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300px 200px 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/3NuTmTNakrljqRVFfpR1EQ.png&quot; alt=&quot;Exemple des lignes numérotées&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Les propriétés de positionnement dans une grille&lt;/h3&gt;
&lt;p&gt;Afin de positionner des éléments à l’intérieur d’une grille, il va falloir utiliser des &lt;a href=&quot;https://drafts.csswg.org/css-grid/#line-placement&quot;&gt;propriétés de positionnement&lt;/a&gt;. Ces propriétés sont les suivantes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-column-start&lt;/code&gt;&lt;/strong&gt;: Sert à définir la ligne verticale où l’élément commence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-column-end&lt;/code&gt;&lt;/strong&gt;: Sert à définir la ligne verticale où l’élément s’arrête.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-row-start&lt;/code&gt;&lt;/strong&gt;: Sert à définir la ligne horizontale où l’élément commence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-row-end&lt;/code&gt;&lt;/strong&gt;: Sert à définir la ligne horizontale où l’élément s’arrête.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Grâce à ces propriétés vous pouvez définir la zone où un élément va venir se positionner, en utilisant les numéros des lignes.&lt;/p&gt;
&lt;p&gt;La valeur initiale de ces propriétés à auto. Cela rend possible le positionnement automatique d’un élément (au sein de cellules disponibles dans la grille). Pour plus d’information à ce sujet, vous pouvez consulter &lt;a href=&quot;http://blogs.igalia.com/mrego/2015/02/25/grid-auto-placement-is-ready/&quot;&gt;un article précédent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il existe aussi des propriétés &lt;a href=&quot;https://drafts.csswg.org/css-grid/#placement-shorthands&quot;&gt;raccourcies&lt;/a&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-column&lt;/code&gt;&lt;/strong&gt;: Raccourci des propriétés grid-column-start et grid-column-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-row&lt;/code&gt;&lt;/strong&gt;: Raccourci des propriétés grid-row-start et grid-row-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;grid-area&lt;/code&gt;&lt;/strong&gt;: Raccourci servant à définir les 4 propriétés à la fois.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ajoutons l’élément suivant dans la grille définie précédemment :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;grid-column-&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-column-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-row-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; grid-row-end 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou, plus simplement, en utilisant les propriétés raccourcies :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2 / 4&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 / 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui signifie que l’élément sera positionné sur la première ligne, et sur les deuxième et troisième colonnes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/wv0sImq7-yXG003fLWtzEA.png&quot; alt=&quot;Exemple de positionnement d’élément en utilisant les numéros de ligne&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Le recouvrement de cellules&lt;/h3&gt;
&lt;p&gt;Grâce aux numéros de lignes, l’élément précédent a recouvert 2 colonnes (la 2ème et la 3ème). Nous pourrions faire la même chose en utilisant le mot clé &lt;code&gt;span&lt;/code&gt; et en indiquant le nombre de cellules à recouvrir.&lt;/p&gt;
&lt;p&gt;En effet, l’élément peut être positionné de manière identique avec :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2 / span 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/9l1DA7SP7FGdrWuGEzr4bg.png&quot; alt=&quot;Exemple de positionnement d’élément en utilisant span&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Dans l’exemple précédent, nous n’avons défini que le numéro de ligne où l’élément commence. Cela signifie que grid-row-end prend la valeur auto, et dans ce cas auto couvre par défaut 1 cellule.&lt;/p&gt;
&lt;h3&gt;Lignes négatives&lt;/h3&gt;
&lt;p&gt;Pour le moment, nous avons uniquement numéroté les lignes avec des nombres positifs, mais les lignes peuvent aussi être référencées avec des nombres &lt;strong&gt;négatifs&lt;/strong&gt;, permettant ainsi de référencer des lignes depuis la fin de la grille.&lt;/p&gt;
&lt;p&gt;En utilisant l’exemple précédent, nous pouvons positionner une nouvelle fois l’élément au même endroit en utilisant des nombres négatifs :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -3 / -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -3 / -2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/pGeTLX8eQUD805vZXI-8uA.png&quot; alt=&quot;Exemple de positionnement en utilisant des numéros de lignes négatifs&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cela peut s’avérer très utile dans certaines situations. Par exemple, afin de positionner un élément dans la dernière colonne, sans pour autant connaître le nombre total de colonnes de la grille, il nous suffira de définir : &lt;code&gt;grid-column-end: -1&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Nommage des lignes&lt;/h3&gt;
&lt;p&gt;Non seulement les lignes peuvent être référencées grâce à leurs indices (positifs ou négatifs), mais elles peuvent être &lt;strong&gt;nommées&lt;/strong&gt;. Nommer une ligne permet de ne pas avoir à en connaître le numéro lorsqu’on la référence.&lt;/p&gt;
&lt;p&gt;Modifions à présent la définition de notre grille, en gardant les mêmes tailles mais en nommant cette fois les lignes :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [start] 300px [main] 200px [aside] 100px [end]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [top] 100px [middle] 50px [bottom]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ainsi, pour placer l’élément à la même position que précédemment, il nous suffit simplement d’utiliser le nom des lignes :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; main / end&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; top / middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/eHMGOmjVK_dTGRTj5k7abA.png&quot; alt=&quot;Exemple de positionnement avec lignes nommées&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Une même ligne peut avoir plusieurs noms différents, il suffit pour cela de les lister dans la définition : &lt;code&gt;grid-template-rows: [top start] 100px [middle center] 50px [bottom end];&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;De plus, les noms de lignes peuvent être répétés. Afin de référencer une ligne dont le nom a été utilisé à plusieurs reprises, il faudra utiliser un nombre (positif ou négatif). Prenons un nouvel exemple pour illustrer cela :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [col] 200px [gap] 50px [col] 200px [gap] 50px [col] 200px [gap]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et imaginons les éléments suivants :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; col 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; col 1 / gap 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; col -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/ab8rTaTiXcfbL-6B0W_iDg.png&quot; alt=&quot;Exemple de noms de lignes répétés&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Bien entendu, il est aussi possible de recouvrir plusieurs cellules jusqu’à une ligne nommée :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; col 2 / span 2 gap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/B_7T9KvkAkWsf8X1juXx4w.png&quot; alt=&quot;Exemple de recouvrement de cellules en utilisant des lignes nommées&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Les aires&lt;/h3&gt;
&lt;p&gt;Les &lt;strong&gt;aires de grille&lt;/strong&gt; sont encore plus intéressantes, il est possible d’y positionner directement des éléments. Pour cela, il nous faudra utiliser la propriété &lt;a href=&quot;https://drafts.csswg.org/css-grid/#grid-template-areas-property&quot;&gt;&lt;code&gt;grid-template-areas&lt;/code&gt;&lt;/a&gt; qui permet de nommer différentes zones (les aires) de la grille.
Il est ensuite possible d’utiliser la propriété raccourcie grid-area pour positionner les éléments dans ces aires.&lt;/p&gt;
&lt;p&gt;Utilisons une grille légèrement plus grande (5x4) dans cet exemple :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px 100px 100px 100px 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px 100px 100px 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token string&quot;&gt;&#39;title  title  title  title  aside&#39;&lt;/span&gt;
              &lt;span class=&quot;token string&quot;&gt;&#39;nav    main   main   main   aside&#39;&lt;/span&gt;
              &lt;span class=&quot;token string&quot;&gt;&#39;nav    main   main   main   aside&#39;&lt;/span&gt;
              &lt;span class=&quot;token string&quot;&gt;&#39;footer footer footer footer footer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et positionnons 1 élément au sein de chacune des ces aires :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nav&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; main&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aside&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; footer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/XFBWWxiOHYrlHT1xIHTPoQ.png&quot; alt=&quot;Exemple de positionnement dans des aires&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Aires et lignes nommées&lt;/h3&gt;
&lt;p&gt;L’un des points intéressants relatif aux aires est qu’elles créent des noms implicites pour les lignes qui les entourent. Ces noms implicites utilisent les suffixes “-start” et “-end”. Il est donc possible d’utiliser ces noms pour positionner un élément à la place d’utiliser l’aire elle-même.&lt;/p&gt;
&lt;p&gt;Par exemple, l’aire “title” de l’exemple précédent génère 4 noms implicites pour les lignes qui l’entourent (2 par axe) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ligne de gauche : “title-start”&lt;/li&gt;
&lt;li&gt;Ligne de droite : “title-end”&lt;/li&gt;
&lt;li&gt;Ligne du haut : “title-start”&lt;/li&gt;
&lt;li&gt;Ligne du bas : “title-end”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nous pourrions donc positionner un élément en utilisant ces noms de lignes :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aside-end&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; title-start / nav-end&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/7LEV3iOqOVRpFtKd4e-3kA.png&quot; alt=&quot;Exemple de positionnement selon les noms implicites de lignes d’aires&quot; /&gt;&lt;/p&gt;
&lt;p&gt;De plus, la même chose est possible dans l’autre sens. En nommant explicitement des lignes avec ces suffixes, les aires correspondantes seront générées implicitement. Il est donc possible de créer la même grille que précédemment en utilisant uniquement des noms de lignes :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [title-start nav-start footer-start] 100px [main-start nav-end] 100px 100px 100px [aside-start title-end main-end] 100px [aside-end footer-end]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [title-start aside-start] 100px [nav-start main-start title-end] 100px 100px [footer-start nav-end main-end aside-end] 100px [footer-end]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tous les exemples d’éléments positionnés précédemment se retrouveront exactement au même endroit dans cette nouvelle grille.&lt;/p&gt;
&lt;h3&gt;Grille implicite&lt;/h3&gt;
&lt;p&gt;Grâce aux &lt;a href=&quot;https://drafts.csswg.org/css-grid/#grid-definition&quot;&gt;propriétés de définition d’une grille&lt;/a&gt; (&lt;code&gt;grid-template-columns&lt;/code&gt;, &lt;code&gt;grid-template-rows&lt;/code&gt; et &lt;code&gt;grid-template-areas&lt;/code&gt;), il est possible de déterminer les colonnes et rangées d’une grille. Cependant, la spécification prévoit aussi la possibilité de positionner des éléments en dehors de cette grille explicite. Pour cela, de nouvelles colonnes et rangées sont créées implicitement et automatiquement, et leurs tailles sont contrôlées par &lt;a href=&quot;https://drafts.csswg.org/css-grid/#auto-tracks&quot;&gt;les propriétés &lt;code&gt;grid-auto-columns&lt;/code&gt; et &lt;code&gt;grid-auto-rows&lt;/code&gt;&lt;/a&gt;. Dans les exemples qui suivent, nous utiliserons la couleur rouge pour représenter les lignes implicites.&lt;/p&gt;
&lt;p&gt;Utilisons une grille très simple de 2x1 :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-auto-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, imaginons placer un élément dans la 5ème colonne (&lt;code&gt;grid-column: 5;&lt;/code&gt;). Cette grille n’ayant que 2 colonnes, 3 nouvelles colonnes implicites sont créées afin de positionner l’élément.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/-TDBgCDnlKM5cTRz7GmLbw.png&quot; alt=&quot;Exemple de grille implicite&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Il est aussi possible de créer des colonnes et rangées implicites en positionnant des éléments qui recouvrent plusieurs cellules. Par exemple, prenons un élément qui commence à la 2ème colonne et en recouvre 3 (&lt;code&gt;grid-column: 2 / span 3&lt;/code&gt;) :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/Fc_7gBnhygFsCzhNYhVT2A.png&quot; alt=&quot;Exemple de grille implicite avec recouvrement&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Notons que la grille peut être implicitement étendue dans les deux sens. Par exemple, en plaçant un élément avec &lt;code&gt;grid-column: -5;&lt;/code&gt;, 2 nouvelles colonnes seront ajoutées sur la gauche de la grille et l’élément sera positionné dans la colonne &lt;strong&gt;-2&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/pOq3Crj0pcQCaIo36yxAaw.png&quot; alt=&quot;Exemple de grille implicite avant explicite&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Grille implicite et lignes nommées&lt;/h3&gt;
&lt;p&gt;Ce que nous venons de voir n’est pas la seule façon de créer des colonnes et rangées implicites. Il est aussi possible de les créer en utilisant des lignes nommées non définies. Ceci relève plus d’une manière de trouver des erreurs de CSS plutôt que d’une fonctionnalité, mais certains pourront trouver cela utile. L’idée sous-jacente est que toutes les lignes faisant partie de la grille implicite se retrouvent nommées avec tous les noms utilisés pour positionner les éléments.&lt;/p&gt;
&lt;p&gt;Utilisons un exemple simple : plaçons des éléments qui référencent une ligne non existante appelée “foo”. Créons 3 colonnes implicites (1 avant et 2 après la grille explicite) avec les éléments suivants :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2 / span 2 foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1 foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/2-8MzyS6C3lXegJMR_Y14w.png&quot; alt=&quot;Exemple de grille implicite utilisant une ligne indéfinie&quot; /&gt;&lt;/p&gt;
&lt;p&gt;L’élément ayant grid-column: foo est positionné sur la 4ème colonne (une colonne vide ayant été rajoutée juste après la grille explicite). En effet, la première ligne considérée comme ayant le nom “foo” est la première ligne implicite (ligne 4), la dernière ligne de la grille explicite (ligne 3) n’étant pas incluse.&lt;/p&gt;
&lt;p&gt;De plus, le dernier élément &lt;code&gt;grid-column: -1 foo&lt;/code&gt; est positionné dans la colonne -1. La raison pour cela étant que la recherche de ligne nommée “foo” démarre au bord de la grille explicite. Les lignes -1, -2
et -3 sont ignorées (puisqu’elles ne portent pas le nom “foo”) et la ligne -4 est donc celle qui prend ce nom (la première ligne de la grille implicite).&lt;/p&gt;
&lt;p&gt;Les choses se corsent un peu lorsque la ligne existe vraiment car, dans ce cas, la ligne est prise en compte quand l’élément est positionné. C’est d’autant plus complexe lorsqu’il y a du recouvrement de cellules en jeu, et qu’une ligne nommée est recouverte mais qu’il n’y a plus assez de lignes. Dans ce cas, seules les lignes implicites se trouvant dans la direction de la recherche sont considérées comme ayant le nom en question.&lt;/p&gt;
&lt;p&gt;Un exemple devrait aider à la compréhension. Dans l’exemple précédent, donnons un nom à la ligne du milieu :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px [middle] 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-auto-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, positionnons quelques éléments référençant cette ligne “middle” :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3 middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; span 2 middle / 5&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -4 / span middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/WKfQ0rMsydYDI7EIy_6Cow.png&quot; alt=&quot;Exemple de grille implicite utilisant plus de lignes nommées que disponibles&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Le cas le plus étrange ici est &lt;code&gt;grid-column: span 2 middle / 5;&lt;/code&gt;. Comme indiqué sur le schéma, l’élément occupe la place de la colonne -1 à la colonne 4 (incluses). L’élément se termine à la ligne 5, et doit recouvrir 2 lignes s’appelant “middle” afin de trouver son point de départ. Peut-être devrait-il simplement compter les lignes 4 et 2 comme étant les 2 lignes “middle” recouvertes? Non, puisque comme expliqué auparavant, la recherche se fait en partant du bord de la grille explicite. Donc la ligne 2 étant dejà prise en compte dans le recouvrement, la prochaine ligne considérée est, dans le sense de la recherche, la ligne implicite à gauche de la grille explicite : ligne -4.&lt;/p&gt;
&lt;h3&gt;Cas spéciaux&lt;/h3&gt;
&lt;p&gt;Les propriétés de positionnement peuvent donner lieu à certains cas spéciaux qui sont alors résolus par &lt;a href=&quot;https://drafts.csswg.org/css-grid/#grid-placement-errors&quot;&gt;la section de la spécification sur la gestion des conflits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Par exemple, si un élément se retrouve positionné avec une ligne de fin se trouvant avant la ligne de départ, les deux lignes sont inversées. &lt;code&gt;grid-column: 5 / 2;&lt;/code&gt; devient alors &lt;code&gt;grid-column: 2 / 5;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;De plus, s’il y a recouvrement à la fois aux positions de départ et de fin, le span de la position finale sera ignoré. Donc &lt;code&gt;grid-column: span 2 / span 3;&lt;/code&gt; devient grid-column: span 2;. Ceci aura pour effet d’utiliser algorithme de positionnement afin de trouver une zone vide pour placer l’élément (une zone de 2 colonnes dans cet exemple).&lt;/p&gt;
&lt;p&gt;Le dernier cas spécial se produit lorsqu’il y a recouvrement uniquement vers une ligne nommée. Dans ce cas, le recouvrement est remplacé par span 1. Par exemple &lt;code&gt;grid-column: span foo;&lt;/code&gt; devient &lt;code&gt;grid-column: span 1;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/96txNnstWcllGM1TlX-BVg.png&quot; alt=&quot;Exemple de positionnement spécial&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Récapitulatif&lt;/h3&gt;
&lt;p&gt;Si vous avez lu jusqu’ici, vous êtes visiblement très intéressé par les grilles CSS. En conclusion, le point le plus important de cette spécification est la grande flexibilité offerte quant au positionnement des éléments sur la grille. En effet, comme nous venons de le voir, il y a de nombreuses manières de positionner un élément au même endroit. Il est probable que les utilisateurs s’habituent à quelques-unes de ces manières et oublient les autres.&lt;/p&gt;
&lt;p&gt;De mon point de vue, le positionnement simple (à base d’indices de lignes, noms de lignes et aires) est plutot aisé à prendre en main. Le concept de grille implicite n’est pas si compliqué non plus. Les indices négatifs rajoutent un côté amusant. Par contre, le comportement des lignes indéfinies est assez complexe (mais avec un peu de chance, ce n’est pas quelque chose qui se révélera très important dans l’utilisation des grilles). Cela dit, mon avis est forcément biaisé, puisque je travaille depuis maintenant longtemps sur les grilles CSS.&lt;/p&gt;
&lt;p&gt;Pour finir, j’ai pensé qu’il serait intéressant de terminer par un &lt;strong&gt;exemple complexe&lt;/strong&gt; qui utilise la plupart des concepts décrits dans cet article.
Si vous êtes capables de le comprendre complètement, vous maîtrisez alors le positionnement dans les grilles CSS. Félicitations!&lt;/p&gt;
&lt;p&gt;Voici la définition de la grille que nous utiliserons dans cet exemple :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [left] 200px [main-start] 100px [center] 100px 100px [main-end] 50px 50px [right]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; [top title-start] 50px [title-end main-start] 200px [main-end center] 150px 100px [bottom]&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-auto-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;grid-auto-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans le schéma suivant, vous pouvez voir la manière dont les différents éléments sont positionnés.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/rego-grid/7dJ9s-vhOeRZrKNuqZaNNA.png&quot; alt=&quot;Exemple complexe de positonnement&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez essayer la démo sur notre site d’exemples : &lt;a href=&quot;http://igalia.github.io/css-grid-layout/grid-placement.html&quot;&gt;http://igalia.github.io/css-grid-layout/grid-placement.html&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;État des lieux&lt;/h3&gt;
&lt;p&gt;Comme je l’ai déjà dit dans l’introduction &lt;a href=&quot;https://code.google.com/p/chromium/issues/detail?id=444011&quot;&gt;&lt;strong&gt;l’implémentation dans Blink&lt;/strong&gt;&lt;/a&gt; devrait désormais supporter (Chrome 50+) toutes ces possibilités de positionnement. Igalia y a travaillé, et nous sommes maintenant en train de transposer l’implémentation vers &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=153488&quot;&gt;&lt;strong&gt;WebKit&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;De plus, &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1009776&quot;&gt;&lt;strong&gt;Gecko&lt;/strong&gt;&lt;/a&gt;, développé par Mozilla, supporte aussi ces possibilités de positionnement.&lt;/p&gt;
&lt;p&gt;Enfin, j’aimerais préciser à nouveau que ce travail a été réalisé grâce à une collaboration entre &lt;strong&gt;&lt;a href=&quot;http://www.igalia.com/&quot;&gt;Igalia&lt;/a&gt;&lt;/strong&gt; et &lt;strong&gt;&lt;a href=&quot;http://www.bloomberg.com/company/&quot;&gt;Bloomberg&lt;/a&gt;&lt;/strong&gt;. Merci beaucoup pour votre support!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Measuring elements and distances in Firefox DevTools</title>
    <link href="https://patrickbrosset.com/articles/2016-05-25-measuring-elements-and-distances-in-firefox-devtools/"/>
    <updated>2016-05-25T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2016-05-25-measuring-elements-and-distances-in-firefox-devtools/</id>
    <content type="html">
      
        &lt;p&gt;There’s a few ways you can measure elements or distances between elements in Firefox DevTools. In this article, you’ll learn about them and hopefully discover some things which are useful.&lt;/p&gt;
&lt;h3&gt;Using the rulers&lt;/h3&gt;
&lt;p&gt;The rulers tool is useful to have around at all times. It provides a way to quickly check how tall or wide a page is, how big are the various columns or sidebars, and how much you’ve scrolled.&lt;/p&gt;
&lt;p&gt;The rulers tool is not ON by default, so you have to enable it first. To do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;open the toolbox (I often use &lt;code&gt;ctrl+shift+I&lt;/code&gt; as a quick way to toggle the tools, but that’s just me, &lt;code&gt;F12&lt;/code&gt; works too, and of course &lt;code&gt;right-click&lt;/code&gt; on the page and select “inspect element” is often an easy way to do it),&lt;/li&gt;
&lt;li&gt;switch to the options panel (that’s the cog icon in the toolbar, far right, or &lt;code&gt;ctrl+shift+O&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;there are many options in there, don’t be scared, just scroll down to the “Available Toolbox Buttons” section and make sure the “Toggle rulers for the page” box is checked,&lt;/li&gt;
&lt;li&gt;once done, you’ll have a nice little icon in the toolbox toolbar that you can just click to toggle the rulers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/enable-rulers.gif&quot; alt=&quot;Gif showing how to enable the rulers in Firefox&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once you have enabled the rulers tools, it’s really easy to use. Click on the rulers icon to display the rulers, and click again to hide them:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/use-rulers.gif&quot; alt=&quot;Gif showing what the rulers look like in Firefox&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Measuring elements&lt;/h3&gt;
&lt;p&gt;Whether you’re debugging a layout problem or arranging elements in a new page’s layout, you may need to measure elements to verify they are the right size and aligned correctly. Sometimes an alignment problem is subtle enough that you just can’t see the problem with the naked eye and you need detailed information.&lt;/p&gt;
&lt;p&gt;The Firefox DevTools have you covered in a few ways that you may find useful.&lt;/p&gt;
&lt;p&gt;First of all, by hitting &lt;code&gt;ctrl+shift+C&lt;/code&gt; or by clicking on the cursor icon in the top left corner of the toolbox, you enter the element picker mode.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/enable-picker.png&quot; alt=&quot;Highlighting the node picker button in Firefox&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This tool obviously allows you to select new elements in the page by clicking on them, this is what people use it for, but while moving your mouse over elements in the page, notice that elements are highlighted:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/picker-highlighter.gif&quot; alt=&quot;Gif showing the visual information that the element picker in Firefox displays&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This highlighter also appears when you simply hover over elements inside the inspector (in the DOM tree that’s displayed in the main panel).&lt;/p&gt;
&lt;p&gt;What’s interesting about the highlighter is that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the element’s box-model is highlighted, which means that you get different colors for each of the element’s content area, padding, border and margin,&lt;/li&gt;
&lt;li&gt;vertical and horizontal guides are displayed around the element’s content area.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The guides are particularly nice if you’re using the rulers tool at the same time, because they extend all the way up to the rulers so it’s easy to quickly visualize how tall or wide an element is:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/picker-guides-and-rulers.png&quot; alt=&quot;Showing how the guides of the element picker play well with the rulers&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Secondly, as you saw in the previous screenshot, as you hover elements in the page, a tooltip appears next to them.
We call this tooltip the node info bar and it’s useful when it comes to measuring elements because it contains the dimensions of the element being hovered.&lt;/p&gt;
&lt;p&gt;It’s worth noting that these are the dimensions of the border box of the element. So if an element has a content of 20px and 10px padding to the right and left and 5px border to the right and left, then the width displayed in the node info bar is going to be 50px.
The reason for this is that it’s often more useful to know the size of the thing you see, and padding and border are often visually part of the element.&lt;/p&gt;
&lt;p&gt;If you need even more information about an element’s box model, head over to the “Box Model” tab that you’ll find in the inspector’s sidebar tab panel.&lt;/p&gt;
&lt;p&gt;Indeed, while an element’s size might seem right, you might be wondering about it’s margin, or border size.&lt;/p&gt;
&lt;p&gt;This panel gives you information about all of the computed element’s box model regions:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/box-model-panel.png&quot; alt=&quot;The Firefox DevTools box-model panel&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But what’s also pretty useful in this view is that you can simply hover over the different colored regions to see them being highlighted individually in the page, with the vertical and horizontal guides around them:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/box-model-hover.gif&quot; alt=&quot;Gif showing how hovering over the regions of the box-model panel highlights the corresponding regions in the page&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Measuring arbitrary distances on the page&lt;/h3&gt;
&lt;p&gt;Finally, I wanted to highlight one last tool that might prove useful when you want to get a quick measure of anything on the page: the distance between 2 elements or the size of a given area, whether or not those things are DOM elements or not.&lt;/p&gt;
&lt;p&gt;This is the measurement tool, which you need to activate first (just like we did with the rulers tool at the start of this article):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;in the options panel, scroll down to the “Available Toolbox Buttons” section,&lt;/li&gt;
&lt;li&gt;make sure the “Measure a portion of the page” box is checked,&lt;/li&gt;
&lt;li&gt;once done, you’ll have a new icon in the toolbox toolbar which you can click on to display or hide the measurement tool.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/enable-measure-tool.gif&quot; alt=&quot;Gif showing how to enable the measurement tool in Firefox&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The measurement tool is useful to know how tall or wide something is, or how far apart two things are, as illustrated below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/use-measure-tool.gif&quot; alt=&quot;Gif showing how to use the measurement tool in Firefox, by drag and dropping anywher on the page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There’s also &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1263768&quot;&gt;an experiment going on at the moment&lt;/a&gt; on a measurement tool that would detect edges of various things on the page and provide distances between them (which may sound familiar to people using &lt;a href=&quot;https://www.sketchapp.com/&quot;&gt;Sketch&lt;/a&gt; or &lt;a href=&quot;http://xscopeapp.com/&quot;&gt;xScope&lt;/a&gt;, because that’s where the idea comes from).&lt;/p&gt;
&lt;p&gt;That’s it! I hope you find these useful. Don’t hesitate to dig around in the toolbox, there are many features that aren’t immediately visible but that might really ease your workflow.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Visual Tools — Prototyping in the browser</title>
    <link href="https://patrickbrosset.com/articles/2016-05-27-visual-Tools--Prototyping-in-the-browser/"/>
    <updated>2016-05-27T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2016-05-27-visual-Tools--Prototyping-in-the-browser/</id>
    <content type="html">
      
        &lt;p&gt;Today, browsers come with built-in tools that allow to inspect and debug existing web content.&lt;/p&gt;
&lt;p&gt;In this post, I’ll argue that these tools could also allow creating content and working on this content in more visual ways. Ways that offer alternatives to having to deal with code too much, or rather help you deal with complex code in more intuitive ways.&lt;/p&gt;
&lt;p&gt;I think visual tools address 2 specific use cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Some people already use web technologies today to prototype, create layouts and design ideas. But browser devtools are often complex and intimidating. There is a mismatch between what these people want to use the browser for and what the tools offer. Therefore, when prototyping things in the browser today, the usual “a) edit some code and b) refresh the page” workflow is still the best tool around. We can change this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And on top of this, there are even more people not using the web at all for this type of work. Designers in particular often feel much more at ease with other tools. Tools made for them specifically. I think browser devtools can reach this audience, and ultimately get more people creating within the browser, with web technologies directly.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Using the browser to our advantage&lt;/h3&gt;
&lt;p&gt;The web is weird in some sense, there are specific rules that define how a web page behaves if you compare it to other media:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input is often undetermined (user-generated content),&lt;/li&gt;
&lt;li&gt;output is almost always unknown (various viewport and font size).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Non-browser tools that attempt to let users create web pages have to deal with this in some way. Often hiding these aspects behind abstractions, generating magic code (that no-one is supposed to look at or understand) on the user’s behalf.&lt;/p&gt;
&lt;p&gt;I think we owe it to users to let them understand and write the code that’s used for re-arranging a layout when the page size changes for example, just make it easier to write it in the first place.&lt;/p&gt;
&lt;p&gt;Browsers know how to deal with these things anyway, and ultimately, the content is going to be consumed by users in a browser, so it makes sense that we would create it there in the first place.&lt;/p&gt;
&lt;h3&gt;What would it look like?&lt;/h3&gt;
&lt;p&gt;What’s a typical prototyping user story? What should prototyping in the browser look like?
These remain open questions. I think browser devtools will need to create their own very specific answer.&lt;/p&gt;
&lt;p&gt;Right now though, browser devtools sit on one end of the spectrum, with a very technical set of features, mostly developer-oriented, and very little creation abilities.
On the other end of the spectrum, we find general prototype and design tools where pretty much any workflow can exist, and that people use to do pretty much anything from logos, to illustrations, to comics, to web page mockups.
In the middle, there’s some exploration worth doing.&lt;/p&gt;
&lt;p&gt;From a very high level, when you’re creating something for the web, you’re going through these steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;adding content to the page,&lt;/li&gt;
&lt;li&gt;defining a layout for this content,&lt;/li&gt;
&lt;li&gt;polishing the design,&lt;/li&gt;
&lt;li&gt;adding interaction animations,&lt;/li&gt;
&lt;li&gt;testing and debugging,&lt;/li&gt;
&lt;li&gt;and shipping.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Browser devtools have a part to play in each of these areas. Here’s what a user flow could look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Adding content&lt;/em&gt;: create a page directly in the browser to start prototyping, add elements to it, text and images, rename tags, add class names and move them around in the DOM tree (much like you’d move and groups layers in, say, Photoshop).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Defining a layout&lt;/em&gt;: resize and move elements (or classes of elements) in the page, add padding, margin or borders, give elements a position, arrange things in a flexible, grid or column layout.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Styling the content&lt;/em&gt;: fiddle with colors and gradients, add blur or shadow filters, change opacity or blend-modes, and have live previews while doing so.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Animating interactions&lt;/em&gt;: create and adjust animations on elements or transitions between states.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Testing, debugging, adjusting&lt;/em&gt;: use a responsive-design mode to test the page under various screen size and user agent combinations. Visually create breakpoints in your design with media queries.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Shipping&lt;/em&gt;: finally save or export your prototype.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;We’re not starting from scratch&lt;/h3&gt;
&lt;p&gt;If you really know your devtools, the above flow isn’t too far from being possible today, but it’s not trivial and you can’t do everything. Also, a lot of it still requires coding.&lt;/p&gt;
&lt;p&gt;But we’re definitely not starting from scratch. Some designer-oriented features have started to appear in browser devtools in the past, and some really good work has been done.&lt;/p&gt;
&lt;p&gt;I work on the devtools for Firefox, so here’s a review of what we’ve done about this in Firefox until now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_HTML#Editing_HTML_2&quot;&gt;Edit HTML to create new content&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_HTML#Element_popup_menu&quot;&gt;Edit tag names and other attributes like classes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_HTML#Editing_HTML_2&quot;&gt;Add, duplicate or delete nodes easily from the inspector&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_HTML#Drag_and_drop&quot;&gt;Drag and drop nodes in the markup to reorder them&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Select_an_element&quot;&gt;Select elements visually from the page with the highlighter&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_the_box_model&quot;&gt;Edit padding, border and margins in a visual box-model view&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Reposition_elements_in_the_page&quot;&gt;Move absolutely/relatively positioned elements in the page by dragging them&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Highlight all elements that match a given selector on the page.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_CSS#Add_rules&quot;&gt;Add new CSS rules&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Edit CSS selectors.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Visualize_transforms&quot;&gt;Preview how CSS transforms impact elements’ size, shape and position&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@patrickbrosset/measuring-elements-and-distances-in-firefox-devtools-1d6c57bc1f3f#.xyvjggkg5&quot;&gt;Display rulers along the page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@patrickbrosset/measuring-elements-and-distances-in-firefox-devtools-1d6c57bc1f3f#.xyvjggkg5&quot;&gt;Measure distances&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Inspect_and_select_colors&quot;&gt;Change colors with a color picker&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Eyedropper&quot;&gt;Sample colors from the page with the eye dropper&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add or edit CSS filters by choosing from a list or reusing saved presets.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/View_background_images&quot;&gt;Previewing background images&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/View_fonts#font-family_tooltip&quot;&gt;Previewing fonts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Work_with_animations&quot;&gt;Displaying all current animations in the page in a timeline&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Work_with_animations&quot;&gt;Listing the animated elements and pausing, rewinding, slowing down or speeding up the animation on them&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Seeing the keyframes and animated properties in animations.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Work_with_animations#Edit_timing_functions&quot;&gt;Editing cubic-bezier curves for tweaking how animations and transitions progress through time&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Responsive_Design_Mode&quot;&gt;Resizing pages in the responsive design mode, and emulating user agents and touch devices&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Getting help from MDN on any CSS property.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://meyerweb.com/eric/thoughts/2015/10/22/firefoxs-screenshot-command/&quot;&gt;Screenshot the viewport, or the full page or a single DOM node&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What are the next steps?&lt;/h3&gt;
&lt;p&gt;Well, the above was a mouth full, but as I said before, there’s much to be done before we can truly prototype in the browser. Here are some of the things we have in mind for the future:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better color pickers with palettes and color extraction from the page.&lt;/li&gt;
&lt;li&gt;Border, gradient, blend-modes or shadow visual editor.&lt;/li&gt;
&lt;li&gt;Background image, repeat, size, position, clip visual editor.&lt;/li&gt;
&lt;li&gt;Font visual editor for font-family, letter and word spacing, line-height and vertical alignment and text decoration and style.&lt;/li&gt;
&lt;li&gt;Better screenshot tool.&lt;/li&gt;
&lt;li&gt;Visualize and create media-query breakpoints.&lt;/li&gt;
&lt;li&gt;Tweak responsive images’ rules.&lt;/li&gt;
&lt;li&gt;Extract changes or saving to disk.&lt;/li&gt;
&lt;li&gt;Easily resize element’s box-models with the mouse.&lt;/li&gt;
&lt;li&gt;Display elements’ relationships to other things in the page.&lt;/li&gt;
&lt;li&gt;Find font glyphs and enable opentype features.&lt;/li&gt;
&lt;li&gt;Transform and move elements in the page, snap to edges.&lt;/li&gt;
&lt;li&gt;Draw gradients in the page by moving the gradient line and color stops.&lt;/li&gt;
&lt;li&gt;Editors for grids and flexible layouts.&lt;/li&gt;
&lt;li&gt;Create animations from scratch.&lt;/li&gt;
&lt;li&gt;Edit durations and delays and keyframes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Does this sound exciting? It definitely does to me. Make sure to watch future versions of Firefox DevTools as we try and ship more of these.&lt;/p&gt;
&lt;p&gt;I hope this was useful or interesting, thanks for reading!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>When does white space matter in HTML?</title>
    <link href="https://patrickbrosset.com/articles/2016-10-21-when-does-white-space-matter-in-html/"/>
    <updated>2016-10-21T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2016-10-21-when-does-white-space-matter-in-html/</id>
    <content type="html">
      
        &lt;p&gt;As a web developer, you don’t often spend time thinking about white space, right? I mean, how often do they actually matter?
Well, hopefully with this article, you’ll think of them more often, or at least will know when they do matter and know how to track them down!&lt;/p&gt;
&lt;h3&gt;What is white space?&lt;/h3&gt;
&lt;p&gt;White space is any string of text composed only of spaces, tabs or line breaks (to be precise, either CRLF sequences, carriage returns or line feeds).&lt;/p&gt;
&lt;p&gt;As someone who writes code, you probably know the vital importance of these characters. They allow you to format your code in a way that will make it easily readable by yourself and other people. In fact much of our source code is full of these white space characters (that is, unless you write obfuscated code). They’re most often used for breaking the code on multiple lines and indenting lines to represent the nesting of elements.&lt;/p&gt;
&lt;p&gt;But, because these characters are important for people who read the code doesn’t mean they’re important for people who visit your web page. These formatting-only characters wouldn’t look too good if they did impact the layout of your page, right?&lt;/p&gt;
&lt;p&gt;Let’s take a simple example:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;   Hello World! &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This source code contains a line feed after the DOCTYPE and a bunch of space characters before and inside the h1 tag, but the browser doesn’t seem to care at all and just shows the words &lt;em&gt;Hello World!&lt;/em&gt; as if these characters didn’t exist at all!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/8HMhC1n8AXXOAeyMmqUSSw.png&quot; alt=&quot;What the above HTML snippet looks like when rendered in a browser&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Unlike a word processing application, the browser seems to completely ignore white spaces (most of the time at least).&lt;/p&gt;
&lt;h3&gt;How does CSS process white spaces?&lt;/h3&gt;
&lt;p&gt;If most white space characters are ignored, not all of them are. In the previous example the space between Hello and World! still exists when the page is rendered in a browser. So there must be something in the browser engine that decides which white space characters are useful and which aren’t.&lt;/p&gt;
&lt;p&gt;If you’re the kind of person who likes reading specifications, you might like &lt;a href=&quot;https://www.w3.org/TR/css-text-3&quot;&gt;the CSS Text Module Level 3 spec&lt;/a&gt;, and especially the parts about &lt;a href=&quot;https://www.w3.org/TR/css-text-3/#white-space-property&quot;&gt;the CSS white-space property&lt;/a&gt; and &lt;a href=&quot;https://www.w3.org/TR/css-text-3/#white-space-processing&quot;&gt;white space processing details&lt;/a&gt;, but then again, if you’re that type of person, you’re probably not reading this article right now.&lt;/p&gt;
&lt;p&gt;Let’s take another really simple example (to make it easy, I’ve illustrated all spaces with ◦, all tabs with ⇥ and all line breaks with ⏎):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦Hello◦⏎
⇥⇥⇥⇥&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⇥◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s how this example markup is rendered in a browser:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/8HMhC1n8AXXOAeyMmqUSSw.png&quot; alt=&quot;What the above HTML snippet looks like when rendered in a browser&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The h1 element contains only inline elements. In fact it contains a text node (consisting of some spaces, the word &lt;em&gt;Hello&lt;/em&gt; and some tabs), an inline element (the span, which contains a space, and the word &lt;em&gt;World!&lt;/em&gt;) and another text node (consisting only of tabs and spaces).&lt;/p&gt;
&lt;p&gt;Because of this, it establishes what is called &lt;a href=&quot;https://www.w3.org/TR/CSS21/visuren.html#inline-formatting&quot;&gt;an inline formatting context&lt;/a&gt;. This is one of the possible layout rendering contexts that browser engines work with.&lt;/p&gt;
&lt;p&gt;Inside this context, white space characters are processed as followed (this is overly simplified, the specification goes into much more details):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;first, all spaces and tabs immediately before and after a line break are ignored so, if we take our example markup from before and apply this first rule, we get:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦Hello⏎
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⇥◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;then, all tab characters are handled as space characters, so the example becomes:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦Hello⏎
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;next, line breaks are converted to spaces:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦Hello◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;then, any space immediately following another space (even across two separate inline elements) is ignored, so we end up with:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦Hello◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;and finally, sequences of spaces at the beginning and end of a line are removed, so we finally get this:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is why people visiting the web page will simply see the phrase &lt;em&gt;Hello World!&lt;/em&gt; nicely written at the top of the page, rather than a weirdly indented &lt;em&gt;Hello&lt;/em&gt; followed by an even more weirdly indented &lt;em&gt;World!&lt;/em&gt; on the line below that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/lx56kLIuWir6-iDEI57shA.png&quot; alt=&quot;Comparison screenshot of what users see in a browser today vs. what they would see if whitespace wasn&#39;t handled the way it is&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In fact, using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools&quot;&gt;Firefox DevTools&lt;/a&gt; (since version 52 which now supports highlighting text nodes), you can see how the space separating the 2 words is part of the node that contains &lt;em&gt;Hello&lt;/em&gt; just like the markup we ended up with after applying the white space processing rules:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/hNSUVhhTZDvu6-B7DlQ06w.gif&quot; alt=&quot;Animation showing how Firefox DevTools lets you highlight text nodes, and showing that the space is inside the same text node as the word Hello&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now we know how white space is processed within an inline formatting context (which, remember, is basically an element that contains only inline elements).&lt;/p&gt;
&lt;p&gt;You might be wondering how white space is processed in other types of contexts and what these contexts even are.
Well, if an element contains at least one block element, then it establishes what is called a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context&quot;&gt;block formatting context&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Within this context, white space is treated very differently. Let’s take a look at this example (see live at https://captainbrosset.github.io/white-space-article/example3.html):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
⇥&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦Hello◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
⏎
◦◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦World!◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦⏎
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have 3 text nodes in there that contain only white spaces, one before the first div, one between the 2 divs, and one after the second div.&lt;/p&gt;
&lt;p&gt;Browser engines may be optimizing this differently, but for the sake of understanding, I’ll go with the following explanation:&lt;/p&gt;
&lt;p&gt;Because we’re inside a block formatting context, everything must be a block, so our 3 text nodes also become blocks, just like the 2 divs.
Blocks occupy the full width available and are stacked on top of each other, which means that we end up with a layout composed of this list of blocks:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎⇥&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦Hello◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎◦◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦World!◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦⏎&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can simplify it further by applying the processing rules for white space in inline formatting contexts:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The 3 empty blocks we now have are just not going to occupy any space in the final layout, because they just don’t contain anything, so we’ll indeed end up positioning 2 blocks in the page only. And people viewing the web page will see the words Hello and then World! on 2 separate lines as you’d expect 2 divs to be laid out.&lt;/p&gt;
&lt;p&gt;So in this case, the browser engine has essentially ignored all of the white space that was added in the source code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/qrp3VcKiYZEBVJ-hZ4CNLQ.gif&quot; alt=&quot;Animation showing how the browser has removed all whitespaces&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Why don’t we see white spaces in devtools?&lt;/h3&gt;
&lt;p&gt;We’ve seen in the previous section how white space was often ignored when rendering the layout, but we’ve said that it still played a role in the DOM tree. Text nodes are still being created in the DOM tree of the page, so the following code:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
⇥&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦Hello◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
⏎
◦◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦World!◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦⏎
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;still generates the following tree:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;element node: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
 ∟ text node: ⏎⇥
 ∟ element node: div
    ∟ text node: ◦◦Hello◦◦
 ∟ text node: ⏎⏎◦◦◦
 ∟ element node: div
    ∟ text node: ◦◦World!◦◦
 ∟ text node: ◦◦⏎&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the primary job of any inspector panel in any devtools out there is to display the DOM tree, but if you try for yourself, you’ll see that these text nodes are just not there.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/psIbuiT-ckAHBBGr8F0m5g.png&quot; alt=&quot;Showing that firefox devtools does not show the whitespace text nodes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/i7luPjjD1x7M5oWVXpYnVA.png&quot; alt=&quot;Showing that chrome devtools also does not show the whitespace text nodes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The reason being that if the browser engine ignores these white space only text nodes when creating the layout, then it’s probably safe for devtools to ignore them too. After all, authors only use them for formatting, and visitors don’t see them. So there’s no real need for devtools to show them.&lt;/p&gt;
&lt;p&gt;Now, there are in fact cases where showing white space text nodes in devtools could be useful. The following sections will describe what they are.&lt;/p&gt;
&lt;h3&gt;Spaces between inline and inline-block elements&lt;/h3&gt;
&lt;p&gt;In fact, we’ve seen this already with the very first example in this article, when we described how white space was processed inside inline formatting contexts.&lt;/p&gt;
&lt;p&gt;We said that there were rules to ignore most characters but that certain spaces staid in order to, basically, separate words.&lt;/p&gt;
&lt;p&gt;So, when you’re actually dealing with text only, paragraphs that may contain inline elements such as em, strong, span, etc. you don’t normally care about this because the extra white spaces that do make it to the layout are actually helpful to separate the words.&lt;/p&gt;
&lt;p&gt;But it gets more interesting when you start using inline-block elements. These elements appear as inline elements from the outside, but behave like blocks on the inside, so they are very often used to display more complex pieces of UI than just text side by side on the same line (just like if you floated blocks).&lt;/p&gt;
&lt;p&gt;I think the expectation from web developers is that because they are blocks, they will behave as such, and just stack side by side (rather than on top of each other), but really they don’t. If there is formatting white space in the markup between them, then that will create space in the layout just like between text.&lt;/p&gt;
&lt;p&gt;Consider this example:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
  &lt;span class=&quot;token selector&quot;&gt;.people-list&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;list-style-type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.people-list li&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f06&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;people-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
                        
◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
                        
◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
                        
◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
                        
◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
                        
◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⏎
                        
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you open this in a browser, you’ll see the following result:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/_OCThEatQqzndeqgJN9Fzw.png&quot; alt=&quot;Showing the line with the list items from the code above, separated by some space&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Which is most probably not what you intended. Let’s assume this is a list of people’s avatars and you wanted them displayed like this instead:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/Vly3EOAPmBSsupOQsT_Nzw.png&quot; alt=&quot;The line of list items, with no space in between&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Well, this is a very common CSS layout problem and &lt;a href=&quot;http://stackoverflow.com/questions/5078239/how-to-remove-the-space-between-inline-block-elements&quot;&gt;questions&lt;/a&gt; and &lt;a href=&quot;https://css-tricks.com/fighting-the-space-between-inline-block-elements/&quot;&gt;articles&lt;/a&gt; have been written about this. There exist solutions, things like getting rid of the white space altogether, setting your font-size to 0, or using negative margin, etc.&lt;/p&gt;
&lt;p&gt;What is interesting here isn’t really the solution to this common problem, but the fact that this is a common problem at all, and that many web developers have spent at least a little bit of time confronted to.
Suddenly white space does show up in your layout in a way you didn’t expect and it may take you a while to figure out the problem.&lt;/p&gt;
&lt;p&gt;Because the corresponding text nodes aren’t in devtools, people loose time on this common problem if they haven’t faced it before. They’ll check if there is margin somewhere but won’t find any.&lt;/p&gt;
&lt;p&gt;So that’s one example of when showing white space text nodes in devtools would actually be useful. Let’s see another one.&lt;/p&gt;
&lt;h3&gt;Controlling white space rendering&lt;/h3&gt;
&lt;p&gt;Using the CSS &lt;code&gt;white-space&lt;/code&gt; property, you can control how white space characters are processed when a given inline formatting context is laid out.&lt;/p&gt;
&lt;p&gt;css-tricks.com has a &lt;a href=&quot;https://css-tricks.com/almanac/properties/w/whitespace/&quot;&gt;nice article about this property&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The important thing is that if you set this property to &lt;code&gt;pre&lt;/code&gt;, &lt;code&gt;pre-wrap&lt;/code&gt; or &lt;code&gt;pre-line&lt;/code&gt;, this will actually honor some or all of the white space character in the source HTML code, and they will start taking space in the layout.&lt;/p&gt;
&lt;p&gt;If we take a simple example from earlier:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦◦◦Hello◦⏎
⇥⇥⇥⇥&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⇥◦◦&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But add the following CSS rule:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;h1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pre&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then end up with the following layout:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/Dz-vnOLt_eG0vweIkaJMoQ.gif&quot; alt=&quot;Animation showing how Firefox DevTools shows where the whitespace has been assigned&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Using Firefox DevTools to highlight text nodes, you can see exactly what space is occupied by the “Hello” text node. See live at https://captainbrosset.github.io/white-space-article/example5.html.&lt;/p&gt;
&lt;p&gt;As you can see above, the layout inside the h1 element respects the formatting of the source HTML file. There is some space before the word &lt;em&gt;Hello&lt;/em&gt;, then a line break, then more space and the word &lt;em&gt;World!&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In fact, as you can see, the first node shown in devtools inside the h1 element is a text node and hovering over it does highlight the space taken by that node in the page.&lt;/p&gt;
&lt;p&gt;Hovering over the span element also highlights the space taken in the page, and in particular, you can see the space before the word World! coming from:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;◦World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore, that’s a second reason why devtools should show white space text nodes. Indeed, someone trying to understand the layout and not knowing about the &lt;code&gt;white-space&lt;/code&gt; property may be confused.&lt;/p&gt;
&lt;h3&gt;Firefox DevTools to the rescue!&lt;/h3&gt;
&lt;p&gt;Starting with version 52 of Firefox, &lt;a href=&quot;https://blog.nightly.mozilla.org/2016/10/17/devtools-now-display-white-space-text-nodes-in-the-dom-inspector/&quot;&gt;the inspector panel shows white space text nodes&lt;/a&gt; when they do have an impact on the layout and also highlights them in the page.&lt;/p&gt;
&lt;p&gt;How does the inspector know when a node impacts the layout? It simply checks if that white space text node has a size. When a text node is ignored, it’ll have a width and height of 0, but when it participate in the layout, it’ll have some dimension.
So, using this simple heuristic, Firefox DevTools can show the white space text nodes that are important.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/FxrkWgJ9axNWk-BdvZyHbQ.gif&quot; alt=&quot;Animation showing how Firefox DevTools displayed whitespace text nodes when they matter&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see above, the white space text nodes that do have a size in the page are now shown in the inspector panel and, if you hover over them, they are also highlighted in the page so you know exactly where they are and how big they are.&lt;/p&gt;
&lt;p&gt;This way, if you were originally confused about why the avatars in the page didn’t sit next to each other, it will now be very obvious why that is. No more loosing time looking for margins that aren’t there or crafting the right google search query that will give you the answer.&lt;/p&gt;
&lt;p&gt;In fact, you can even remove these text nodes and see that the inline-block elements are now displayed exactly as you wanted them to.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/lDvyOw-qUrt8S9KHPlV3WQ.gif&quot; alt=&quot;Animation showing how you can remove the whitespace text nodes in the inspector&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nightly.mozilla.org/&quot;&gt;Firefox 52 is now available as a Nightly build&lt;/a&gt;, so go ahead and grab it and take it for a spin.&lt;/p&gt;
&lt;p&gt;Hopefully this new feature and this article have been helpful, thanks for reading!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Visualize animations easing in DevTools</title>
    <link href="https://patrickbrosset.com/articles/2016-11-28-visualize-animations-easing-in-devtools/"/>
    <updated>2016-11-28T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2016-11-28-visualize-animations-easing-in-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://hacks.mozilla.org/2016/11/visualize-animations-easing-in-devtools/">https://hacks.mozilla.org/2016/11/visualize-animations-easing-in-devtools/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Firebug lives on in Firefox DevTools</title>
    <link href="https://patrickbrosset.com/articles/2016-12-20-firebug-lives-on-in-firefox-devtools/"/>
    <updated>2016-12-20T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2016-12-20-firebug-lives-on-in-firefox-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://hacks.mozilla.org/2016/12/firebug-lives-on-in-firefox-devtools/">https://hacks.mozilla.org/2016/12/firebug-lives-on-in-firefox-devtools/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>DevTools: What you need to know</title>
    <link href="https://patrickbrosset.com/articles/2017-01-31-devtools-what-you-need-to-know/"/>
    <updated>2017-01-31T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2017-01-31-devtools-what-you-need-to-know/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://hacks.mozilla.org/2017/01/devtools-what-you-need-to-know/">https://hacks.mozilla.org/2017/01/devtools-what-you-need-to-know/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Where is DevTools headed?</title>
    <link href="https://patrickbrosset.com/articles/2017-02-01-where-is-devtools-headed/"/>
    <updated>2017-02-01T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2017-02-01-where-is-devtools-headed/</id>
    <content type="html">
      
        &lt;p&gt;Lately I&#39;ve been thinking more about the future strategy for Firefox DevTools, and so I decided to write this article. Hopefully you find it inspirational, and motivating.&lt;/p&gt;
&lt;h3&gt;Using standard web technologies only&lt;/h3&gt;
&lt;p&gt;Browser devtools have historically always been part of browsers&#39; user interfaces and, as such, are desktop applications, not web applications.
Now, if you look under the hood at Chrome or Firefox, you&#39;ll find that they actually are rather close to how web applications are built. Sure, they use some special APIs or languages, but their user interfaces make use of markup, CSS, and JavaScript.&lt;/p&gt;
&lt;p&gt;Firefox DevTools is still making use of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL&quot;&gt;XUL&lt;/a&gt; markup in some places (instead of HTML), and special privileged javascript APIs or Firefox-only CSS features that haven&#39;t yet become available to web sites.&lt;/p&gt;
&lt;p&gt;At Mozilla, we&#39;re in the process of changing this. We are aiming at only using standard and cross-browser HTML, CSS and JS. In particular, &lt;a href=&quot;https://github.com/devtools-html/debugger.html&quot;&gt;our new debugger&lt;/a&gt; is already just web, our inspector doens&#39;t have XUL anymore, and our console and network monitor are in the process of being converted too.&lt;/p&gt;
&lt;p&gt;We&#39;re building tools for web developers, we need to be web developers ourselves, not Firefox developers only. We therefore need to be confronted to the same problems, and use the same technologies.&lt;/p&gt;
&lt;p&gt;Additionally, standard web technologies allow us to run the tools anywhere the web is supported.
That means, being able to run the tools in any browser, therefore turning DevTools into just a web site.&lt;/p&gt;
&lt;p&gt;This opens up the door to so many potential ideas: our tools could run in standalone &lt;a href=&quot;http://electron.atom.io/&quot;&gt;electron&lt;/a&gt;-based applications, or be embedded in code editors, or run as web extensions in other browsers, or even be hosted on a web server so they can benefit from the web shipping model.
Additionally, Mozilla&#39;s research browser: &lt;a href=&quot;https://servo.org/&quot;&gt;Servo&lt;/a&gt;, does not support XUL or privileged JS, so if we want DevTools to be able to debug pages in Servo, this is needed.
Finally, using non-standard technologies makes it harder for web developers to contribute to DevTools.&lt;/p&gt;
&lt;h3&gt;Shipping and experimenting better/faster&lt;/h3&gt;
&lt;p&gt;Browsers have a rather long release cycle. Mozilla Firefox for instance ships every 6 weeks and has 4 channels.
So when an engineer adds a feature, it lands on the Nightly channel and will stay there for 6 weeks (or less depending on when during the cycle they land it). Nightly then merges into Aurora and the code stays there for another 6 weeks so it can get more testing. After that comes the Beta channel, itself also 6 weeks. If everything goes fine and the feature was not removed at any stage of the way, it ends up in our release channel and becomes available to all our users.
Other browser vendors have similar release processes.&lt;/p&gt;
&lt;p&gt;Today, DevTools is part of the browser. So it follows the same release cadence.&lt;/p&gt;
&lt;p&gt;However, we know that the majority of our users use the Firefox release channel. This means if one of those release users runs into a bug with our tools it will take more than 4 months for us to ship that fix (assuming we actually identify and fix the problem immediately).&lt;/p&gt;
&lt;p&gt;So we want to ship things faster. Make it possible for important fixes to make it to our release users as fast as possible. And experimenting with new features faster too.&lt;/p&gt;
&lt;p&gt;Speed is not the only factor here. If we take the example of the web console panel for instance. We want to be able to ship new experimental console features to the users out there who actually care about and use the console.
We have systems in place that gather usage statistics, we should make use of them more intelligently in order to drive how our new features are getting enabled.&lt;/p&gt;
&lt;p&gt;Right now, we ship our new things to pre-release channels (nightly, aurora, beta), but we have very few users there. And the ones we do have are not necessarily heavy console users. So if we ship a new console feature enabled by default only on these pre-release channels for a bit, we can&#39;t really get much feedback, and we have no way of knowing if we can trust it.&lt;/p&gt;
&lt;p&gt;We have to wait until release for people to find out the new console feature and potentially not like it. By this time, it&#39;s too late for us to address the problem quickly. The incremental changes we will make then will have to follow the usual release cycle.&lt;/p&gt;
&lt;p&gt;So DevTools features should ship quickly, and target users who actually need them, and be generally available behind some kind of experimental flag for a while, until they stabilise and we gather enough feedback.&lt;/p&gt;
&lt;h3&gt;Going out of the central browser code repository&lt;/h3&gt;
&lt;p&gt;I think we need people to hack on devtools more than ever. It needs to be easy, a lot easier than it is now.
DevTools is for web developers. They are the ones who need it, they have the ideas for it, they report the bugs, they should have the means to fix/enhance DevTools on their own.&lt;/p&gt;
&lt;p&gt;Asking them to clone a huge browser repository and build the whole browser is too much, this isn&#39;t what web developers are used to.&lt;/p&gt;
&lt;p&gt;For instance, Firefox lives in &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/&quot;&gt;the mozilla-central mercurial&lt;/a&gt; (or &lt;a href=&quot;https://github.com/mozilla/gecko-dev&quot;&gt;Git&lt;/a&gt;) repository. It is several gigabytes big, and takes a long while to build.&lt;/p&gt;
&lt;p&gt;We&#39;ve made several improvements to this, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Artifact_builds&quot;&gt;artifact builds&lt;/a&gt; take seconds/minutes instead of hours because you don&#39;t build the C++, or you can just download Firefox nightly and use our &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Contributing/Contribute_on_nightly&quot;&gt;addon-based development workflow&lt;/a&gt;. But still, we&#39;ll be making more changes to this in the future.&lt;/p&gt;
&lt;p&gt;Contributors should have access to an enjoyable and familiar development environment.
The DevTools project should be part of the current web ecosystem. We provide the tools for it, but we&#39;re not even part of it, or use it all that much. This needs to change, big time.&lt;/p&gt;
&lt;p&gt;Moving to &lt;a href=&quot;https://github.com/devtools-html/&quot;&gt;GitHub&lt;/a&gt; seems like the right choice for this. Obviously, it&#39;s not as simple as moving code from one repo to the other. There are many questions associated with it, and we&#39;re allowing experimentation time right now to figure this out.&lt;/p&gt;
&lt;p&gt;Having said this, we already have some really strong evidence of the benefits of GitHub. Our new debugger is a very popular project. It&#39;s not just a bet, it actually works really well.
Now, imagine moving the inspector to GitHub! The debugger is really great, but way more people use an inspector than a debugger, so this is bound to generate even more excitement.&lt;/p&gt;
&lt;h3&gt;Debug anything&lt;/h3&gt;
&lt;p&gt;Web developers don&#39;t just run their code in one browser, and they write code for both server and client. Right now, they need to learn multiple tools and switch between them often (or they don&#39;t, and web compatibility suffers).&lt;/p&gt;
&lt;p&gt;They need a cross-browser testing and development environment. We can give it to them, we can work out the differences between debugging protocols, and we can provide single tools that they can use to target anything: NodeJS, Firefox, Chrome, Safari on iPhone, Edge, tomorrow things like Servo, etc.&lt;/p&gt;
&lt;p&gt;If you care for the web, doing this makes sense. DevTools are for developers, not for browsers, they should help developers get the work done, not be completely different from one browser to the other just because they decided to invest in different things.&lt;/p&gt;
&lt;p&gt;Now, that doesn&#39;t necessarily mean that there shouldn&#39;t be any differences between browser DevTools either. Browsers do invest in different things and are best at implementing different web APIs and features. And they can, in turn, make these great features available for debugging to Devtools.
After all, DevTools is just an app that connects to a debugging target that might expose different things. Firefox may have a really cool CSS layout tool, while Chrome may have better service-worker debugging. There just happens to be common tooling that can target both.&lt;/p&gt;
&lt;h3&gt;Conclusion: an independent product&lt;/h3&gt;
&lt;p&gt;What I&#39;ve described so far is nothing else than a web-based standalone product.&lt;/p&gt;
&lt;p&gt;And this is how I think of DevTools now, as its own product. At least that is the long term vision for it. It doesn&#39;t mean that we should start creating a product right now, stop everything and do this. It just means that if everything we do brings us a little closer to that vision, step by step, then we&#39;ll be in a great position in the future. A position that allows us to do more than we do today, faster and better, and be much more of an ally for the web development community.&lt;/p&gt;
&lt;p&gt;We need tools that web developers can enhance, reuse, customize, embed, and more. We should be web developers building a web app for web developers.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>A quick history of Firefox DevTools</title>
    <link href="https://patrickbrosset.com/articles/2017-04-05-a-quick-history-of-firefox-devtools/"/>
    <updated>2017-04-05T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2017-04-05-a-quick-history-of-firefox-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://medium.com/mozilla-tech/a-quick-history-of-firefox-devtools-620d3074b510">https://medium.com/mozilla-tech/a-quick-history-of-firefox-devtools-620d3074b510</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Demystifying CSS alignment</title>
    <link href="https://patrickbrosset.com/articles/2018-01-11-demystifying-css-alignment/"/>
    <updated>2018-01-11T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2018-01-11-demystifying-css-alignment/</id>
    <content type="html">
      
        &lt;p&gt;Aligning things in CSS has been a common source of frustration, fun and even memes for a long time. However CSS evolves quickly and new specifications are written and implemented in browsers all the time.
As a result, aligning things in CSS today isn’t quite what it used to be. We now have to take more modern ways to do CSS layout into account, like Flexbox and Grid among other things.&lt;/p&gt;
&lt;p&gt;In this post, I’d like to talk about alignment a little bit. What it means in Flexbox and Grid and how to think about it more generally too, so you can be equipped for the future, when aligning in Blocks, Multi-Columns, Tables, Grids and Flexbox all works the same.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: if you’ve read this already and all you want is a quick access to my cheatsheet, &lt;a href=&quot;https://patrickbrosset.com/lab/2018-01-10-css-alignment-cheatsheet/&quot;&gt;click here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;The context&lt;/h3&gt;
&lt;p&gt;The context here is essentially the W3C specification called &lt;a href=&quot;https://www.w3.org/TR/css-align-3/&quot;&gt;CSS Box Alignment&lt;/a&gt;. And within that spec, the &lt;code&gt;justify-content&lt;/code&gt;, &lt;code&gt;justify-items&lt;/code&gt;, &lt;code&gt;justify-self&lt;/code&gt; and &lt;code&gt;align-content&lt;/code&gt;, &lt;code&gt;align-items&lt;/code&gt;, &lt;code&gt;align-self&lt;/code&gt; properties.&lt;/p&gt;
&lt;p&gt;I’ve been reading this spec over the past few days, and I thought I’d write about it.
(On that note, reading specs and trying to explain them to people is a really good exercise you can try if you want to make sense of CSS and really understand what&#39;s going on rather than trying stuff out until they work).&lt;/p&gt;
&lt;h3&gt;Justify or align?&lt;/h3&gt;
&lt;p&gt;You probably have come across these names if you have used Flexbox before. Indeed, they are the first part of CSS property names like &lt;code&gt;justify-content&lt;/code&gt; and &lt;code&gt;align-items&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you’re like me, you’ve visited &lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;&gt;css-tricks’ Flexbox guide&lt;/a&gt; many times and now remember things like &lt;code&gt;justify-content:center; align-items:center;&lt;/code&gt; by heart because they do cool things (like, in that case, center elements).&lt;/p&gt;
&lt;p&gt;But you might not know which direction &lt;code&gt;justify&lt;/code&gt; and &lt;code&gt;align&lt;/code&gt; apply in.&lt;/p&gt;
&lt;h4&gt;Two directions&lt;/h4&gt;
&lt;p&gt;First, let’s learn about the two directions of a web page. You should keep it in your mind that there are two, perpendicular, axes on your page, and that content runs along them (I&#39;m ignoring the Z axis here which z-index can be used for).&lt;/p&gt;
&lt;p&gt;Reading &lt;a href=&quot;https://24ways.org/2016/css-writing-modes/&quot;&gt;Jen Simmons’ Writing-Modes article&lt;/a&gt; on that topic will help you a lot.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One of the axes is the inline axis, it&#39;s the one each line of text runs on.
By default on a web page, if you don&#39;t specify any writing-mode, this axis is horizontal, from left to right.&lt;/li&gt;
&lt;li&gt;The other one is the block axis and it’s the one along which blocks are stacked.
Again, by default, this axis is vertical, from top to bottom.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/default-writing-mode-directions.png&quot; alt=&quot;Diagram showing the direction of each axis in default writing mode&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Remembering which direction is justify and which is align&lt;/h4&gt;
&lt;p&gt;If at any point in your life you’ve used a text processing application (like Microsoft Word), you are already equipped to remember this.&lt;/p&gt;
&lt;p&gt;Indeed, these applications almost always have a button to &lt;em&gt;justify&lt;/em&gt; text on a line. What it does is distribute words along a line. Therefore, &lt;code&gt;justify&lt;/code&gt; works along the &lt;code&gt;inline&lt;/code&gt; axis. That&#39;s the trick I now use to remember this (thank you &lt;a href=&quot;https://www.chenhuijing.com/&quot;&gt;Chen Hui Jing&lt;/a&gt; for explaining it this way to me a few weeks ago).&lt;/p&gt;
&lt;p&gt;And by a very elaborate process of elimination, &lt;code&gt;align&lt;/code&gt; works along the &lt;code&gt;block&lt;/code&gt; axis.&lt;/p&gt;
&lt;p&gt;So, if you know which way is inline, and which way is block, then you know where justify and align apply.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/default-writing-mode-directions-with-axes.png&quot; alt=&quot;Diagram showing the direction of each axis in default writing mode with inline and block axes shown&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(Note: if you use &lt;code&gt;flex-direction:column&lt;/code&gt; in a Flexbox container, then this isn’t true anymore, the inline and block still point in the same direction they did before, but &lt;code&gt;align&lt;/code&gt; and &lt;code&gt;justify&lt;/code&gt; now don’t work along these directions. But for the sake of simplicity in this article, let’s ignore this for now).&lt;/p&gt;
&lt;h3&gt;Content, self or items?&lt;/h3&gt;
&lt;p&gt;Now we&#39;ve only looked at the &lt;code&gt;justify&lt;/code&gt; and &lt;code&gt;align&lt;/code&gt; parts of the CSS properties that interest us here. Let&#39;s look at the rest of these property names.&lt;/p&gt;
&lt;p&gt;Taking the &lt;code&gt;justify-content:center; align-items:center;&lt;/code&gt; example again, you now know which direction &lt;code&gt;justify&lt;/code&gt; and &lt;code&gt;align&lt;/code&gt; correspond to, but you might be confused as to why one property ends with &lt;code&gt;content&lt;/code&gt; and the other with &lt;code&gt;items&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To explain why that is, we need to first understand a general concept about alignment.&lt;/p&gt;
&lt;h4&gt;Containers and subjects&lt;/h4&gt;
&lt;p&gt;This is a generic way of thinking about alignment that will apply to any layout where these properties can be used. It&#39;s very useful to understand this without necessarily only thinking about Flexbox. This way you gain a better understanding of how things are defined holistically and you can apply this knowledge to other layouts in the future.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;container&lt;/strong&gt; is a rectangle within which a &lt;strong&gt;subject&lt;/strong&gt; is being aligned.&lt;/li&gt;
&lt;li&gt;And a &lt;strong&gt;subject&lt;/strong&gt; is the thing (or things) that is (are) being aligned within the &lt;strong&gt;container&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pretty simple, right? Now, how do these correspond to &lt;code&gt;content&lt;/code&gt;, &lt;code&gt;items&lt;/code&gt; and &lt;code&gt;self&lt;/code&gt;?&lt;/p&gt;
&lt;h4&gt;Content&lt;/h4&gt;
&lt;p&gt;When you see &lt;code&gt;content&lt;/code&gt; in the property name, then that means this property applies to an alignment container. It defines how subjects are aligned or distributed within this container.&lt;/p&gt;
&lt;p&gt;The spec calls this &lt;strong&gt;content distribution&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So, in a way, it&#39;s a bit like setting the &lt;code&gt;padding&lt;/code&gt; on an element. It applies to the element itself and ends up aligning the content within this element.&lt;/p&gt;
&lt;h4&gt;Self&lt;/h4&gt;
&lt;p&gt;If, instead you see &lt;code&gt;self&lt;/code&gt; in the property name, then that means it applies to a subject instead. It defines how this particular subject is aligned within its container.&lt;/p&gt;
&lt;p&gt;The spec calls this &lt;strong&gt;self-alignment&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In a way, you can think of it as &lt;code&gt;margin&lt;/code&gt;, because it applies to an element but aligns it within its container.&lt;/p&gt;
&lt;h4&gt;Items&lt;/h4&gt;
&lt;p&gt;And finally, the properties ending with the &lt;code&gt;items&lt;/code&gt; word are a special case. They apply to containers like &lt;code&gt;content&lt;/code&gt; does, &lt;strong&gt;but&lt;/strong&gt; doesn&#39;t impact them at all. Instead, this is a way to set the default value of &lt;code&gt;self&lt;/code&gt; for all subjects within a container in one go.&lt;/p&gt;
&lt;p&gt;So, this is &lt;strong&gt;self-alignment&lt;/strong&gt; too, but the property just goes on the container instead of the subject, and sets the default self-alignment of all the subjects inside this container.&lt;/p&gt;
&lt;h4&gt;Example&lt;/h4&gt;
&lt;p&gt;So far, this has been a lot of theory and may be hard to reason about. So let&#39;s look at one example, and then in the next section we&#39;ll dive into more details with different types of CSS layouts.&lt;/p&gt;
&lt;p&gt;Let&#39;s go back one more time to our common Flexbox centering example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;justify-content:center; align-items:center;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What you learned so far should make it possible for you to make sense of these two CSS declarations now. Let&#39;s decompose them below:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;justify-content:center;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;content&lt;/code&gt; means content distribution. This applies to a container which, in the case of Flexbox would be the Flexbox element container itself (e.g. the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with &lt;code&gt;display:flex&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;&lt;code&gt;justify&lt;/code&gt; works along the inline axis (so, horizontally by default),&lt;/li&gt;
&lt;li&gt;finally the value is &lt;code&gt;center&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, as a conclusion, this piece of CSS means we&#39;re centering content within this Flexbox container along the inline axis.
In this case, the content we&#39;re centering is the various Flexbox items themselves.&lt;/p&gt;
&lt;p&gt;This is defined in the spec and we&#39;ll look at it in the next section. For each axis and for each layout type, the spec tells us what is the alignment container and what are the alignment subjects. Here, the subjects are the Flexbox items.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/justify-content-center-diagram.png&quot; alt=&quot;Diagram showing a flex container with 3 items grouped in its center vertically&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;align-items:center;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;items&lt;/code&gt; means self-alignment, right? But if you remember from before, it is a special part of the property that really applies to subjects, even if it is set on the container,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;align&lt;/code&gt; works along the block axis (vertically by default),&lt;/li&gt;
&lt;li&gt;and the alignment value is &lt;code&gt;center&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a conclusion, this piece of CSS means set the default self alignment of all items inside this container to be centered along the block axis&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/align-items-center-diagram.png&quot; alt=&quot;Diagram showing a flex container with 3 items grouped in its center vertically, and horizontally centered too&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now, you might be thinking &lt;code&gt;align-content:center;&lt;/code&gt; should do the same as &lt;code&gt;align-items:center;&lt;/code&gt; right? I thought that too, but reading the spec provided a reason for why that is not the case in this simple example.&lt;/p&gt;
&lt;p&gt;It comes down to knowing what is the alignment container and subject you are currently dealing with.
And that&#39;s exactly what I&#39;ll be focusing on in the next section.&lt;/p&gt;
&lt;h3&gt;Flexbox alignment&lt;/h3&gt;
&lt;p&gt;Let&#39;s start with Flexbox since we&#39;ve talked about it already.
We&#39;ll go over the 2 types of alignment: content distribution and self-alignment.&lt;/p&gt;
&lt;h4&gt;Content distribution (justify/align-content)&lt;/h4&gt;
&lt;p&gt;Along the inline axis (&lt;code&gt;justify-content&lt;/code&gt;), things are quite simple.
The alignment container is the Flexbox container. And alignment subjects are the Flexbox items.
So, &lt;code&gt;justify-content&lt;/code&gt; can be used on the Flexbox container to align its items along its inline axis, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-justify-content.png&quot; alt=&quot;Diagram showing the effect of justify-content on the inline axis of a flex container&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Along the block axis (&lt;code&gt;align-content&lt;/code&gt;), things get a bit more complicated.
In this case, the alignment container is still the Flexbox container, but the alignment subject is something called the Flexbox line. Let’s explain this a bit:&lt;/p&gt;
&lt;p&gt;In a lot of cases, Flexbox is used with all items being on just one line.
But sometimes wrapping happens, and in those cases it’s important to realise that items on each lines are wrapped into a Flexbox line.
This is a thing that contains items, is as tall (or wide) as the tallest (or widest) of them, and that can be aligned within the container.
So, in the case of &lt;code&gt;align-content&lt;/code&gt;, we’re aligning Flexbox lines within a Flexbox container as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-align-content.png&quot; alt=&quot;Diagram showing the effect of align-content on the block axis of a flex container&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This explains why &lt;code&gt;align-content&lt;/code&gt; doesn&#39;t work in the case where a Flexbox container only has one line. When it does, that line has the same size as the container, so aligning it anywhere has no visible effect.&lt;/p&gt;
&lt;h4&gt;Self-alignment (justify/align-self)&lt;/h4&gt;
&lt;p&gt;In Flexbox, self-alignment can only be used on the block axis. Indeed, &lt;code&gt;justify-self&lt;/code&gt; can&#39;t be used to align one item, because there might be other items on the same line.&lt;/p&gt;
&lt;p&gt;Along the block axis however, &lt;code&gt;align-self&lt;/code&gt; can be used to align a given item. In this case, the alignment container is the Flexbox line that the item current is in, and the alignment subject is the item itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-align-self.png&quot; alt=&quot;Diagram showing the effect of align-self on the block axis of a flex item&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Grid alignment&lt;/h3&gt;
&lt;h4&gt;Content distribution (justify/align-content)&lt;/h4&gt;
&lt;p&gt;Content distribution, if you remember correctly, is a thing you do on a container to distribute/align subjects within itself.&lt;/p&gt;
&lt;p&gt;Well, in the case of content distribution in a Grid layout, the container is the Grid container itself (the element with &lt;code&gt;display:grid&lt;/code&gt; on), and subjects are tracks (the columns and rows).
In this case we&#39;re controlling where space goes between rows or columns, just like grid gaps would.&lt;/p&gt;
&lt;p&gt;So, &lt;code&gt;justify-content&lt;/code&gt; controls the space along the inline axis, between column tracks, and align-content controls the space along the block axis, between row tracks, as illustrated below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid-justify-content.png&quot; alt=&quot;Diagram showing the effect of justify-content on the inline axis of a grid container&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that content distribution might not always be possible in a grid. For instance, if the rows are defined in a way that they cover the whole grid height, then there is no space in between those rows to be distributed.&lt;/p&gt;
&lt;p&gt;For instance, &lt;code&gt;grid-template-rows: repeat(2, 1fr);&lt;/code&gt; will make the 2 rows take an equal share of the available height, until no space is left.&lt;/p&gt;
&lt;h4&gt;Self-alignment (justify/align-self)&lt;/h4&gt;
&lt;p&gt;Self-alignment, on the other hand, is the act of determining where inside a container a given subject is aligned.&lt;/p&gt;
&lt;p&gt;In the case of CSS Grids, subjects are Grid items themselves (the children of the &lt;code&gt;display:grid&lt;/code&gt; container), and containers are defined as &lt;strong&gt;grid areas&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;What that means depends on where the item currently is. If the item is positioned in a grid cell, then that grid cell is the alignment container.
If the item is placed in a grid area, then that grid area is the alignment container.&lt;/p&gt;
&lt;p&gt;This works the same on both axis, so &lt;code&gt;justify-self&lt;/code&gt; and &lt;code&gt;align-self&lt;/code&gt; are easy to reason about in a CSS Grid: it simply allows you to align an item within its cell or area along both axis, like illustrated below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid-justify-align-self.png&quot; alt=&quot;Diagram showing the effect of justify-self and align-self on a grid item&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Multi-column alignment&lt;/h3&gt;
&lt;p&gt;If you don&#39;t know what CSS multi-column is or haven&#39;t used it yet, I recommended heading over to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Columns/Using_multi-column_layouts&quot;&gt;the &amp;quot;Using Multi-column layouts&amp;quot; MDN page&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;Essentially, it&#39;s a set of CSS properties that together allow to break a block layout into several columns so that text (and other sub elements) can flow through them like newspaper or magazine articles often do.&lt;/p&gt;
&lt;p&gt;Although I&#39;m not aware of browsers out there supporting the justify/align-content/self/items properties in multi-column layout yet, the spec defines these properties for this type of layout too.&lt;/p&gt;
&lt;p&gt;As I said earlier, it&#39;s useful to think of these properties globally across all sorts of layouts, because it makes it easier to reason about them. And at some stage in the future, these alignment properties &lt;em&gt;will&lt;/em&gt; start to work for multi-column layouts too.&lt;/p&gt;
&lt;h4&gt;Content distribution (justify/align-content)&lt;/h4&gt;
&lt;p&gt;When distributing content in a multi-col layout, the alignment container is the block element that has the columns, and subjects are the columns themselves.&lt;/p&gt;
&lt;p&gt;Along the inline axis (&lt;code&gt;justify-content&lt;/code&gt;), what happens is a bit similar to Grid layout. The property controls spacing between columns (just like it controls spacing between column tracks in a Grid layout).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/multicol-justify-content.png&quot; alt=&quot;Diagram showing the effect of justify-content on a multicol layout&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Along the block axis (&lt;code&gt;align-content&lt;/code&gt;), the alignment container and subject are the same than before.
The property becomes useful when not all columns have the same height. Indeed, there may not be enough content to fill up all columns to the same height. So &lt;code&gt;align-content&lt;/code&gt; can be used to align the column boxes inside the column container along the block axis like so:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/multicol-align-content.png&quot; alt=&quot;Diagram showing the effect of align-content on a multicol layout&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Self-alignment (justify/align-self)&lt;/h4&gt;
&lt;p&gt;Self-alignment doesn&#39;t apply to multi-column layouts.&lt;/p&gt;
&lt;p&gt;Indeed, self-alignment is something that applies to alignment subjects, where those subjects are actual elements in the page that &lt;code&gt;justify/align-self&lt;/code&gt; can be applied on. There&#39;s nothing like this in a multi-column layout. The column boxes themselves can&#39;t be styled with these properties individually.&lt;/p&gt;
&lt;h3&gt;Block alignment&lt;/h3&gt;
&lt;p&gt;So far we&#39;ve talked about rather advanced layout types, but the spec also defines how alignment works in simple blocks (really just any element with &lt;code&gt;display:block;&lt;/code&gt; on it).&lt;/p&gt;
&lt;p&gt;Note that there&#39;s no browsers out there that allow block alignment this way yet. But implementations will come at some stage.&lt;/p&gt;
&lt;h4&gt;Content distribution (justify/align-content)&lt;/h4&gt;
&lt;p&gt;With this type of layout, the alignment container is the block element itself, and the alignment subject is the entire content of the block, &lt;strong&gt;as one unit&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s very easy to think of this as setting the padding on the block element. Except here, we&#39;re using the &lt;code&gt;align-content&lt;/code&gt; property instead, in the same consistent way we&#39;ve done it with other layout types too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/block-align-content.png&quot; alt=&quot;Diagram showing the effect of align-content on a block element&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The spec however says that &lt;code&gt;justify-content&lt;/code&gt; doesn’t work in this case.&lt;/p&gt;
&lt;h4&gt;Self-alignment (justify/align-self)&lt;/h4&gt;
&lt;p&gt;Along the inline axis (&lt;code&gt;justify-self&lt;/code&gt;), self-alignment happens between the subject: a block, and its container: another block.&lt;/p&gt;
&lt;p&gt;Like before, this is very similar to setting the margin on the subject:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/block-justify-self.png&quot; alt=&quot;Diagram showing the effect of justify-self on a block element&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Along the block axis however (&lt;code&gt;align-self&lt;/code&gt;), this has no effect. Indeed, there can be more than one block along this axis, and it is therefore not possible to align one differently than the others.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Well, there we go, I hope this was a useful and not too boring read.&lt;/p&gt;
&lt;p&gt;I really hope I accomplished my goal of making it easier for you to reason about these various CSS properties.
I know writing this article and reading the spec really did it for me, I can now center, distribute and align things in Grid and Flexbox very easily without trying random things until it works. Now, my code works the first time.&lt;/p&gt;
&lt;p&gt;I created &lt;a href=&quot;https://patrickbrosset.com/lab/2018-01-10-css-alignment-cheatsheet/&quot;&gt;a cheatsheet that contains all of the diagrams above&lt;/a&gt; in a small and more easily usable format. So go check it out!&lt;/p&gt;
&lt;p&gt;Thanks for reading. Bye for now.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>CSS Grid + CSS Multi-Columns = ♥</title>
    <link href="https://patrickbrosset.com/articles/2018-01-15-css-grid-css-multicolumns/"/>
    <updated>2018-01-15T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2018-01-15-css-grid-css-multicolumns/</id>
    <content type="html">
      
        &lt;p&gt;In this short article, I am exploring the relationship between two CSS layout features: CSS Grid and CSS Multi-Columns.&lt;/p&gt;
&lt;p&gt;These two features can be used together in very interesting ways for building responsive web designs.&lt;/p&gt;
&lt;p&gt;Let’s start by reviewing what each of those features do.&lt;/p&gt;
&lt;h3&gt;CSS columns&lt;/h3&gt;
&lt;p&gt;CSS has had the ability to organize content into &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Columns/Using_multi-column_layouts&quot;&gt;columns&lt;/a&gt; for a while now and &lt;a href=&quot;http://caniuse.com/#feat=multicolumn&quot;&gt;support is really good&lt;/a&gt; all across the board (apart from the need to use vendor prefixes still).&lt;/p&gt;
&lt;p&gt;The main use case for CSS columns is to break long sections of text into several columns in order to avoid lines from being too long and therefore hard too read.&lt;/p&gt;
&lt;p&gt;The great thing about CSS columns is that the columns are defined with CSS only, and they don’t require any additional markup. Content just flows from one column to the next naturally and automatically depending on the currently available width.&lt;/p&gt;
&lt;p&gt;For instance, you can define a really simple multi-column layout by using the CSS property &lt;code&gt;column-width: 150px;&lt;/code&gt; and the browser will just add as many columns as it needs to fill the available space:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/multicol.gif&quot; alt=&quot;Animation of a multi column layout where the number of columns depends on the width of the container&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;CSS Grids&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout&quot;&gt;CSS Grids&lt;/a&gt; is this awesome new CSS layout system that allows web authors to organize items on a 2D grid very easily.&lt;/p&gt;
&lt;p&gt;If you haven’t heard about it at some point during this past year, you have probably been living under a rock. The sheer amount of articles, documentation, videos and talks has ben phenomenal, and it’s been really hard to miss.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;http://jensimmons.com/writing&quot;&gt;Jen Simmons’ website&lt;/a&gt;, &lt;a href=&quot;https://rachelandrew.co.uk/archives/tag/css-grid&quot;&gt;Rachel Andrew’s website&lt;/a&gt;, or &lt;a href=&quot;https://mozilladevelopers.github.io/playground/css-grid&quot;&gt;Mozilla’s Grid Playground&lt;/a&gt; to learn all about grids.&lt;/p&gt;
&lt;h3&gt;Columns + Grids = ♥&lt;/h3&gt;
&lt;p&gt;Let’s start by creating a simple grid layout that positions a series of labels and their input fields next to each other, in 2 columns:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;.grid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr 1fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-auto-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-gap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .5em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;label&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;label&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;label&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  ...
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CSS above creates 2 equally sized columns which all of the items are going to flow through as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/2-column-grid.gif&quot; alt=&quot;Simple 2-columns CSS Grid layout with labels in the left column and inputs in the right column&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now, here comes the interesting part: you can actually put a grid in a column layout! And it’s pretty amazing.&lt;/p&gt;
&lt;p&gt;Basically, grid supports fragmentation, which means that a grid will also flow into a multiple columns layout. It will become fragmented as needed, with one fragment per column. These fragments are just parts of the same grid.&lt;/p&gt;
&lt;p&gt;So, with the simple grid-based form example from before, let’s see what happens if we put it inside a multi-column layout with &lt;code&gt;column-width: 150px&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/fragmented-2-column-grid.gif&quot; alt=&quot;Animation showing that the 2 column grid gets broken into columns when inside a multicol CSS layout&quot; /&gt;&lt;/p&gt;
&lt;p&gt;See how, as space becomes available, the grid gets fragmented to occupy the available columns.&lt;/p&gt;
&lt;p&gt;Here’s what the entire code could look like:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;.columns&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;column-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 800px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.grid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr 1fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-auto-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-gap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .5em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;label&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;label&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;label&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  ...
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the nice things about this is all the content is in the same grid, even when the grid is broken down into multiple fragments. So, the size of the grid columns, for instance, will always be the same across fragments.&lt;/p&gt;
&lt;h3&gt;Fragmenting padding and border too&lt;/h3&gt;
&lt;p&gt;If you wanted, for some reason, to add some borders and padding to the grid itself, then something like this would happen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/cut-off-border-in-multicol.png&quot; alt=&quot;Screenshot showing that border gets cut off in multicol layouts&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Indeed, the element the grid is applied to is being fragmented into the multi-column container, but there’s still only one top and one bottom sides to it. So the bottom-border only applies to the bottom side of the grid element for example.&lt;/p&gt;
&lt;p&gt;Obviously that doesn’t look so good. Fortunately, CSS provides a way to solve this too: &lt;code&gt;box-decoration-break&lt;/code&gt;. This CSS property can be used to define how things like background, borders, margins and paddings work in fragmentation cases (or in cases like an inline element wrapping on several lines of text).&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;box-decoration-break: clone;&lt;/code&gt; we get the effect we want:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/cloned-border-in-multicol.png&quot; alt=&quot;Screenshot showing the cloned border in each fragment, thanks to box-decoration-break&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That’s it! You can play with &lt;a href=&quot;https://codepen.io/captainbrosset/pen/RxBELP&quot;&gt;the complete example here on codepen&lt;/a&gt;, it also includes some fun little CSS counters which I used to number the labels via CSS only.&lt;/p&gt;
&lt;p&gt;Happy coding, bye for now.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>When CSS doesn&#39;t do anything</title>
    <link href="https://patrickbrosset.com/articles/2020-03-09-when-css-doesnt-do-anything/"/>
    <updated>2020-03-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2020-03-09-when-css-doesnt-do-anything/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blog.logrocket.com/when-css-doesnt-do-anything/">https://blog.logrocket.com/when-css-doesnt-do-anything/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>My career at Mozilla</title>
    <link href="https://patrickbrosset.com/articles/2020-03-18-my-career-at-mozilla/"/>
    <updated>2020-03-18T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2020-03-18-my-career-at-mozilla/</id>
    <content type="html">
      
        &lt;p&gt;Over the past 6,5 years, while working at Mozilla, I was involved in a lot of different projects related to Firefox DevTools.&lt;/p&gt;
&lt;p&gt;During that time, a lot happened, and so much of it was really amazing to be a part of. I had a lot of fun, met so many wonderful people who shared my passion for web tooling.&lt;/p&gt;
&lt;h3&gt;My career in numbers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I filed more than 1100 new bugs on &lt;a href=&quot;https://bugzilla.mozilla.org/&quot;&gt;bugzilla&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I commented 15000 times over 5400 different bugs.&lt;/li&gt;
&lt;li&gt;I fixed almost 500 bugs.&lt;/li&gt;
&lt;li&gt;I reviewed about 1800 commits that landed in &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/&quot;&gt;the mozilla-central repository&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;My different roles&lt;/h3&gt;
&lt;p&gt;I started as a software engineer, moved on to become one of the leads for the Inspector part, and then the &lt;a href=&quot;https://wiki.mozilla.org/Modules/All#DevTools&quot;&gt;DevTools module owner&lt;/a&gt; (in Mozilla linguo, this means the technical decision maker for the entire DevTools codebase).
I finally ended up as an engineering manager for a few years.&lt;/p&gt;
&lt;p&gt;I have been involved in pretty much all of the phases that make up the product engineering lifecycle and I have learned so much from each of them.
I implemented new features, fixed bugs, fixed tests, worked with other engineers, ideated on new user features, ran user tets, built prototypes, communicated with designers, QA, management, product managers, and managed amazing engineers.&lt;/p&gt;
&lt;h3&gt;The things I helped build&lt;/h3&gt;
&lt;p&gt;The list below is, by no means, a list of the things I, personally, implemented. I did do some of them myself, but for a large part, they were team efforts, and I only contributed to them in some way or other (managing the team, reviewing patches, etc.).
This list is also not complete. A lot of the work that happened (and this is true for any codebase) was hidden: fixing tests, fixing bugs, cleaning things up, etc.&lt;/p&gt;
&lt;h4&gt;A bunch of smaller features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Color picker for CSS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; and CSS &lt;code&gt;background-image&lt;/code&gt; preview on hover (in Rules, Computed and DOM)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.cls&lt;/code&gt; panel in the Rules sidebar to edit classes&lt;/li&gt;
&lt;li&gt;Flashing of elements in the DOM when mutations occur&lt;/li&gt;
&lt;li&gt;Ability to highlight all elements matched by CSS selectors in the Rules sidebar&lt;/li&gt;
&lt;li&gt;Displaying whitespace text nodes when they impact layout (&lt;a href=&quot;https://patrickbrosset.com/articles/2016-10-21-when-does-white-space-matter-in-HTML.html&quot;&gt;more about this here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Highlighter for text nodes&lt;/li&gt;
&lt;li&gt;Copy images as data-uri strings&lt;/li&gt;
&lt;li&gt;Copy various css paths and xpath for elements&lt;/li&gt;
&lt;li&gt;Highlight DOM nodes from properties in the Console and Debugger panels&lt;/li&gt;
&lt;li&gt;Expand all nodes in Inspector with &lt;code&gt;alt+click&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Display non-rendered nodes differently in Inspector&lt;/li&gt;
&lt;li&gt;Make URLs clickable in Inspector&lt;/li&gt;
&lt;li&gt;Highlight the corresponding box-model region in the page on hover of Box-Model panel&lt;/li&gt;
&lt;li&gt;Button to insert new DOM nodes&lt;/li&gt;
&lt;li&gt;Paused debugger page overlay&lt;/li&gt;
&lt;li&gt;Display offset parents for positioned elements in Box-Model panel&lt;/li&gt;
&lt;li&gt;Reveal font usage on the page on hover in the Fonts sidebar&lt;/li&gt;
&lt;li&gt;New scroll badge in Inspector to find elements with overflows&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;The bigger features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;New cross-browser Compatibility sidebar to show all compat issues on the page&lt;/li&gt;
&lt;li&gt;Wysiwyg editor for positioned nodes&lt;/li&gt;
&lt;li&gt;Tooltip to edit CSS filters&lt;/li&gt;
&lt;li&gt;Tooltip to edit animation cubic-bezier timing functions&lt;/li&gt;
&lt;li&gt;Grid Inspector&lt;/li&gt;
&lt;li&gt;Flexbox Inspector&lt;/li&gt;
&lt;li&gt;Fonts Editor&lt;/li&gt;
&lt;li&gt;Shapes Path Editor&lt;/li&gt;
&lt;li&gt;CSS Changes sidebar&lt;/li&gt;
&lt;li&gt;Inactive CSS&lt;/li&gt;
&lt;li&gt;CSS Transform visualizer&lt;/li&gt;
&lt;li&gt;3-pane layout&lt;/li&gt;
&lt;li&gt;Animation Inspector&lt;/li&gt;
&lt;li&gt;First version of the Performance monitoring panel&lt;/li&gt;
&lt;li&gt;RDM &lt;meta viewport=&quot;&quot; /&gt; handling&lt;/li&gt;
&lt;li&gt;Accessibility Inspector panel&lt;/li&gt;
&lt;li&gt;Valence, a protocol adapter to debug Chrome and Safari from Firefox DevTools&lt;/li&gt;
&lt;/ul&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>3 things about CSS variables you might not know</title>
    <link href="https://patrickbrosset.com/articles/2020-09-21-3-things-about-css-variables-you-might-not-know/"/>
    <updated>2020-09-21T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2020-09-21-3-things-about-css-variables-you-might-not-know/</id>
    <content type="html">
      
        &lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/--*&quot;&gt;CSS variables&lt;/a&gt; (or custom properties, depending on how you prefer to call them) are really cool, and chances are, you&#39;re already using them in your projects.&lt;/p&gt;
&lt;p&gt;In this article, I&#39;ll talk about 3 things that I think a lot of people might now know about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what happens when a &lt;code&gt;var()&lt;/code&gt; function uses an undefined variable,&lt;/li&gt;
&lt;li&gt;how fallback values work,&lt;/li&gt;
&lt;li&gt;and how browser DevTools can help you.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Undefined variables&lt;/h3&gt;
&lt;p&gt;There are several reasons why you may be trying to use an undefined variable in a &lt;code&gt;var()&lt;/code&gt; function. We&#39;ll look at a few in a minute. But before we do that, it&#39;s important to know that when this happens, CSS falls back on its feet.&lt;br /&gt;
CSS and HTML are super resilient languages where mistakes are forgiven and one tiny error doesn&#39;t prevent the entire page from rendering.&lt;/p&gt;
&lt;p&gt;So, using an undefined variable won&#39;t lead to a parsing error, it won&#39;t prevent your stylesheet to load or parse or render. In fact, you might not even realize that something went wrong without a lot of investigation.&lt;/p&gt;
&lt;p&gt;Some of the reasons using an undefined variable might happen are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you&#39;ve simply made a typo in the name of the variable,&lt;/li&gt;
&lt;li&gt;you&#39;re trying to use a variable you thought existed, but doesn&#39;t,&lt;/li&gt;
&lt;li&gt;or you&#39;re trying to use a totally valid variable, but it happens to not be visible where you want to use it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s go over that last example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Variables participate in the cascade&lt;/strong&gt;. This means, for a variable to be available to a &lt;code&gt;var()&lt;/code&gt; function, that variable needs to be declared in a rule that also applies to the element being styled, or to one of its ancestors.&lt;/p&gt;
&lt;p&gt;For the sake of giving an example, let&#39;s look at this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;ol li&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;ul li&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course your particular case will likely be more complicated than this, with many more rules and much more complicated selectors. But what happens here is that for &lt;code&gt;ul li&lt;/code&gt; elements to have the color red, they would also need to match the rule &lt;code&gt;ol li&lt;/code&gt; where that color is actually defined. And that will probably not happen.&lt;/p&gt;
&lt;p&gt;Now, in many cases, CSS variables tend to get defined in some top-level selector like &lt;code&gt;:root&lt;/code&gt; or &lt;code&gt;html&lt;/code&gt; and are therefore available throughout the DOM tree (those selectors match ancestor elements of all other elements in the page).&lt;br /&gt;
In this case, the problem of a missing variable almost never occurs.&lt;/p&gt;
&lt;p&gt;However sometimes it&#39;s handy to declare variables in other places and when you do this, you&#39;ve got to start paying more attention to whether your variable will actually be visible (via the cascade) to where you intend to use it.&lt;/p&gt;
&lt;p&gt;So, with this out of the way, let&#39;s see how browsers deal with undefined variables:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the property is not inheritable (like &lt;code&gt;border&lt;/code&gt;), then the property is set to its initial value.&lt;/li&gt;
&lt;li&gt;If it is inheritable (like &lt;code&gt;color&lt;/code&gt;), then the property is set to its inherited value. If there isn&#39;t any, then it is set to its initial value.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&#39;s look at 2 examples to illustrate this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--main-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f06&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.my-lovely-component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--main-colo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this first example above, a typo was made in &lt;code&gt;var(-main-colo)&lt;/code&gt; and as a result the browser cannot tell what the final value for the &lt;code&gt;border&lt;/code&gt; property should be. Because the &lt;code&gt;border&lt;/code&gt; property is not inheritable in CSS, the browser finds itself in case 1 from above. It therefore sets the value to its initial state which happens to be &lt;code&gt;medium none currentColor&lt;/code&gt; (see &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/border#Formal_definition&quot;&gt;the initial value on MDN&lt;/a&gt;).&lt;br /&gt;
So, even if only the color part of the border was missing, the border will be missing entirely.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at a second example now.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--main-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f06&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.my-lovely-component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--secondary-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this one, an undefined variable was used in &lt;code&gt;.my-lovely-component&lt;/code&gt; for the value of &lt;code&gt;color&lt;/code&gt;.&lt;br /&gt;
Now, because this property is inherited, the browser will traverse the DOM up through the element&#39;s ancestors until it finds one that does define a color value. The &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element has a rule applied to it that specifies &lt;code&gt;gold&lt;/code&gt; as a value, so that&#39;s what will end up being used.&lt;/p&gt;
&lt;p&gt;In both of these examples, the hard part is figuring out what&#39;s happening exactly. In the last part of this article, I&#39;ll explain how DevTools can help, but without specific tooling here, it would be really hard to understand the problem.&lt;/p&gt;
&lt;p&gt;The source of it is that, even if those &lt;code&gt;var()&lt;/code&gt; functions use invalid properties, when the browser parses the stylesheet, it has no way of knowing this. As far as it&#39;s concerned, those border and color properties are totally valid. So we&#39;re left with wondering why the border is missing in the first example, and why the color is black in the second.&lt;/p&gt;
&lt;p&gt;Property names or values are only considered invalid by the style engine in a browser when those are not known. But since a &lt;code&gt;var()&lt;/code&gt; function can resolve to pretty much anything at all, the style engine doesn&#39;t know whether the value containing the function is known or not.&lt;br /&gt;
It will only know when the property actually gets used, at which point, it will fall back to an inherited or initial state silently, and leave you wondering what happened.&lt;/p&gt;
&lt;p&gt;Thankfully, we&#39;ll see later in this article how some new DevTools can help with this.&lt;/p&gt;
&lt;h3&gt;Fallback values and nesting&lt;/h3&gt;
&lt;p&gt;Here is another thing about CSS variables that doesn&#39;t seem to get used a lot, and therefore is probably not very well known either: &lt;code&gt;var()&lt;/code&gt; functions accept a second, optional, parameter.&lt;br /&gt;
So you can write something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--theme-color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens in this case is: if &lt;code&gt;--theme-color&lt;/code&gt; is not found by the browser, it will fall back to using the value &lt;code&gt;red&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, why would you use this? I can see a few reasons why this could be interesting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It might come in handy if you&#39;re creating a component that gets embedded into a bigger system and can be customized with variables, but you still want some safe defaults.&lt;/li&gt;
&lt;li&gt;Or maybe you&#39;re styling an element that has several states, with the default state using a &lt;code&gt;var()&lt;/code&gt; with fallback value, and the other state defining the variable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let me give an example to clarify that second case:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.my-component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--active-color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.my-component:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--active-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, while the element isn&#39;t being hovered, its color property uses an undefined variable (indeed, the &lt;code&gt;--active-color&lt;/code&gt; property does not apply to the element yet), so the browser uses the fallback value. As soon as the user hovers over the element, the second rule starts to apply, and &lt;code&gt;--active-color&lt;/code&gt; becomes defined, so the element switches to red.&lt;/p&gt;
&lt;p&gt;Of course, this is a dummy example, and you could simply have defined &lt;code&gt;color: red&lt;/code&gt; in the second rule and let it override the first one. But sometimes you need to use the variable in several properties at once.&lt;/p&gt;
&lt;p&gt;Now, let&#39;s look at a second weird thing that happens with fallback values: &lt;strong&gt;nesting &lt;code&gt;var()&lt;/code&gt; functions.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Have you ever seen something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--bar&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--baz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--are&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--you&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--crazy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is totally valid, but probably completely useless.&lt;br /&gt;
What&#39;s happening here is that the fallback value is, itself, another &lt;code&gt;var()&lt;/code&gt; function. And because it is, it (in turn) can also use a fallback value, and so on...&lt;/p&gt;
&lt;p&gt;To be honest, I don&#39;t think I&#39;ve seen &lt;code&gt;var()&lt;/code&gt; functions used as fallback values very often, if at all. Fallback values themselves are probably rarely used to begin with.&lt;br /&gt;
But at least you know this is possible, and hopefully won&#39;t be surprised if you ever see this.&lt;/p&gt;
&lt;p&gt;Let me conclude on fallback values by looking at a third aspect which I think is rarely known and may lead to confusion: using commas.&lt;/p&gt;
&lt;p&gt;A CSS variable is, basically, any text you want. Because a variable can be used anywhere a value would go, it doesn&#39;t have any strong typing at all, and therefore the only important rule is that it shouldn&#39;t contain a semicolon, since that signifies the end of the value (in reality, &lt;a href=&quot;https://drafts.csswg.org/css-syntax-3/#any-value&quot;&gt;it&#39;s more complicated than this&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;As a result, something like this is valid:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--my-variable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; one&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; two&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; three&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interesting thing here is that fallback values also follow the same rule in that they can be any text you want. So the above example could also be used as a fallback value:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; one&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; two&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; three&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though it really looks like the &lt;code&gt;one, two, three&lt;/code&gt; part is three different parameters to the &lt;code&gt;var()&lt;/code&gt; function, it&#39;s really just one. Don&#39;t get confused by that.&lt;/p&gt;
&lt;h3&gt;DevTools tips and tricks&lt;/h3&gt;
&lt;p&gt;In this last section I&#39;ll go over some of the DevTools around CSS variables that you might now know about and which should make your life easier.&lt;/p&gt;
&lt;h4&gt;Autocompleting variable names&lt;/h4&gt;
&lt;p&gt;This one is super useful when doing some quick changes in the Elements/Inspector panel and your site defines a lot of different variables:&lt;br /&gt;
DevTools will automatically suggest the list of existing variables when you start typing &lt;code&gt;var(&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/firefox-css-var-autocomplete.png&quot; alt=&quot;Screenshot of Firefox&#39;s css var autocomplete&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The above screenshot is in Firefox, but both Edge and Chrome also support this feature.&lt;/p&gt;
&lt;h4&gt;Knowing what the computed value is&lt;/h4&gt;
&lt;p&gt;When looking at a &lt;code&gt;var()&lt;/code&gt; function in DevTools, it&#39;s not necessarily easy to know what final value it computes to. The variable it uses may be lost in a really long list of rules, or it may itself point to another variable, making the hunt for the final value more difficult.&lt;/p&gt;
&lt;p&gt;You can switch to the Computed tab in DevTools to see what the final computed value of the entire property is. But if you want to know what the computed value for just that &lt;code&gt;var()&lt;/code&gt; function is, you can simply hover over it in DevTools:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/edge-computed-css-var.png&quot; alt=&quot;Screenshot of the tooltip that appears in Edge when you hover over a var() function&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The above screenshot is in Edge, but the same thing happens in Chrome and Firefox too.&lt;/p&gt;
&lt;p&gt;Interestingly, if no tooltip appears, then that&#39;s a good clue that the variable used is undefined.&lt;/p&gt;
&lt;h4&gt;Color types&lt;/h4&gt;
&lt;p&gt;A little earlier, I said that variables accepted any text at all, because when they&#39;re defined, variables don&#39;t really have a type yet.&lt;/p&gt;
&lt;p&gt;A consequence of this is that what looks like a color may actually not be used as a color.&lt;br /&gt;
Consider the following example:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--i-look-like-a-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even thought &lt;code&gt;black&lt;/code&gt; is a valid color, at this point, we can&#39;t assume that it will actually be used as one. It may end up being used in a &lt;code&gt;animation-name&lt;/code&gt; property, or somewhere else.&lt;/p&gt;
&lt;p&gt;That said, DevTools still show a little color swatch next to css variables that look like colors. That is done as a convenience, because they are very likely to actually be colors! And even if they aren&#39;t, it&#39;s not going to be a problem for users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/chrome-color-looking-vars.png&quot; alt=&quot;Screenshot of what color-looking variables look like in chrome DevTools&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Future features&lt;/h4&gt;
&lt;p&gt;Now, 2 things that, I think, would be super useful but don&#39;t exist yet is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jumping from a &lt;code&gt;var()&lt;/code&gt; function to where the variable used in it is declared,&lt;/li&gt;
&lt;li&gt;quickly seeing that a &lt;code&gt;var()&lt;/code&gt; function uses an undefined variable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As it turns out, I&#39;m working on exactly that right now in the DevTools for Chromium-based Edge and Chrome! You can check out &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1124707&quot;&gt;the bug entry&lt;/a&gt; to follow along if you want.&lt;/p&gt;
&lt;p&gt;Hopefully this article has been useful and you learnt a few things! Happy coding!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>A few web console tricks</title>
    <link href="https://patrickbrosset.com/articles/2020-12-22-a-few-web-console-tricks/"/>
    <updated>2020-12-22T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2020-12-22-a-few-web-console-tricks/</id>
    <content type="html">
      
        &lt;p&gt;The console panel in your favorite browser&#39;s DevTools can be a very powerful ally when troubleshooting problems in a web app.
Log data, send requests, manipulate DOM elements, you name it, the console can do it all!&lt;/p&gt;
&lt;p&gt;There are so many ways to get interesting information out of the console, and I know I use only a few of them. So I asked around for what made people more productive in this tool, and I&#39;m sharing the results with everyone here.&lt;/p&gt;
&lt;p&gt;Note that there are many resources online already about tips and tricks with devtools, but I hope this one will prove useful to you because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rather than being a complete and overwhelming reference to all the features, it&#39;s a small subset of the things some people agreed were the most useful to them, and&lt;/li&gt;
&lt;li&gt;it&#39;s not just a list of the built-in commands, but also some code snippets that you may find useful.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;$ and friends&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;$&lt;/code&gt; and its friends are probably the most used tricks in the console. It pays off to know some of them as they can really help you get more out the tools.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$()&lt;/code&gt; is a function that&#39;s essentially a shortcut to the longer &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector&quot;&gt;&lt;code&gt;document.querySelector()&lt;/code&gt;&lt;/a&gt; native function. It&#39;s nice and short, and for those of you who worked with jQuery in the past, it will feel familiar.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$$()&lt;/code&gt; acts as a shortcut to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll&quot;&gt;&lt;code&gt;document.querySelectorAll()&lt;/code&gt;&lt;/a&gt; but returns an array of nodes rather than a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/NodeList&quot;&gt;NodeList&lt;/a&gt; which means you can use things like &lt;code&gt;map()&lt;/code&gt; or &lt;code&gt;filter()&lt;/code&gt; right away on the returned list of elements.&lt;/p&gt;
&lt;p&gt;I often use it to retrieve all elements on the current page and then filter it depending on what I&#39;m looking for:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, you&#39;d end up with the list of all of the elements on the page that are Flexbox containers. Pretty handy right!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$0&lt;/code&gt; is a super useful reference to the element that&#39;s currently selected in the Elements panel (in Edge or Chrome) or Inspector panel (in Firefox). If you want to know things about the element that aren&#39;t available to you in one of the other tools, then &lt;code&gt;$0&lt;/code&gt; is your friend.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$_&lt;/code&gt; is a cool shortcut that accesses the last result that got printed out in the console.&lt;/p&gt;
&lt;h3&gt;Make HTTP requests with fetch&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;The &lt;code&gt;fetch&lt;/code&gt; API&lt;/a&gt; (which is basically &lt;code&gt;XMLHTTPRequest&lt;/code&gt; but easier to use) makes it really easy to go and get resources from the network.
What&#39;s really nice is that the console (both in Firefox and Chromium-based browsers) now supports top-level await statements, which means you can write something like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://api.github.com/repos/microsoft/vscode/issues?state=all&amp;amp;per_page=100&amp;amp;page=1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you&#39;ll have the parsed JSON from that github API call straight in your console!&lt;/p&gt;
&lt;p&gt;Normally, you&#39;d have to wrap the &lt;code&gt;await&lt;/code&gt; statement in an &lt;code&gt;async&lt;/code&gt; function, but for convenience, you don&#39;t have to do here in the console.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/console-fetch.png&quot; alt=&quot;Screenshot of chrome devtools console panel showing the result of the fetch call&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Format data like a pro&lt;/h3&gt;
&lt;p&gt;Dealing with lot&#39;s of data on the console isn&#39;t particularly easy. DevTools does a nice job at formatting objects, arrays, strings, numbers, etc. in useful ways, but sometimes you need more.
And for those times, &lt;code&gt;console.table&lt;/code&gt; might be what you need.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;console.log(array)&lt;/code&gt; displays the provided array as a sortable table, expanding properties of items on multiple columns if possible.&lt;/p&gt;
&lt;p&gt;If we take the &lt;code&gt;fetch&lt;/code&gt; example from above, the call returns 100 objects, in an array, each with a lot of properties.
But let&#39;s imagine you&#39;re only interested in a few properties (the number, title and state). The &lt;code&gt;console.table&lt;/code&gt; function accepts a handy second argument that allows you to list which properties of the items you want to see in the resulting table:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://api.github.com/repos/microsoft/vscode/issues?state=all&amp;amp;per_page=100&amp;amp;page=1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;number&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;title&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;state&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/console-filtered-table.png&quot; alt=&quot;Screenshot of the console panel in Firefox showing the result of the previous commands: a table with the 3 columns for number, title and state properties&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Copy things&lt;/h3&gt;
&lt;p&gt;This is probably one of the lesser known console tricks. Sometimes you need to copy the result of something you just did in the console. Maybe you need to send it to someone, or paste it in a text editor.
You can, of course, use your mouse to select text and then hit ctrl/cmd+C to copy the selection, but there&#39;s also a built-in function that might make your life easier: &lt;code&gt;copy()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This function copies whatever you pass to it to your clipboard, as a string.&lt;/p&gt;
&lt;p&gt;For me, it comes in very handy when I spent time listing elements or objects in the console, and massaging the data to find what I&#39;m interested in.&lt;/p&gt;
&lt;p&gt;Let&#39;s assume I was looking for all flexbox containers on the page:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then wanted to list all of the classes that these elements have:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Get all individual classes from classList&lt;/span&gt;
$_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Reduce the list to unique classes only using a Set&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$_&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and now I want to copy the result out to paste it in some code I&#39;m working on:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$_&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At that point, my clipboard will contain the following content:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;_23tra1HsiiP6cT-Cka-ycB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;c-uhfh-gcontainer-st&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;c-uhfh-actions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;mectrl_header&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;m-page-bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;pagebar-module&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;c-sequence-indicator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;c-group&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;drawerheader&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;c-glyph&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;glyph-chevron-right&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;cli_default_focus&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;tileproductplacement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;mini&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;f-sticky&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Debug JavaScript functions with &lt;code&gt;debug()&lt;/code&gt; (⚠ only in Edge and Chrome)&lt;/h3&gt;
&lt;p&gt;Sometimes you need a little more help when troubleshooting an issue, and need to step into the code to inspect what&#39;s going wrong.
Logging data from the code using &lt;code&gt;console.log&lt;/code&gt; is often enough, but sometimes using a debugger is requires.&lt;/p&gt;
&lt;p&gt;If you&#39;re a heavy console user and spend most of your debugging time there, you might enjoy the &lt;code&gt;debug()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;If the function you are trying to debug is accessible from the console, then you can just pass a reference to it to the &lt;code&gt;debug&lt;/code&gt; helper, like &lt;code&gt;debug(path.to.my.Function)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next time your function gets executed, the JavaScript debugger will pause its execution and DevTools will switch to the Sources panel.&lt;/p&gt;
&lt;h3&gt;Advanced screenshot features with &lt;code&gt;:screenshot&lt;/code&gt; (⚠ only in Firefox)&lt;/h3&gt;
&lt;p&gt;Nowadays there are many ways to take screenshots of the viewport, the full page, or a DOM node. But none of these methods really compare to the advanced features the &lt;code&gt;:screenshot&lt;/code&gt; built-in command give you in Firefox.&lt;/p&gt;
&lt;p&gt;You can simply type &lt;code&gt;:screenshot&lt;/code&gt; in the console and it will go ahead and grab a screenshot of the page with the default configuration.
If you have a specific need you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;either take a full page screnshot with &lt;code&gt;--fullpage&lt;/code&gt; or just an element with &lt;code&gt;--selector&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;either save to a file and specify the name with &lt;code&gt;--filename&lt;/code&gt; or copy the image to the clipboard with &lt;code&gt;--clipboard&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;grab the screenshot only after some amount of time with &lt;code&gt;--delay&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;set the image resolution with &lt;code&gt;--dpr&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can read the full &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Taking_screenshots#Taking_screenshots_with_the_web_console&quot;&gt;documentation on MDN&lt;/a&gt; or learn more from &lt;a href=&quot;https://twitter.com/meyerweb&quot;&gt;Eric Meyer&lt;/a&gt;&#39;s &lt;a href=&quot;https://meyerweb.com/eric/thoughts/2018/08/24/firefoxs-screenshot-command-2018/&quot;&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Know when data gets evaluated&lt;/h3&gt;
&lt;p&gt;This one isn&#39;t a trick but more a gotcha that may save you a lot of time lost if you don&#39;t know about it.&lt;/p&gt;
&lt;p&gt;The gist of it is that when you expand a property of an object in the console (by clicking on the little triangle next to it), that property gets evaluated &lt;strong&gt;when you click&lt;/strong&gt;. As a result, what you are going to see then might not match what the value of that property was at the time the parent object was logged.&lt;/p&gt;
&lt;p&gt;Let&#39;s go through an example to understand what that means.&lt;/p&gt;
&lt;p&gt;Let&#39;s assume you have a function that deals with some object, and you need to log that object out at some point to check what the properties are:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the function gets executed, you&#39;ll see a nice little log in your console showing you a preview of that object.&lt;/p&gt;
&lt;p&gt;Now, if, later on in the execution of your program that same object gets updated like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And let&#39;s assume you click the object in the console to expand its properties only at that point, you&#39;ll be seeing something that might surprised you:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/console-evaluate-property.png&quot; alt=&quot;Screenshot of the console panel in Edge DevTools showing that when the property is expanded, its value is 43, even if it used to be 42 when logged&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The reason is the properties of that object got read 2 times by the console. First when the object was logged, and the console printed the preview of the object. And secondly when you clicked on the expander icon. Knowing that the value of the &lt;code&gt;foo&lt;/code&gt; property changed between these 2 points in time, you get this weird result.&lt;/p&gt;
&lt;p&gt;If this becomes a problem for you, one solution is to create a copy of the object when logging it, like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;obj &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when you click, even if the original &lt;code&gt;obj&lt;/code&gt; object was changed, what you&#39;re expanding is only a copy of that object that hasn&#39;t been modified since it was logged.&lt;/p&gt;
&lt;h3&gt;Write on multiple lines&lt;/h3&gt;
&lt;p&gt;If you&#39;re feeling adventurous, you might want to write longer pieces of code in the console to execute more complex code. There are a few ways that will make your life easier if so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;shift+Enter&lt;/code&gt; to create a new line without executing the expression.&lt;/li&gt;
&lt;li&gt;Use Firefox&#39;s multiline editor:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/console-multiline-firefox.png&quot; alt=&quot;Screenshot of the Firefox console panel showing where the multiline icon is located&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create code snippets in Edge or Chrome&#39;s Sources panel. The added advantage of this technique is that code snippets are saved on your disk, and therefore can be used even after you&#39;ve restarted the browser:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/snippets-edge.png&quot; alt=&quot;Screenshot of Edge DevTools showing the Sources panel, and within it the Snippets pane that allows creating new code snippets and executing them&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Access your history&lt;/h3&gt;
&lt;p&gt;This last trick really made a huge difference for me a while back.
I often use similar pieces of code in the console over and over again, and typing these commands every time is getting tedious.&lt;/p&gt;
&lt;p&gt;Thankfully, the console panel maintains a history and you can access it. Here&#39;s how:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The simplest but also most limiting way is pressing the &lt;code&gt;Up&lt;/code&gt; arrow on your keyboard. This will cycle through all of the latest commands you&#39;ve entered, one by one, and you can stop pressing when you found the one you were looking for. Simple, fast, and very useful for repeating things over and over again. However if you&#39;re looking for something you used a long time ago, this method will prove very time consuming.&lt;/li&gt;
&lt;li&gt;If you know (at least part of) what you&#39;re looking for, just start typing it in Chrome or Edge and the autocomplete suggestion box that appears will contain all of the past commands you typed that match:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/console-history-autocomplete.png&quot; alt=&quot;Screenshot of Edge DevTools showing the autocomplete suggestion list in the console panel that contains past commands matching what you typed&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firefox also has a useful &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Web_Console/The_command_line_interpreter#Execution_history&quot;&gt;reverse history search&lt;/a&gt; that mimicks Bash&#39;s &lt;code&gt;ctrl+R&lt;/code&gt; reverse search. Pressing &lt;code&gt;F9&lt;/code&gt; on Windows/Linux or &lt;code&gt;ctrl+R&lt;/code&gt; on Mac lets you type text and displays the matching results based on that.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;That&#39;s it for now! I hope these were useful to you. If you know other tricks, I&#39;d be happy to edit this article to include them. You can find me on &lt;a href=&quot;https://twitter.com/patrickbrosset&quot;&gt;twitter&lt;/a&gt;.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>4 Weird Tricks To Become A 10x Flexbox Engineer</title>
    <link href="https://patrickbrosset.com/articles/2021-02-02-4-weird-tricks-to-become-a-10x-flexbox-engineer/"/>
    <updated>2021-02-02T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-02-02-4-weird-tricks-to-become-a-10x-flexbox-engineer/</id>
    <content type="html">
      
        &lt;p&gt;Do you ever find yourself trying stuff out until they work? Did you make &lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;&gt;css-tricks’ guide to flexbox &lt;/a&gt; your default new-tab page (or even better, &lt;a href=&quot;https://css-tricks.com/product/css-flexbox-poster/&quot;&gt;purchase the poster&lt;/a&gt;)?&lt;/p&gt;
&lt;p&gt;If so, these tricks are for you!&lt;/p&gt;
&lt;p&gt;Maybe you’re not going to become a &lt;a href=&quot;https://twitter.com/mike_conley/status/1149845391153737728&quot;&gt;10x engineer&lt;/a&gt; like I promised. I do promise, however, that you’ll get more confident when authoring and debugging flexbox layouts in DevTools.&lt;/p&gt;
&lt;h3&gt;Authoring vs. Debugging&lt;/h3&gt;
&lt;p&gt;I believe DevTools sits in a unique position between our authoring and debugging workflows.&lt;/p&gt;
&lt;p&gt;Traditionally, we write our code in text editors and then debug problems in DevTools. But with CSS more than with anything else, the line between these 2 steps in our dev cycle is a little blurry.&lt;br /&gt;
Sometimes we find ourselves writing lot’s of CSS code in DevTools and only then copying it back into our editors.&lt;br /&gt;
When it comes to JavaScript, or network calls, or even HTML structure, most of what we do in DevTools is debugging. But with CSS things are different.&lt;/p&gt;
&lt;p&gt;The reason for this is when we edit CSS in the browser, something magical happens: debugging information appears right when we need them! Colors and sizes change in real time and computed values can be accessed as we type new properties.&lt;/p&gt;
&lt;p&gt;This is something text editors have trouble doing. They act at the source file level, as they should. They don’t (and often can’t) know where the particular piece of code we’re writing will end up running.&lt;br /&gt;
But in DevTools, the piece of code we write &lt;strong&gt;is&lt;/strong&gt; running at the same time. No compilation and download needed, we can re-write an entire piece of code, with logic and variables and see it take effect live.&lt;/p&gt;
&lt;p&gt;So, even if DevTools will likely never become a full blown text editor, that supports saving, running extensions, with keyboard shortcuts and everything, it does something that no text editor can thanks to its ability to provide runtime debugging information.&lt;/p&gt;
&lt;p&gt;That’s why tooling for the more complicated scenarios involving CSS is very interesting, it feels like &lt;a href=&quot;https://vimeo.com/36579366&quot;&gt;Bret Victor’s &amp;quot;Inventing on Principles&amp;quot;&lt;/a&gt; where you write code, run it and debug it all at the same time and there’s no mode you have to be into to do these things.&lt;/p&gt;
&lt;p&gt;With that in mind, let’s look at the usual problems people tend to have with flexbox: it’s hard to remember what properties do and we often have to try them all until the desired effect is reached.&lt;/p&gt;
&lt;p&gt;Now, DevTools has access to the runtime environment, it knows which element we are working with, and the type of flexbox layout we’ve defined, as well as other things like text direction. Because of this it knows what the various flex properties will do when we use them. That means we can build tools that guide you while writing CSS, thanks to the runtime information available, with the goal to eliminate the trial and error process usually involved.&lt;/p&gt;
&lt;p&gt;So, at the end of 2020, after we had shipped &lt;a href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/css/grid&quot;&gt;new css grid tooling in chromium&lt;/a&gt;, we formed again our small crew of designers, PMs and engineers, across Google and Microsoft, and set out to create a first series of tools for flexbox.&lt;/p&gt;
&lt;p&gt;These features are now available in Edge 89 (which is, at the time of writing, available on &lt;a href=&quot;https://www.microsoftedgeinsider.com/en-us/download&quot;&gt;the Dev channel&lt;/a&gt;), and in Chrome 89.&lt;/p&gt;
&lt;h3&gt;Turning on the features&lt;/h3&gt;
&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/experiment.png&quot; alt=&quot;Screenshot showing how to turn on the experiment in the settings panel&quot; style=&quot;float:right;width:50%;margin-left:1rem;&quot; /&gt;
&lt;p&gt;For now, the features are still hidden by default since this is the first version and we wanted to slowly roll them out. They’ll soon be enabled by default, and when they are, we’ll be listening to feedback to address issues and work on an even better v2.&lt;/p&gt;
&lt;p&gt;So, first things first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open DevTools&lt;/li&gt;
&lt;li&gt;Click the cog icon to open the settings&lt;/li&gt;
&lt;li&gt;Click on the Experiments tab&lt;/li&gt;
&lt;li&gt;Check the &lt;em&gt;&amp;quot;Enable new CSS Flexbox debugging features&amp;quot;&lt;/em&gt; box&lt;/li&gt;
&lt;li&gt;Reload DevTools (there should be a button at the top letting you do that, but you could also just press F12 twice)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Trick 1 - Overlay&lt;/h3&gt;
&lt;p&gt;This might be the first one you see after you’ve enabled the feature. The easiest way to experience it is by clicking on the &amp;quot;Select Element&amp;quot; button in the toolbar and then moving your mouse over the page.&lt;br /&gt;
As soon as the element below the mouse is a flexbox container, the usual blue/green/purple highlight that appears on most elements will look a bit different.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/inspect-element-overlay.png&quot; alt=&quot;The flexbox overlay that appears on elements that are flexbox containers&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We call this thing the overlay, and for flexbox containers, it shows a few more things than for other elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it draws a dashed border around the flexbox container itself,&lt;/li&gt;
&lt;li&gt;it draws a border around each flex line (if there is wrapping with &lt;code&gt;flex-wrap:wrap&lt;/code&gt;) and around each flex item, so it’s easy to see what’s an item and what isn’t (indeed, there are cases where it’s not obvious when e.g. absolute positioning is involved, or text nodes are present),&lt;/li&gt;
&lt;li&gt;it displays a hatching pattern in the empty space caused by the various alignment properties you use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will also see the overlay as you move your mouse over elements inside the Elements panel.&lt;/p&gt;
&lt;p&gt;It’s convenient because it’s right there when you need it and already gives you quite a number of interesting layout visual debugging information.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Trick 2 - Alignment icons&lt;/h3&gt;
&lt;p&gt;You can experience this new feature when editing CSS in the Styles pane of DevTools.&lt;/p&gt;
&lt;p&gt;After you’ve put together a layout in your text editor and realize there’s a bug when you test it in the browser, that’s usually when you start messing around with the various properties available to you.&lt;br /&gt;
At this point, trial and error is usually required until you figure out the right property and value that you need.&lt;/p&gt;
&lt;p&gt;With this new feature, you will get a little help right there. As you start typing property names in the Styles panel, you will notice icons in the autocomplete popup to tell you exactly what to expect from the various values.&lt;/p&gt;
&lt;p&gt;These icons are very convenient because they make it immediately obvious which direction a CSS property apply in.&lt;/p&gt;
&lt;div style=&quot;display:grid;grid-template-columns:repeat(2, 1fr);gap:1rem&quot;&gt;
&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/justify-content-icons.gif&quot; alt=&quot;Screenshot of the various icons displayed in the autocomplete popup for the justify-content property&quot; /&gt;
&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/align-items-icons.gif&quot; alt=&quot;Screenshot of the various icons displayed in the autocomplete popup for the align-items property&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;This works with a number of properties like &lt;code&gt;align-items&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;, &lt;code&gt;align-content&lt;/code&gt;, &lt;code&gt;align-self&lt;/code&gt; or &lt;code&gt;flex-direction&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s also worth noting that these icons will always be correct even if your layout is in column direction rather than row, and even if you were to use a vertical &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode&quot;&gt;writing-mode&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Trick 3 - CSS properties highlight&lt;/h3&gt;
&lt;p&gt;Another feature we added that goes hand in hand with the previous one is the ability to highlight parts of the page that result of certain flexbox CSS properties.&lt;/p&gt;
&lt;p&gt;The situation is the following one: you’re looking at a website and notice a problem with the layout. You open DevTools on the element that has the problem and a number of CSS rules and properties are displayed, but you’re not sure which one is responsible for the problem.&lt;/p&gt;
&lt;p&gt;Instead of changing the value of these properties at random, you can now just move your mouse over the properties and you will see parts of the page starting to light up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/hover-properties.gif&quot; alt=&quot;Gif animation showing how hovering over properties in the Styles pane highlight the corresponding parts of the page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For example, as seen above, if you hover over the &lt;code&gt;justify-content&lt;/code&gt; property, the areas around and in between flex items (along the main axis of the flexbox container) will get highlighted in the page. It makes it easy to see the way space is distributed between items, and exactly what &lt;code&gt;justify-content&lt;/code&gt; influences.&lt;/p&gt;
&lt;p&gt;Hovering over &lt;code&gt;align-items&lt;/code&gt; draws a line with an arrow pointing in the direction where the flex items are aligned along the cross axis.&lt;br /&gt;
So when you can’t remember what direction &lt;code&gt;align-items&lt;/code&gt; works in, just hover over it and you’ll see.&lt;/p&gt;
&lt;p&gt;What’s nice about this too is it works regardless of the direction of your flex layout or your text direction.&lt;/p&gt;
&lt;p&gt;This works with other properties too, like &lt;code&gt;align-content&lt;/code&gt; or &lt;code&gt;gap&lt;/code&gt; as demonstrated below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/hover-properties-2.gif&quot; alt=&quot;Another gif animation showing the effect of hovering over properties in the Styles pane&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Trick 4 - Badges and Layout panel&lt;/h3&gt;
&lt;p&gt;My final trick for you today is something that already exists for CSS Grid and now also works for Flexbox.&lt;br /&gt;
It is the ability to quickly find elements in the page that define flebox layouts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/flex-badges.png&quot; alt=&quot;Screenshot of the Elements panel that shows certain elements have a grid badge, and others a flex badge&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As pictured above, the Elements panel in DevTools now displays little flex badges next to elements that are flexbox containers. I find it very useful when navigating the DOM tree, it avoids having to click on each node and looking through the Styles pane looking for a &lt;code&gt;display:flex&lt;/code&gt; declaration.&lt;/p&gt;
&lt;p&gt;If you’re looking for a specific element but don’t know or remember where it is in the DOM, instead of expanding the tree, it might be quicker to head over to the (recently added) Layout panel.&lt;br /&gt;
This panel gives you the entire list of all flex and grid layouts in the page, in one place:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/layout-panel.png&quot; alt=&quot;Screenshot of the Layout panel showing the Flexbox section which lists all flexbox containers on the page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In addition, both the badges and Layout panel give you a way to toggle a &lt;strong&gt;persistent&lt;/strong&gt; overlay. It’s basically a version of the overlay you see on hover when selecting element that persists on the page while you do other things.&lt;br /&gt;
You can show overlays for as many flexbox layouts as you want.&lt;/p&gt;
&lt;p&gt;It makes it easy to debug the position or alignment of flexbox layouts while being able to see the boundaries of your containers and items as you mess with CSS properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flex-tools-article/persistent.gif&quot; alt=&quot;GIF animation of DevTools and the page with 2 flexbox layouts highlighted with persistent overlays and making changes to CSS styles at the same time&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;That’s it for now! I hope you enjoy those new features, and please do let me know on Twitter if you have feedback about them.&lt;br /&gt;
There’s more to come in this area too, so stay tuned for even more helpful features.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>How we built the DevTools Tooltips</title>
    <link href="https://patrickbrosset.com/articles/2021-03-08-how-we-built-the-devtools-tooltips/"/>
    <updated>2021-03-08T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-03-08-how-we-built-the-devtools-tooltips/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://medium.com/web-on-the-edge/how-we-built-the-devtools-tooltips-4e9933abbd8a">https://medium.com/web-on-the-edge/how-we-built-the-devtools-tooltips-4e9933abbd8a</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>A Year at Microsoft</title>
    <link href="https://patrickbrosset.com/articles/2021-03-22-a-year-at-microsoft/"/>
    <updated>2021-03-22T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-03-22-a-year-at-microsoft/</id>
    <content type="html">
      
        &lt;p&gt;I&#39;m almost coming up to a year at Microsoft, and I thought now would be a good time to look back and talk about it a little.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(spoiler alert: things are great!)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;How it all started&lt;/h3&gt;
&lt;p&gt;2020 was a very difficult year for a lot of people, but it also was the year when Mozilla had to layoff a lot of employees.&lt;/p&gt;
&lt;p&gt;At the end of 2019, I had been at Mozilla for 6 years and I realized I was ready for my next job. I was looking to switch teams inside Mozilla, but also looking around for other jobs. So, in January of 2020, when &lt;a href=&quot;https://blog.mozilla.org/blog/2020/01/15/readying-for-the-future-at-mozilla/&quot;&gt;the first wave of layoffs&lt;/a&gt; was announced, it made me double down on my effort to switch.&lt;/p&gt;
&lt;p&gt;I remember our All Hands meeting in Berlin in January 2020 as a very sad one. It took place a couple of weeks after we all found out about the layoffs.&lt;br /&gt;
Of course it was, as always, very nice to see everybody face to face and spend some quality team time, but it was also sad to have to say goodbye to colleagues and friends, and several of us (including myself) didn&#39;t yet know whether they would be part of the layoffs. Indeed, France has a very complicated and slow process in this case, and us Mozilla France employees didn&#39;t know if our roles were going to be eliminated or not at that point, and not for another month.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.mozilla.org/careers/files/2020/03/20200130-BerlinAllHands-Remotee-Group-1400x770.jpg&quot; alt=&quot;Group photo of Mozilla remotees during the Berlin AllHands in January 2020&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Before knowing if I was going to be part of the impacted people, I had already contacted a lot of different companies and was interviewing in various places. One of which being Microsoft!&lt;/p&gt;
&lt;p&gt;Two pretty wild things happened.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, I learned just one week after I interviewed at Microsoft that, yes, I was going to be part of the Mozilla layoffs 😢. Thankfully, I already knew the interviews with Microsoft had gone well, and I was going to be getting an offer. But this was cutting it pretty close!&lt;/li&gt;
&lt;li&gt;Secondly, I flew to Seattle for the interviews literally days before the covid-19 outbreaks started in several places, including Seattle! Just a few days later, and I wouldn&#39;t have been able to fly at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Anyway, I got the job 🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/ie-neon-sign.jpg&quot; alt=&quot;Photo of the Internet Explorer neon sign in building 113 of the Microsoft Redmond campus&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Out of all the jobs I was interviewing for, this was by far my number 1 choice, so I couldn&#39;t be happier. I was going to join &lt;a href=&quot;https://twitter.com/edgedevtools&quot;&gt;the Microsoft Edge Developer Tools team&lt;/a&gt; which meant I could continue to use my knowledge in the field of browsers and web dev tools, and do this at a company that I respected very much.&lt;/p&gt;
&lt;h3&gt;The first few days&lt;/h3&gt;
&lt;p&gt;I&#39;m not going to lie, the first week or so was a wild ride.&lt;/p&gt;
&lt;p&gt;First of all, I didn&#39;t get a laptop from Microsoft until the second week, so I had to connect to the corporate network from my personal computer which didn&#39;t get me access to everything. Secondly, all of my team, including my manager, was in Redmond, USA (and still is) while I live in France. That means I didn&#39;t have anybody to ask for help during my working hours.&lt;/p&gt;
&lt;p&gt;You can imagine how stressful this was at the beginning when all I was trying to do was be productive. I know I wasn&#39;t expected to perform from day one, people were fine with me needing time and there was a lot of training for me to do anyway. I guess it&#39;s compulsive, when you start a new job, you want to live up to the expectation you think people have of you as soon possible.&lt;/p&gt;
&lt;p&gt;I am thankful to my colleagues and managers who took the time to chat with me, be friendly and teach me all sorts of internal knownledge I needed. If you&#39;re welcoming a new person on your team, take the time to answer any questions they have, even if you have a lot of work, this is the most important thing you can be doing for them right now.&lt;/p&gt;
&lt;p&gt;Knowing that I wasn&#39;t going to be meeting people for real any time soon, I spent time introducing myself personally to each and everyone on the team, one by one. I learned a lot about people doing so, we would share stories of what we&#39;d been working on before, etc. and this helped a lot feeling like I started knowing them a little better.&lt;/p&gt;
&lt;p&gt;I also made it a point to have several one-on-one meetings per week. First of all with my manager of course, but also with other colleagues. I still do this today, and I really enjoy being able to see people every once in a while.&lt;/p&gt;
&lt;p&gt;To this day, I still haven&#39;t seen anybody from my team for real! Ok, I did see my manager, and her manager, when I interviewed, but I never once met the other engineers on the team for real. What&#39;s more, people here often face mute during our daily &lt;a href=&quot;https://www.microsoft.com/microsoft-teams/group-chat-software&quot;&gt;Teams&lt;/a&gt; calls, so there are a few people who I&#39;ve literally never seen.&lt;/p&gt;
&lt;p&gt;Here&#39;s what I usually see when I&#39;m on a call, initials or avatars, and sometimes a couple of people who have their cameras on:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/face-muted-team.png&quot; alt=&quot;Screenshot of a Teams call with everyone face muted&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that I totally understand why someone would face mute and I also need to do it from time to time. It&#39;s just that in my situation I lack the human element a little bit.&lt;/p&gt;
&lt;h3&gt;Things I&#39;ve done&lt;/h3&gt;
&lt;p&gt;After settling in the new job, I quickly became productive. I was assigned to a project that I really liked, with people who helped me learn the environment and become comfortable with the day to day work.&lt;/p&gt;
&lt;p&gt;My background working on DevTools at Mozilla became immediately useful to me as the things I worked on now were very similar, and I had expertise in this domain that proved useful.&lt;/p&gt;
&lt;p&gt;I couldn&#39;t be happier to be working on CSS Grid tooling again, but this time for Chromium. This was in collaboration with the Google team, which was exciting. I could use my CSS layout knowledge, my experience from having built something similar in Firefox, and work with a super smart team of PMs, DevRels, designers and engineers across 2 companies.&lt;/p&gt;
&lt;p&gt;Since then I&#39;ve participated in several projects. Here&#39;s a quick list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/css/grid&quot;&gt;CSS Grid tooling&lt;/a&gt;: new overlay to see rows, columns, gaps and areas and a new Layout panel.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/whats-new/2021/01/devtools#css-flexbox-debugging-tools&quot;&gt;Flexbox tooling&lt;/a&gt;: same as above, but for flexbox, and we also added abunch of Styles pane features to make sense of the various flexbox properties (learn more in &lt;a href=&quot;https://patrickbrosset.com/articles/2021-02-02-4-Weird-Tricks-To-Become-A-10x-Flexbox-Engineer&quot;&gt;this other article I wrote&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/whats-new/2020/11/devtools#css-variable-definitions-in-styles-pane&quot;&gt;Jump to CSS variables&lt;/a&gt;: a way to jump from a CSS variable usage to where it&#39;s defined in the Styles pane.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/DevTools/FocusMode/explainer.md&quot;&gt;Focus Mode and DevTools Tooltips&lt;/a&gt;: a new UI for Edge DevTools, to group similar tools together in presets, and a help mode that gives documentation about the tools, directly within the tools (if you&#39;re curious, I wrote about &lt;a href=&quot;https://medium.com/web-on-the-edge/how-we-built-the-devtools-tooltips-4e9933abbd8a&quot;&gt;how we made the tooltips&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Fixed a bunch of bugs in the CDP &lt;a href=&quot;https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-startScreencast&quot;&gt;screencast&lt;/a&gt; methods to make size and colors more predictable.&lt;/li&gt;
&lt;li&gt;And fixed &lt;a href=&quot;https://github.com/ChromeDevTools/devtools-frontend/commits?author=captainbrosset&quot;&gt;many different bugs&lt;/a&gt; across DevTools.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Going back to coding&lt;/h3&gt;
&lt;p&gt;Prior to joining Microsoft, I had been a manager for 5 years. In fact, I have been going back and forth between management and coding a couple of times already, even before Mozilla.&lt;/p&gt;
&lt;p&gt;Even if I originally wanted a management role at Microsoft, there wasn&#39;t one for me to fill at the time. Microsoft is quickly re-inventing itself when it comes to working remotely and across timezones, but it isn&#39;t ready yet to have managers who are remote from their teams. So I came in as a senior software engineer, and that&#39;s great. I far prefer knowing what it&#39;s like to be coding in this environment before becoming a manager again maybe one day.&lt;/p&gt;
&lt;p&gt;Overall going back to coding hasn&#39;t been as hard as I expected it to be. I&#39;ve needed to flex some muscles that had atrophied a little bit lately, but I knew JavaScript and CSS so that part came back quickly. &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; was new to me, but I immediately liked the comfort that gave me while working in &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt;, and it is sufficiently similar to JavaScript that it was simple to make sense of it.&lt;/p&gt;
&lt;p&gt;The hardest part was C++. I had done a little bit at Mozilla, but C++ has so many features that it&#39;s sometimes really hard to read. I had to step up my game quite significantly since the entire DevTools backend was written in this language. I had no choice but to dive in. Thankfully, browsers are such huge codebases that whatever you want to do has probably already been done somewhere else in there, so it&#39;s usually a matter of finding the code you need and adapt it (and for this &lt;a href=&quot;https://source.chromium.org/&quot;&gt;the chromium code search website&lt;/a&gt; is invaluable).&lt;/p&gt;
&lt;p&gt;Now, after almost 1 year coding again, I have to say I still really enjoy it but I also know that this is not the end game for me. I love working on the product side of things, writing articles, communicating with users, planning work, defining strategies, managing people and projects, and more. There are many aspects of the software development world that attract me greatly on top of the pure technical part.&lt;/p&gt;
&lt;h3&gt;The pros and cons&lt;/h3&gt;
&lt;p&gt;Let&#39;s wrap this up with a simple list of pros and cons related to this year at Microsoft.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Take this with a grain of salt: this is based on my own experience and may vary for you if you&#39;re starting at or looking to join Microsoft.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;The good stuff&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Customer focus&lt;/em&gt;:&lt;br /&gt;
I don&#39;t think I&#39;ve ever worked at a company that is more customer-focused than Microsoft. It is very reassuring to know you&#39;re working on the right features for your end users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;No after thoughts&lt;/em&gt;:&lt;br /&gt;
Microsoft takes things like accessibility super seriously. A lot of places don&#39;t really think of this as part of the normal coding work and often forget about i, or just do it as a best-effort-when-we-have-time kind of basis. It&#39;s not the case here, we&#39;ve got processes and tools in place to track this and work on it along with the other &amp;quot;normal&amp;quot; coding tasks, and you just don&#39;t get to ship without it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Investment in DevTools&lt;/em&gt;:&lt;br /&gt;
Edge invests in its DevTools, it isn&#39;t just a thing we need to do because other browsers have it too. We&#39;re based on Chromium, so we could very well not do anything. But we do invest in it. We have our own strategy for it and dedicate a fair level of investment for it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;PM-driven&lt;/em&gt;:&lt;br /&gt;
The work we do is driven by our product managers. We can count on them to give us the right priorities and requirements. At the same time, we also have flexibility as engineers. We can dedicate a bit of our time to work on side projects, we can come up with prototypes and propose new features and we have opportunities to learn. Our PMs and managers are also very receptive of us spending time on paying off technical debt, so we got secured time for this as well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Google&lt;/em&gt;:&lt;br /&gt;
Collaborating with the Chrome DevTools team (since we both work on Chromium) has been great. It&#39;s exciting to make the tools better for many more people than just the ones using Edge.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Evolving&lt;/em&gt;:&lt;br /&gt;
Microsoft is constantly evolving. Even though it&#39;s huge and old, it moves really fast. I think we get the best of both worlds: we&#39;re allowed to make changes to how we work and be more agile, but we also have access to all of the services we need by virtue of being part of such a huge company.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;The not-so-good stuff&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Timezones&lt;/em&gt;:&lt;br /&gt;
I&#39;m used to working remotely and with a geographically distributed team, but this is different. Now (almost) all my colleagues are in Redmond so their days start when mine ends. I can&#39;t really shift my working hours because I&#39;ve got family stuff I want to do. Being shifted from my team means that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Emails, messages, notifications, etc. start getting super intense right when I&#39;m supposed to be winding down and helping the kids or cooking dinner. It makes it hard to be in the moment with my loved ones, hard to disconnect.&lt;/li&gt;
&lt;li&gt;In the morning, when I get back to work, I have a ton of email and chat messages to sort through. Sometimes it can be hard to even start when you see this, you feel sort of blocked, like you suddenly have so many different things to do at once (my coping mechanism for this is going through them slowly, not all at once, to avoid forgetting about things or feeling too stressed out).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Seeing people&lt;/em&gt;:&lt;br /&gt;
I still haven&#39;t seen anybody from my team in real life! This has nothing to do with Microsoft. I&#39;m sure that, barring covid-19, I&#39;d already have gone to visit them a few times. I can&#39;t wait for people to be vaccinated and for borders to be open again. I&#39;m longing for a week of team building/morale event somewhere nice together where we can get to know eachother for real and not only talk about work through a video camera.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Remote friendly&lt;/em&gt;:&lt;br /&gt;
Microsoft isn&#39;t super remote friendly yet. Covid-19 is forcing it to be a bit more, but it&#39;s obvious that it hasn&#39;t traditionally been used to this way of working.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Google&lt;/em&gt;:&lt;br /&gt;
I said collaborating with Google was good previously, but it has also had its down points. We used to have co-ownership of the codebase, because we know parts of the code as much as they do. But &lt;a href=&quot;https://groups.google.com/a/chromium.org/g/devtools-dev/c/uRVqlHRfQ4k&quot;&gt;they recently decided that DevTools was a &amp;quot;part of the product&amp;quot;&lt;/a&gt; and they wanted to be able to move fast without having to ask permission. What that means is they took away any non-Google owners from the code. It doesn&#39;t mean we don&#39;t collaborate anymore, Chromium is still open source and we still send a lot of changes to it, but we don&#39;t really have a say in where Chromium DevTools is going now.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Let&#39;s end on a positive note 😊: I love my job, my colleagues are super smart and nice human beings, our team is functioning well, and I work on things that motivate me! On top of this, Microsoft as a company has not disapointed me at all, on the contrary, I&#39;ve been surprised by how well things have been and the level of support I&#39;m getting. I also think there are nice opportunities for me here to keep evolving my career. I would definitely recommended working here.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/devex.jpg&quot; alt=&quot;Photo of my new DevEx hoodie with the Microsoft Edge logo on it&quot; /&gt;&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>BlinkOn 14 Talk - Flexbox and Grid Tooling in Chromium</title>
    <link href="https://patrickbrosset.com/articles/2021-05-14-BlinkOn-14-talk/"/>
    <updated>2021-05-14T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-05-14-BlinkOn-14-talk/</id>
    <content type="html">
      
        &lt;p&gt;Last year, a crew of engineers, designers and PMs got together across Microsoft and Google, and we worked together on adding tooling dedicated to CSS Grid and Flexbox.&lt;/p&gt;
&lt;p&gt;These are 2 of the most used layout techniques in CSS, and we wanted all web developers using Chromium-based browsers to have the tools they need to debug problems and understand how these techniques work.&lt;/p&gt;
&lt;p&gt;This week, for &lt;a href=&quot;https://www.chromium.org/events/blinkon-14&quot;&gt;BlinkOn 14&lt;/a&gt;, I gave a short talk about why we implemented those tools, the features they have, and how we implemented them.&lt;/p&gt;
&lt;p&gt;Here&#39;s the talk, hope you like it!&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=D5x1Fa-Atew&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Improving contrast in Microsoft Edge DevTools: A bugfix case study</title>
    <link href="https://patrickbrosset.com/articles/2021-06-15-improving-contrast-in-microsoft-edge-devtools-a-bugfix-case-study/"/>
    <updated>2021-06-15T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-06-15-improving-contrast-in-microsoft-edge-devtools-a-bugfix-case-study/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2021/06/15/improving-contrast-in-microsoft-edge-devtools-a-bugfix-case-study/">https://blogs.windows.com/msedgedev/2021/06/15/improving-contrast-in-microsoft-edge-devtools-a-bugfix-case-study/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Introducing DevTools Tips, for all your cross-browser DevTools tips and tricks</title>
    <link href="https://patrickbrosset.com/articles/2021-07-01-introducing-devtools-tips/"/>
    <updated>2021-07-01T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-07-01-introducing-devtools-tips/</id>
    <content type="html">
      
        &lt;p&gt;I recently launched &lt;a href=&quot;https://devtoolstips.org/&quot;&gt;https://devtoolstips.org/&lt;/a&gt; (and on &lt;a href=&quot;https://twitter.com/_devtoolstips&quot;&gt;twitter&lt;/a&gt;) and made it an open-source project that anyone is welcome to contribute to on &lt;a href=&quot;https://github.com/captainbrosset/devtools-tips&quot;&gt;the github repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/devtools-tips-site.png&quot; alt=&quot;A screenshot of the devtools tips home page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The site is meant to be a collection of nice and simple cross-browser tips and tricks to get anyone more comfortable and productive with using the DevTools of their choice.&lt;/p&gt;
&lt;p&gt;My goal is for the site to contain tips for everyone, whether they are seasoned developers or only starting, whether they do mostly CSS and HTML development, or JavaScript, performance improvement, and anything in between.&lt;/p&gt;
&lt;p&gt;Browser vendors do have reference documentation for their DevTools already, and it&#39;s great and I&#39;m definitely not trying to compete with those.&lt;br /&gt;
You can find those reference documentation here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools&quot;&gt;Firefox Developer Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/&quot;&gt;Edge Developer Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot;&gt;Chrome Developer Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.apple.com/en-us/guide/safari-developer/dev073038698/mac&quot;&gt;Safari Web Inspector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I strongly suggest going through some of the docs there as there is a lot to be learned about each tool.&lt;/p&gt;
&lt;p&gt;My observation though, is that a lot of people don&#39;t really take the time to read those docs. They get introduced to the tools by co-workers, or blog posts, or on their own, and go from there, clicking around and discovering on their own. And reference docs can sometimes be daunting as there is a lot to read.&lt;/p&gt;
&lt;p&gt;Short and specific articles about how to do certain things is a way for people to learn new and useful things that seems to work better.&lt;/p&gt;
&lt;p&gt;I&#39;ve seen a lot of people share tips on their blogs, at conferences or on twitter, and it&#39;s a great way to discover new things and raise awareness about other tools you might not be using.&lt;br /&gt;
Here are a couple you might like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://umaar.com/dev-tips/&quot;&gt;Umaar&#39;s dev tips&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://alexlakatos.com/devtricks/&quot;&gt;Alex Lakatos&#39; dev tricks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are great, and I wish we could join forces. For the moment though, I haven&#39;t found one that was based on an open-source project that anyone could contribute to, and they tend to be mono-browser.&lt;/p&gt;
&lt;p&gt;With DevTools Tips, I&#39;d love to create a community of web developers who care about sharing their learnings with others in the form of quick tips, no matter what browser they happen to be using.&lt;/p&gt;
&lt;p&gt;After a couple of weeks of working on this as a side project, I&#39;ve already received contributions from &lt;a href=&quot;https://github.com/captainbrosset/devtools-tips/graphs/contributors&quot;&gt;4 different people&lt;/a&gt;, which is awesome, and the site already counts more than 30 different tips.&lt;/p&gt;
&lt;p&gt;If this sounds exciting to you, please don&#39;t hesitate to send ideas on &lt;a href=&quot;https://twitter.com/_devtoolstips&quot;&gt;twitter&lt;/a&gt;, or on &lt;a href=&quot;https://github.com/captainbrosset/devtools-tips/issues&quot;&gt;GitHub&lt;/a&gt;, and if you feel like it, send me a PR.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What’s New With DevTools: Cross-Browser Edition</title>
    <link href="https://patrickbrosset.com/articles/2021-09-07-whats-new-with-devtools-cross-browser-edition/"/>
    <updated>2021-09-07T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-09-07-whats-new-with-devtools-cross-browser-edition/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2021/09/devtools-cross-browser-edition/">https://www.smashingmagazine.com/2021/09/devtools-cross-browser-edition/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Make Microsoft Edge DevTools your own</title>
    <link href="https://patrickbrosset.com/articles/2021-09-14-make-microsoft-edge-devtools-your-own/"/>
    <updated>2021-09-14T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-09-14-make-microsoft-edge-devtools-your-own/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2021/09/14/edge-devtools-93-personalization/">https://blogs.windows.com/msedgedev/2021/09/14/edge-devtools-93-personalization/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Minding the gap</title>
    <link href="https://patrickbrosset.com/articles/2021-09-16-minding-the-gap/"/>
    <updated>2021-09-16T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-09-16-minding-the-gap/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://css-tricks.com/minding-the-gap/">https://css-tricks.com/minding-the-gap/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Learn to build great Progressive Web Apps</title>
    <link href="https://patrickbrosset.com/articles/2021-10-19-learn-to-build-great-progressive-web-apps/"/>
    <updated>2021-10-19T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-10-19-learn-to-build-great-progressive-web-apps/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2021/10/19/pwa-summit-learn-progressive-web-apps-documentation/">https://blogs.windows.com/msedgedev/2021/10/19/pwa-summit-learn-progressive-web-apps-documentation/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>10 Ways to Handle Files on the Web!</title>
    <link href="https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/"/>
    <updated>2021-10-22T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/</id>
    <content type="html">
      
        &lt;p&gt;Websites usually don’t use files much. Files are usually a thing that desktop applications like working with. In fact, mobile devices also don’t usually use files, they prefer sharing data between apps directly, and putting everything in the cloud where things are just images, movies, songs, or messages with no information whatsoever as to how these are stored on the disk.&lt;/p&gt;
&lt;p&gt;It’s come to a point where younger people, who have mostly been exposed to technology via mobile devices, Google, and the web, &lt;a href=&quot;https://www.theverge.com/22684730/students-file-folder-directory-structure-education-gen-z&quot;&gt;don’t even know what files and folders are&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But files are still extremely important and millions of people use them every day, creating documents and organizing them in folders.&lt;/p&gt;
&lt;p&gt;So, even if the concept of files itself may, one day, disappear, it is still important for applications to know how to deal with them effectively. And because the web is, more and more, the go to technology for software development, even on desktop, it is equally important for it to handle files.&lt;/p&gt;
&lt;p&gt;Now, working with files using web technologies isn’t very new, it’s been possible to upload and download files to and from websites for a long time, but new APIs have emerged lately that make it possible for web applications to provide more engaging app-like experiences.&lt;/p&gt;
&lt;p&gt;In this article, we’ll go over all techniques that make it possible to work with files on the web. We’ll also review browser support and look at demos (we’ll be using the same demo application throughout the article, you can try it &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Because this article is long, here&#39;s a table of content to jump to specific sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#relevant-interfaces&quot;&gt;Relevant interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#uploading-files&quot;&gt;Uploading files&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#using-an-input-element&quot;&gt;Using an input element&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#dropping-files&quot;&gt;Dropping files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#pasting-files&quot;&gt;Pasting files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#downloading-files&quot;&gt;Downloading files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#full-file-system-access&quot;&gt;Full file system access&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#opening-a-file&quot;&gt;Opening a file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#saving-back-to-disk&quot;&gt;Saving back to disk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#saving-as&quot;&gt;Saving as&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#handling-files-like-compiled-apps-with-pwas&quot;&gt;Handling files like compiled apps with PWAs&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#enabling-the-feature&quot;&gt;Enabling the feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#defining-which-files-your-app-handles&quot;&gt;Defining which files your app handles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#handling-the-files-on-launch&quot;&gt;Handling the files on launch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#sharing-files&quot;&gt;Sharing files&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#sharing-with-other-apps&quot;&gt;Sharing with other apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#accepting-shared-files&quot;&gt;Accepting shared files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;relevant-interfaces&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Relevant interfaces&lt;/h3&gt;
&lt;p&gt;Before diving in, it’s important to review how files appear to JavaScript code. There are multiple types of objects that one need to work with when working with files, and knowing the interfaces to these objects can be helpful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer&quot;&gt;DataTransfer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;DataTransfer&lt;/code&gt; object is passed in with the event object when listening to drag/drop and pasting events. This object is used to get access to File objects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList&quot;&gt;DataTransferItemList&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FileList&quot;&gt;FileList&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Both can be retrieved from a &lt;code&gt;DataTransfer&lt;/code&gt; object, and can be used to get the dropped or pasted files.&lt;/p&gt;
&lt;p&gt;The latter can also be retrieved by listening to the change event on an &lt;code&gt;&amp;lt;input type=file&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem&quot;&gt;DataTransferItem&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This object is one of the items inside a &lt;code&gt;DataTransferItemList&lt;/code&gt;, and can be used to get the associated file object with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFile&quot;&gt;&lt;code&gt;getAsFile()&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/File&quot;&gt;File&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;File&lt;/code&gt; object is used to access the content of a file. It’s retrieved by either using the &lt;code&gt;DataTransformItem.getAsFile()&lt;/code&gt; method, or by iterating over a &lt;code&gt;FileList&lt;/code&gt; collection object.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle&quot;&gt;FileSystemHandle&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While similar to the &lt;code&gt;File&lt;/code&gt; object we saw just before, the &lt;code&gt;FileSystemHandle&lt;/code&gt; is different in that it represents a file on the actual operating system file system. It is retrieved by using the &lt;code&gt;File System Access API&lt;/code&gt; when asking the browser to show the file picker with &lt;code&gt;showOpenFilePicker()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is a parent interface that both the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle&quot;&gt;&lt;code&gt;FileSystemFileHandle&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle&quot;&gt;&lt;code&gt;FileSystemDirectoryHandle&lt;/code&gt;&lt;/a&gt; extend.&lt;/p&gt;
&lt;div id=&quot;uploading-files&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Uploading files&lt;/h3&gt;
&lt;p&gt;Let’s review 3 different ways in which a browser can “open” a file. Open isn’t necessarily the right word here, upload works better. Indeed, the browser doesn’t open the file in the same was as, say, Microsoft Word opens the file. The browser doesn’t lock the file for editing, and cannot write in it.&lt;/p&gt;
&lt;div id=&quot;using-an-input-element&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Using an input element&lt;/h4&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of a file input element opening the OS file browser window&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/input-element.png&quot; /&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file&quot;&gt;&lt;code&gt;&amp;lt;input type=file&amp;gt;&lt;/code&gt;&lt;/a&gt; has been around for a very long time, and can be used nested inside a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; to upload files to web servers.&lt;/p&gt;
&lt;p&gt;But the input can also be used to access the content from the uploaded files directly using JavaScript code in the browser.&lt;/p&gt;
&lt;p&gt;First, we need to add the input somewhere in the page:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;input-file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text/*&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;multiple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The multiple attribute is useful when you want to allow users to select more than 1 file in the file picker. And the accept attribute allows to restrict the types of files that can be picked to a given mime-type.&lt;/p&gt;
&lt;p&gt;As a side note, you can &lt;a href=&quot;https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/&quot;&gt;use CSS to style the input and label&lt;/a&gt; and make something a little better than what that code generates by default.&lt;/p&gt;
&lt;p&gt;Next, let’s listen to changes in the input so we know when a file (or files) has been selected. This can be done by listening to the change event.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#input-file&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;change&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When files are selected by the user, and the listener is called, the input’s files property will contain the information we need to access the files.&lt;/p&gt;
&lt;p&gt;It is then possible to iterate over the list of files and, for each, create a &lt;code&gt;FileReader&lt;/code&gt; object to access the content. If the file contains text (&lt;code&gt;text/*&lt;/code&gt; mime-type), then the &lt;code&gt;FileReader&lt;/code&gt;’s onload event listener will give us access to the content of the file directly. Otherwise, we might have to decode the content using a &lt;code&gt;TextDecoder&lt;/code&gt; object.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; files &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; files&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FileReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;string&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; enc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;utf-8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; enc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Do something with `text` here&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAsText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overall, this is a very simple and effective way to let the user choose a file and upload it to the site. It uses the familiar file picker pattern and is &lt;a href=&quot;https://caniuse.com/mdn-html_elements_input_input-file&quot;&gt;supported in all browsers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: using &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt;, click the “Browse” button in the top left to select one or more text files from your file system. Once selected, the content of the files is accessed and displayed in the text area in the middle of the app.&lt;br /&gt;
&lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/import/input-file.ts&quot;&gt;Link to the source code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/input-element-demo.png&quot; alt=&quot;Screenshot of the demo app with the windows open file dialog&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;dropping-files&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Dropping files&lt;/h4&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of dropping a file in the browser window&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/drop-file.png&quot; /&gt;
&lt;p&gt;Another way to upload a file to a web page is by dragging it and dropping it into the browser window. By default, this will make the browser navigate away from the current site and open the file, but the site can actually listen for the drop event and cancel this default behavior.&lt;/p&gt;
&lt;p&gt;To indicate that files are accepted by drag and drop, it can be useful to use the &lt;code&gt;dragover&lt;/code&gt; and &lt;code&gt;dragleave&lt;/code&gt; events, to highlight the drop zone temporarily.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dropEl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.content-wrapper&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

dropEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;dragover&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    dropEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;drag-over&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dropEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;dragleave&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    dropEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;drag-over&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, to actually handle the dropped file we should listen to the drop event. When it occurs, it will receive an event object of type &lt;code&gt;DragEvent&lt;/code&gt;. This type of event has access to a dataTransfer property which can be used to iterate over the dropped items.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;dropEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;drop&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    dropEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;drag-over&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Prevent default behavior (Prevent file from being opened)&lt;/span&gt;
    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;handleDroppedItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataTransfer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that dropped items don’t have to be files, so our code needs to check the type of the items before processing them.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleDroppedItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;droppedItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; droppedItems&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kind &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;file&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAsFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Do something with `text` here&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, after getting the File object with &lt;code&gt;file.getAsFile()&lt;/code&gt;, we access its text content with the &lt;code&gt;text()&lt;/code&gt; method. This method returns a promise as it is asynchronous. This is why we made &lt;code&gt;handleDroppedItems&lt;/code&gt; an async function.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://caniuse.com/mdn-api_document_drop_event&quot;&gt;Support for dropping files in the browser is very good on desktop&lt;/a&gt;, and that’s what matters here anyway.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: select one or more files from your file system and drop them in &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt; text area. Once dropped, their content is displayed in the text area.&lt;br /&gt;
&lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/import/drag-drop.ts&quot;&gt;Link to the source code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/drop-file-demo.png&quot; alt=&quot;Screenshot of the demo app showing that it reacts to a file being dropped in the window&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;pasting-files&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Pasting files&lt;/h4&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of pasting a file in the browser window&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/paste-file.png&quot; /&gt;
&lt;p&gt;Files can also be pasted in the browser window. Pasting and dropping files are very similar and they use the same &lt;code&gt;DataTransfer&lt;/code&gt; object, so the code is very similar.&lt;/p&gt;
&lt;p&gt;First, listen to the paste event on the document.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;paste&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;handlePastedItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clipboardData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, handle the pasted items just like we’d handle dropped items.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handlePastedItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;pastedItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; pastedItems&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kind &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;file&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Note that we&#39;re not using await here.&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// The DataTransferItemList passed on a paste event  becomes empty &lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// after an sync step, so we have to access all items synchronously.&lt;/span&gt;
            item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAsFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// Do something with `text` here&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://caniuse.com/mdn-api_window_paste_event&quot;&gt;Support for the paste event is great&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One limitation of both dropping and pasting files in the browser is that it isn’t as natural to users and requires the site to let them know that it is even possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: copy one or more files in your file system and paste them in &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt; (use the &lt;code&gt;ctrl+V&lt;/code&gt; keyboard shortcut). Once pasted, their content is displayed in the text area.&lt;br /&gt;
&lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/import/paste.ts&quot;&gt;Link to the source code&lt;/a&gt;.&lt;/p&gt;
&lt;div id=&quot;downloading-files&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Downloading files&lt;/h3&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of downloading a file from the browser&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/download-file.png&quot; /&gt;
&lt;p&gt;Now that we’ve seen how files can be uploaded to a site, let’s see how they can be downloaded.&lt;/p&gt;
&lt;p&gt;If you’re making a site that can edit photos, or text, and you’ve used one of the above uploading mechanisms, you might want to allow users to save their changes again by downloading a new file.&lt;/p&gt;
&lt;p&gt;To do this, you can use the download attribute on a link (&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element). The &lt;code&gt;download&lt;/code&gt; attribute allows browsers to trigger a file download instead of navigating when the link is clicked. The attribute can also have a value that will be used as the name of the downloaded file:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;file-path&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;download&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;file-name.text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Download&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your JavaScript  code has access to the content of the file as a string, you can do all of this programmatically too. First, create a data url from the text content:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text/plain&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, create the link element and activate it:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; link &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;download&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;file.txt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;href&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the link doesn’t even have to be in the DOM for the above to work. So this is a great little piece of utility code you can put in a function, and call it anytime you need to make some text content available as a file download. Also, while you&#39;re at it, make sure you &lt;a href=&quot;https://robrelyea.wordpress.com/2013/01/26/designing-file-names-improving-user-experience-for-file-downloads/&quot;&gt;design your downloaded file names in a way that makes sense to your users&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;The download attribute is &lt;a href=&quot;https://caniuse.com/download&quot;&gt;supported across the board&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: In &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt;, click the “Download” button in the top-right corner to transform the current text content in a file and make the browser download it.&lt;br /&gt;
&lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/export/anchor-download.ts&quot;&gt;Link to the source code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/download-file-demo.png&quot; alt=&quot;Screenshot of the demo app showing the download button and the browser&#39;s download popup&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;full-file-system-access&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Full file system access&lt;/h3&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of the browser&#39;s full file system access&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/file-system.png&quot; /&gt;
&lt;p&gt;While the methods listed so far are great ways to access the content of a file on a site, they don’t allow making changes to it and saving those changes back to disk in a way that desktop applications normally do. VS Code doesn’t require you to download a new file in your Downloads folder every time you make text changes, it has full access to the file on disk.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API&quot;&gt;File System Access API&lt;/a&gt; was made to fill this gap in the web platform, and allow web apps to open and save files.&lt;/p&gt;
&lt;p&gt;This API is part of the &lt;a href=&quot;https://www.chromium.org/teams/web-capabilities-fugu&quot;&gt;Web Capabilities&lt;/a&gt; project (aka project Fugu 🐡) which aims at giving web apps the capabilities that compiled apps normally have.&lt;/p&gt;
&lt;p&gt;The File System Access API’s browser support is limited still, but it is implemented by Chromium, which means that Microsoft Edge or Google Chrome can use it already (or other Chromium-based browsers - note that Brave, however, has the API disabled). It is not behind a flag, and can be used right away simply by checking that the feature exists.&lt;/p&gt;
&lt;p&gt;Note that &lt;a href=&quot;https://github.com/mozilla/standards-positions/issues/154&quot;&gt;Firefox&lt;/a&gt; hans&#39;t yet sent positive signals around implementing this API and discussions are still ongoing.
Safari, however, &lt;a href=&quot;https://webkit.org/blog/12257/the-file-system-access-api-with-origin-private-file-system/&quot;&gt;started implementing the API&lt;/a&gt; (based on an origin-private file system for now though).&lt;/p&gt;
&lt;div id=&quot;opening-a-file&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Opening a file&lt;/h4&gt;
&lt;p&gt;Opening a file can be done by the using the &lt;code&gt;window.showOpenFilePicker&lt;/code&gt; function which triggers the OS-specific file picker, like when using an &lt;code&gt;&amp;lt;input file&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; openButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#open&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Make sure the API exists&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;showOpenFilePicker&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    openButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;fileHandle&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showOpenFilePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;multiple&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fileHandle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Do something with `text` here&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code only deals with one file. That’s why the multiple property is set to false, and also why we only used the first &lt;code&gt;FileSystemHandle&lt;/code&gt; object returned by &lt;code&gt;showOpenFilePicker&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to be able to save back to disk, always keep a reference to the &lt;code&gt;FileSystemHandle&lt;/code&gt; object. That’s the object that represents the file on the actual device file system.&lt;/p&gt;
&lt;p&gt;Using this object you can get the &lt;code&gt;File&lt;/code&gt; object (using &lt;code&gt;getFile()&lt;/code&gt;), which can, in turn, be used to retrieve the file content as seen above.&lt;/p&gt;
&lt;p&gt;Interestingly, a &lt;code&gt;DataTransferItem&lt;/code&gt; object can also be used to get a &lt;code&gt;FileSystemHandle&lt;/code&gt;. Using the &lt;code&gt;getAsFileSystemHandle()&lt;/code&gt; method, you can get a reference to the file handle from a dropped file. This means that your app can allow saving changes back to disk for a dropped file too.&lt;/p&gt;
&lt;div id=&quot;saving-back-to-disk&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Saving back to disk&lt;/h4&gt;
&lt;p&gt;If you have the file handle object stored somewhere in your app, you can use it to make changes to the file on disk by using the &lt;code&gt;createWritable()&lt;/code&gt; function, as seen below:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; saveButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#save&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;foo bar&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

saveButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fileHandle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; writable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fileHandle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createWritable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; writable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; writable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As seen in the 2 code snippets above, using the File System Access API is rather simple.&lt;/p&gt;
&lt;div id=&quot;saving-as&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Saving as&lt;/h4&gt;
&lt;p&gt;The API also allows to save the content as a new file using the &lt;code&gt;window.showSaveFilePicker&lt;/code&gt; function. This function works like &lt;code&gt;window.showOpenFilePicker&lt;/code&gt; in that it returns a new file handle.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; saveAsButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#save-as&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Make sure the API exists&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;showSaveFilePicker&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;foo bar&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

saveAsButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Ask the user to select the new location.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newHandle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showSaveFilePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// And save the file to this new file handle.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; writable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; newHandle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createWritable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; writable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; writable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: Open &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt; in Microsoft Edge, and click the “Open” button in the lower left corner to select a file from the file system. Its content is displayed in the text area. You can then edit the content and save the changes back to disk by clicking the “Save” button in the lower right corner.&lt;br /&gt;
You can also click the “Save as” button instead to save the content under a new file name.&lt;br /&gt;
Links to the source code: &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/import/file-system-access.ts&quot;&gt;open&lt;/a&gt;, &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/export/file-system-access.ts&quot;&gt;save and save as&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/file-system-demo.png&quot; alt=&quot;Screenshot of the demo app showing the open and save buttons and the OS file picker&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;handling-files-like-compiled-apps-with-pwas&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Handling files like compiled apps with PWAs&lt;/h3&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of a PWA handling a file&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/pwa-file-handling.png&quot; /&gt;
&lt;p&gt;Progressive Web Apps (or PWAs) are meant to help make websites feel like applications that integrate more deeply within the operating system.&lt;/p&gt;
&lt;p&gt;Now, with the PWA File Handling feature, they can also handle files like non-web apps can. Simply put, this means that double-clicking on a file on your desktop can launch the associated PWA automatically.&lt;/p&gt;
&lt;div id=&quot;enabling-the-feature&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Enabling the feature&lt;/h4&gt;
&lt;p&gt;PWA file handling is, for now, only supported in Chromium and is still an experimental feature. It therefore needs to be enabled first. You can enable it by going to &lt;code&gt;about://flags&lt;/code&gt; in Microsoft Edge or Google Chrome and enabling the “File handling API” flag.&lt;/p&gt;
&lt;div id=&quot;defining-which-files-your-app-handles&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Defining which files your app handles&lt;/h4&gt;
&lt;p&gt;Because this feature only works with PWAs, you first need to make sure you have a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Manifest&quot;&gt;web app manifest&lt;/a&gt; file. Using the manifest, the PWA will tell the operating system which files it wants to handle using the &lt;code&gt;file_handlers&lt;/code&gt; manifest member.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;file_handlers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;accept&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;text/*&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the app registers a single file handler that accepts text files and tells the OS to go to the app root (using the action property) when one is launched.&lt;/p&gt;
&lt;div id=&quot;handling-the-files-on-launch&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Handling the files on launch&lt;/h4&gt;
&lt;p&gt;When the PWA launches, it can check if there are files to be handled. This is done using the &lt;code&gt;window.launchQueue&lt;/code&gt; object.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Check that the launchQueue object exists. &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;launchQueue&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Consume the launchQueue.&lt;/span&gt;
    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;launchQueue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConsumer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;handleFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;launchQueue&lt;/code&gt; parameter passed to the &lt;code&gt;setConsumer&lt;/code&gt; function gives us access to the list of files, and each file within that list is a &lt;code&gt;FileSystemFileHandle&lt;/code&gt; object. This means we’re dealing with the same object types as when using the File System Access API. We can therefore access the file content like this:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;async function &lt;span class=&quot;token function&quot;&gt;handleFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileHandle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; await fileHandle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; await file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Do something with text here.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having access to a file handle also means you can save changes back to disk using the &lt;code&gt;createWritable()&lt;/code&gt; method we used before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: Open &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt; in Microsoft Edge, and install it as a PWA (click the “install app” button in the address bar when it appears). Now, choose any text file in your file system and double-click on it. You can also use the context menu (right-click) and select “Open with” to choose the demo PWA app.&lt;br /&gt;
Links to the source code: &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/public/manifest.json&quot;&gt;manifest&lt;/a&gt;, &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/import/pwa-file-handling.ts&quot;&gt;launchQueue handling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/pwa-file-handling-demo.png&quot; alt=&quot;Screenshot of the demo app installed as a PWA, and the windows explorer showing that text files can be with the PWA&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;sharing-files&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Sharing files&lt;/h3&gt;
&lt;img class=&quot;mini&quot; alt=&quot;Illustration of sharing files between apps, browsers, and PWAs&quot; src=&quot;https://patrickbrosset.com/assets/file-handling/file-sharing.png&quot; /&gt;
&lt;p&gt;Finally, let’s talk about sharing. Sharing originally appeared on mobile devices where passing information between apps isn’t very easily achieved with a file system. On mobile, apps are able to share content with other apps directly.&lt;/p&gt;
&lt;p&gt;For example, you can share an event stored in your calendar with somebody on a text messaging app. The sharing happens automatically without you having to save a file from the calendar app, and then open it from the message app. Instead, the calendar app knows how share its content, and the message app knows how to accept shared content.&lt;/p&gt;
&lt;p&gt;It is now possible to do both of these things on the web. Let’s see how.&lt;/p&gt;
&lt;div id=&quot;sharing-with-other-apps&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Sharing with other apps&lt;/h4&gt;
&lt;p&gt;Using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API&quot;&gt;Web Share API&lt;/a&gt;, websites can share content. This API can be used to share a url, some text, or even a file, and have all of this content be handled by the operating system’s share utility, so that other apps can consume this shared content.&lt;/p&gt;
&lt;p&gt;Before using the API, check that it is available:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;share&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The Web Share API can be used&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The type of data that can be shared with this API is an object with the following properties: title, text, url, and files. All of these properties are optional, but you should at least provide one. It’s up to the target application to do what it wants with this data. A message app may decide to use the text and url for example.&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;navigator.share()&lt;/code&gt; method to share your content. If you aren’t sure about the data, you can also use the &lt;code&gt;navigator.canShare()&lt;/code&gt; method to test it.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; shareButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#web-share&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;shareFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canShare &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canShare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; files &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;share&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; files &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Error sharing the content&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

shareButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;shareFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someFiles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code, the &lt;code&gt;someFiles&lt;/code&gt; array is a list of &lt;code&gt;File&lt;/code&gt; objects, just like the ones we used when dropping and pasting files in the browser (the ones retrieved from &lt;code&gt;DataTransferItem.getAsFile()&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;You can also construct a file object from text if you need:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;shared-file.txt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text/plain&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;navigator.share()&lt;/code&gt; function is &lt;a href=&quot;https://caniuse.com/web-share&quot;&gt;well supported&lt;/a&gt; across Edge, Chrome, Firefox, and Safari, but it’s worth noting that Edge and Chrome only support it on Desktop, and that Firefox does not support sharing files (or the &lt;code&gt;navigator.canShare()&lt;/code&gt; function).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: In &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt;, click the “Share” button in the right sidebar. The text content of the textarea will be used to create a file which will be shared with the operating system’s share dialog.&lt;br /&gt;
Each app handles shared content differently, but if you select Microsoft Mail for example, the file will appear as an attachment to a new email.&lt;br /&gt;
&lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/export/web-share.ts&quot;&gt;Link to the source code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/file-sharing-demo-1.png&quot; alt=&quot;Screenshot of the demo app showing the share button and the Windows share dialog&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;accepting-shared-files&quot;&gt;&lt;/div&gt;
&lt;h4&gt;Accepting shared files&lt;/h4&gt;
&lt;p&gt;Sharing files with other apps is just half of the story. The other parts is accepting shared files. It is possible to do so by using the &lt;a href=&quot;https://w3c.github.io/web-share-target/&quot;&gt;Web Share Target API&lt;/a&gt;. This API is, for now, at an early draft stage and only supported on Chromium browsers (&lt;a href=&quot;https://mozilla.github.io/standards-positions/#web-share-target&quot;&gt;Firefox’s intent&lt;/a&gt; is positive though).&lt;/p&gt;
&lt;p&gt;Being recognized by the operating system as a potential target for shared content only works if the app is a PWA. It isn’t possible for a website in the browser to be a share target, this only works with installed PWAs.&lt;/p&gt;
&lt;p&gt;So the first thing you’ll need is a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Manifest&quot;&gt;web app manifest&lt;/a&gt; file and, in it, a &lt;code&gt;share_target&lt;/code&gt; member:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;share_target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/share-target&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;enctype&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;params&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;files&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;accept&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                        &lt;span class=&quot;token string&quot;&gt;&quot;text/*&quot;&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the OS installs the app, it will look at the &lt;code&gt;share_target&lt;/code&gt; manifest member, and register the app is one that handles text files. When the user shares a text file from another app, the OS will prompt the user to choose from a list of apps that can handle text files, which will include this app.&lt;/p&gt;
&lt;p&gt;Now, when the app starts, a &lt;code&gt;POST&lt;/code&gt; request will be sent to the action specified in the manifest (&lt;code&gt;/share-target&lt;/code&gt; here). Your server could handle the form submission at this point, but because the app is a PWA, you can also intercept the request from the service worker, and handle the file there.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;method &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/share-target&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;            
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;file&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above is a fetch event listener in the service worker. It handles the &lt;code&gt;/share-target&lt;/code&gt; &lt;code&gt;POST&lt;/code&gt; form submission and use the &lt;code&gt;request.formData()&lt;/code&gt; function to access the shared file.&lt;/p&gt;
&lt;p&gt;At this point, you might need to pass the file to the front-end code (rather than the service worker code). There are multiple solutions for this. You could read the content as text and use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage&quot;&gt;&lt;code&gt;postMessage&lt;/code&gt;&lt;/a&gt; to send it to the client or you could store it in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB&quot;&gt;&lt;code&gt;IndexedDB&lt;/code&gt;&lt;/a&gt; (by the way, working with &lt;code&gt;IndexedDB&lt;/code&gt; can be hard, I recommend using the &lt;a href=&quot;https://localforage.github.io/localForage/&quot;&gt;LocalForage&lt;/a&gt; library which makes it much easier).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: Make sure you installed &lt;a href=&quot;https://cranky-shaw-fe95e8.netlify.app/&quot;&gt;the demo app&lt;/a&gt; as a PWA first, then choose a text file from your file system, and share it. On Windows, you can use the context menu (right-click) and select “Share” to do this. The operating system’s share dialog will appear and list the demo app as one of the possible targets. Selecting the demo app will launch it and display the content of the file.&lt;br /&gt;
Links to the source code: &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/public/manifest.json&quot;&gt;manifest&lt;/a&gt;, &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/sw/index.ts&quot;&gt;service worker&lt;/a&gt;, &lt;a href=&quot;https://github.com/captainbrosset/file-handling-demo/blob/main/src/client/import/pwa-share-target.ts&quot;&gt;client code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/file-handling/file-sharing-demo-2.gif&quot; alt=&quot;Animated screenshot showing sharing a file from the Windows explorer to the demo app installed as a PWA&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;And, that’s it! We’ve reviewed all of the current ways that web sites and apps can work with files today. There are many so it can be a bit confusing. Hopefully this article was useful in understanding the differences and which of these ways might be best suited for you.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Picking colors of any pixel on the screen with the EyeDropper API</title>
    <link href="https://patrickbrosset.com/articles/2021-10-27-picking-colors-of-any-pixel-on-the-screen-with-the-eyedropper-api/"/>
    <updated>2021-10-27T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-10-27-picking-colors-of-any-pixel-on-the-screen-with-the-eyedropper-api/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/docs/capabilities/web-apis/eyedropper">https://developer.chrome.com/docs/capabilities/web-apis/eyedropper</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>How I built an Eye Dropper browser extension</title>
    <link href="https://patrickbrosset.com/articles/2021-11-24-how-i-built-an-eye-dropper-browser-extension/"/>
    <updated>2021-11-24T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-11-24-how-i-built-an-eye-dropper-browser-extension/</id>
    <content type="html">
      
        &lt;p&gt;I recently &lt;a href=&quot;https://web.dev/eyedropper/&quot;&gt;wrote about the EyeDropper API&lt;/a&gt;, this great new little JavaScript API that allows users to pick colors from anywhere on their screens.&lt;/p&gt;
&lt;p&gt;In a few words, by executing just a couple of JS lines of code, web devs can make an eye dropper tool appear, letting users click on a pixel, and the color of the pixel is returned to the code, for the devs to use.&lt;/p&gt;
&lt;p&gt;The API was implemented in Chromium-based browsers, including Edge and Chrome, in version 96. This version is now available to all! No need to install a pre-release channel of the browser, or turn on any flags. You can just use it! Of course, this is only Chromium, so don&#39;t rely on it in production just yet, but it&#39;s definitely a great time to experiment with it.&lt;/p&gt;
&lt;p&gt;Back when I worked on the article, I got interested in whether the API would also work in browser extensions, which it does! So I built a small extension that lets you grab colors from your screen, and store them locally for later reuse. It&#39;s great to build a color palette from an image for example, and always have those colors handy.&lt;/p&gt;
&lt;p&gt;➡️ &lt;strong&gt;Get the extension &lt;a href=&quot;https://chrome.google.com/webstore/detail/eye-dropper/fjnpfdnpkpdccebgadceaieifiiblabh&quot;&gt;here&lt;/a&gt; (for both Chrome and Edge)&lt;/strong&gt; (it&#39;s for Chromium-browsers only for now because &lt;a href=&quot;https://caniuse.com/?search=eyedropper&quot;&gt;the EyeDropper API only works there&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;➡️ &lt;strong&gt;And browse &lt;a href=&quot;https://github.com/captainbrosset/eye-dropper-ext&quot;&gt;the source code&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are of course other existing software that do this, but since web developers spend most of their time in the browser, I figured having an icon right there, in the browser toolbar, would be quite handy. After using myself for a while, I have to say it is very useful, at least to me.&lt;/p&gt;
&lt;p&gt;Now, let&#39;s dive into some of the interesting details behind this extension.&lt;/p&gt;
&lt;h3&gt;Building a browser extension&lt;/h3&gt;
&lt;p&gt;If you&#39;re interested in building one, but don&#39;t quite know where to start, I think the most important thing to know is that they&#39;re built using HTML, CSS, and JavaScript! So you can reuse the knowledge you already have.&lt;/p&gt;
&lt;p&gt;There&#39;s a bunch of specific extension stuff added on top too, but if you&#39;re looking to create an extension like this one, it&#39;s really not that much.&lt;/p&gt;
&lt;p&gt;The entry point of any extension is its manifest file. This file is used by the browser to know what the name, description, author, icons, etc. of the extension are.&lt;/p&gt;
&lt;p&gt;Here is the manifest of the Eyedropper extension:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Eye Dropper&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A browser extension to copy colors from your screen&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;homepage_url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/captainbrosset/eye-dropper-ext/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Patrick Brosset&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;manifest_version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;icons&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;icons/icon16.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;32&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;icons/icon32.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ...
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;browser_action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;default_icon&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;icons/icon16.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;32&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;icons/icon32.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ...
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;default_title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Pick a color!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;default_popup&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;popup/popup.html&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also used to list the various entry points of the extension. In particular, my extension shows an icon in the toolbar. To do this, I used the &lt;code&gt;browser_action&lt;/code&gt; property in the manifest file as seen above. This property needs an icon, and either an action to execute on click, or a popup to display on click.&lt;/p&gt;
&lt;p&gt;Adding the popup.html file and this &lt;code&gt;browser_action&lt;/code&gt; property was all I needed to have the icon in the toolbar. It&#39;s therefore quite simple to use, and we can then focus on the HTML of the popup and the CSS and JS code embedded in it.&lt;/p&gt;
&lt;p&gt;As for many other things, MDN is a great place to learn more about writing extensions in the browser. The main documentation entry point can be found here: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions&quot;&gt;Browser Extensions&lt;/a&gt;. You there&#39;s documentation about the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json&quot;&gt;manifest&lt;/a&gt;, and the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action&quot;&gt;browser_action&lt;/a&gt; property.&lt;/p&gt;
&lt;h3&gt;Using the EyeDropper API&lt;/h3&gt;
&lt;p&gt;The core part of the extension is the eye dropper. To make it work, I used the new EyeDropper API (which I &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/EyeDropper&quot;&gt;documented on MDN&lt;/a&gt; recently too).&lt;/p&gt;
&lt;p&gt;It takes just a few lines of JS code to use the EyeDropper API. This API doesn&#39;t have a lot of extra features for now, and that&#39;s good. This may change in the future depending on the &lt;a href=&quot;https://github.com/WICG/eyedropper-api/issues&quot;&gt;feedback received on the specification&lt;/a&gt; but, for now, here is the code I&#39;m using the display the eye dropper on click:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pickNewColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EyeDropper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; ed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sRGBHex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// after this, store the color, copy in clipboard, etc.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pickNewColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, using the eye dropper requires to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Instantiate the &lt;code&gt;EyeDropper&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Call the &lt;code&gt;open&lt;/code&gt; method.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, &lt;code&gt;open&lt;/code&gt; returns a promise. It&#39;s useful because the promise will resolve when the user has picked a color. If the user decides to not pick a color after all and presses ESCAPE on the keyboard, the promise rejects.&lt;/p&gt;
&lt;p&gt;In the code sample above, I&#39;m using an &lt;code&gt;aync&lt;/code&gt; function which makes working with promise-based asynchronous code really nice. It reads like synchronous code, and avoid the nested callback hell we normally deal with when using promises with the &lt;code&gt;.then&lt;/code&gt; handler. Learn more &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function&quot;&gt;about &lt;code&gt;async&lt;/code&gt; functions&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;When the promise resolves, it returns an object with the &lt;code&gt;sRGBHex&lt;/code&gt; property on it. This property is a string containing the color value in hex format.&lt;/p&gt;
&lt;h3&gt;Using the Clipboard API&lt;/h3&gt;
&lt;p&gt;The second most useful piece of code in the extension is its use of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API&quot;&gt;Clipboard API&lt;/a&gt;. That&#39;s what makes the extension useful. Without it, you&#39;d only be able to pick colors, but not use them anywhere.&lt;/p&gt;
&lt;p&gt;The extension uses the API to store the color value of a newly picked color, and also when you click on a previously picked color, in the popup.&lt;/p&gt;
&lt;p&gt;There are 2 steps to doing this with the API:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Requesting permission.&lt;/li&gt;
&lt;li&gt;Writing text in the clipboard.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the code I used to do it:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendToClipboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;permissions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;clipboard-write&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;granted&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prompt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clipboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Failed to write to the clipboard.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the first line is used to request the user&#39;s permission to write to the clipboard using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API&quot;&gt;Permissions API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This part is asynchronous and returns a promise. This is because asking for the user&#39;s permission requires them to manually interact with a popup, which takes time. The promise returned by the &lt;code&gt;query&lt;/code&gt; method does not resolve before this happens, and the returned value contains the state of the user&#39;s permission.&lt;/p&gt;
&lt;p&gt;I used an &lt;code&gt;async&lt;/code&gt; function here again to make dealing with this asynchronous code easier.&lt;/p&gt;
&lt;p&gt;The second part is using the Clipboard API. In my case, I need to write to the clipboard, so I used the &lt;code&gt;navigator.clipboard.writeText&lt;/code&gt; function (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText&quot;&gt;documented here&lt;/a&gt;), but there are &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Clipboard#methods&quot;&gt;other methods&lt;/a&gt; to read too.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;In terms of code, that&#39;s about it. The extension does only one thing, and it relies on APIs to do the heavy lifting, so its code is pretty short. I hope the extension helps you! Let me know on &lt;a href=&quot;https://github.com/captainbrosset/eye-dropper-ext&quot;&gt;the GitHub repo&lt;/a&gt; if it does, or if you have bugs.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Breaking Out of the Box</title>
    <link href="https://patrickbrosset.com/articles/2021-12-09-breaking-out-of-the-box/"/>
    <updated>2021-12-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-12-09-breaking-out-of-the-box/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://alistapart.com/article/breaking-out-of-the-box/">https://alistapart.com/article/breaking-out-of-the-box/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Debug memory leaks with the Microsoft Edge Detached Elements tool</title>
    <link href="https://patrickbrosset.com/articles/2021-12-09-debug-memory-leaks-with-the-microsoft-edge-detached-elements-tool/"/>
    <updated>2021-12-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2021-12-09-debug-memory-leaks-with-the-microsoft-edge-detached-elements-tool/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2021/12/09/debug-memory-leaks-detached-elements-tool-devtools/">https://blogs.windows.com/msedgedev/2021/12/09/debug-memory-leaks-detached-elements-tool-devtools/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Looking back at Microsoft Edge for developers in 2021</title>
    <link href="https://patrickbrosset.com/articles/2022-01-19-looking-back-at-microsoft-edge-for-developers-in-2021/"/>
    <updated>2022-01-19T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-01-19-looking-back-at-microsoft-edge-for-developers-in-2021/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2022/01/19/looking-back-at-microsoft-edge-for-developers-in-2021/">https://blogs.windows.com/msedgedev/2022/01/19/looking-back-at-microsoft-edge-for-developers-in-2021/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What’s New In DevTools?</title>
    <link href="https://patrickbrosset.com/articles/2022-01-25-whats-new-in-devtools/"/>
    <updated>2022-01-25T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-01-25-whats-new-in-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2022/01/devtools-updates-2022/">https://www.smashingmagazine.com/2022/01/devtools-updates-2022/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web</title>
    <link href="https://patrickbrosset.com/articles/2022-03-01-css-custom-highlight-api-the-future-of-highlighting-text-ranges-on-the-web/"/>
    <updated>2022-03-01T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-03-01-css-custom-highlight-api-the-future-of-highlighting-text-ranges-on-the-web/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://css-tricks.com/css-custom-highlight-api-early-loo/">https://css-tricks.com/css-custom-highlight-api-early-loo/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>PWA Debugging Tools</title>
    <link href="https://patrickbrosset.com/articles/2022-03-02-pwa-debugging-tools/"/>
    <updated>2022-03-02T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-03-02-pwa-debugging-tools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://microsoft.github.io/win-student-devs/#/30DaysOfPWA/dev-tools/03">https://microsoft.github.io/win-student-devs/#/30DaysOfPWA/dev-tools/03</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Say Hello to selectmenu, a Fully Style-able select Element</title>
    <link href="https://patrickbrosset.com/articles/2022-03-03-say-hello-to-selectmenu-a-fully-style-able-select-element/"/>
    <updated>2022-03-03T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-03-03-say-hello-to-selectmenu-a-fully-style-able-select-element/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://css-tricks.com/the-selectmenu-element/">https://css-tricks.com/the-selectmenu-element/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Improving DevTools together: Announcing the new Edge DevTools feedback repository</title>
    <link href="https://patrickbrosset.com/articles/2022-03-09-improving-devtools-together-announcing-the-new-edge-devtools-feedback-repository/"/>
    <updated>2022-03-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-03-09-improving-devtools-together-announcing-the-new-edge-devtools-feedback-repository/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2022/03/09/improving-devtools-together-announcing-the-new-edge-devtools-feedback-repository/">https://blogs.windows.com/msedgedev/2022/03/09/improving-devtools-together-announcing-the-new-edge-devtools-feedback-repository/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>30 days of PWA: Fall in love with Progressive Web Apps</title>
    <link href="https://patrickbrosset.com/articles/2022-04-14-30-days-of-pwa-fall-in-love-with-progressive-web-apps/"/>
    <updated>2022-04-14T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-04-14-30-days-of-pwa-fall-in-love-with-progressive-web-apps/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2022/04/14/30-days-of-pwa-fall-in-love-with-progressive-web-apps/">https://blogs.windows.com/msedgedev/2022/04/14/30-days-of-pwa-fall-in-love-with-progressive-web-apps/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Better error stacktraces for faster debugging</title>
    <link href="https://patrickbrosset.com/articles/2022-05-05-better-stacktraces-for-faster-debugging/"/>
    <updated>2022-05-05T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-05-better-stacktraces-for-faster-debugging/</id>
    <content type="html">
      
        &lt;h3&gt;The problem&lt;/h3&gt;
&lt;p&gt;Web devs debug their code in different ways. Some people will tell you that stepping debuggers and breakpoints are better, more powerful, ways to find the root cause of a problem. But let&#39;s face it, most of us just console.log our way out of it, and it&#39;s fine!&lt;/p&gt;
&lt;p&gt;I&#39;ve been on the fence myself, I started my career with alert() debugging, and these days I do a mix of using breakpoints and just writing logs in the my code. Sometimes, when I remember this feature even exists, I use &lt;a href=&quot;https://devtoolstips.org/tips/en/use-logpoints/&quot;&gt;logpoints&lt;/a&gt; too, and this saves me a trip to my editor and shame when I realize I committed a leftover &lt;code&gt;console.log()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But whichever debugging technique you use, I believe both are limited and there should be a better way. Let&#39;s review a quick example that will help make the point.&lt;/p&gt;
&lt;p&gt;Let&#39;s imagine we have a function that retrieves a collection of items and then highlights them.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; els &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAllItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  els&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;highlightItem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the highlighting function is quite simple: it is given a DOM element and adds a background color to it:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;highlightItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16777215&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&#39;s imagine that the &lt;code&gt;getAllItems&lt;/code&gt; function calls through to other functions which, themselves call yet other functions. The idea here is the process is not straightforward. Few codebases are. These could be spread across many files, authored by different people, with lots of complexity involved, such that bugs can creep in unnoticed.&lt;/p&gt;
&lt;p&gt;In the example below, I&#39;ve just added 4 extra functions to make my point. Even just 20 or so lines of code make it hard to spot the problem at a glance, so imagine if this was a much more complicated code base.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAllItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAppItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAppItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWrapperItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getListItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getWrapperItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ul&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;getMiscEl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMiscEl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getListItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;li&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the problem is that &lt;code&gt;getMiscEl&lt;/code&gt; returns &lt;code&gt;undefined&lt;/code&gt;, and that this values ends up in the list of items returned by &lt;code&gt;getAllItems&lt;/code&gt;. So one of the items that we&#39;ll try to highlight later will cause a runtime error.&lt;/p&gt;
&lt;p&gt;Opening DevTools and checking the console would produce an error stack trace similar to this:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Uncaught TypeError: Cannot read properties of undefined (reading &#39;style&#39;)
    at highlightItem (test.html:112)
    at Array.forEach (&lt;anonymous&gt;)
    at start (test.html:81)
    at test.html:115&lt;/anonymous&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The error message is quite clear, we know the &lt;code&gt;highlightItem&lt;/code&gt; function failed and we know exactly where it failed and why.&lt;/p&gt;
&lt;p&gt;But this trace doesn&#39;t tell us anything about where the root cause actually is. We know &lt;code&gt;highlightItem&lt;/code&gt; was called in a &lt;code&gt;forEach&lt;/code&gt; loop, but which item failed? Which of our many functions returned &lt;code&gt;undefined&lt;/code&gt;? Where did we go wrong?&lt;/p&gt;
&lt;p&gt;At this point, whether you&#39;re a breakpoint kind of developer or a console.log one, you&#39;re faced with the same problem: reading your code backwards from where it failed, randomly pocking at it, adding more breakpoints and logs hoping they&#39;re in a place near enough the actual root cause to make you see the problem.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.replay.io/&quot;&gt;Replay&lt;/a&gt; takes a really interesting, and very different approach to this by constantly recording and letting you just &amp;quot;step back&amp;quot; through your code at any point, reviewing variables. You can just add logs after the fact and get immediate results without re-running the code. It removes the need from setting breakpoints or altering your code with more logs. But to some extent, it&#39;s still a bit of a shot in the dark. You&#39;re still hunting for the root cause.&lt;/p&gt;
&lt;h3&gt;Stack traces&lt;/h3&gt;
&lt;p&gt;All errors that originate from the JavaScript engine you use come with a stacktrace. The engine collects information about the most recent functions that have been called so that when an error occurs, it can give you this information.&lt;/p&gt;
&lt;p&gt;You, as a developer, can even access this via the non-standard &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error/stack&quot;&gt;Error.stack&lt;/a&gt; API. Learn more about the &lt;a href=&quot;https://v8.dev/docs/stack-trace-api&quot;&gt;stack trace API in V8 here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In fact, this stack trace is what the DevTools in your browser use to display errors in the console. They somehow parse the string returned by the API to give you a more interesting representation of it.&lt;/p&gt;
&lt;p&gt;Because JavaScript engines are finely tuned machines that are trying to go as fast as possible, not all function calls are recorded, and only a minimal amount of information about them is recorded: there is no information about arguments and returned values.&lt;/p&gt;
&lt;p&gt;Moreover, and this is crucial here, the engine only cares about the functions that correspond to the current call stack. In our case, &lt;code&gt;highlightItem&lt;/code&gt; failed, and we only get information about what called it: &lt;code&gt;forEach&lt;/code&gt;, and before that &lt;code&gt;start&lt;/code&gt;. We have no information whatsoever about the fact that &lt;code&gt;getAllItems&lt;/code&gt; was called before. That call worked fine and returned, it&#39;s long been popped from the stack.&lt;/p&gt;
&lt;h3&gt;Tracing instead?&lt;/h3&gt;
&lt;p&gt;What if we could move beyond the error stack trace performance limitation and add more information to it?&lt;/p&gt;
&lt;p&gt;What if we could also keep previous stacks in memory, so that the information about &lt;code&gt;getAllItems&lt;/code&gt; having been called is still here when the error occurs.&lt;/p&gt;
&lt;p&gt;And what if we also recorded the arguments and return values for each function in the trace.&lt;/p&gt;
&lt;p&gt;We don&#39;t want to slow down the engine at all, so that&#39;s a concern. But I want to believe that in a &amp;quot;developer&amp;quot; mode we could get to a trace like this:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;start (test.html:81), args &#39;[]&#39;
    getAllItems (test.html:84), args &#39;[]&#39;, return &#39;[Node,Node,undefined,Node,Node]&#39;
      getAppItems (test.html:88), args &#39;[]&#39;, return &#39;[Node,Node,undefined,Node,Node]&#39;
          getWrapperItems (test.html2), args &#39;[]&#39;, return &#39;[Node,Node,undefined,Node]&#39;
              getMiscEl (test.html:101), args &#39;[]&#39;, return &#39;udefined&#39;
          getListItems (test.html:105), args &#39;[]&#39;, return &#39;[Node]&#39;
    Array.forEach (&lt;anonymous&gt;)
        highlightItem (test.html:112), args &#39;[Node,Node,undefined,Node,Node]
            /!&#92; Uncaught TypeError: Cannot read properties of undefined (reading &#39;style&#39;)&lt;/anonymous&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this trace, not only do we have the same information we have in normal error stack traces (i.e. the fact that &lt;code&gt;highlightItem&lt;/code&gt; failed because the provided element was undefined), but we also see preceding calls and their return values.&lt;/p&gt;
&lt;p&gt;We can clearly see that &lt;code&gt;getAllItems&lt;/code&gt; returns a list that contains one undefined item, but we can also track this undefined item all the way down to &lt;code&gt;getMiscEl&lt;/code&gt;, and that&#39;s our root cause!&lt;/p&gt;
&lt;p&gt;No more need to hunt for places to put breakpoints and console.logs in, we can zero in on our root cause much quicker.&lt;/p&gt;
&lt;p&gt;This is nothing more than a trace. Tracing debugging is already a thing, but unfortunately not often used in our JavaScript ecosystem. I vaguely remember Firefox having a tracing debugging feature back in the days, but it got removed at some point.&lt;/p&gt;
&lt;p&gt;The Performance profiler in Chromium browsers comes close, it records the entire execution of a program and provides a nice visualization of it, but it&#39;s not possible to get the arguments or return values.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/performance-profile.png&quot; alt=&quot;Screenshot of part of a performance profile in Edge&#39;s Performance tool, showing a visual trace of the program execution&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m not saying we should introduce (or rather re-introduce) tracing debuggers, but I do think we should mix traces and error stacks together when errors are logged to the console by the JavaScript engine. Because tracing has performance impacts on the engine, we should only do this if DevTools was already opened before. Perhaps we should even only do it when an option is turned on. And I don&#39;t think we should trace everything (like the Performance tool does) but instead only capture the most recent traces since those are likely to contain our root cause anyway.&lt;/p&gt;
&lt;p&gt;The Console is one of the most used tools by JavaScript frontend developers. Teaching everyone about breakpoints isn&#39;t necessarily the way to go, we&#39;ve tried it and not only does it not seem to work but it&#39;s not the best debugging experience either. I believe there are other ways in which we can improve the JavaScript debugging experience that doesn&#39;t rely on making people feel dumb for not using a real debugger.&lt;/p&gt;
&lt;p&gt;Sure, what I&#39;m proposing here might not always be THE way, and it may not always be helpful. But hey, if it helps in even 50% of cases, I think it&#39;s a huge win for everyone.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Styling &lt;select&gt; elements for real</title>
    <link href="https://patrickbrosset.com/articles/2022-05-05-styling-lessselectgreater-elements-for-real/"/>
    <updated>2022-05-05T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-05-styling-lessselectgreater-elements-for-real/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2022/05/05/styling-select-elements-for-real/">https://blogs.windows.com/msedgedev/2022/05/05/styling-select-elements-for-real/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>DevTools interop</title>
    <link href="https://patrickbrosset.com/articles/2022-05-10-devtools-interop/"/>
    <updated>2022-05-10T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-10-devtools-interop/</id>
    <content type="html">
      
        &lt;p&gt;As the &lt;a href=&quot;https://insights.developer.mozilla.org/&quot;&gt;MDN Web Developer Assessment (Web DNA) surveys&lt;/a&gt; showed, the number one frustration web developers have is due to incompatibilities of the web platform between browser. New features don&#39;t usually get implemented at the same time in all browsers, and existing features are not supported in exactly the same in all browsers. This makes it often really hard for developers to know exactly what is safe to use and whether they&#39;ll have an easy time using a particular feature across their target browsers.&lt;/p&gt;
&lt;p&gt;Efforts like &lt;a href=&quot;https://web.dev/compat2021/&quot;&gt;Compat 2021&lt;/a&gt; and &lt;a href=&quot;https://web.dev/interop-2022/&quot;&gt;Interop 2022&lt;/a&gt; are great collaborative projects where browser vendors and other stakeholders are coming together to address this top pain point. Using the findings of the Web DNA surveys (and other criteria), they identified specific areas of the web that suffered from compatibility problems and worked (and are working) together to fix them.&lt;/p&gt;
&lt;p&gt;This is great and needs to continue happening. Going forward, we absolutely need to find ways to continue capturing what the top pain points are and address them in order to create a platform that&#39;s more enjoyable and welcoming to work with.&lt;/p&gt;
&lt;p&gt;Another aspect that shapes a developer&#39;s experience of the web platform is the discoverability and debuggability of its features. It&#39;s great for features to be available, and to be consistently implemented between browsers, but people need to know about them, and have the right type of support when they try to use them.&lt;/p&gt;
&lt;h3&gt;Following in the footsteps of CSS Grid&lt;/h3&gt;
&lt;p&gt;CSS Grid was both very long in the making and really fast.&lt;/p&gt;
&lt;p&gt;It was long because the time it took between the first ideation and prototype (by Internet Explorer), and the time it actually appeared in a CSS spec implemented in major browsers was somewhere between 5 to 10 years.
But it was also really fast because the time between when people starting hearing about grid and the time it became available in all browsers felt instant.&lt;/p&gt;
&lt;p&gt;This really hasn&#39;t happened many times. New web features typically get implemented in each browser engine depending on their own priorities, and those vary.&lt;/p&gt;
&lt;p&gt;Recently, CSS Cascade Layers had similar luck (although it&#39;s not luck at all, but the hard work of many people working together) and got implemented in Chrome, Edge, Firefox and Safari almost at the same time.&lt;/p&gt;
&lt;p&gt;But, coming back to Grid, it&#39;s available everywhere, has great docs, articles, and conference talks, and great debugging tools as well!&lt;/p&gt;
&lt;p&gt;Open Firefox, Chrome, Edge, or Safari DevTools now and take a look at any element on a page that uses CSS Grid. You&#39;ll quickly realize that there&#39;s a tool in there specific to grid which helps with finding out where grid lines are, what their numbers or names are, etc.&lt;/p&gt;
&lt;p&gt;This tooling is awesome for discoverability, easy to find, and makes it much easier to understand the concept of grid at a glance. And it&#39;s great for debuggability as well. With the grid lines overlay, it&#39;s much easier to try things out and take risks. You&#39;re not trying to fix bugs in the dark.&lt;/p&gt;
&lt;p&gt;I remember when the first Grid tooling appeared in Firefox, it was a huge success. For some time, all we could see at CSS conferences were screenshots of the Firefox Inspector. And now every browser has something like this.&lt;/p&gt;
&lt;h3&gt;Tooling as a part of the feature&lt;/h3&gt;
&lt;p&gt;A web feature isn&#39;t only about its implementation in browsers. The implementation is half of the story only.&lt;/p&gt;
&lt;p&gt;There are various other aspects to a successful feature: to what extent it is implemented, whether documentation exists (often on MDN), whether people have spent time creating learning material (blogs, conference talks, tutorials, demos, etc.), whether the browser compatibility information is available (on MDN and can I use), and whether development and/or debugging tooling exists.&lt;/p&gt;
&lt;p&gt;With regards to the browser implementation aspect, projects like Interop 2022 help a lot.
With regards to documentation, things are pretty great thanks to MDN which everybody, especially browser vendors, can contribute to directly, and thanks to the Open Web Docs support.&lt;/p&gt;
&lt;p&gt;But tooling is a whole other story. The various DevTools teams know each other and sometimes work together. It&#39;s a very small world. They mostly agree on the basic feature that every DevTools should have. But it&#39;s all non-standard and DevTools is sometimes a place where fierce competition happens. And this leads to sometimes very different tools being available across browsers.&lt;/p&gt;
&lt;p&gt;From a developer&#39;s point of view, it makes very little sense. Developers mostly don&#39;t choose DevTools, they choose a browser and use the DevTools that comes with it. So this fragmentation between browser is once more a source of frustration. When a developer needs to switch to another browser to test something, it can be frustrating that the same tools don&#39;t just exist.&lt;/p&gt;
&lt;p&gt;DevTools is a big part of a developer experience with the web, and is uniquely place to boost adoption of new features too. It can help developers discover features, debug them, and find help about them (most DevTools link to MDN from their UIs in some way).&lt;/p&gt;
&lt;h3&gt;Proposal&lt;/h3&gt;
&lt;p&gt;I would love for DevTools to be part of efforts like Interop 2022 or/and future similar endeavors.&lt;/p&gt;
&lt;p&gt;There are 15 areas of focus in Interop 2022, almost all of them have to do with CSS. Turns out CSS is an ideal technology for DevTools to help with because of the real-time nature of its debugging loop. DevTools can provide contextual help on CSS features, help adoption with user-friendly wysiwyg-style playful UI, and surface layout engine information when needed.&lt;/p&gt;
&lt;p&gt;While DevTools teams tend to agree on a basic level of feature parity, there&#39;s no concerted effort to bring the same tooling for these important compat features yet, and I&#39;d love for this to change.&lt;/p&gt;
&lt;p&gt;We probably shouldn&#39;t go to the other extreme side of the spectrum either and standardize DevTools. DevTools is a complex user product, not an API, it makes little sense to standardize every aspects of it. It might even kill the existing sane competition that pushes the state of web debugging forward.&lt;/p&gt;
&lt;p&gt;However, it would be beneficial for all the DevTools teams to get together as a group and agree on high-level product UI descriptions for how the debug a number of web features such as the ones identified in the Interop 2022 project.&lt;/p&gt;
&lt;p&gt;An important role tooling can play in this context is one of documentation, adoption, and compatibility. Tooling that helps developers understand what&#39;s available, and how to use it would go a long way.&lt;/p&gt;
&lt;p&gt;The Flexbox tool in Chromium and Safari is a great example because it teaches developers about the various kinds of alignments and how they work.
The Flexbox tool in Firefox goes even further and surfaces some insights about how the flex algorithm works. This is great, but should probably not be in scope for this proposal. Browser vendors should feel free to innovate and go further on their own.&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;color-mix()&lt;/code&gt; function could use some DevTools support to at least show a color swatch next to the nested colors, and one next to the result.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;color-contrast&lt;/code&gt; function is in a similar case. Showing color swatches and an indication of which color won, and what the contrast for each was seems important for the developer experience.&lt;/li&gt;
&lt;li&gt;Scroll snapping is also an area of improvement and adoption could benefit from tooling. There is some in Chromium already and it would be good to standardize on the basic user-stories that have tooling needs.&lt;/li&gt;
&lt;li&gt;Subgrid might also benefit from new tools.&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>4 JavaScript Array methods you didn&#39;t know you needed</title>
    <link href="https://patrickbrosset.com/articles/2022-05-12-4-javascript-array-methods-you-need/"/>
    <updated>2022-05-12T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-12-4-javascript-array-methods-you-need/</id>
    <content type="html">
      
        &lt;p&gt;The Array JavaScript built-in object comes with a lot of methods that make manipulating arrays easy. I often forget about them and end up writing code with complicated for loops that could have been done in much simpler ways.&lt;/p&gt;
&lt;p&gt;So here are 4 methods available to JavaScript Arrays that might help you too.&lt;/p&gt;
&lt;h3&gt;Get the last item in an array&lt;/h3&gt;
&lt;p&gt;Have you ever done &lt;code&gt;myList[myList.length - 1]&lt;/code&gt; to get the last item in the &lt;code&gt;myList&lt;/code&gt; array?&lt;/p&gt;
&lt;p&gt;If so, it just got a lot easier with the &lt;code&gt;Array.at&lt;/code&gt; method. This method takes an single integer argument that indicates which item you want in the array, and a negative number will count back from the last item.&lt;/p&gt;
&lt;p&gt;➡️ &lt;code&gt;const lastItem = myList.at(-1);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Array.at&lt;/code&gt; is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/at#browser_compatibility&quot;&gt;supported&lt;/a&gt; in all browser engines, as well as Node.js and Deno.&lt;/p&gt;
&lt;h3&gt;Flatten an array of arrays&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;Array.flat&lt;/code&gt; method  creates a new array from the values contained in an array of arrays.&lt;/p&gt;
&lt;p&gt;➡️ &lt;code&gt;const list = [0, 1, [2, 3], [4, 5]].flat(); // [0, 1, 2, 3, 4, 5]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The method also takes an optional argument to specify the depth at which the recursive flattening should stop.
So flattening this 3 level deep array for example will require passing &lt;code&gt;3&lt;/code&gt; as an argument to the method:&lt;/p&gt;
&lt;p&gt;➡️ &lt;code&gt;const list = [0, [1, [2, [3]]]].flat(3); // [0, 1, 2, 3]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Array.flat&lt;/code&gt; is also &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/flat#browser_compatibility&quot;&gt;supported&lt;/a&gt; in all browsers, as well as Node.js and Deno.&lt;/p&gt;
&lt;h3&gt;Test an array&#39;s values&lt;/h3&gt;
&lt;p&gt;Sometimes you need to check that at least 1 item in an array matches some condition. You can write a for loop to do it:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; doesItHaveTheValueINeed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; myList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    doesItHaveTheValueINeed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, use the &lt;code&gt;Array.some&lt;/code&gt; method instead!&lt;/p&gt;
&lt;p&gt;➡️ &lt;code&gt;myList.some(condition);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The very similar &lt;code&gt;Array.every&lt;/code&gt; method is also interesting here. It works the same but tests if all items in the array match the condition.&lt;/p&gt;
&lt;p&gt;➡️ &lt;code&gt;myList.every(condition);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some#browser_compatibility&quot;&gt;Support&lt;/a&gt; for both &lt;code&gt;some&lt;/code&gt; and &lt;code&gt;every&lt;/code&gt; is green all across the board too.&lt;/p&gt;
&lt;h3&gt;Get the iteration index in a &lt;code&gt;for..of&lt;/code&gt; loop&lt;/h3&gt;
&lt;p&gt;This last method is &lt;code&gt;Array.entries&lt;/code&gt; which makes this useful tip possible: getting the current iteration index even when using a &lt;code&gt;for..of&lt;/code&gt; loop.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;for..of&lt;/code&gt; loops are great to make your code simpler to write and read:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 😒&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; myList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myList&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 😃&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; myList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However the second, more compact, for loop does not have access to the current iteration index, which the first for loop does. Here&#39;s how to get it:&lt;/p&gt;
&lt;p&gt;➡️ &lt;code&gt;for (const [index, item] of myList.entries()) { ... }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Array.entries&lt;/code&gt; method returns an iterator that provides access to the key and value of each item in the array.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/entries#browser_compatibility&quot;&gt;Support&lt;/a&gt; for &lt;code&gt;entries&lt;/code&gt; is green all across the board too.&lt;/p&gt;
&lt;p&gt;That&#39;s it for this short blog post. I hope these methods come in handy at some point!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Find and manage your installed apps and sites</title>
    <link href="https://patrickbrosset.com/articles/2022-05-18-find-and-manage-your-installed-apps-and-sites/"/>
    <updated>2022-05-18T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-18-find-and-manage-your-installed-apps-and-sites/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2022/05/18/find-and-manage-your-installed-apps-and-sites/">https://blogs.windows.com/msedgedev/2022/05/18/find-and-manage-your-installed-apps-and-sites/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>#30DaysOfSWA: Debug your Static Web Apps with Edge DevTools</title>
    <link href="https://patrickbrosset.com/articles/2022-05-19-30daysofswa-debug-your-static-web-apps-with-edge-devtools/"/>
    <updated>2022-05-19T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-19-30daysofswa-debug-your-static-web-apps-with-edge-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.azurestaticwebapps.dev/blog/devtools-msedge">https://www.azurestaticwebapps.dev/blog/devtools-msedge</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What’s That (Dev) Tool?</title>
    <link href="https://patrickbrosset.com/articles/2022-05-30-whats-that-dev-tool/"/>
    <updated>2022-05-30T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-05-30-whats-that-dev-tool/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2022/05/whats-that-dev-tool/">https://www.smashingmagazine.com/2022/05/whats-that-dev-tool/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>An informal state of PWA, 2022</title>
    <link href="https://patrickbrosset.com/articles/2022-09-08-state-of-pwa/"/>
    <updated>2022-09-08T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-09-08-state-of-pwa/</id>
    <content type="html">
      
        &lt;p&gt;Progressive Web Apps (or PWA) is a topic I find myself quite interested in lately.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you don&#39;t know what a PWA is: it&#39;s a way to build an app that can be used cross-platforms, in browsers, or installed locally on operating systems (mobile and desktop) with just one code base, using web technologies. PWAs are like websites: easy to discover and reach, secure, responsive, etc. but also like apps: installable, work offline, with access to advanced features.&lt;br /&gt;
To learn more, check out &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps&quot;&gt;MDN&lt;/a&gt;, &lt;a href=&quot;https://web.dev/progressive-web-apps/&quot;&gt;web.dev&lt;/a&gt;, or &lt;a href=&quot;https://docs.microsoft.com/microsoft-edge/progressive-web-apps-chromium/&quot;&gt;docs.microsoft.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I love web technologies and to me, being able to use HTML, CSS, and JavaScript, to build real apps that can be installed feels like a super power. &lt;strong&gt;But, I also hear complaints about PWAs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Not having built a PWA in a professional context yet (apart from demo apps), I wanted to understand where these complaints were coming from. So I ran a little survey on Twitter. This was very informal but I got a lot of interesting qualitative feedback.&lt;/p&gt;
&lt;p&gt;Here was my original tweet:&lt;/p&gt;
&lt;p&gt;https://twitter.com/patrickbrosset/status/1567503909110824962&lt;/p&gt;
&lt;p&gt;So, for the rest of this article, I&#39;ll go over what people said were the main blockers. Feel free to check the actual responses to the tweet yourself. I&#39;ll just &lt;strong&gt;summarize the findings&lt;/strong&gt; below. I&#39;ll also just &lt;strong&gt;state the facts&lt;/strong&gt;, not draw any conclusions or image any solutions.&lt;/p&gt;
&lt;h3&gt;1. PWAs are not &amp;quot;real apps&amp;quot; 🔍&lt;/h3&gt;
&lt;p&gt;Users don&#39;t seem to perceive PWAs as real apps. Even when a lot of care is taken in creating native-looking app, there are minute differences in the look and feel, and users can often tell the difference. Browser vendors are always closing the gap, but this is still a problematic area.&lt;/p&gt;
&lt;p&gt;One factor that doesn&#39;t help is that many PWAs out there are &amp;quot;just websites&amp;quot; with a manifest and a service worker. They don&#39;t really try to be more than this and are far from being up to par with their equivalent native apps.&lt;/p&gt;
&lt;h3&gt;2. Steep learning curve 🏔️&lt;/h3&gt;
&lt;p&gt;PWAs are attractive to both web developers (because they know the platform already) and native app developers (because they see a potentially low-cost way to target all platforms with one codebase).&lt;/p&gt;
&lt;p&gt;Unfortunately, PWAs seem to be hard to learn for both groups.&lt;/p&gt;
&lt;p&gt;Native app developers come from a very different development model, and all tutorials out there just assume that people know web development.&lt;/p&gt;
&lt;p&gt;Web developers also seem to be having a hard time learning the APIs and understanding how to use service workers for example.&lt;/p&gt;
&lt;h3&gt;3. Safari 🍏&lt;/h3&gt;
&lt;p&gt;Safari seems to remain a major blocker in PWA adoption with people complaining that the browser does not support all functionalities, and that getting into the store is impossible.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pwabuilder.com/&quot;&gt;PWABuilder.com&lt;/a&gt; is a big help nowadays, but doesn&#39;t solve the &lt;a href=&quot;https://firt.dev/notes/pwa-ios/&quot;&gt;compatibility gap&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;4. Offline 🔌&lt;/h3&gt;
&lt;p&gt;Apps just work™️, whether you are connected to the internet or not. They usually know how to gracefully handle offline mode even if the content they provide comes from the internet.&lt;/p&gt;
&lt;p&gt;To create a convincing app with PWA technology requires creating an awesome offline experience, and this seems to be a major pain point as well.&lt;/p&gt;
&lt;p&gt;Understanding how service workers work, handling spotty connections, knowing if what is stored locally will be available over time aren&#39;t easy things to do.&lt;/p&gt;
&lt;h3&gt;5. Testing 🧪&lt;/h3&gt;
&lt;p&gt;PWAs are hard to test. The main pattern I found here was about testing PWA features.&lt;/p&gt;
&lt;p&gt;People complain that changes to the web app manifest (for example when using the share target API) are really hard to test and require uninstalling and reinstalling the app.&lt;/p&gt;
&lt;h3&gt;6. Icons 🖼️&lt;/h3&gt;
&lt;p&gt;Generating icons for all kinds of devices seems to be a pain point too. Multiple sizes are required per operating systems, and especially on Apple devices.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;That&#39;s it for now. I hope this quick survey can help inform some decisions out there. I think a lot of it is addressable, and in fact, being addressed now already. But there is still room for a lot of improvements: better features, better docs, better development and testing tools, more libraries, simpler APIs, and more.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Closing a 30 pixel gap between native and web</title>
    <link href="https://patrickbrosset.com/articles/2022-09-27-closing-a-30-pixel-gap-between-native-and-web/"/>
    <updated>2022-09-27T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-09-27-closing-a-30-pixel-gap-between-native-and-web/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2022/09/27/closing-pixel-gap-native-web-window-controls-overlay/">https://blogs.windows.com/msedgedev/2022/09/27/closing-pixel-gap-native-web-window-controls-overlay/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Is the developer experience on the Web so terrible?</title>
    <link href="https://patrickbrosset.com/articles/2022-09-29-is-web-dx-so-terrible/"/>
    <updated>2022-09-29T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-09-29-is-web-dx-so-terrible/</id>
    <content type="html">
      
        &lt;p&gt;Let me start with a caveat: I&#39;ve not done actual professional web development for about 10 years I think. I used to work in companies where we&#39;d do big fancy web apps for clients, and we&#39;d have a backend and a frontend and some database or whatever, the whole thing. But ever since I started working in browser maker companies, even thought I use web technologies all the time, I&#39;m using them very differently than most web devs out there.&lt;/p&gt;
&lt;p&gt;The most development I do today is limited to front-end only, and very focused and small scale demo apps. And to do these apps, I absolutely never rely on frameworks, and very rarely on libraries.&lt;/p&gt;
&lt;p&gt;Again, these apps are small, and I probably just don&#39;t need external libraries at all. But I&#39;ve also come to greatly appreciate the power that comes with the web platform alone.&lt;/p&gt;
&lt;p&gt;So I keep wondering, &lt;strong&gt;what is it with the web that people find so awful that they keep inventing new frameworks every year&lt;/strong&gt;? What is it with vanilla JavaScript, HTML, and CSS that people prefer inventing different languages and entire paradigms to avoid writing them?&lt;/p&gt;
&lt;p&gt;Is the web platform really so awful that you can&#39;t do any professional development without first adding a big abstraction over it?&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Over the past 10 years or so, we&#39;ve slowly but very surely transitioned to a state where frameworks are the norm, and I think it&#39;s a problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strong opinion loosely held time!&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web devs don&#39;t take the time to learn the web platform and understand what&#39;s possible with it alone anymore. They learn frameworks first.&lt;/li&gt;
&lt;li&gt;Learning a framework runs the risk of not being able to easily adapt when switching teams, or when your team switches to the new flavor of the year.&lt;/li&gt;
&lt;li&gt;Because of these frameworks, the webpages grew in size by a whole lot.&lt;/li&gt;
&lt;li&gt;It&#39;s common to encounter front-end performance issues that are really hard to fix because you don&#39;t really have access to the underlying mechanics of the framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://almanac.httparchive.org/en/2022/&quot;&gt;2022 HTTP Archive Web Almanac&lt;/a&gt; is, once again this year, pretty depressing:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In the 10-year period between June 2012 to June 2022, the median page weight increased by 221%, or 1.6 MB, for desktop page loads, &lt;strong&gt;594%&lt;/strong&gt;, or 1.7 MB for mobile page loads.&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
Source: &lt;a href=&quot;https://almanac.httparchive.org/en/2022/page-weight#request-bytes&quot;&gt;https://almanac.httparchive.org/en/2022/page-weight#request-bytes&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;While magnificent when used in moderation, the intoxicating allure of JavaScript can also lead to serious performance, search engine optimization, and user experience issues.&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
Source: &lt;a href=&quot;https://almanac.httparchive.org/en/2022/page-weight#javascript&quot;&gt;https://almanac.httparchive.org/en/2022/page-weight#javascript&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now I&#39;m not saying that all JavaScript is bad, but using a framework inherently ties you to JavaScript, even for the things that you might have been able to do with just HTML and CSS.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;One of the reasons that frameworks are so popular these days is the promise they make to web developers: &lt;em&gt;&amp;quot;use me and you&#39;ll have a much better experience than if you don&#39;t&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So, &lt;strong&gt;developers choose frameworks in part due to their developer experience (or DX), and often don&#39;t really take the final user experience (or UX) into account&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sure, when you spend 8 hours a day in front of the computer writing code for an app, DX is important. You need efficient tools, pleasant languages, fast turn around times. But at the end of the day, who pays the bill? Your users do! It&#39;s too easy to get blinded by amazing DX and forget that what really matters is the final experience that your users will have (and I say even if I work on a team which name contains &amp;quot;developer experience&amp;quot;).&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Please, please, don&#39;t get blinded!&lt;/strong&gt; Use the frameworks and libraries that make sense for you to deliver the best UX possible. But also learn the web platform from the ground up. Take time to understand how web browsers work and render webpages. Learn HTML, CSS, JavaScript. And keep an eye, if you can, on the new things.&lt;/p&gt;
&lt;p&gt;The web platform keeps evolving all the time. More and more things get added to fill gaps that web developers find along the way. JQuery was (still is in fact) such a great JavaScript library, and it pushed the web platform forward so much that one might not need JQuery anymore today.&lt;/p&gt;
&lt;p&gt;The web platform is never at rest, there are always new things coming up that make it easier and easier to work with.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Before closing, I want to list some examples of things you can do with the web that I consider great DX and which maybe people aren&#39;t that aware of just yet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog&quot;&gt;The Dialog element&lt;/a&gt;: this element isn&#39;t exactly new, but its browser support recently got a whole lot better and you can use it pretty much everywhere now. It gives you a way to display a dialog box on top of your regular web content without having to include a component library to it, without the z-index nightmare, and with all of the right accessibility semantics built-in. Check it out!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components&quot;&gt;Web Components&lt;/a&gt; are definitely not new, but are very well supported too, and they give you a built-in way to create configurable, isolated, reusable UI components in your app. Sure, if you&#39;ve used frameworks that make it easy to use components before, there will be a learning curve. But they come with everything you need as a developer. A really simple way to include them in a webpage, super simple props passing, templating, and isolation of styles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules&quot;&gt;JavaScript modules&lt;/a&gt;. Yes, modules just work by default on the web today! You can load them with &lt;code&gt;&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;&lt;/code&gt; in a page, and then deal with dependencies with the &lt;code&gt;import ... from ...&lt;/code&gt; statement and &lt;code&gt;export&lt;/code&gt; statement. What&#39;s even better? Modules are available in workers too (except on Firefox, but this is &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1247687&quot;&gt;coming soon&lt;/a&gt;), and &lt;a href=&quot;https://chromestatus.com/feature/5948572598009856&quot;&gt;CSS&lt;/a&gt; and &lt;a href=&quot;https://chromestatus.com/feature/5749863620804608&quot;&gt;JSON&lt;/a&gt; modules are coming soon too.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Building desktop apps without native code</title>
    <link href="https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/"/>
    <updated>2022-10-06T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/</id>
    <content type="html">
      
        &lt;p&gt;I&#39;ve had a passion for the web ever since the end of the 90&#39;s and have been working with web technologies since. In fact, here is a photo of me surfing the web like a cool kid on Netscape 2 in 1996 😀.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/me-in-96.jpg&quot; alt=&quot;A photo of me, in front of an old Macintosh computer, surfing the web with Netscape Navigator 2, in 1996&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But I&#39;ve never formally studied computer science. I&#39;m really bad at implementing complex algorithms, I don&#39;t really know any system-level programming, and I&#39;ve never built anything else than web sites/apps. I just picked up some web dev skills along the way.&lt;/p&gt;
&lt;p&gt;I sometimes wish I knew more, and especially I wish I knew how to code &amp;quot;real&amp;quot; &lt;sup id=&quot;note-1&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; desktop applications. Why? Well, I use a desktop computer for work, and most people with office jobs do too, so I use desktop apps all the time.&lt;/p&gt;
&lt;p&gt;Beside a quick introduction to &lt;a href=&quot;https://en.wikipedia.org/wiki/Delphi_(software)&quot;&gt;Delphi&lt;/a&gt; many years ago, I never learnt the languages, libraries, and frameworks that one seems to require to build a desktop application.&lt;/p&gt;
&lt;p&gt;But with the web constantly evolving, I ask myself: what makes a web and a desktop app different? Do I really need to learn C++ to implement a desktop app? How far could I go with just the web?&lt;/p&gt;
&lt;p&gt;So I created an app to test this! And, in this article, I&#39;ll go over how I used as many of the technologies available to make it feel like it really belongs on a desktop operating system.&lt;/p&gt;
&lt;h3&gt;PWAmp&lt;/h3&gt;
&lt;p&gt;Here&#39;s the app we&#39;ll use in this article: PWAmp (pronounced P-W-Amp), &lt;em&gt;a skinable music player app that plays your favorite local audio files&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/MicrosoftEdge/Demos/main/pwamp/screenshot-playlist.png&quot; alt=&quot;The main user interface of the PWAmp app, showing a playlist of songs and the usual play, pause, next, previous buttons&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can &lt;strong&gt;&lt;a href=&quot;https://microsoftedge.github.io/Demos/pwamp/&quot;&gt;access the app live here&lt;/a&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;a href=&quot;https://github.com/MicrosoftEdge/Demos/tree/main/pwamp&quot;&gt;check out the code here&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This is a demo app only. Even though I took some care in making sure it works on different browsers and devices, I still mostly tested it on Chromium-based browsers, and on desktop operating systems. Expect bugs elsewhere.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;But what defines a desktop app exactly?&lt;/h3&gt;
&lt;p&gt;Before we start, think of the apps you usually use on your desktop computer, and compare them to browser-based web apps. You&#39;ll likely notice a few key differences, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You install, not navigate to, desktop apps on your computer, and you can access them from an icon somewhere.&lt;/li&gt;
&lt;li&gt;They have their own separate windows, and they can display their own content anywhere within these windows.&lt;/li&gt;
&lt;li&gt;Your desktop apps always work, whether you have access to the internet or not. At the very least, they start and show something.&lt;/li&gt;
&lt;li&gt;They can handle local files natively.&lt;/li&gt;
&lt;li&gt;They integrate with other apps and with the operating system in some ways.&lt;/li&gt;
&lt;li&gt;They perform non-trivial computing tasks.&lt;/li&gt;
&lt;li&gt;You can find them on app stores.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, our goal here is to make an app, using only standard web technologies, that has the same general traits.&lt;/p&gt;
&lt;h3&gt;Making the app installable and have its own icon&lt;/h3&gt;
&lt;p&gt;For this, we&#39;ll need to make the app a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Progressive_web_apps&quot;&gt;Progressive Web App&lt;/a&gt; (or PWA).&lt;/p&gt;
&lt;p&gt;PWA is a mix of different web features, but one thing that it makes possible is to install web apps on devices. This originated on mobile as a way to pin websites to your home screen. People love app icons on their home screens, and so a natural evolution of the web was to, also, have access to the home screen.&lt;/p&gt;
&lt;p&gt;Years have passed, the technology has matured, browser support has improved, and now PWA is a great option for building installable cross-browser/cross-device web apps. And it&#39;s an awesome way to build desktop apps too.&lt;/p&gt;
&lt;p&gt;To do this, three building blocks are needed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A secure connection.&lt;/li&gt;
&lt;li&gt;A web app manifest.&lt;/li&gt;
&lt;li&gt;A service worker.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can quickly get the first building block out of the way. It means the app needs to be served over the &lt;code&gt;HTTPS&lt;/code&gt; protocol. But while developing with a local web server and the &lt;code&gt;localhost&lt;/code&gt; domain, that&#39;s not even needed.&lt;/p&gt;
&lt;p&gt;Let&#39;s move on to the web app manifest. Let&#39;s create a file called &lt;code&gt;manifest.json&lt;/code&gt; at the root of the web project and fill it with the following content:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PWAmp music player&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;short_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PWAmp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A skinable music player app to play your favorite mp3 files&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;lang&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;en-US&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;start_url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;display&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;standalone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;icons&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/favicon-96.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;96x96&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/favicon-128.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;128x128&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/favicon-256.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;256x256&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/favicon-512.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;512x512&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, reference this file in the &lt;code&gt;index.html&lt;/code&gt; home page:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;manifest&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;manifest.json&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells the browser that the PWAmp website is actually an app, what&#39;s its name, description, the icon it wants to use, and so on. There&#39;s more we could do here, but this is a good start.&lt;/p&gt;
&lt;p&gt;And the last thing to turn the app into a PWA is to add a Service Worker. We&#39;ll talk more about this special type of worker later, but for now, this is a mandatory step to make sure the app can be installed by the browser.&lt;/p&gt;
&lt;p&gt;So let&#39;s create a JavaScript file at the root of the project, called &lt;code&gt;sw.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fetch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// For now let&#39;s not do anything.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And let&#39;s load this file at the end of &lt;code&gt;index.html&lt;/code&gt; home page too:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceWorker&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./sw.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it, those are the only three mandatory technical pieces we need to make the website a PWA. Now, our goal is to go much further of course, and we&#39;ll see this in the rest of the article. But for now, this is enough to make supporting browsers realize that PWAmp is an installable app.&lt;/p&gt;
&lt;p&gt;With this done, we can install the app on desktop, using a supporting browser &lt;sup id=&quot;note-2&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, by clicking on the app installation button in the address bar:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/edge-install-dialog.png&quot; alt=&quot;PWAmp, loaded in Microsoft Edge, showing the install app button in the address bar, and the app installation prompt&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once installed, PWAmp can be accessed from the Taskbar and the Start menu, and it also appears in the app switcher when using &lt;kbd&gt;alt+tab&lt;/kbd&gt;. Basically, everywhere you expect to find apps in the operating system (Windows, macOS, Linux).&lt;/p&gt;
&lt;h3&gt;Controlling the entire app&#39;s window&lt;/h3&gt;
&lt;p&gt;Now when we launch the app, it opens in its own separate window, not in the browser window. And its window doesn&#39;t have any navigation buttons, URL bar, or tabs. Perfect!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pwamp-default-window.png&quot; alt=&quot;PWAmp in its own window, showing the app with only a title bar at the top&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But the default system title bar at the top is a bit out of place. I don&#39;t think we need the title of the app to be visible, desktop apps don&#39;t really do that anymore. Plus the color of the titlebar contrasts with the color of the app and I don&#39;t really like that, and really, I&#39;d love to be able to use this entire titlebar area for my own web content.&lt;/p&gt;
&lt;p&gt;First, let&#39;s change the color of the titlebar. It turns out that the web app manifest allows to define a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Manifest/theme_color&quot;&gt;theme color&lt;/a&gt;. This color is used by different operating systems in different ways, but on desktop it&#39;s used as the titlebar color. So let&#39;s add this to our &lt;code&gt;manifest.json&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...
  &lt;span class=&quot;token property&quot;&gt;&quot;theme_color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#181c25&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And after re-installing the app, here&#39;s what we get:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pwamp-titlebar-theme.png&quot; alt=&quot;PWAmp in its own window, with the titlebar of the same color as the app, blending in&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That&#39;s much better, but we can go even further with a new feature called &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window_Controls_Overlay_API&quot;&gt;Window Controls Overlay&lt;/a&gt; &lt;sup id=&quot;note-3&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-3&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s add this to our web app manifest:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...
  &lt;span class=&quot;token property&quot;&gt;&quot;display_override&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;window-controls-overlay&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This enables the Window Controls Overlay feature in the installed app. Now, to take advantage of it we need two other things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Users need to opt-in. This feature is, by default, off. Once they install the app, the only difference is that they&#39;ll have a new chevron icon in the titlebar allowing them to opt-in to the feature.&lt;/li&gt;
&lt;li&gt;And we need to add the necessary code to handle the feature properly in our layout.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Window Controls Overlay feature works this way: it removes the default titlebar, provides the app access to the entire surface area of the app window, and displays the system critical buttons (close, maximize, minimize, etc.) as an overlay on top of the web content. A bit like when dealing with a mobile device notch in your CSS code &lt;sup id=&quot;note-4&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-4&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, you also need to deal with the overlay so as not to overlap with your app content.&lt;/p&gt;
&lt;p&gt;If this wasn&#39;t clear, here is the illustration from MDN about it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developer.mozilla.org/docs/Web/API/Window_Controls_Overlay_API/desktop-pwa-window-wco.png&quot; alt=&quot;Illustration of a PWA installed on desktop with the Window Controls Overlay feature, with window control buttons, no title bar, and web content spanning the whole window&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To work with this, we can use the new &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/env#using_env_to_ensure_content_is_not_obscured_by_window_control_buttons_in_desktop_pwas&quot;&gt;&lt;code&gt;titlebar-area-*&lt;/code&gt; CSS environment variables&lt;/a&gt;. In our case, we only want to make sure the first DOM element at the top of the app doesn&#39;t overlap with the titlebar, so we can use something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.player&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* If WCO is enabled, push the player down a little to avoid overlapping the titlebar area.
     Otherwise, fall back to 1rem. */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-block-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;titlebar-area-height&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the element a &lt;code&gt;1rem&lt;/code&gt; padding all around when the app is not installed, or installed but on a device that does not support Window Controls Overlay, or when the user hasn&#39;t opted-in to the feature. But when the user opts-in, then the top padding will be &lt;code&gt;titlebar-area-height&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/wco-top-padding.png&quot; alt=&quot;To player part of PWAmp, with its padding area highlighting, showing that the top padding is bigger than the other padding&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The above image shows that the top padding is bigger than on the other sides, and it matches exactly the size of the system critical button overlay.&lt;/p&gt;
&lt;h3&gt;Making the app always work&lt;/h3&gt;
&lt;p&gt;This is where the Service worker we made before comes in. A Service worker is a special type of web worker. Like other web workers, it runs in a thread that&#39;s separate from the webpage. But unlike other web workers, its lifecycle is mostly managed by the browser.&lt;/p&gt;
&lt;p&gt;The service worker really acts as a proxy between the webpage and the web server, and as such can intercept requests made from the page to the server and respond in its place.&lt;/p&gt;
&lt;p&gt;This is very interesting for implementing great offline support for the app. Because the worker runs in the browser, it&#39;s always available even when the network can&#39;t be accessed, and it can therefore be used to return previously cached responses.&lt;/p&gt;
&lt;p&gt;Now, our app is pretty simple in that it doesn&#39;t require any dynamic resources from the server. It&#39;s a client-side app to start with. So all it needs is a few JavaScript and CSS files and then songs are played locally in the browser.&lt;/p&gt;
&lt;p&gt;So we can implement a cache-first strategy where we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First cache all of the resources we need when the app is first accessed.&lt;/li&gt;
&lt;li&gt;And then intercept all requests to resources and respond to them by getting the corresponding resources from the cache.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So let&#39;s cache our resources first:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CACHE_NAME&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;pwamp-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Those are all the resources our app needs to work.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// We&#39;ll cache them on install.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;INITIAL_CACHED_RESOURCES&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;/skins/default.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;/about.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;/album-art-placeholder.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;/app.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// On install, fill the cache with all the resources we know we need.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Install happens when the app is used for the first time, or when a&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// new version of the SW is detected by the browser.&lt;/span&gt;
self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CACHE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INITIAL_CACHED_RESOURCES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then let&#39;s respond to requests with the cached resources:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// A cache-first strategy is used, with a fallback to the network.&lt;/span&gt;
self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fetch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CACHE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cachedResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachedResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cachedResponse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// If the response couldn&#39;t be found in the cache, try the network.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fetchResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; fetchResponse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, that should be enough to make our app always work, just like a &amp;quot;real&amp;quot; desktop app. No need for an internet connection to load the app and play our songs since when the app was first accessed, all of its resources got downloaded in the cache.&lt;/p&gt;
&lt;p&gt;In fact, let&#39;s check by switching the app to offline from the DevTools Network tool.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/offline-pwamp.png&quot; alt=&quot;PWAmp with DevTools next to it, in offline mode, showing that requests still succeed and are all fulfilled by the Service Worker&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is only the tip of the iceberg however. Service workers can do much more, and there are some subtle use cases to take into account. One example is app updates. Let&#39;s say we want to roll out a new version of PWAmp. How do we make sure all clients get the updated resources instead of always loading the old ones from the cache?&lt;/p&gt;
&lt;p&gt;There are solutions to this, and one common way is to add a suffix to the file names that depends on the version (like a hash of the file content for example). But in the interest of keeping this article simpler, let&#39;s not dive into this topic.&lt;/p&gt;
&lt;p&gt;I would strongly encourage you to use a library specifically for this though. &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot;&gt;Workbox&lt;/a&gt; is probably the most used service worker tool and library that should make implementing whatever strategy you want much simpler.&lt;/p&gt;
&lt;h3&gt;Handling local files&lt;/h3&gt;
&lt;p&gt;PWAmp plays local audio files, that&#39;s the whole point of the app. So it&#39;d be great if it could handle these audio files natively.&lt;/p&gt;
&lt;p&gt;If you double-click on an mp3 file on your computer right now, what do you think happens? The operating system looks up what app (or apps) is (or are) associated with this particular file type, and then launches the associated app with your file.&lt;/p&gt;
&lt;p&gt;Can we register PWAmp as one off the handlers for a particular file type? Well, yes, it turns out we can! File handling is a recent PWA feature that can be used in Chromium browsers now!&lt;/p&gt;
&lt;p&gt;To learn more, check out &lt;a href=&quot;https://patrickbrosset.com/articles/2021-10-22-handling-files-on-the-web/#handling-files-like-compiled-apps-with-pwas&quot;&gt;Handling files on the Web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To make it work, we need two main pieces of code. First, let&#39;s define the file handler in our web app manifest (&lt;code&gt;manifest.json&lt;/code&gt;), so that the operating system (OS) knows to register the app as a handler when the app is installed:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...
  &lt;span class=&quot;token property&quot;&gt;&quot;file_handlers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;accept&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/wav&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.wav&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/mpeg&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.mp3&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/mp4&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.mp4&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/aac&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.adts&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/ogg&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.ogg&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/webm&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.webm&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;audio/flac&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;.flac&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code snippet, we&#39;re asking the OS to add PWAmp as one of the handlers for a number of audio file formats, and to launch the app at its root URL (the &lt;code&gt;action&lt;/code&gt; property) when a file is opened.&lt;/p&gt;
&lt;p&gt;Now, we actually need to handle the file, and we can do this with the &lt;code&gt;launchQueue&lt;/code&gt; JavaScript API:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Do something with files here&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Check that the launchQueue API is supported.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;launchQueue&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  launchQueue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConsumer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;launchParams&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;handleFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;launchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&#39;s all we need! Now when we re-install the app locally, we can right-click on an audio file on the desktop for example, and choose &amp;quot;Open with PWAmp&amp;quot;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/open-song-with-pwamp.png&quot; alt=&quot;A Windows explorer window with mp3 files, and the Open With context menu showing that PWAmp is part of the list&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Integrating with other apps and the operating system&lt;/h3&gt;
&lt;p&gt;In order to make our app feel like it really belongs in the operating system, it needs to integrate more deeply into it. There are many ways to do this, and not all apps have the same needs, but here are a few examples of things desktop apps commonly do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handling custom links. When you share a Spotify link with a friend and they click on it, then the app opens up, not the website.&lt;/li&gt;
&lt;li&gt;Sharing files and other content to and from other apps. Apps can share things with each other without knowing anything about the other app. The shared content goes through the OS sharing dialog.&lt;/li&gt;
&lt;li&gt;Exposing common tasks as shortcuts. Right-clicking on an app icon (or long tapping, on mobile) usually displays common tasks. This helps make the app easier to use and more naturally integrated in the OS.&lt;/li&gt;
&lt;li&gt;Exposing widgets. Most OSs have widget dashboards that contain useful little pieces of apps that let you do common tasks quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For PWAmp, let&#39;s add support for link handling and file sharing. And then we&#39;ll talk about the two other ones.&lt;/p&gt;
&lt;h4&gt;Handling links and protocols&lt;/h4&gt;
&lt;p&gt;Our PWA app can handle links in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Handle http links to the app&lt;/strong&gt;. The app lives at &lt;a href=&quot;https://microsoftedge.github.io/Demos/pwamp/&quot;&gt;https://microsoftedge.github.io/Demos/pwamp/&lt;/a&gt; and clicking on a link that belongs to the app scope will open the app, and not the web browser. This way you can easily deep link to any place in the app by just clicking on a URL. Now, I&#39;m a bit fuzzy on the support for this. I know that with Microsoft Edge on Windows, this just works, but I&#39;m not sure about other browsers and operating systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handle protocols&lt;/strong&gt;. We can add support for existing or custom protocols to the app. Let&#39;s add support for the &lt;code&gt;web+amp:&lt;/code&gt; custom protocol.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To do this, we need to add a new manifest member in our &lt;code&gt;manifest.json&lt;/code&gt; web app manifest file:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...
  &lt;span class=&quot;token property&quot;&gt;&quot;protocol_handlers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;protocol&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;web+amp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/?cmd=%s&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will tell the operating system to register PWAmp as a handler for any link that uses the &lt;code&gt;web+amp&lt;/code&gt; protocol. The &lt;code&gt;url&lt;/code&gt; property tells the OS to start our app at this url, and the &lt;code&gt;%s&lt;/code&gt; placeholder will get replaced with the full custom protocol link that was used.&lt;/p&gt;
&lt;p&gt;For example, say we want to handle links such as &lt;code&gt;web+amp:remote-song:https://example.com/song.mp3&lt;/code&gt; to add new songs to the library from the internet in our app, then upon handling, our app will be opened at the following url: &lt;code&gt;/?cmd=web+amp:remote-song:https://example.com/song.mp3&lt;/code&gt;. And all we need to do now is, when the app starts, check if the &lt;code&gt;cmd&lt;/code&gt; parameter is present and parse it. Let&#39;s do this now:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// When the app starts, let&#39;s parse the current location.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; commandUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;cmd&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commandUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The link is expected to be in the form of web+amp:command:arguments.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Remove the web+amp: custom protocol part, and split the rest around the colon character.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; commandAndArg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; commandUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;web+amp:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; commandAndArg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;:(.+)&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;remote-song&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// arg is the link to a song on the internet, let&#39;s add it to the library.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now people can share links to the app with each other. You could send a link to some song hosted somewhere to a friend via a chat message, and when they click on it, the app would open up and add the song, making the whole thing feel much more seamless.&lt;/p&gt;
&lt;h4&gt;Sharing to and from the app&lt;/h4&gt;
&lt;p&gt;Now, let&#39;s add some file sharing to the app as well. It&#39;s common to be able to share links, photos, or files from an app and send them to another app. Think of a mobile device for example, where you want to share a photo on Twitter. One common way to do this is to first go in the photos app, select the photo and tap &lt;strong&gt;Share&lt;/strong&gt;. And from there, select the Twitter app. The Twitter app then opens up and preloads the photo in a draft tweet, ready to send.&lt;/p&gt;
&lt;p&gt;Could we do this with PWAmp as well? You&#39;ve guessed it, yes we can!&lt;/p&gt;
&lt;p&gt;Sharing from the app is quite straightforward as the &lt;code&gt;navigator.share&lt;/code&gt; API has been supported by most browsers for some time already &lt;sup id=&quot;note-5&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-5&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. So we can use it to share audio songs with other apps:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;shareButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Retrieve the JavaScript File object for the song we want to share.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCurrentSongFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dataToShare &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCurrentSongTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;share &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canShare &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; 
      &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canShare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataToShare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// We can&#39;t share this file. Bail out.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Let&#39;s go ahead and share the data. This will make the operating&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// system share dialog appear, showing apps that can receive the&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// shared data.&lt;/span&gt;
  navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;share&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataToShare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, we can share from the app! Notice in the screenshot below that Windows suggests Mail, Teams, and Outlook as possible targets for the shared song.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pwamp-sharing.png&quot; alt=&quot;The Windows share dialog, shown above the PWAmp app, with several apps that can receive the shared file&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now let&#39;s do the other way around and become a share target for audio files by using the &lt;a href=&quot;https://web.dev/web-share-target/&quot;&gt;Web Share Target API&lt;/a&gt;! For this, we need to add a new manifest member again:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...
  &lt;span class=&quot;token property&quot;&gt;&quot;share_target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/handle-shared-song&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;enctype&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;params&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;files&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;audioFiles&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;accept&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/wav&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/mpeg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/mp4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/aac&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/ogg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/webm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;audio/flac&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This member is a little bit more complicated than the ones we saw before. Here is how being a share target works: we tell the operating system that we want to be a target for shared data by adding the &lt;code&gt;share_target&lt;/code&gt; member. We then say what type of shared data we want to handle with the &lt;code&gt;params&lt;/code&gt; property. And we then define how we want to handle the shared data.&lt;/p&gt;
&lt;p&gt;It&#39;s important to understand that the OS share dialog with send the shared data to your app as if it was a form submission. That&#39;s why we need to define an &lt;code&gt;action&lt;/code&gt;, a &lt;code&gt;method&lt;/code&gt;, and a potential &lt;code&gt;enctype&lt;/code&gt; just like when using the HTML &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element. In our case, because we want to handle shared files, we need to use the &lt;code&gt;POST&lt;/code&gt; method and &lt;code&gt;multipart/form-data&lt;/code&gt; encoding, which makes it a little more complicated.&lt;/p&gt;
&lt;p&gt;When a file is shared and PWAmp is selected as the target for it, the OS will launch our app at &lt;code&gt;handle-shared-song&lt;/code&gt; with a POST request and some form data attached to it. Now, remember, our app doesn&#39;t really have a server. It does have a simple static server that contains the resources for the app, but it can&#39;t receive POST requests. Not only that, but we want this to also work offline. So let&#39;s reach out to our service worker once again:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Let&#39;s add a special fetch handler for song file sharing.&lt;/span&gt;
self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Only care about POST requests on the /handle-shared-song URL.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;method &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/handle-shared-song&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// First, let&#39;s immediately redirect to the start URL, we&#39;ll be&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// handling the file in the background, in the service worker, but&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// there&#39;s no special page to load here, just go back to the root.&lt;/span&gt;
  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// And now, handle the shared data.&lt;/span&gt;
  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; files &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;audioFiles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// That&#39;s it, we&#39;ve got the shared audio files now.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Let&#39;s add them to our local indexedDB storage&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After re-installing the app, and sharing audio files from somewhere else, PWAmp shows up in the share dialog as a possible target!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pwamp-share-target.png&quot; alt=&quot;The Windows share dialog, showing that PWAmp is part of the target apps&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Adding shortcuts and widgets&lt;/h4&gt;
&lt;p&gt;As I mentioned earlier, shortcuts and widgets are two other things desktop apps commonly use to more meaningfully integrate in their host operating system.&lt;/p&gt;
&lt;p&gt;Progressive Web Apps can do this too. In the interest of keeping the article not too long, we won&#39;t implement this in PWAmp, but only quickly mention them.&lt;/p&gt;
&lt;p&gt;First, shortcuts. These are common tasks that can be accessed usually from an app&#39;s icon. If you haven&#39;t tried this yet, try to long press an app icon on your mobile device home screen. Or, if you&#39;re on a desktop computer, try right-clicking on an app icon in the Dock or Taskbar. Chances are that the app you clicked on will display a few tasks that are possible to do right from the icon.&lt;/p&gt;
&lt;p&gt;You can do this in a PWA too by using the &lt;code&gt;shortcuts&lt;/code&gt; manifest member. Here is a good resource on this topic: &lt;a href=&quot;https://microsoft.github.io/win-student-devs/#/30DaysOfPWA/advanced-capabilities/02&quot;&gt;Creating application shortcuts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And, second, widgets. This is way more on the bleeding edge as, right now, no operating system and browser combination allows you to do it, but it&#39;s coming soon to Windows and Edge. The idea here is to let apps, including PWAs, insert their own custom widgets in the operating system&#39;s widget dashboard. If you want to find out more, check out the &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/PWAWidgets/README.md&quot;&gt;PWA-driven Widgets Explainer&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Doing non-trivial work&lt;/h3&gt;
&lt;p&gt;Desktop apps are usually associated with heavy-duty tasks, things that require a bigger screen, access to hardware, and more processing power. Can the web compete with this?&lt;/p&gt;
&lt;p&gt;Well, it depends. Our little music player app isn&#39;t necessarily a great example of this, but it does use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot;&gt;IndexedDB&lt;/a&gt; to store the audio files for offline playback, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Audio_API&quot;&gt;Web Audio API&lt;/a&gt; for analyzing the playing music, and the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Canvas_API&quot;&gt;Canvas API&lt;/a&gt; for drawing the visualizer, so it&#39;s definitely more than just a website at least.&lt;/p&gt;
&lt;p&gt;But can things like word processors, image manipulation, or 3D modelling apps exist as Web apps? Not all apps will make sense as Web apps, but consider the following points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adobe is bringing Photoshop to the Web &lt;sup id=&quot;note-6&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-6&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webassembly.org/&quot;&gt;WASM&lt;/a&gt; is a great way to execute large computing tasks at near-native speeds by cross-compiling C/C++ and other languages to the Web.&lt;/li&gt;
&lt;li&gt;Hardware access is no longer limited to native apps only. Now, browser support is very uneven here, and most things are very experimental, but consider the following APIs: &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebUSB_API&quot;&gt;WebUSB&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Serial_API&quot;&gt;WebSerial&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebHID_API&quot;&gt;WebHID&lt;/a&gt;, &lt;a href=&quot;https://developer.chrome.com/docs/web-platform/webgpu/&quot;&gt;WebGPU&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Bluetooth_API&quot;&gt;WebBluetooth&lt;/a&gt;, &lt;a href=&quot;https://web.dev/nfc/&quot;&gt;WebNFC&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So my thought here is that you shouldn&#39;t ignore the Web as a potential target for your next app just because you think what you want to do is too complex. The web is no longer just a medium to read text documents. It has grown way past this.&lt;/p&gt;
&lt;p&gt;Instead, think of the pros and cons. Will it be easier for you to hire web developers than native app developers? Does the Web support the features you want to do? Have you really checked the latest available features in modern web browsers?&lt;/p&gt;
&lt;p&gt;Even though I&#39;ve been in this industry for a long time, I keep being amazed by what people manage to build with web technologies, and what the platform is capable of doing.&lt;/p&gt;
&lt;h3&gt;App stores&lt;/h3&gt;
&lt;p&gt;At the beginning of the article, I said that &amp;quot;real&amp;quot; apps could be found in app stores. We&#39;re very used to this model on locked down devices such as a iPhones where that&#39;s the only way to install apps. But app stores also exist on desktop operating systems. They&#39;re less used there because we usually download apps from the internet. Nevertheless, being on an app store conveys a sense of trust to users that just accessing a website on a random URL doesn&#39;t. It&#39;s going to be hard for PWAs to compete if users have to first navigate to the site, and then find the right little button to install the app.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.pwabuilder.com/&quot;&gt;PWABuilder&lt;/a&gt; is a great solution to this. It&#39;s both a PWA validation tool that helps you check the quality of your code, and a packaging tool. Give it the URL to your PWA and it will create packages for you to distribute your app on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Microsoft Store, for Windows.&lt;/li&gt;
&lt;li&gt;The Google Play Store, for Android.&lt;/li&gt;
&lt;li&gt;The App Store, for iOS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even if those stores don&#39;t support PWA by default, you can use PWABuilder to create packages made specifically for those stores and distribute your apps to users this way!&lt;/p&gt;
&lt;p&gt;In fact, I&#39;ve already gone ahead and published &lt;a href=&quot;https://www.microsoft.com/store/productId/9PHQQ40MZ10L&quot;&gt;PWAmp in the Microsoft Store&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pwamp-store.png&quot; alt=&quot;The Microsoft Store app, showing the PWAmp app screen&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;We&#39;re done! 🎉&lt;/p&gt;
&lt;p&gt;We&#39;ve gone over all the things we said were characteristics of desktop applications, and showed that Progressive Web Apps could share the same traits. It&#39;s possible to create great desktop app experiences with PWA nowadays. There&#39;s so much capabilities that make it possible to create pretty much anything you have mind.&lt;/p&gt;
&lt;p&gt;I&#39;m not saying everything absolutely needs to be based on web technologies and distributed as a PWA. But more and more is possible these days. And my hope is that, now, you understand that PWA &lt;strong&gt;is&lt;/strong&gt; an option and you&#39;ll think of it the next time you to make a decision for which technology to use.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;sup id=&quot;footnote-1&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#note-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I&#39;ve used the word &amp;quot;real&amp;quot; between quotes here, because &lt;a href=&quot;https://patrickbrosset.com/articles/2022-09-08-state-of-pwa/#1-pwas-are-not-real-apps-%F0%9F%94%8D&quot;&gt;I&#39;ve learned&lt;/a&gt; a lot of people think of platform-specific (or native) apps as real apps, as opposed to web apps. I, myself, don&#39;t really make a difference. I&#39;ve been using web-based apps for decades to do professional work and whether an app uses web technologies or some OS-specific libraries and a system-level programming language doesn&#39;t make a difference, as long as it does the job.&lt;/p&gt;
&lt;p&gt;&lt;sup id=&quot;footnote-2&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#note-2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; On mobile iOS devices, Safari supports installing PWAs locally by using the &lt;strong&gt;Add to home screen&lt;/strong&gt; option. On Android, you can also install PWAs locally. On desktop, for now, only Chromium-based browsers (such as Edge and Chrome) support installing PWAs.&lt;/p&gt;
&lt;p&gt;&lt;sup id=&quot;footnote-3&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#note-3&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; I&#39;ve also written a bunch of other things about Window Controls Overlay, if you&#39;re interested: &lt;a href=&quot;https://alistapart.com/article/breaking-out-of-the-box/&quot;&gt;Breaking Out of the Box&lt;/a&gt;, &lt;a href=&quot;https://learn.microsoft.com/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay&quot;&gt;Display content in the title bar&lt;/a&gt;, and &lt;a href=&quot;https://blogs.windows.com/msedgedev/2022/09/27/closing-pixel-gap-native-web-window-controls-overlay/&quot;&gt;Closing a 30 pixels gap between native and web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;sup id=&quot;footnote-4&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#note-4&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; When the iPhone X came out 5 years ago, web developers had to learn how to deal with the &amp;quot;notch&amp;quot; making their web pages look awkward. Check out &lt;a href=&quot;https://css-tricks.com/the-notch-and-css/&quot;&gt;&amp;quot;The Notch&amp;quot; and CSS&lt;/a&gt; on css-tricks.com.&lt;/p&gt;
&lt;p&gt;&lt;sup id=&quot;footnote-5&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#note-5&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; To learn more about how to use the &lt;code&gt;navigator.share&lt;/code&gt; API, check out &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/share&quot;&gt;Navigator.share()&lt;/a&gt; on MDN. Note, however, that sharing files isn&#39;t supported everywhere yet. But there&#39;s a very useful &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/canShare&quot;&gt;&lt;code&gt;navigator.canShare()&lt;/code&gt;&lt;/a&gt; function you can use to test whether sharing files works.&lt;/p&gt;
&lt;p&gt;&lt;sup id=&quot;footnote-6&quot;&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#note-6&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; From &lt;a href=&quot;https://blog.adobe.com/en/publish/2021/10/26/photoshop-ships-major-updates-across-desktop-ipad-apps-extends-light-editing-collaboration-features-web-beta&quot;&gt;Adobe&#39;s MAX 2021 announcements&lt;/a&gt;: &lt;em&gt;Today we are extending Photoshop to the web as a beta (running in Chrome and Edge browsers). In it, you can try out the commenting workflow and test some early Photoshop editing features we are piloting on the web&lt;/em&gt;.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What&#39;s New In DevTool: Halloween Edition 🎃</title>
    <link href="https://patrickbrosset.com/articles/2022-10-20-whats-new-in-devtool-halloween-edition/"/>
    <updated>2022-10-20T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-10-20-whats-new-in-devtool-halloween-edition/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2022/10/devtools-updates-halloween-edition/">https://www.smashingmagazine.com/2022/10/devtools-updates-halloween-edition/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Do you really understand CSS radial-gradients?</title>
    <link href="https://patrickbrosset.com/articles/2022-10-24-do-you-really-understand-css-radial-gradients/"/>
    <updated>2022-10-24T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2022-10-24-do-you-really-understand-css-radial-gradients/</id>
    <content type="html">
      
        &lt;p&gt;Seven years ago, I taught myself all about &lt;a href=&quot;https://patrickbrosset.com/articles/2015-03-27-do-you-really-understand-css-linear-gradients&quot;&gt;CSS linear gradients&lt;/a&gt; and, ever since, I&#39;ve been meaning to learn more about how radial gradients worked as well. It took me a long time to find an excuse to dig deeper into the logic behind it but I&#39;m very thankful I did.&lt;/p&gt;
&lt;p&gt;To learn how to use the &lt;code&gt;radial-gradient&lt;/code&gt; CSS function, I attempted to draw a pair or eyes, only with CSS, just one element par eye, and only using radial gradients. Here is the result (it&#39;s an iframe, so go ahead and use DevTools to see how it works if you want).&lt;/p&gt;
&lt;iframe src=&quot;https://patrickbrosset.com/lab/2022-10-21-eyes/index.html&quot; style=&quot;width:100%;aspect-ratio:2;border:2px solid var(--separator);&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Sure, this isn&#39;t the best drawing of human eyes you&#39;ve ever seen (plus, it doesn&#39;t render properly in Safari, see why in the &lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-24-do-you-really-understand-css-radial-gradients/#browser-support&quot;&gt;browser support section&lt;/a&gt;), but it did help me understand a lot more about how radial gradients worked and, with this article, my hope is that you too can learn a thing or two about them.&lt;/p&gt;
&lt;h3&gt;High-level syntax description&lt;/h3&gt;
&lt;p&gt;At a high level, here is what the &lt;code&gt;radial-gradient&lt;/code&gt; syntax looks like:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&amp;lt;shape&gt; &amp;lt;size&gt; at &amp;lt;position&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &amp;lt;color-stops&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shape&lt;/strong&gt;. The shape of the gradient. This defaults to an ellipse, but can also be a circle. The colors defined in the gradient will be displayed in concentric layers inside this shape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Size&lt;/strong&gt;. The size of the gradient shape. This corresponds to the outer-most concentric shape where the 100% color stop would be positioned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Position&lt;/strong&gt;. Where the gradient shape is positioned. By default the gradient shape is centered in the gradient box, but you can choose any position you want.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Color stops&lt;/strong&gt;. The list of colors you want to use in the gradient. The colors should be listed from the inner-most to the outer-most. The first color will start at the center of the shape.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s illustrate this a little bit to help make sense of this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/high-level-description.png&quot; alt=&quot;A radial-gradient, with concentric circles highlighted on top, to show how things works&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In the above example, we&#39;ve drawn a circle gradient. The rectangle is the gradient box (which we&#39;ll define in a second), the white dot is where the gradient is positioned, the horizontal line is the gradient ray (also defined later), the thick outer-most circle is the gradient shape, and the gray inner-circles represent where the color stops are positioned.&lt;/p&gt;
&lt;p&gt;Now, let&#39;s define these various parts in more details.&lt;/p&gt;
&lt;h3&gt;The gradient box&lt;/h3&gt;
&lt;p&gt;A gradient image has no dimensions, it is infinite, unlike traditional background images. What gives a visible dimension to the gradient is its gradient box i.e. the area where it is displayed.&lt;/p&gt;
&lt;p&gt;Usually, the gradient box is the border-box of the element which the background is applied to. That&#39;s the same area where a background-color or an background-image would be displayed.&lt;/p&gt;
&lt;p&gt;Here is a red to blue radial gradient applied to an element:&lt;/p&gt;
&lt;div style=&quot;background:radial-gradient(red, blue);width:100%;aspect-ratio:3;border:2px solid var(--primary);&quot;&gt;&lt;/div&gt;
&lt;p&gt;However, using the &lt;code&gt;background-size&lt;/code&gt; property, you can set the size of the gradient box. You can also position it using &lt;code&gt;background-position&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is the same red to blue radial gradient, but with &lt;code&gt;background-size&lt;/code&gt; set to 50% of the width and height of the element, and &lt;code&gt;background-position&lt;/code&gt; set to &lt;code&gt;left center&lt;/code&gt; (oh, and no background repeat, so we can see where the gradient box is more easily):&lt;/p&gt;
&lt;div style=&quot;background:radial-gradient(red, blue);width:100%;aspect-ratio:3;border:2px solid var(--primary);background-size:50% 50%;background-position:left center;background-repeat:no-repeat;&quot;&gt;&lt;/div&gt;
&lt;p&gt;So, the gradient box isn’t always positioned and sized like the DOM element the gradient applies to. But for the sake of simplicity, for the rest of this article, we&#39;ll assume the gradient box matches exactly the DOM element it applies to.&lt;/p&gt;
&lt;h3&gt;The center point&lt;/h3&gt;
&lt;p&gt;Because a radial gradient is either an ellipse or a circle, it has a center point.&lt;/p&gt;
&lt;p&gt;Even if the center point is, by default, at the center of the gradient box, it can be positioned wherever you want by using the &lt;code&gt;&amp;lt;position&amp;gt;&lt;/code&gt; parameter. That&#39;s the part that comes after the &lt;code&gt;at&lt;/code&gt; keyword in the first argument of the &lt;code&gt;radial-gradient()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Let&#39;s see are a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No &lt;code&gt;at&lt;/code&gt; keyword, so, no position defined. This means the center point defaults to center:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(fuchsia, darkblue, black, cadetblue)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/default-center-point.png&quot; alt=&quot;A radial gradient, with the center point in the center of the gradient box&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Position defined with keywords:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(at top left, fuchsia, darkblue, black, cadetblue)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/top-left-center-point.png&quot; alt=&quot;A radial gradient, with the center point at the top left of the gradient box&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Position defined with absolute coordinates:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(at 150px 70px, fuchsia, darkblue, black, cadetblue)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/absolute-center-point.png&quot; alt=&quot;A radial gradient, with the center point at 150px from the left and 70px from the top of the gradient box&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, the position can be either omitted, or defined just like the &lt;code&gt;background-position&lt;/code&gt; property: using the &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt; keywords, or using a two lengths for the left and top offsets within the gradient box.&lt;/p&gt;
&lt;h3&gt;The ending shape&lt;/h3&gt;
&lt;p&gt;Now that we have a box to draw in (the gradient box), and a center point for our gradient shape, let&#39;s discuss about the ending shape.&lt;/p&gt;
&lt;p&gt;This shape is called the &lt;em&gt;ending&lt;/em&gt; shape because, in most cases, this is the outer-most shape that contains all of the other concentric shapes used to render the different colors of the gradient.&lt;/p&gt;
&lt;p&gt;For example, consider the following gradient:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle 120px at center center, red, aqua, lime, gold, pink)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now, let&#39;s highlight the center point, ending shape, and all color-stop concentric shapes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/ending-shape.png&quot; alt=&quot;Illustration of the above gradient, with the shapes highlighted&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The ending shape is the biggest circle, highlighted with the thicker line.&lt;/p&gt;
&lt;p&gt;The various colors of the gradient are also circles around the same center point.&lt;/p&gt;
&lt;p&gt;What&#39;s interesting here is that the ending shape can be defined and sized by using the &lt;code&gt;&amp;lt;shape&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;size&amp;gt;&lt;/code&gt; parameters we saw earlier in the &lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-24-do-you-really-understand-css-radial-gradients/#high-level-syntax-description&quot;&gt;high-level syntax description&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shape&lt;/strong&gt;: this can be either &lt;code&gt;ellipse&lt;/code&gt; or &lt;code&gt;circle&lt;/code&gt;, and defaults to &lt;code&gt;ellipse&lt;/code&gt; when missing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Size&lt;/strong&gt;: this defines the size of the ending shape, and can have the following values:
&lt;ul&gt;
&lt;li&gt;For an ellipse, it can be two lengths, for its horizontal radius, and vertical radius.&lt;/li&gt;
&lt;li&gt;For a circle, it can be one length, for the radius of the circle.&lt;/li&gt;
&lt;li&gt;It can be one of these keywords: &lt;code&gt;closest-side&lt;/code&gt;, &lt;code&gt;farthest-side&lt;/code&gt;, &lt;code&gt;closest-corner&lt;/code&gt;, &lt;code&gt;farthest-corner&lt;/code&gt;, which we&#39;ll explain in a minute.&lt;/li&gt;
&lt;li&gt;If it&#39;s missing, then it defaults to &lt;code&gt;farthest-corner&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To familiarize ourselves with this syntax, here are few valid examples of radial gradients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No shape (defaults to ellipse), with a &lt;code&gt;closest-side&lt;/code&gt; size:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(closest-side, black, lime, fuchsia)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/closest-side-ending-shape.png&quot; alt=&quot;An ellipse gradient, with closest-side size&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Circle shape, with a 100px radius:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle 100px, black, lime, fuchsia)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/circle-ending-shape.png&quot; alt=&quot;A circle gradient, with a radius of 100px&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ellipse, with a 200px horizontal radius, and a 50px vertical radius:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(200px 50px, black, lime, fuchsia)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/ellipse-ending-shape.png&quot; alt=&quot;An ellipse gradient, with its 2 radii set in px&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While defining a size by using lengths is rather simple, using keywords requires an explanation.&lt;/p&gt;
&lt;h4&gt;Closest-side&lt;/h4&gt;
&lt;p&gt;With this keyword, the ending shape will be sized such that it meets the closest side (or sides) of the gradient box from its center.&lt;/p&gt;
&lt;p&gt;For a circle, that&#39;s easy, the browser rendering engine finds which, of the top, right, bottom, or left sides of the box is closest to the center point, finds out the distance, and uses this as the radius of the ending shape.&lt;/p&gt;
&lt;p&gt;For example, if we wanted a circle positioned at 100px/150px inside the gradient box, and if we wanted it to be sized such that it met the closest side, we could use the following syntax:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle closest-side at 100px 150px, white, white)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And this would give us the following ending shape:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/closest-side-circle.png&quot; alt=&quot;A circle, positioned at 100px/150px, with its radius being 100px&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The left side of the box is at 100px away from the center, which is the closest from all sides, so the shape&#39;s radius is 100px.&lt;/p&gt;
&lt;p&gt;For ellipses, things are a bit more complicated, because ellipses have two radii: a horizontal radius, and vertical radius.&lt;/p&gt;
&lt;p&gt;In this case, the ellipse&#39;s horizontal radius is sized such that the ending shape meets the left or right side, depending on which is the closest. And the ellipse&#39;s vertical radius is sized such that the shape meets the top or bottom side, depending on which is closest.&lt;/p&gt;
&lt;p&gt;For example, let&#39;s define a gradient centered at 200px/100px within the shape:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(closest-side at 200px 100px, white, white)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Which gives us the following ending shape:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/closest-side-ellipse.png&quot; alt=&quot;An ellipse, positioned at 200px/100px touching the left and top sides&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see in the above figure, the left and top sides are the closest sides to the point, horizontally, and vertically respectively. Therefore, they are the sides that the ending shape meets with the &lt;code&gt;closest-side&lt;/code&gt; keyword.&lt;/p&gt;
&lt;h4&gt;Farthest-side&lt;/h4&gt;
&lt;p&gt;Now that we know how the &lt;code&gt;closest-side&lt;/code&gt; keyword works, it&#39;s easier to understand the &lt;code&gt;farthest-side&lt;/code&gt; keyword.&lt;/p&gt;
&lt;p&gt;Instead of looking for the closest side (or sides), the browser rendering engine will look for the farthest side (or sides).&lt;/p&gt;
&lt;p&gt;Here is an example with the same circle shape as before:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle farthest-side at 100px 150px, white, white)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And the result:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/farthest-side-circle.png&quot; alt=&quot;A circle, positioned at 100px/150px, extending way out of the gradient box&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, we can&#39;t even see the whole circle, because the radius is much larger than before.&lt;/p&gt;
&lt;h4&gt;Closest-corner and farthest-corner&lt;/h4&gt;
&lt;p&gt;Now, replace side with corner, and you should see how these two keywords work.&lt;/p&gt;
&lt;p&gt;Instead of measuring the distance between the center point and the sides, the browser rendering engine now measures the distance to the closest and farthest corners of the gradient box.&lt;/p&gt;
&lt;p&gt;For example, with the following gradient, we should end up with a circle that passes through the closest corner of the box:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle closest-corner at 70px 70px, white, white)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And we do! As you can see below, the top-left corner of the box is the closest to the shape&#39;s center, so that&#39;s where the shape passes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/closest-corner-circle.png&quot; alt=&quot;A circle, positioned at 70px/70px, and that passes through the top left corner of the box&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finally, consider this gradient:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle farthest-corner at 70px 70px, white, white)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The bottom-right corner of the gradient box is the farthest corner from the center point of the ending shape, and so that&#39;s where the shape passes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/farthest-corner-circle.png&quot; alt=&quot;A circle, positioned at 70px/70px, and that passes through the bottom right corner of the box&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One more things about these keywords: if you don&#39;t define a size for the ending shape, it defaults to &lt;code&gt;farthest-corner&lt;/code&gt;. But why?&lt;/p&gt;
&lt;p&gt;Well, my understanding is that this value produces the best result in most cases, especially if the shape is positioned at the center. With &lt;code&gt;farthest-corner&lt;/code&gt;, the last color in your gradient can be positioned exactly at the farthest visible point of the box, giving you the most room possible for your gradient to be displayed in.&lt;/p&gt;
&lt;p&gt;Let&#39;s take the example of a red to blue gradient and compare the difference between &lt;code&gt;closest-side&lt;/code&gt; and &lt;code&gt;farthest-corner&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/compare-closest-farthest.png&quot; alt=&quot;Comparison of the 2 keywords&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, in the latter case, the gradient has more room to breath. But, of course, this will depend on the effect that you&#39;re trying to achieve.&lt;/p&gt;
&lt;h3&gt;The gradient ray&lt;/h3&gt;
&lt;p&gt;The gradient ray is a half-line that starts at the center point, and extends horizontally, and to the right, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/ray.png&quot; alt=&quot;The gradient ray, shown on a gradient&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now, this line is, in itself, not very interesting, but it does play a role when the rendering engine of the browser distributes colors along the gradient.&lt;/p&gt;
&lt;p&gt;Because a radial gradient can be an ellipse, it may not have the same horizontal and vertical dimensions. Think about what that entails if you wanted a gradient with the color red at 20px, then the color green at 50px. Where would the 20 and 50px be measured at?&lt;/p&gt;
&lt;p&gt;That&#39;s where the gradient ray comes in.&lt;/p&gt;
&lt;p&gt;The colors defined in the color stops value are placed on the gradient ray, starting at the center point, and extending infinitely to the right.&lt;/p&gt;
&lt;p&gt;This ray is similar to the &lt;strong&gt;gradient line&lt;/strong&gt; in &lt;a href=&quot;https://patrickbrosset.com/articles/2015-03-27-do-you-really-understand-css-linear-gradients/&quot;&gt;linear gradients&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s take the following example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle 300px at left center, fuchsia 100px, aqua 200px, white 300px)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The first color, fuchsia, will be positioned along the gradient ray, 100px right of the center point, aqua at 200px, and white at 300px (which happens to be the radius of the circle):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/ray-stops.png&quot; alt=&quot;The three color stops, distributed along the gradient ray&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Based on this, the rendering engine just has to figure out how to draw the concentric shapes. For circles, that&#39;s going to be easy, because the radius is already known: that&#39;s the position where the color stop is.&lt;/p&gt;
&lt;p&gt;For ellipses, the rendering engine does a bit more maths. It knows the two radii of the ending shape, it knows the center point, and it knows the horizontal radius, since that&#39;s the position of the color stop.&lt;/p&gt;
&lt;p&gt;From this, it needs to find the vertical radius of the color stop shape.&lt;/p&gt;
&lt;p&gt;Here is an example, similar to the last one, but using an ellipse shape:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(300px 150px at left center, fuchsia 100px, aqua 200px, white 300px)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And here is how the color stops will be distributed along the gradient ray, and what the concentric ellipses will look like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/ray-stops-ellipse.png&quot; alt=&quot;The three color stops, distributed along the gradient ray, for an ellipse&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Color stops&lt;/h3&gt;
&lt;p&gt;And this brings us to the last part of the radial gradient syntax: the color stops.&lt;/p&gt;
&lt;p&gt;As a reminder, that&#39;s the part that appears after the shape, the size, and the position the following expression:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(&amp;lt;shape&amp;gt; &amp;lt;size&amp;gt; at &amp;lt;position&amp;gt;, &amp;lt;color stops&amp;gt;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We&#39;ve already seen a bunch of examples of color stops in this article so far, so it shouldn&#39;t be a surprise that color stops can be expressed in the following ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Just a color, e.g. &lt;code&gt;red&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A color and a position, e.g. &lt;code&gt;red 100px&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But there&#39;s also a third way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A color, a start position, and an end position, e.g. &lt;code&gt;red 100px 200px&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When no position is defined, this means we&#39;re letting the browser decide where to put the color. It will do this by distributing it evenly along the gradient ray, depending on other colors.&lt;/p&gt;
&lt;p&gt;When only one position is defined, then that&#39;s the place where the color should appear.&lt;/p&gt;
&lt;p&gt;When two positions are defined, then the color should span from the start position to the end position.&lt;/p&gt;
&lt;p&gt;Let&#39;s review a few examples next.&lt;/p&gt;
&lt;h4&gt;Auto-distribution&lt;/h4&gt;
&lt;p&gt;Consider the following gradient:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle, red, blue, green, yellow)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here, we&#39;re not defining any position. Instead, we want the browser to auto distribute the color stops, which it will do gladly.&lt;/p&gt;
&lt;p&gt;To do this, it will place the first color at 0% on the gradient ray (so, at the center point). Then it will place the last color where the ray meets the ending shape (that&#39;s the 100% position). And it will then distribute the rest of the colors in between those two bounds, evenly.&lt;/p&gt;
&lt;p&gt;We&#39;ll therefore end up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;red at 0%.&lt;/li&gt;
&lt;li&gt;blue at 33.333%.&lt;/li&gt;
&lt;li&gt;green at 66.666%.&lt;/li&gt;
&lt;li&gt;yellow at 100%.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/auto-distribution.png&quot; alt=&quot;Auto-distributed colors, along the gradient ray&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Can you guess why yellow is outside the gradient box?&lt;/p&gt;
&lt;p&gt;Well that&#39;s because we didn&#39;t specify a size for the shape, and so it defaulted to &lt;code&gt;farthest-corner&lt;/code&gt;, which means that the 100% position is actually slightly outside the box. But it also means that the corners are exactly yellow.&lt;/p&gt;
&lt;p&gt;So far, auto-distribution of color stops seems quite simple, doesn&#39;t it? It gets more complicated when some of the stops have positions, and others don&#39;t. Consider the following gradient:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle, red 50px, blue, yellow 100px, aqua, lime, white 300px)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We have the following stops:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;red at 50px&lt;/li&gt;
&lt;li&gt;blue&lt;/li&gt;
&lt;li&gt;yellow at 100px&lt;/li&gt;
&lt;li&gt;aqua&lt;/li&gt;
&lt;li&gt;lime&lt;/li&gt;
&lt;li&gt;white at 300px&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While some of the stops have positions, others don&#39;t.&lt;/p&gt;
&lt;p&gt;In this case, the browser will take what it has, and figure out the rest.&lt;/p&gt;
&lt;p&gt;To do this, here&#39;s a way: find ranges, from one positioned stop, to the next, and evenly distribute the non-positioned stops within the range. And continue with other ranges.&lt;/p&gt;
&lt;p&gt;In our case, that means considering the red to yellow range (from 50 to 100px), and finding a position for blue. Because that&#39;s only one stop, put it in the center of the range, at 75px.&lt;/p&gt;
&lt;p&gt;Then, take the yellow to white range (from 100 to 300px), and distribute aqua and lime evenly in this range (at 166.666px and 233.333px).&lt;/p&gt;
&lt;p&gt;We end up with the following gradient:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/auto-mixed-distribution.png&quot; alt=&quot;The result of mixing defined and undefined positions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are even more corner cases with this, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If we have only one positioned stop.&lt;/li&gt;
&lt;li&gt;If the first positioned stop has a negative position.&lt;/li&gt;
&lt;li&gt;If the last positioned stop has a position greater than 100%.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s not spend more time on this though. Hopefully, this is enough to understand how auto-distribution works.&lt;/p&gt;
&lt;p&gt;Play around with this, and let the browser figure out the final distribution of your colors!&lt;/p&gt;
&lt;h4&gt;Hard stops&lt;/h4&gt;
&lt;p&gt;One very useful thing you can do with color stops is display colors next to each other with no transition between them.&lt;/p&gt;
&lt;p&gt;Yes, that&#39;s a bit counter-intuitive to the whole idea of using gradients where colors are supposed to transition smoothly from one to the next, but it can lead to very interesting effects. Here are a few examples:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/hard-stops.png&quot; alt=&quot;4 examples of radial gradients that use hard stops to create interesting designs&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hard stops happen when two neighbor color stops share the same position. In the top-left example in the above figure, the red circle could be done with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(red 250px, white 250px)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Another way to achieve hard stops is to use ranges, like in the top-right example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(lime 0 5vmin, #ffe88e 5vmin 10vmin, red 10vmin 15vmin, black 15vmin 20vmin, #21a3f3 0)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In this example, each color has two positions: a start position and an end position. And the next color starts right where the previous one ends.&lt;/p&gt;
&lt;h4&gt;Out of order positions&lt;/h4&gt;
&lt;p&gt;Of course, it would be too easy if color stops were always given in their order of appearance.&lt;/p&gt;
&lt;p&gt;CSS is such a wonderful language that always knows how to display something, even if you screw up the code. In the case of color stops, that means you can actually provide colors in the wrong order, and the browser will still try to figure something out.&lt;/p&gt;
&lt;p&gt;Consider the following gradient for example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(rebeccapurple 250px, #000 100px, white 350px)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What would happen in this case? The browser would start at 250px, then realize the next stop is positioned at 100px, which is wrong. Stops are supposed to be given in increasing positions. So, here, the browser would just auto-correct the position and set it to the same position as the previous stop: 250px. Then it would continue to black, positioned at 300px.&lt;/p&gt;
&lt;p&gt;We would end up with the following gradient:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/out-of-order.png&quot; alt=&quot;The previous gradient&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This error-correction browser mechanism can actually come in handy. Say you want to draw circles as the background of an element, but you want the outside of those circles to be transparent so that other circles are visible too.&lt;/p&gt;
&lt;p&gt;You can set the last stop of each radial gradient to &lt;code&gt;transparent 0&lt;/code&gt;. The browser will correct 0 to be the same as the previous stop, therefore creating a hard limit at the edge of your shape, like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/bubbles.png&quot; alt=&quot;5 circles, of different sizes and colors, as background to an element&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The above figure was created by using 5 different radial gradients on the same element, each with two color stops, one with the color of the circle, and the other one being &lt;code&gt;transparent 0&lt;/code&gt;. Here is the full code:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.bubbles&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;circle 100px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #783d54 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transparent 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;circle 200px at top left&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #4a2030 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transparent 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;circle 50px at 100px 250px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #dd206b 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transparent 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;circle 70px at 500px 250px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f06 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transparent 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;circle 140px at 120px 500px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #d5a6b9 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transparent 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Out of range positions&lt;/h4&gt;
&lt;p&gt;The final color stops example I wanted to give is out of range positions. So far, we&#39;ve seen color stop positions between 0 and 100% of the gradient ray. But, nothing prevents us from setting positions outside this range.&lt;/p&gt;
&lt;p&gt;For example, this is valid:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;radial-gradient(circle at left center, blue -200%, red 200%)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This can be useful to create a much more subtle gradient. Here, because the two color stops are quite far apart, and outside the gradient box, we only see a &lt;em&gt;zoomed in&lt;/em&gt; part of the red to blue gradient:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/out-of-range.png&quot; alt=&quot;Subtle blue to red gradient, where we see mostly purple&quot; /&gt;&lt;/p&gt;
&lt;div id=&quot;browser-support&quot;&gt;&lt;/div&gt;
&lt;h3&gt;Browser support&lt;/h3&gt;
&lt;p&gt;Last but not least, let&#39;s take a quick look at &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/gradient/radial-gradient#browser_compatibility&quot;&gt;browser support&lt;/a&gt; for the &lt;code&gt;radial-gradient&lt;/code&gt; CSS function:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/radial-gradient/support.png&quot; alt=&quot;A copy of the MDN browser compat table for radial-gradient&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The function is very well supported across browsers.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;And with this, that&#39;s it! We&#39;ve seen the different parts that make a radial gradient. I hope you now understand the &lt;code&gt;radial-gradient&lt;/code&gt; function better. I know I do. I can now more comfortably use this function and be pretty certain of the result before I even see it, which is great.&lt;/p&gt;
&lt;p&gt;Now, before we wrap this up, let&#39;s have fun with a few quick demos that use radial gradients for interesting effects. Use DevTools to inspect the demos.&lt;/p&gt;
&lt;div style=&quot;
width:50%;
margin: 3rem auto;
aspect-ratio:1;
background-image:
radial-gradient(farthest-side, transparent 100%, black 0),
radial-gradient(circle farthest-side at top left, #f06 50%, transparent 0),
radial-gradient(circle farthest-side at top right, #f06 50%, transparent 0),
radial-gradient(circle farthest-side at bottom right, #f06 50%, transparent 0),
radial-gradient(circle farthest-side at bottom left, #f06 50%, transparent 0),
radial-gradient(black, black);
&quot;&gt;&lt;/div&gt;
&lt;div style=&quot;
width:100%;
aspect-ratio:2;
margin: 3rem auto;
background-image:
radial-gradient(circle closest-side at 75% 20%, white 60%, #f6f0d4 60%, transparent 94%),
radial-gradient(circle 4px at 10% 10%, white 1px, #b2d1fc 1px, transparent),
radial-gradient(circle 4px at 20% 37%, white 1px, #b2d1fc 1px, transparent),
radial-gradient(circle 4px at 40% 17%, white 1px, #b2d1fc 1px, transparent),
radial-gradient(circle 4px at 80% 29%, white 1px, #b2d1fc 1px, transparent),
radial-gradient(circle 4px at 60% 60%, white 1px, #b2d1fc 1px, transparent),
radial-gradient(circle 4px at 90% 18%, white 1px, #b2d1fc 1px, transparent),
radial-gradient(closest-corner at 50% 95%, #111 100%, #fff2, transparent 250%),
radial-gradient(at bottom center, blue -150%, black);
&quot;&gt;&lt;/div&gt;
&lt;div style=&quot;
width:400px;
height:240px;
margin: 3rem auto;
background-color:#fef9ef;
background-image:
radial-gradient(circle 40px at top left, #fef9ef 100%, transparent 0),
radial-gradient(circle 40px at bottom left, #fef9ef 100%, transparent 0),
radial-gradient(circle 40px at bottom right, #fef9ef 100%, transparent 0),
radial-gradient(circle 40px at top right, #fef9ef 100%, transparent 0),
radial-gradient(circle 40px at center center, black 20%, transparent 20% 40%, black 40% 60%, transparent 50% 80%, black 80% 100%, transparent 0),
radial-gradient(circle 40px at 40px center, black 20%, transparent 20% 40%, black 40% 60%, transparent 50% 80%, black 80% 100%, transparent 0),
radial-gradient(circle 40px at 360px center, black 20%, transparent 20% 40%, black 40% 60%, transparent 50% 80%, black 80% 100%, transparent 0),
radial-gradient(circle 40px at 120px 40px, #fbda39 100%, transparent 0),
radial-gradient(circle 40px at 280px 40px, #f94e1d 100%, transparent 0),
radial-gradient(circle 40px at 120px 200px, black 100%, transparent 0),
radial-gradient(circle 40px at 280px 200px, #1c6ae3 100%, transparent 0),
radial-gradient(circle 40px at center 240px, #f94e1d 100%, transparent 0),
radial-gradient(circle 40px at center 0px, #1c6ae3 100%, transparent 0),
radial-gradient(circle 40px at left center, #f6c0d0 100%, transparent 0),
radial-gradient(circle 40px at left center, #f94e1d 100%, transparent 0),
radial-gradient(circle 40px at right center, black 100%, transparent 0),
radial-gradient(circle 40px at right center, #fbda39 100%, transparent 0),
radial-gradient(circle 40px at center top, black 100%, transparent 0),
radial-gradient(circle 40px at center bottom, #f6c0d0 100%, transparent 0);
background-position:
120px 40px,
120px 200px,
280px 200px,
280px 40px,
0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,
80px 0,
120px 0,
320px 0,
280px 0,
50% 40px,
50% 200px;
&quot;&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Note: this last one doesn&#39;t work on Safari, see &lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-24-do-you-really-understand-css-radial-gradients/#browser-support&quot;&gt;browser support&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;While these are purely decorative examples, and unlikely to be useful on real websites, CSS gradients, both radial, &lt;a href=&quot;https://patrickbrosset.com/articles/2015-03-27-do-you-really-understand-css-linear-gradients/&quot;&gt;linear&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/gradient/conic-gradient&quot;&gt;conic&lt;/a&gt;, offer a lot of flexibility when it comes to drawing details in the background of elements without having to add more elements to your document.&lt;/p&gt;
&lt;p&gt;I hope this article helped you. Drop me a note on &lt;a href=&quot;https://twitter.com/patrickbrosset&quot;&gt;Twitter&lt;/a&gt; if you liked it!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What&#39;s a Progressive Web App exactly?</title>
    <link href="https://patrickbrosset.com/articles/2023-01-16-whats-a-pwa/"/>
    <updated>2023-01-16T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-01-16-whats-a-pwa/</id>
    <content type="html">
      
        &lt;p&gt;The most common way that I hear people define Progressive Web App (PWA) is: &lt;em&gt;&amp;quot;it&#39;s just a website with a manifest file and a service worker&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Although there is technical truth to this, I don&#39;t like this definition very much. In this article, I&#39;ll go over how I think about PWA. I hope this will be helpful to you, especially if you&#39;re getting started in this field.&lt;/p&gt;
&lt;p&gt;To me, &lt;strong&gt;a PWA is an application that you build by using web technologies.&lt;/strong&gt; It&#39;s an app you write using HTML, CSS, and JavaScript, and that can be installed on all devices, from just one codebase.&lt;/p&gt;
&lt;p&gt;Even though a PWA is written in the same languages used to build websites, it has access to features that are normally reserved to &amp;quot;native&amp;quot; apps. Now, a PWA doesn&#39;t need to use these features, as long as it meets the install criteria (which, today, are: https, service worker, and web app manifest), then it&#39;s a PWA. But these native-like features can make a PWA feel a lot more natural to users and better integrated in the operating system it&#39;s installed on.&lt;/p&gt;
&lt;p&gt;Finally, a PWA can also run inside web browsers, just like websites.&lt;/p&gt;
&lt;h3&gt;Is PWA just a website?&lt;/h3&gt;
&lt;p&gt;It&#39;s built like a website, and it can run in a web browser just like any other websites. In fact, this ability to run like a website gives a PWA several advantages over other traditional ways to build apps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It can be indexed by search engines, which means people can find the app by just googling it.&lt;/li&gt;
&lt;li&gt;You can give someone else the link to a PWA, which they can use to access it without even installing it first.&lt;/li&gt;
&lt;li&gt;The PWA&#39;s ability to adapt to any screen size, orientation, input method, etc. is part of its DNA. After all, it&#39;s built like a website, and websites can be used on all devices.&lt;/li&gt;
&lt;li&gt;It runs using the HTTPS protocol, which guarantees that communications between the app and the servers it uses are secure.&lt;/li&gt;
&lt;li&gt;To update your PWA, you deploy new files to a web server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But it&#39;s also more than a website. See below.&lt;/p&gt;
&lt;h3&gt;Is PWA a real app?&lt;/h3&gt;
&lt;p&gt;Tough question, what does &lt;a href=&quot;https://patrickbrosset.com/articles/2022-10-06-building-desktop-apps-without-native-code/#footnote-1&quot;&gt;&amp;quot;real&amp;quot;&lt;/a&gt; even mean?&lt;/p&gt;
&lt;p&gt;What I &lt;em&gt;can&lt;/em&gt; say is a PWA can be installed like any other app on a device, for example on an Android phone, an iOS device, or a laptop computer. When installed on a device, a PWA functions pretty much like any other app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The PWA has its own application icon, and its own dedicated window.&lt;/li&gt;
&lt;li&gt;It can be launched automatically when associated files are opened.&lt;/li&gt;
&lt;li&gt;It can be launched when the user signs in.&lt;/li&gt;
&lt;li&gt;It can continue to work even when the device is offline.&lt;/li&gt;
&lt;li&gt;It can support push notifications.&lt;/li&gt;
&lt;li&gt;It can run background tasks, like performing periodic updates, even if the application window or screen is not open.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Does that make it a &amp;quot;real&amp;quot; app for you? For me, as long as the app lets me perform the tasks I installed it for, in a reliable way, and as long as I can launch the app whenever I need it, then it&#39;s definitely a real app!&lt;/p&gt;
&lt;p&gt;A PWA can also be published on stores such as the Google Play Store or the App Store.&lt;/p&gt;
&lt;h3&gt;But surely, a PWA can&#39;t do as much as a native app?&lt;/h3&gt;
&lt;p&gt;If you&#39;re in the business of building a new application, you probably have very specific scenarios and you&#39;re trying to choose the technology that&#39;s right for them.&lt;/p&gt;
&lt;p&gt;In this situation, it&#39;s very easy to default to using operating system-specific technologies (aka &amp;quot;native&amp;quot; code). You&#39;re likely thinking that your scenarios just can&#39;t be built with web code, and that there&#39;s no limit to what native code will let you do.&lt;/p&gt;
&lt;p&gt;To be clear, there are definitely cases where a PWA will not be the right choice for you. But before making a quick decision, check what PWA can do for you. If the capabilities available to PWA are up to the task, it would be a shame not to benefit from its web-related advantages and single code base.&lt;/p&gt;
&lt;p&gt;Keep in mind that a PWA has a much lower cross-platform development cost than operating system-specific apps since it doesn&#39;t require a separate codebase for each platform.&lt;/p&gt;
&lt;h3&gt;Find out more, and get started&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;What can the web do today: check out &lt;a href=&quot;https://fugu-tracker.web.app/&quot;&gt;the Fugu API tracker&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://whatpwacando.today/&quot;&gt;What PWA can do today&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Progressive_web_apps&quot;&gt;PWA, on MDN&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/pwa/&quot;&gt;Learn PWA, on web.dev&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/microsoft-edge/progressive-web-apps-chromium/&quot;&gt;PWA documentation from Microsoft Edge&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pwabuilder.com/&quot;&gt;PWABuilder&lt;/a&gt; is a tool to help you publish your PWA to app stores. They also have great &lt;a href=&quot;https://docs.pwabuilder.com/&quot;&gt;docs&lt;/a&gt; and tools to help you get started writing your PWA.&lt;/li&gt;
&lt;/ul&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>The truth about CSS selector performance</title>
    <link href="https://patrickbrosset.com/articles/2023-01-17-the-truth-about-css-selector-performance/"/>
    <updated>2023-01-17T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-01-17-the-truth-about-css-selector-performance/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2023/01/17/the-truth-about-css-selector-performance/">https://blogs.windows.com/msedgedev/2023/01/17/the-truth-about-css-selector-performance/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>(Almost) everything about storing data on the web</title>
    <link href="https://patrickbrosset.com/articles/2023-01-17-web-storage/"/>
    <updated>2023-01-17T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-01-17-web-storage/</id>
    <content type="html">
      
        &lt;div style=&quot;padding:.5rem 1rem;background:var(--separator);&quot;&gt;
&lt;p&gt;&lt;strong&gt;⚠️ MDN now has more up-to-date information on this ⚠️&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After releasing this blog post, I started working on a big project to update the storage quota and eviction documentation on MDN too. I worked with browser engineers directly to make sure that the documentation was as correct and current as it could be.&lt;/p&gt;
&lt;p&gt;This new documentation is now available ➡️ &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria&quot;&gt;Storage quotas and eviction criteria&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;ll try my best to keep this blog post up to date, but you should probably go to MDN now instead.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Imagine you&#39;re building a web application and you want to store some data locally on the user&#39;s device. This could be because this data is only needed on the client-side, or it could be because you want to offer some kind of offline access to the your app&#39;s content.&lt;/p&gt;
&lt;p&gt;To do this, you&#39;ll need to store data on the device that&#39;s running your app. You might be asking yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Can I actually store all the data I need?&lt;/li&gt;
&lt;li&gt;Can I trust the browser to really persist it?&lt;/li&gt;
&lt;li&gt;How much space do I have?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The answers are better than you might think:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Can I actually store all the data I need? &lt;strong&gt;Sure, you can store all kinds of data, and a lot of it too.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Can I trust the browser to really persist it? &lt;strong&gt;Yes you can.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;How much space do I have? &lt;strong&gt;A lot, in most cases!&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But as always, the devil is in the details! Let&#39;s try to dive into these details.&lt;/p&gt;
&lt;h3&gt;What are my options?&lt;/h3&gt;
&lt;p&gt;There are multiple ways to store data on the device, from your web app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web Storage, namely &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;IndexedDB (let&#39;s call it IDB for short).&lt;/li&gt;
&lt;li&gt;Cache API.&lt;/li&gt;
&lt;li&gt;The Origin-Private File System API (or OPFS).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are others too, but these are the main I can think of. Each have their own use and their own pros and cons.&lt;/p&gt;
&lt;h4&gt;Web Storage (&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Storage_API&quot;&gt;docs&lt;/a&gt;)&lt;/h4&gt;
&lt;p&gt;Web Storage is simple and well supported, but should only be used for very simple needs. They can only hold strings, and are limited to 5MiB each. They&#39;re also synchronous, and can&#39;t be used in workers.&lt;/p&gt;
&lt;p&gt;Being synchronous means that any data access will block the main thread of your app, potentially making it slow to respond.&lt;/p&gt;
&lt;p&gt;Not being available in workers is a problem too if you&#39;re considering using a service worker in your app for offline scenarios for example.&lt;/p&gt;
&lt;h4&gt;IndexedDB (or IDB) (&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot;&gt;docs&lt;/a&gt;)&lt;/h4&gt;
&lt;p&gt;In theory, IDB is great. Its API is asynchronous, it can be used in workers, and it can store pretty much anything. In practice, I find the API quite hard to use.&lt;/p&gt;
&lt;p&gt;I personally have never used it directly without an abstraction. In fact, for simple key/value pair use cases, consider using Jake Archibald&#39;s &lt;a href=&quot;https://github.com/jakearchibald/idb-keyval&quot;&gt;idb-keyval library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The amount of data you can store in IDB can be huge! Ultimately it will of course depend on the amount of data available on the device. Read on for more information about storage quotas.&lt;/p&gt;
&lt;h4&gt;Cache API (&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/CacheStorage&quot;&gt;docs&lt;/a&gt;)&lt;/h4&gt;
&lt;p&gt;The Cache API is super useful when you use a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot;&gt;service worker&lt;/a&gt;, and intercept network requests by using the &lt;code&gt;fetch&lt;/code&gt; event. With this API, you can store and retrieve server responses programmatically, and make your app resilient to network problems.&lt;/p&gt;
&lt;p&gt;The API is quite straightforward to use. The harder part is understanding the different options available to you to implement a good offline scenario in your app.&lt;/p&gt;
&lt;p&gt;The API can be used in workers, and is asynchronous.&lt;/p&gt;
&lt;p&gt;Finally, the amount of data you can store in the cache can, here again, be huge. More on that later.&lt;/p&gt;
&lt;h4&gt;OPFS (&lt;a href=&quot;https://web.dev/file-system-access/#accessing-the-origin-private-file-system&quot;&gt;docs&lt;/a&gt;)&lt;/h4&gt;
&lt;p&gt;The Origin-Private File System API is based on the File System Access API. So let&#39;s talk about that first.&lt;/p&gt;
&lt;p&gt;The File System Access API can give you access to the actual file system on the device, as long as the user grants you the access. This might be really powerful if you&#39;re building an app that deals with actual user files, like a text editor.&lt;/p&gt;
&lt;p&gt;However, this API is out of scope here. The content you store lives in files that the user has control over. The content does not live in browser-managed storage space. What I&#39;m really interested in here is browser-managed storage that&#39;s transparent to users.&lt;/p&gt;
&lt;p&gt;The Origin-Private version of this API gives your app a &lt;em&gt;virtual&lt;/em&gt; file system. This file system is just for your app&#39;s origin, and you can use it to store directories and files. The files aren&#39;t necessarily possible to find on the user&#39;s device. In fact, they might not even be stored as individual files at all. The browser implementation ultimately decides how to store these. But, from your app&#39;s point of view, it&#39;s a real file system you can use as normal.&lt;/p&gt;
&lt;p&gt;The origin-private file system can be useful in many cases, for example when you need to store media files for offline access via your app only, or game assets.&lt;/p&gt;
&lt;p&gt;This API is asynchronous, which means that it won&#39;t block your app&#39;s main thread while trying to retrieve a file. It&#39;s also subject to the same storage quota that IDB and Cache storage are subject too, discussed later in this article.&lt;/p&gt;
&lt;p&gt;OPFS is implemented in chromium-based browsers and in Safari. It doesn&#39;t work in Firefox yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In need of a SQL database?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While we&#39;re on the topic of OPFS, if you are in need of a more traditional database on the client side, and are interested in using SQL queries, perhaps because that&#39;s what you&#39;re used to on the server side, you might have heard about WebSQL. You should know that &lt;strong&gt;WebSQL is deprecated&lt;/strong&gt; and being removed from browsers where it was implemented.&lt;/p&gt;
&lt;p&gt;But, you might be interested in learning that SQLite, the popular SQL database, comes as a wasm build as well which you can use directly in your web app. This version of SQLite is backed by OPFS. It&#39;s therefore subject to the same storage quota as OPFS. It also runs in a worker, keeping your app responsive while storing or querying data.&lt;/p&gt;
&lt;p&gt;If you&#39;re willing to add a couple hundred KB to your app&#39;s download, then that&#39;s a great option to consider. Check out the &lt;a href=&quot;https://sqlite.org/wasm/doc/trunk/index.md&quot;&gt;project page&lt;/a&gt;, and this &lt;a href=&quot;https://developer.chrome.com/blog/sqlite-wasm-in-the-browser-backed-by-the-origin-private-file-system/&quot;&gt;web.dev article&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3&gt;How is data stored in the browser?&lt;/h3&gt;
&lt;p&gt;Let&#39;s leave Web Storage aside. For now it has its own dedicated storage mechanism. However, everything else is handled via a storage management model that&#39;s defined in the &lt;a href=&quot;https://storage.spec.whatwg.org/&quot;&gt;Storage spec&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The theory is as follows:&lt;/p&gt;
&lt;p&gt;Each of the storage types mentioned before is called a &lt;em&gt;storage endpoint&lt;/em&gt;. Each storage endpoint stores its data in a &lt;em&gt;storage bottle&lt;/em&gt;. There&#39;s a bottle for IDB, there&#39;s one for Cache, etc.&lt;/p&gt;
&lt;p&gt;Bottles are stored in &lt;em&gt;storage buckets&lt;/em&gt;. Buckets are either &lt;em&gt;best-effort&lt;/em&gt;, or &lt;em&gt;persistent&lt;/em&gt;. A bucket that&#39;s best-effort (also known as temporary) can be emptied by the browser when needed. A persistent bucket can only be emptied by the user. More about temporary and persistent storage later.&lt;/p&gt;
&lt;p&gt;Buckets are stored in &lt;em&gt;storage shelves&lt;/em&gt;. And there&#39;s one shelf per origin.&lt;/p&gt;
&lt;p&gt;My understanding is that there&#39;s only one bucket per shelf for now. This might change in the future to allow the browser to deal with an origin&#39;s data more flexibly.&lt;/p&gt;
&lt;p&gt;And finally, the whole thing is stored in a &lt;em&gt;storage shed&lt;/em&gt;, of which there&#39;s only one in the browser.&lt;/p&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When it comes to storage, the browser is like a shed.&lt;/li&gt;
&lt;li&gt;The shed has several shelves inside. Shelves are separated, and there&#39;s one per origin.&lt;/li&gt;
&lt;li&gt;There&#39;s one bucket per shelf (this might change in the future).&lt;/li&gt;
&lt;li&gt;Each bucket is filled with bottles.&lt;/li&gt;
&lt;li&gt;There&#39;s one bottle for Cache, one bottle for IDB, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The above is only the theory from the spec though. How much of it is implemented in browsers, and whether things are actually implemented this way might vary.&lt;/p&gt;
&lt;h3&gt;How much do I get?&lt;/h3&gt;
&lt;p&gt;As said earlier, an origin can store up to 5MiB of local storage and 5MiB of session storage. Other storage options are managed by the Quota Manager, and the rest of this section will focus on those only.&lt;/p&gt;
&lt;p&gt;Browsers are free to do whatever they want here. How much they give you has changed over the years, and will probably keep changing.&lt;/p&gt;
&lt;p&gt;It&#39;s also pretty hard to find up to date information about this. Here are two useful resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria&quot;&gt;Storage quotas and eviction criteria&lt;/a&gt; on MDN (which I wrote after releasing this blog post).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/storage-for-the-web/&quot;&gt;Storage for the web&lt;/a&gt; on web.dev.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is what I found by reading docs, asking people, and experimenting on my own devices and virtual machines (with &lt;a href=&quot;https://chivalrous-attractive-dosa.glitch.me/&quot;&gt;this simple test app&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It&#39;s fair to say that an origin can store quite a lot of data on a device these days.&lt;/p&gt;
&lt;h4&gt;Safari&lt;/h4&gt;
&lt;p&gt;Safari lets your origin store 1GiB of data. After that limit is reached, it requests the user to approve the additional storage space. For example, if the app tries to store 100MiB above that limit, Safari asks the user for permission. If the storage needs continue increasing, Safari keeps on asking the user for permission.&lt;/p&gt;
&lt;p&gt;I am not sure how much Safari will let the origin store at a maximum, or whether there is a maximum at all. While testing, I was able to just continue allowing the app to store more and more data and my device eventually filled up. In the end, a JavaScript error was raised in the app, and the code couldn&#39;t store any more data.&lt;/p&gt;
&lt;p&gt;This 1GiB quota is already quite comfortable for many app use cases. If your app has very specific storage needs, like storing big media files, then the fact that Safari lets you store even more as long as the user approves it is nice.&lt;/p&gt;
&lt;p&gt;Note, however, that the WebView version of Safari (which other browsers must use on iOS devices), and other browsers that use WebKit (like the GNOME Web browser), seem to just stop at 1GiB and don&#39;t ask the user for more storage space. When this hard limit is reached, a JavaScript error is thrown in the code that attempted to store more data.&lt;/p&gt;
&lt;h4&gt;Chromium&lt;/h4&gt;
&lt;p&gt;I say Chromium here, because that&#39;s what I tested (&lt;a href=&quot;https://download-chromium.appspot.com/&quot;&gt;downloaded here&lt;/a&gt;). I believe Chrome and Edge (which are both based on Chromium) do have the same storage rules as what&#39;s in Chromium, but really, any Chromium-based browser is free to deviate from those rules (although that&#39;s probably unlikely).&lt;/p&gt;
&lt;p&gt;Chromium can use up to 80% of the device total disk size for storage. However, out of this 80%, Chromium is willing to dedicate at most 75% to a single origin. 75% times 80% is 60%. So each origin, in Chromium, can, in theory, use up to 60% of the total disk size.&lt;/p&gt;
&lt;p&gt;This doesn&#39;t mean an origin can actually use the entire 60%. This percentage is calculated based on the &lt;strong&gt;total disk size&lt;/strong&gt; of the device.&lt;/p&gt;
&lt;p&gt;For example, if your device has a 20GiB hard drive, then Chromium will theoretically let an origin use up to 12GiB (20GiB * 60% = 12GiB) of storage space. This might not actually be possible. The operating system and other files also stored on the device might use more than 8GiB, therefore leaving less than the 12GiB quota Chromium says your origin can use.&lt;/p&gt;
&lt;p&gt;In one of my tests, I was able to fill the entire disk space on the device before reaching the 60% limit. When this happens, a JavaScript error is raised, and the origin just can&#39;t store any more.&lt;/p&gt;
&lt;p&gt;In another test, I was able to reach the 60% limit before filling up the disk. Here again, a JavaScript error is raised, and the origin is prevented from storing any more data.&lt;/p&gt;
&lt;p&gt;Now, imagine a mobile device with a 64GiB drive. In theory, an origin could store up to 38GiB (60%). Now the operating system might use 10GiB, and imagine that other apps and files use 40GiB. This leaves your origin 14GiB of actual quota, which is huge, even for a media app that stores audio or video files.&lt;/p&gt;
&lt;p&gt;Note that when using the incognito or private mode in a Chromium-based browser, the quota is different (usually much lower), and the data disappears when the private browser window is closed.&lt;/p&gt;
&lt;h4&gt;Firefox&lt;/h4&gt;
&lt;p&gt;Firefox will let your origin store up to 10% of the total disk size by default, capped at 10GiB (which Firefox actually applies to all origins that are under the same &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/eTLD&quot;&gt;eTLD+1&lt;/a&gt; group).&lt;/p&gt;
&lt;p&gt;Obviously, 10% is a lot less than the 60% Chromium allows. But even if the device the app runs on has a 64GiB drive (which isn&#39;t that much by today&#39;s standards) that means your app could store up to 6GiB of data which should be enough even for media-type apps.&lt;/p&gt;
&lt;p&gt;Once the origin reaches the maximum storage space Firefox allows, an error is thrown, and the origin can&#39;t store any more data.&lt;/p&gt;
&lt;p&gt;Note that 10% is only the default however, your origin will be allowed to store up to 50% of the total disk size (this time capped at 8TiB) if your app asks for and is granted &lt;em&gt;persistent&lt;/em&gt; storage space. More on persistent vs. temporary storage later in this article, but what&#39;s important to note is that all origins start as temporary storage by default.&lt;/p&gt;
&lt;h3&gt;Can I check how much is left?&lt;/h3&gt;
&lt;p&gt;Yes, and no.&lt;/p&gt;
&lt;p&gt;You can use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/StorageManager&quot;&gt;Storage Manager API&lt;/a&gt; via the &lt;code&gt;navigator.storage&lt;/code&gt; object to discover how much your origin currently uses, and how much more it can use.&lt;/p&gt;
&lt;p&gt;To do this, use &lt;code&gt;navigator.storage.estimate()&lt;/code&gt; (which returns a Promise). This particular method is not yet implemented in Safari unfortunately (while the rest of the Storage Manager API is). Edit: The &lt;code&gt;estimate&lt;/code&gt; method is coming soon to Safari according to the &lt;a href=&quot;https://webkit.org/blog/13839/release-notes-for-safari-technology-preview-163/&quot;&gt;Safari Technology Preview 163 release note&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s very important to note that the &lt;code&gt;estimate&lt;/code&gt; method will &lt;strong&gt;not give you the real amount of available storage&lt;/strong&gt;. It will tell you what your origin&#39;s quota is, and how much of it you&#39;ve already used, but it won&#39;t tell you how much data your origin can really store, which might be less than the defined quota since there may be other things on the device too. This is for security reasons, to avoid fingerprinting.&lt;/p&gt;
&lt;p&gt;Therefore, the API will report 60% of the total disk space on Chromium, and 10% on Firefox (or 50% if the origin is persistent).&lt;/p&gt;
&lt;p&gt;You can use this result to get a rough idea of the storage capacity of the device and offer features in your app based on this, then let the user choose whether they want to use those features or not.&lt;/p&gt;
&lt;h3&gt;Temporary vs. persistent data&lt;/h3&gt;
&lt;p&gt;All storage endpoints start as &lt;em&gt;temporary&lt;/em&gt; (or &lt;em&gt;best-effort&lt;/em&gt;, as noted earlier). This sounds much scarier than it really is though.&lt;/p&gt;
&lt;p&gt;It means that if there isn&#39;t enough space left on the device, the browser can start emptying its storage shed, origin by origin (or shelf by shelf).&lt;/p&gt;
&lt;p&gt;The way the browser does this is, again, specific to its implementation. It&#39;s very likely to start removing the data from origins that haven&#39;t been used by the user for a long time first. In fact, Safari does this preemptively and deletes the content stored by origins that haven&#39;t been used for seven days.&lt;/p&gt;
&lt;p&gt;If the device your webapp runs on has plenty of space, there&#39;s not much to worry about. For example, think of a laptop with a 500GiB hard drive that still has 250GiB free. In this situation, even if your data is considered temporary there really isn&#39;t a reason to worry about it being evicted, except on Safari if your app isn&#39;t used for weeks at a time.&lt;/p&gt;
&lt;p&gt;If your app really needs to store data without which it just can&#39;t function, or if this data is important to your users and there&#39;s nowhere else to save it, you can ask for the data to be persisted for real.&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;navigator.storage.persist()&lt;/code&gt; function to request permission to use persistent storage. This is a promise-based function that&#39;s supported in all browsers. In Firefox, it will request the user permission, however on Chromium-based browsers and on Safari, the browser automatically grants or denies the request.&lt;/p&gt;
&lt;p&gt;Once your origin is granted persistent storage, the browser can no longer evict it silently, and only the user can do it on purpose via the browser UI.&lt;/p&gt;
&lt;p&gt;You should think twice before making your data persistent. Do you really want to be responsible for filling up your users&#39; devices? Does your feature really need this? If you&#39;re building a media app that stores big files for offline support, then your code should deal with the fact that stored files can disappear. As a user, I actually think it&#39;s nice that the browser deletes stored files automatically for me when my device is full.&lt;/p&gt;
&lt;p&gt;So keep this in mind, and design your app accordingly.&lt;/p&gt;
&lt;h3&gt;How does eviction work?&lt;/h3&gt;
&lt;p&gt;The logic is, again, browser-specific, and can be pretty complicated quickly. This section is not complete yet, and I will keep updating it as I find more information.&lt;/p&gt;
&lt;h4&gt;Safari&lt;/h4&gt;
&lt;p&gt;Safari seems to evict data from origins that haven&#39;t been used in the last seven days.&lt;/p&gt;
&lt;h4&gt;Chromium&lt;/h4&gt;
&lt;p&gt;In Chromium, when a device is low on storage space, the browser starts evicting data for the origins that were least recently accessed.&lt;/p&gt;
&lt;p&gt;As said previously, the browser is willing to use up to 80% of the total disk space overall (even if each origin is granted 60% of the total disk space maximum).&lt;/p&gt;
&lt;p&gt;When a user visits many different sites and apps, and if each of these origins use a little bit of their quota, there will come a point at which the browser exceeds its 80% overall limit.&lt;/p&gt;
&lt;p&gt;At this point, Chromium will evict the origin-stored data for the least recently accessed origin, and will continue doing so until it&#39;s back under its overall limit. In doing so, it will also skip the origins that have been marked as persistent.&lt;/p&gt;
&lt;!--  
Questions:
- What if the device has lots of data and the 80% overall limit can&#39;t be reached anyway. If only 50% of the total disk space can be used, does the same LRU eviction mechanism happens?
- What happens when there isn&#39;t anything that the browser can evict, when everything is permanent?
- What if the browser is under its 80% limit, and each origin is also under its 60% limit, but the disk gets filled with other things. Does the browser proactively evict data for the user to regain space? If the browser isn&#39;t running, probably not. But if you launch the browser then, the pool is smaller now, does eviction happens then?
 --&gt;
&lt;hr /&gt;
&lt;p&gt;And that&#39;s it for this today. Thanks for reading!&lt;/p&gt;
&lt;p&gt;I hope you learned a few things about storage on the web. And, more importantly, if you weren&#39;t sure if the web was up to the task for your next, storage-heavy, app project, I hope this article reassured you.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What&#39;s Open Web Docs?</title>
    <link href="https://patrickbrosset.com/articles/2023-06-24-open-web-docs/"/>
    <updated>2023-06-24T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-06-24-open-web-docs/</id>
    <content type="html">
      
        &lt;p&gt;&lt;strong&gt;tl;dr: &lt;a href=&quot;https://openwebdocs.org/&quot;&gt;Open Web Docs&lt;/a&gt; is awesome. It&#39;s one of the biggest contributors to &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN Web Docs&lt;/a&gt;. If you use MDN on a regular basis, consider &lt;a href=&quot;https://openwebdocs.org/sponsors/&quot;&gt;sponsoring OWD&lt;/a&gt; (💵) so they can continue doing the work that makes all of our web dev jobs easier. You can also easily donate via GitHub:&lt;/strong&gt;&lt;/p&gt;
&lt;iframe src=&quot;https://github.com/sponsors/openwebdocs/card&quot; title=&quot;Sponsor openwebdocs&quot; frameborder=&quot;0&quot; style=&quot;width:300px;height:300px;display:block;margin:0 auto;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;If you&#39;re a web developer you have most likely heard and used the &lt;a href=&quot;https://developer.mozilla.org/en-US/&quot;&gt;MDN Web Docs&lt;/a&gt; website. Heck, even if you don&#39;t call yourself a web dev, but still write some web code every now and then, you&#39;ve probably used it.&lt;/p&gt;
&lt;p&gt;MDN is an invaluable resource for people using HTML, CSS, JavaScript, SVG, or any other aspect of the web such as security, accessibility, performance, virtual or augmented reality on the web, and so on.&lt;/p&gt;
&lt;p&gt;Maybe you don&#39;t think about it too much, but pause for a second, and realize how many times you&#39;ve used MDN to look something up today (or any other day on the job). Take a look at your browser history if you need to. Here is mine, notice how small the scrollbar is, the list goes on and on and on:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/mdn-browser-history.png&quot; alt=&quot;The Firefox browser history management tool, filtered on developer.mozilla.org, showing a very long list of pages&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If this matches your experience, then take a minute to read this article. It&#39;s important to understand how the things we rely on so much actually exist.&lt;/p&gt;
&lt;h3&gt;Quick history recap&lt;/h3&gt;
&lt;p&gt;Let&#39;s not spend too much time on this, but just for context: MDN was started by Mozilla around 2005. It was then called the Mozilla Developer Network, and it was a wiki used to centralize the documentation for the various Mozilla projects, as well as for the web platform itself (check out &lt;a href=&quot;https://web.archive.org/web/20051201012755/http://developer.mozilla.org/&quot;&gt;this snapshot of MDN in late 2005&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Over time, more and more of the web platform APIs and features got documented on MDN, and it started becoming the de-facto documentation for many web developers. So much that in the late 2010s, other big tech companies, who had their own documentation websites, started redirecting their users to MDN instead and contributing to it. Microsoft, in particular, used to have a lot of web platform documentation over on &lt;a href=&quot;https://wikipedia.org/wiki/Microsoft_Developer_Network&quot;&gt;MSDN&lt;/a&gt;, which no longer exists (or rather is now called &lt;a href=&quot;https://learn.microsoft.com/&quot;&gt;Microsoft Learn&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The idea was simple: MDN is where people go for web platform documentation, let&#39;s focus our collective efforts there instead of duplicating the content and making it harder for people to find what they need.&lt;/p&gt;
&lt;p&gt;This led to &lt;a href=&quot;https://hacks.mozilla.org/2018/01/introducing-the-mdn-product-advisory-board/&quot;&gt;the creation&lt;/a&gt; of the &lt;a href=&quot;https://developer.mozilla.org/docs/MDN/MDN_Product_Advisory_Board&quot;&gt;MDN Product Advisory Board&lt;/a&gt; in 2018, a group of representative from various member organizations who contribute to MDN, and who meet on a regular basis to discuss the future of the project and provide guidance to ensure MDN continues providing unbiased, browser-agnostic, high-quality documentation. The PAB still exists today.&lt;/p&gt;
&lt;p&gt;In 2020, Mozilla unfortunately had to go through a pretty big round of &lt;a href=&quot;https://blog.mozilla.org/mozilla/readying-for-the-future-at-mozilla/&quot;&gt;layoffs&lt;/a&gt;. The MDN team, in particular, was hit pretty hard. At this point, the MDN team was the one responsible not only for maintaining the website infrastructure, but also for writing a lot of the content, and managing the active community of volunteer doc contributors. The layoffs was a big blow to the project.&lt;/p&gt;
&lt;p&gt;Today, Mozilla has a small team of writers and engineers who write content and maintain the website&#39;s infrastructure.&lt;/p&gt;
&lt;h3&gt;Enter Open Web Docs&lt;/h3&gt;
&lt;p&gt;High-quality documentation for the web platform is &lt;strong&gt;critically important&lt;/strong&gt; so, with a bunch of really talented technical writers now out of a job, and so many devs out there who relied on MDN for their day to day jobs, it was clear that something had to be done.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opencollective.com/open-web-docs/updates/introducing-open-web-docs&quot;&gt;Open Web Docs was created&lt;/a&gt; in January 2021 by the people involved in MDN at the time, including the PAB.&lt;/p&gt;
&lt;p&gt;OWD is an independent, vendor-neutral, organization that is responsible for ensuring the long-term health of web platform documentation. It spends all of its resources writing new docs, maintaining existing ones, and managing the community of contributors on today&#39;s de-facto web platform documentation website: MDN Web Docs.&lt;/p&gt;
&lt;p&gt;OWD is composed of a small team of technical writers (4 full-time employees and 1 part-time contractor) who spend their time maintaining the content, which includes writing new docs, updating existing ones, maintaining the &lt;a href=&quot;https://github.com/mdn/browser-compat-data&quot;&gt;browser compatibility data tables&lt;/a&gt; (which, by the way, are also used by &lt;a href=&quot;https://caniuse.com/&quot;&gt;caniuse.com&lt;/a&gt;), doing code reviews for the community, and so on. Take a look at their &lt;a href=&quot;https://openwebdocs.org/content/reports/2022/&quot;&gt;2022 impact report&lt;/a&gt; from more details.&lt;/p&gt;
&lt;p&gt;So how can OWD do this work? The organization needs to pay those 4,5 people for their work. And to do this, because it&#39;s a non-profit organization, it relies on donations! It&#39;s funded by a mix of corporate sponsors, Google and Microsoft being the two biggest, and a bunch of individual contributors.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;And that&#39;s where you come in! If you use MDN on a regular basis, and would find it considerably harder to do your job without it, then consider financially &lt;a href=&quot;https://opencollective.com/open-web-docs&quot;&gt;supporting OWD&lt;/a&gt;. You can become a backer for as little as $5 per month.&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;It&#39;s simple, without money, OWD can&#39;t employ technical writers to continue making MDN as great as it is. Sure, OWD isn&#39;t the only contributor to MDN. Mozilla has a team of writers too, other companies contribute docs, and there are a lot of volunteers. But if you haven&#39;t done so yet, do take a look at the &lt;a href=&quot;https://openwebdocs.org/content/reports/2022/&quot;&gt;2022 impact report&lt;/a&gt;. This should give you perspective on how much work OWD does, and how negatively impacted MDN would be if OWD didn&#39;t exist. On top of this, OWD isn&#39;t just a big part of MDN, it&#39;s also a vendor-neutral part of it. When Microsoft, Google, Mozilla, or Apple write their own docs on MDN, they always come with their own lens and biases.&lt;/p&gt;
&lt;h3&gt;It&#39;s not just about the MDN website&lt;/h3&gt;
&lt;p&gt;Sure the MDN website is very important and if you&#39;ve read this far you probably use it a lot. But OWD&#39;s impact goes further than this.&lt;/p&gt;
&lt;p&gt;I mentioned it already, but OWD also contributes a lot to the &lt;a href=&quot;https://github.com/mdn/browser-compat-data&quot;&gt;browser compatibility data tables&lt;/a&gt; (BCD) which appear at the bottom of almost every MDN page. But this data is also available as &lt;a href=&quot;https://www.npmjs.com/package/@mdn/browser-compat-data&quot;&gt;a package on NPM&lt;/a&gt;, which is used by many other tools, sites, and IDEs which you might also rely on! Here is a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://caniuse.com/&quot;&gt;caniuse.com&lt;/a&gt;, which maintains its own data and mixes it in with the BCD tables.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/caniuse-bcd.png&quot; alt=&quot;caniuse.com, showing a mention to BCD in the footer&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chrome DevTools&#39; &lt;a href=&quot;https://developer.chrome.com/blog/new-in-devtools-112/#css&quot;&gt;Styles pane MDN tooltips&lt;/a&gt; which displays MDN data for CSS properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/chromedevtools-mdn.png&quot; alt=&quot;The Chrome Devtools Styles panel, showing a tooltip with documentation&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firefox DevTools&#39; &lt;a href=&quot;https://firefox-source-docs.mozilla.org/devtools-user/page_inspector/how_to/examine_and_edit_css/index.html#css-compatibility&quot;&gt;compatibility panel&lt;/a&gt; which uses the BCD data to show you which browsers support the CSS properties you&#39;re using.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/ffdevtools-bcd.png&quot; alt=&quot;The Firefox Devtools Compatibility panel&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://webhint.io/&quot;&gt;Webhint&lt;/a&gt; and the &lt;a href=&quot;https://learn.microsoft.com/microsoft-edge/devtools-guide-chromium/issues/&quot;&gt;Issues tool in Edge DevTools&lt;/a&gt; (which relies on WebHint) which use BCD to give you warnings about APIs that might not be supported in all browsers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/edge-issues-bcd.png&quot; alt=&quot;The Edge Devtools Issues panel, showing a CSS property is incompatible on Safari&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ESLint and its &lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-compat&quot;&gt;eslint-plugin-compat&lt;/a&gt; plugin.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/eslint-bcd.png&quot; alt=&quot;An ESLint error tooltip in a piece of code, showing that this ESLint plugin can tell you when APIs are not supported&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VSCode, which displays browser compat info in its HTML and CSS &lt;a href=&quot;https://code.visualstudio.com/docs/editor/intellisense&quot;&gt;intellisense&lt;/a&gt; feature.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/vscode-bcd.png&quot; alt=&quot;VSCode, with the intellisense tooltip visible on a CSS property, showing the MDN docs and browser compat details&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other websites also display MDN documentation. After all the &lt;a href=&quot;https://github.com/mdn/content/&quot;&gt;mdn/content GitHub repository&lt;/a&gt;, where the docs are stored, is public and open-source, and anyone can use the content or even fork it. That&#39;s how &lt;a href=&quot;https://devdocs.io/&quot;&gt;devdocs.io&lt;/a&gt; is able to display CSS reference documentation for example.&lt;/p&gt;
&lt;p&gt;In fact, in a future where the MDN website is no longer the de-facto place where people go to learn about the web, that doesn&#39;t change a thing for OWD. OWD&#39;s mission is to ensure the long-term health of this critical infrastructure that&#39;s web platform documentation, wherever people may be consuming it from.&lt;/p&gt;
&lt;h3&gt;I don&#39;t really want to pay, but my company relies on MDN, what can I do?&lt;/h3&gt;
&lt;p&gt;If your company has made a big bet on the web, it might not be such a bad idea for the company to financially contribute to ensure the web documentation&#39;s future is bright. After all, if the documentation of the browser APIs you&#39;re using is a bit outdated, or if the tools you rely on start to be incorrect, your teams are going to have a much harder time doing their jobs.&lt;/p&gt;
&lt;p&gt;The less reliable the web platform documentation is, the more experts your company will probably need to hire to reverse-engineer browsers and figure out what works and what doesn&#39;t. On the other hand, if high-quality and up-to-date documentation is always there when you need it, &lt;strong&gt;you can focus on building your own products&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here are a few ways you can inform your company about OWD and try to convince them to contribute:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tell them how much you use MDN content through the website and other tools you use daily. Show the impact these resources have on your teams and show usage numbers.&lt;/li&gt;
&lt;li&gt;Show them this blog post.&lt;/li&gt;
&lt;li&gt;Show them the OWD &lt;a href=&quot;https://openwebdocs.org/content/reports/2021/&quot;&gt;2021&lt;/a&gt;, &lt;a href=&quot;https://openwebdocs.org/content/reports/2022/&quot;&gt;2022&lt;/a&gt;, and &lt;a href=&quot;https://openwebdocs.org/content/reports/2023/&quot;&gt;2023&lt;/a&gt; impact reports to clarify to what extent OWD contributes to these resources.&lt;/li&gt;
&lt;li&gt;Show them the &lt;a href=&quot;https://openwebdocs.org/membership/&quot;&gt;OWD membership&lt;/a&gt; page, which contains details about the various sponsorship levels and the benefits that come with them. As a Gold sponsor, for example, your company would get its logo on the website, and a seat on the OWD Steering Committee to discuss with the OWD community and help shape the future of web documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can get in touch with OWD by using the &lt;strong&gt;Contact&lt;/strong&gt; button on the &lt;a href=&quot;https://opencollective.com/open-web-docs&quot;&gt;Open Collective page&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;And that&#39;s it! I hope this post has helped you understand the importance of OWD and why you should consider supporting it. If you have any questions, feel free to reach out to me on &lt;a href=&quot;https://twitter.com/patrickbrosset&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;https://mas.to/@patrickbrosset&quot;&gt;Mastodon&lt;/a&gt;. I&#39;m part of the OWD Governing Committee, and I&#39;ll be happy to answer your questions.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Useful DevTools Tips and Tricks</title>
    <link href="https://patrickbrosset.com/articles/2023-06-27-useful-devtools-tips-and-tricks/"/>
    <updated>2023-06-27T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-06-27-useful-devtools-tips-and-tricks/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2023/06/popular-devtools-tips/">https://www.smashingmagazine.com/2023/06/popular-devtools-tips/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Collaborating with Open Web Docs for great PWA docs</title>
    <link href="https://patrickbrosset.com/articles/2023-08-09-collaborating-with-open-web-docs-for-great-pwa-docs/"/>
    <updated>2023-08-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-08-09-collaborating-with-open-web-docs-for-great-pwa-docs/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2023/08/09/pwa-documentation-mdn-web-docs-open-web-docs/">https://blogs.windows.com/msedgedev/2023/08/09/pwa-documentation-mdn-web-docs-open-web-docs/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Collaborating with the Office Performance team for better web performance tools</title>
    <link href="https://patrickbrosset.com/articles/2023-08-10-collaborating-with-the-office-performance-team-for-better-web-performance-tools/"/>
    <updated>2023-08-10T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-08-10-collaborating-with-the-office-performance-team-for-better-web-performance-tools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2023/08/10/collaborating-office-performance-better-web-performance-tools/">https://blogs.windows.com/msedgedev/2023/08/10/collaborating-office-performance-better-web-performance-tools/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Please take full window screenshots in product documentation</title>
    <link href="https://patrickbrosset.com/articles/2023-08-30-please-take-full-window-screenshots-in-product-documentation/"/>
    <updated>2023-08-30T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-08-30-please-take-full-window-screenshots-in-product-documentation/</id>
    <content type="html">
      
        &lt;p&gt;When you&#39;re documenting an application, reporting a bug about it, or just chatting about it with colleagues, it&#39;s common to have to share screenshots of the app. Please do everyone a favor and take &lt;strong&gt;full window screenshots&lt;/strong&gt; when you do.&lt;/p&gt;
&lt;p&gt;I know this, because I write a lot of technical documentation for various browser stuff. So I need to take a lot of screenshots of browsers. When I started out, I used to take cropped out screenshots that would only show the area of interest. For example, if I was writing about the browser&#39;s settings, I would only capture the settings page, and not the entire browser window. I thought I was being clever, and saving people time by not making them look at irrelevant parts of the screenshot. But, in reality, I was being lazy, and causing a lot of problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My point is: full window screenshots provide context that cropped screenshots just don&#39;t&lt;/strong&gt;. When you see the entire window of an app, you can recognize the app and get a general sense of directions. When you see a small sub-part of an app only, you might have no idea where you are and how to get there.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of a screenshot that&#39;s not so great:&lt;/p&gt;
&lt;div class=&quot;no-border&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/bad-window-screenshot.png&quot; alt=&quot;An example of a cropped out window screenshot where only a part is visible&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In this screenshot, unless you&#39;ve been reading the documentation that (hopefully) came before it with attention, it&#39;ll be very hard to know what you&#39;re looking at. Where is this in the app? What&#39;s important to see in the screenshot?&lt;/p&gt;
&lt;p&gt;Here&#39;s a better screenshot:&lt;/p&gt;
&lt;div class=&quot;no-border&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/good-window-screenshot.png&quot; alt=&quot;An example of a full window screenshot where we see it&#39;s a web browser, with DevTools opened, and a few areas are highlighted with red boxes&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In this screenshot, the entire app window is visible, so it&#39;s clear you&#39;re looking at a web browser which has DevTools in it. Because the full window is visible, its borders are visible too, which avoids white areas of the image to blend in with the white page background (if you&#39;re using the Light theme on my site). It also has red boxes in a few places to attract attention to specific areas of the screenshot.&lt;/p&gt;
&lt;h3&gt;How to take good screenshots&lt;/h3&gt;
&lt;p&gt;Here&#39;s my list of tips for taking good screenshots, I hope it&#39;s useful.&lt;/p&gt;
&lt;h4&gt;Make the window as small as possible&lt;/h4&gt;
&lt;p&gt;First, prepare the window you want to capture. There&#39;s no need to make it super big. If you have a large monitor and maximized the app&#39;s window, you will need to un-maximize it so its boundaries are clearly visible against the desktop background. And then, make the app window as small as possible while preserving all of the details you need to capture. The smaller you make it, the easier it will be for other people to make out the details.&lt;/p&gt;
&lt;h4&gt;Remove customizations&lt;/h4&gt;
&lt;p&gt;The same application can look very different on your computer and on someone else&#39;s computer. Sometimes because you&#39;ve applied a theme at the OS level that impacts the application, other times because that application supports plugins that change its appearance.&lt;/p&gt;
&lt;p&gt;If your screenshot is supposed to be seen by other people, it&#39;s best to remove all customizations and go back to what most people would see when they first install the app. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Switching back to your OS&#39;s default theme, if that theme impacts the app.&lt;/li&gt;
&lt;li&gt;Switching back to the app&#39;s default theme, if the app also supports its own themes.&lt;/li&gt;
&lt;li&gt;Disabling all plugins or extensions which may impact the app (e.g. browser extensions which add icons into the toolbar).&lt;/li&gt;
&lt;li&gt;Turn off any app setting you might have changed from the default. Many apps expose user settings to customize their toolbar for example.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally, you want to make the app look as close to its default state as possible, and as uncluttered as possible.&lt;/p&gt;
&lt;h4&gt;Remove background clutter&lt;/h4&gt;
&lt;p&gt;When taking a full window screenshot, a tiny fraction of the background behind the app will be shown too. On Windows for example, the thin 1px border around an app&#39;s window may slightly blend in with whatever&#39;s behind it. This may create tiny artifacts in the screenshot that will make it less clean.&lt;/p&gt;
&lt;p&gt;To avoid this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hide all desktop icons.&lt;/li&gt;
&lt;li&gt;Set your desktop background to a solid color, ideally white.&lt;/li&gt;
&lt;li&gt;Hide all other windows.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Capture the entire window&lt;/h4&gt;
&lt;p&gt;Once you&#39;ve done all of the above, you&#39;re ready to capture the screenshot. That&#39;s when you want to capture the entire window, not just a part of it.&lt;/p&gt;
&lt;p&gt;On Windows, I suggest using the &lt;strong&gt;Snipping Tool&lt;/strong&gt;. On my Windows 11, I can just press &lt;kbd&gt;Windows key + Shift + S&lt;/kbd&gt; to open it. I think it&#39;s also possible to bind it to the &lt;kbd&gt;Print Screen&lt;/kbd&gt; key. &lt;strong&gt;Snipping Tool&lt;/strong&gt; lets you either define a rectangular area by hand, or set it to Window mode. Do that! Once you set it to the Window mode, you can just click on the window you want to capture, and it&#39;ll capture the entire window.&lt;/p&gt;
&lt;p&gt;On macOS, you can use the &lt;kbd&gt;Command + Shift + 5&lt;/kbd&gt; shortcut to capture an entire window.&lt;/p&gt;
&lt;h4&gt;Highlight areas of interest&lt;/h4&gt;
&lt;p&gt;Finally, it&#39;s time to post-process the screenshot. Think about what readers of your documentation will need to see, and highlight those areas.&lt;/p&gt;
&lt;p&gt;For example, if the steps you wrote before the screenshot say things like &amp;quot;click X, then click Y, the result appears in Z&amp;quot;, you should highlight X, Y, and Z in the screenshot. This will make it easier for readers to follow the steps. Instead of having to look at every little detail in the screenshot, they will immediately know where to look.&lt;/p&gt;
&lt;p&gt;To do this, I often just use Paint on Windows. It&#39;s simple and does the job very easily. I just use the rectangle tool and set the color to red, and then draw red boxes around the areas of interest.&lt;/p&gt;
&lt;p&gt;A few things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Red works well in most cases. Grey, white, or black will risk blending in with the UI in the screenshot. Bright yellow might also be a good option, but I often find it harder to spot than red.&lt;/li&gt;
&lt;li&gt;Choose the thickness of your red boxes carefully. Too thin and users won&#39;t see it. Too thick and it&#39;ll hide important parts of the screenshot.&lt;/li&gt;
&lt;li&gt;Where to draw the red boxes is also important: if you spend time aligning your red boxes to be perfectly around a panel, for example, then there&#39;s a risk someone would confuse this red box with an actual border in the app&#39;s UI. So don&#39;t be too precise. It&#39;s better to draw your boxes slightly too big or slightly too small, just so they stand out more.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;I hope this was useful! If you have any other tips for taking good screenshots, please let me know on &lt;a href=&quot;https://mas.to/@patrickbrosset&quot;&gt;Mastodon&lt;/a&gt;!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  
  <entry>
    <title>My first TPAC conference</title>
    <link href="https://patrickbrosset.com/articles/2023-09-26-my-first-tpac-conference/"/>
    <updated>2023-09-26T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-09-26-my-first-tpac-conference/</id>
    <content type="html">
      
        &lt;p&gt;Two weeks ago I attended the W3C TPAC conference. In this post, I&#39;ll go over my experience and learnings. Read on if you&#39;re interested in how the &amp;quot;sausage is made&amp;quot; when it comes to web features.&lt;/p&gt;
&lt;p&gt;W3C is the standardization organization that develops most of the specifications for the web features that web browsers implement today. TPAC is W3C&#39;s annual face to face meeting (though it was hybrid this year) for the various working groups that work on new web features.&lt;/p&gt;
&lt;p&gt;The 40 (or so) W3C working groups are made up of people who have an interest in the particular technical area a group focuses on. For example, there&#39;s a group about browser automation and tooling, and in it are people from the various browser maker companies, Selenium, and other companies who are particularly invested in browser testing.&lt;/p&gt;
&lt;p&gt;The groups hold video-conferencing calls on a regular cadence in order to review proposals and make updates to their specifications. So, when TPAC comes, they all meet in one geographical location to have a series of face-to-face meetings. This is a great forcing function for the various working groups to come to consensus, make decisions, come up with new proposal, as well as meet with other working groups.&lt;/p&gt;
&lt;p&gt;That&#39;s my high-level definition of TPAC anyway, and this was the very first time I attended this conference. Other people who are more used to it might have different definitions. To me, though, this is all about the people and making connections. Being in one place, surrounded by hundreds of people who basically make the web happen is pretty awesome. It&#39;s a great chance to get to know people personally, break the ice, and create long lasting working relationships.&lt;/p&gt;
&lt;h3&gt;The location&lt;/h3&gt;
&lt;p&gt;This year, TPAC happened in Seville, Spain. And, my, what a beautiful place. I had been to Seville as a teenager, almost 30 years ago. But at the time, I wasn&#39;t really paying attention to the same things. Plus, I blanked most of it from my memory. So I discovered the city all over again. Mind you, I wasn&#39;t there as a tourist, and certainly didn&#39;t have a lot of time to visit. But I had the opportunity to go and explore on the Sunday before the conference, and then early mornings and late evenings too.&lt;/p&gt;
&lt;p&gt;The city is just super pretty, with narrow streets in the older part of town, a beautiful and gigantic cathedral, people playing guitar and dancing flamenco in the parks or squares, many tapas restaurants with people dining on the terrasses. Taking a stroll in the city is very special.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/tpac-2023/photos-seville.png&quot; alt=&quot;Two photos of Seville, one of a street, and one of the plaza de Espana&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As for the conference venue and hotel, it was at a distance from the city center (maybe 15 minutes walk), but super close to the Plaza de España which is a very nice park.&lt;/p&gt;
&lt;p&gt;There were many meeting rooms and they were spacious enough, sometimes even too spacious. The one downside was the acoustics of the meeting rooms, which was pretty bad. The rooms had a lot of echo, and just moving your chair, pouring a glass of water, or clearing your throat would be disturbing to the meeting. Remote attendants on Zoom must have had a hard time with the sound quality.&lt;/p&gt;
&lt;h3&gt;How W3C meetings go&lt;/h3&gt;
&lt;p&gt;I was very impressed with the processes and tools in place for the working group meetings. But, then again, these folks have been meeting for years, slowly perfecting ways to run efficient and inclusive meetings. Now I&#39;m not saying the meetings are necessarily perfect, but the process in place comes with important benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meetings are always minuted, making it possible to go back to past discussions, which is very important when working on (generally) slow-moving spec documents.&lt;/li&gt;
&lt;li&gt;Anyone can ask a question without being an extrovert and having to interrupt somebody.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The process comes down to using IRC (yes, very 90s style, it&#39;s hard to believe it&#39;s still used, but the working groups have a few bots and commands that would be time-consuming to replace). For each and every meeting, the working group uses their dedicated IRC channel, and starts a bot that handles creating a minutes document and manages the question queue. Participants signal their presence with a command, and there&#39;s another command to put yourself on the question queue.&lt;/p&gt;
&lt;p&gt;The person who drives the meeting can then just ask the IRC bot to go through the question queue, therefore letting people say what they have to say, one by one. It does make it a little bit awkward, at times, with conversations that need a lot of back and forth discussion. However, it&#39;s really good, especially when you&#39;re an introvert, that you don&#39;t have to impose yourself verbally to say something.&lt;/p&gt;
&lt;p&gt;The scribe (the person who takes notes) tries their best to record what people say in real-time by writing it all down in the IRC channel. At the end of the meeting, the bot posts the full minutes to the relevant GitHub issue, or to the working group&#39;s website.&lt;/p&gt;
&lt;p&gt;One downside I see with this system is that the full minutes don&#39;t only include what people said. It also includes the various commands people typed during the discussion, like when they want to ask a question. So the IRC minutes are a little hard to read.&lt;/p&gt;
&lt;p&gt;Some working groups used Google Docs instead. It made it a little bit trickier to manage the question queue, but it worked. The scribe would take notes in one area of the document, and people would write their questions in another part, dedicated to the question queue.&lt;/p&gt;
&lt;p&gt;Overall, I thought this was a very good way to run remote meetings and, as a fully remote employee, I wish we did this systematically at work too.&lt;/p&gt;
&lt;h3&gt;The meetings&lt;/h3&gt;
&lt;p&gt;I attended a bunch of working group meetings and breakout sessions. Among others:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Web Applications working group, which reviewed the status of a bunch of specs, from low-level device and sensor APIs, to screen locking, geolocation, and Image Resource APIs. The group also reviewed Microsoft&#39;s proposal for an imperative way to install PWAs with &lt;code&gt;navigator.install()&lt;/code&gt;. PWA launch handling was also reviewed.&lt;/li&gt;
&lt;li&gt;The future of cross-browser web apps breakout session, which was mainly focused on the installability criterias that browser expect of PWAs, and whether they were still needed. Not suprising since Apple recently shipped support for installing any website in Safari, whether it&#39;s a PWA or not.&lt;/li&gt;
&lt;li&gt;The Web Incubator Community group, which discussed about low-level device APIs, MIDI, and permissions.&lt;/li&gt;
&lt;li&gt;The Browser testing and tools working group, which went over various issues and proposals related to the WebDriver BiDi spec.&lt;/li&gt;
&lt;li&gt;A breakout session about the Interop project, which helped me more clearly understand the feature selection process.&lt;/li&gt;
&lt;li&gt;Multiple breakout sessions from the WebDX community group about Baseline and doing developer surveys.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also co-hosted a breakout session, together with Florian Scholz (Open Web Docs) and Rachel Andrew (Google). The topic was technical writing and how spec writers and implementers can help get their features more easily adopted by helping technical writers document the features on MDN.&lt;/p&gt;
&lt;p&gt;The breakout sessions day was super fun and informative. It&#39;s basically a day in the week where anyone at TPAC can propose a topic to present or discuss about. These meetings are shorter than the working group meetings, and are usually best for presenting a new idea and getting some quick feedback or hosting a Q&amp;amp;A session about a particular project or idea.&lt;/p&gt;
&lt;p&gt;The other days were filled with working group meetings which, based on the ones I attended, tended to fall into two main categories:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Business as usual, the working group goes through its list of regular issues, takes decisions if needed, agrees on next steps, etc.&lt;/li&gt;
&lt;li&gt;The other case is when the working group talks about new proposals. The vibe is clearly different in this case. Usually a browser vendor comes with a proposal that makes sense to them for whatever internal strategic reason, presents the proposal, and then attempts to build consensus on it with the other working group members. The tricky part is that the other members often include people from other browser vendors who might have different priorities and strategies in mind.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Don&#39;t get me wrong, it&#39;s all for the better. Out of the thousands of random ideas that people have for what web features should exist, it&#39;s great that they go through so much scrutiny, and that only a few of them make it. It&#39;s also great that this happens as a result of stakeholders debating and agreeing on the details of how a feature fits in with the rest. It does sometimes get tricky when browser vendors go ahead and implement their proposed ideas before they&#39;re even spec&#39;d. In fact, this almost always happens. When you go and present a feature proposal, it&#39;s common to have specified it in a non-official place already, and to have implemented a quick prototype of it. It&#39;s tricky when the feature has started getting adopted, or even became a de-facto standard already. Having cross-browser agreement about the feature and how it should be spec&#39;d is challenging at that point.&lt;/p&gt;
&lt;p&gt;It was very interesting witnessing some of the more heated discussions, and seeing how people represented their respective organization&#39;s points of view.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Attending TPAC was pretty eye-opening. Even though I have been working on browsers for 10 years, I&#39;ve not been super involved in the standardization aspect of the work. If this aspect interests you, I can only recommend following what the various &lt;a href=&quot;https://www.w3.org/groups/wg/&quot;&gt;working groups&lt;/a&gt; do, and maybe getting involved in one of the many &lt;a href=&quot;https://www.w3.org/community/&quot;&gt;community groups&lt;/a&gt;.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Freezing a page in DevTools</title>
    <link href="https://patrickbrosset.com/articles/2023-11-22-freezing-a-page-in-devtools/"/>
    <updated>2023-11-22T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-11-22-freezing-a-page-in-devtools/</id>
    <content type="html">
      
        &lt;p&gt;A &lt;a href=&quot;https://front-end.social/@patrick_h_lauke@mastodon.social/111453240207940907&quot;&gt;recent discussion on Mastodon&lt;/a&gt; reminded my of this age-old problem related to inspecting webpages in DevTools: things tend to move around or disapear when you stop interacting with the page to go and do stuff in DevTools!&lt;/p&gt;
&lt;p&gt;This is most often due to mouse hover or focus effects. The webpage might display a popup only when a specific element is hovered, or focused, which means that as soon you move your mouse away from the element, or click somewhere else, the popup disappears.&lt;/p&gt;
&lt;p&gt;There are a few ways to work around this, which I&#39;ll list here, but I&#39;m convinced better tools need to be invented still, and I&#39;ll provide a few ideas at the end of the post.&lt;/p&gt;
&lt;h2&gt;Existing solutions&lt;/h2&gt;
&lt;h3&gt;Freezing the page by pausing the JavaScript execution&lt;/h3&gt;
&lt;p&gt;Once you get into the desired state, pausing the JavaScript execution might be enough to freeze the page. That&#39;s assuming, of course, that the element you want to inspect is controlled by JavaScript.&lt;/p&gt;
&lt;p&gt;There are two ways to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If the element appears on &lt;code&gt;mouseover&lt;/code&gt; events:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In DevTools, open the &lt;strong&gt;Sources&lt;/strong&gt; tool (or &lt;strong&gt;Debugger&lt;/strong&gt; in Firefox), and keep focus in DevTools.&lt;/li&gt;
&lt;li&gt;In the webpage, hover the page to make the element you want to inspect appear. Don&#39;t click in the webpage, keep focus in DevTools.&lt;/li&gt;
&lt;li&gt;Press &lt;kbd&gt;F8&lt;/kbd&gt;. This pauses JavaScript execution, and the element should stay visible.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See &lt;a href=&quot;https://devtoolstips.org/tips/en/debug-js-hover/&quot;&gt;Debug popups that appear on hover using JS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If the element appears on &lt;code&gt;focus&lt;/code&gt; events:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In DevTools, open the &lt;strong&gt;Console&lt;/strong&gt; tool, enter &lt;code&gt;setTimeout(() =&amp;gt; {debugger}, 3000)&lt;/code&gt;, and press &lt;kbd&gt;Enter&lt;/kbd&gt;.&lt;/li&gt;
&lt;li&gt;Within 3 seconds, focus the element you want to inspect in the webpage. If 3 seconds isn&#39;t enough, increase the delay in the &lt;code&gt;setTimeout&lt;/code&gt; function above.&lt;/li&gt;
&lt;li&gt;After the timeout, JavaScript execution will pause, and the element that appears on focus should stay visible.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See &lt;a href=&quot;https://devtoolstips.org/tips/en/debug-js-hover-2/&quot;&gt;Debug popups that appear on hover using the debugger statement&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also achieve this by adding the &lt;code&gt;debugger&lt;/code&gt; statement directly in your source code, at the right place, or by adding a breakpoint on the right line in the &lt;strong&gt;Sources&lt;/strong&gt; tool (or &lt;strong&gt;Debugger&lt;/strong&gt; in Firefox).&lt;/p&gt;
&lt;h3&gt;Forcing the hover or focus CSS pseudo-classes&lt;/h3&gt;
&lt;p&gt;If the state you are trying to debug is controlled by the CSS &lt;code&gt;:hover&lt;/code&gt; or &lt;code&gt;:focus&lt;/code&gt; pseudo-classes, you can force this state from DevTools, without having to interact with the page with your mouse or keyboard.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In DevTools, open the &lt;strong&gt;Elements&lt;/strong&gt; tool (or &lt;strong&gt;Inspector&lt;/strong&gt; in Firefox).&lt;/li&gt;
&lt;li&gt;Select the element you want to inspect.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Styles&lt;/strong&gt; side panel (or &lt;strong&gt;Rules&lt;/strong&gt; in Firefox), click on the &lt;strong&gt;:hov&lt;/strong&gt; button. A pane appears, showing all the pseudo-classes you can force on the element.&lt;/li&gt;
&lt;li&gt;Click the pseudo-classes checkboxes you want to force.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Emulating focus on the page&lt;/h3&gt;
&lt;p&gt;If the element you want to inspect appears on focus, you can also emulate focus on the page, without your real focus actually being there.&lt;/p&gt;
&lt;p&gt;This only works in Chromium-based browsers such as Chrome and Edge though.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In DevTools, open the &lt;strong&gt;Rendering&lt;/strong&gt; tool. To do this, you can use the &lt;strong&gt;Command Menu&lt;/strong&gt;: press &lt;kbd&gt;Ctrl+Shift+P&lt;/kbd&gt; or &lt;kbd&gt;Cmd+Shift+P&lt;/kbd&gt;, and type &amp;quot;Rendering&amp;quot;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Rendering&lt;/strong&gt; tool, check the &lt;strong&gt;Emulate a focused page&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;In the webpage, focus the element you want to inspect. Now, even if you click somewhere else in DevTools, the emulated focus stays on the element in the page&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See &lt;a href=&quot;https://devtoolstips.org/tips/en/debug-popups-on-hover/&quot;&gt;Debug popups that appear on hover&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Looking forward&lt;/h2&gt;
&lt;p&gt;Conversations about this topic often end up with people saying &lt;em&gt;&amp;quot;we need a better way to freeze the page in DevTools&amp;quot;&lt;/em&gt;. Some of it is due to people not knowing about the above techniques. Hopefully this blog post will help a bit. But sometimes, these techniques are just not what people need.&lt;/p&gt;
&lt;h3&gt;Idea: virtual pointer&lt;/h3&gt;
&lt;p&gt;I had an idea 10 years ago, which you can see in this &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1036722&quot;&gt;Mozilla bugzilla ticket&lt;/a&gt;, of completely simulating a mouse pointer in DevTools. Here is the idea:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;From DevTools, you&#39;d start a virtual pointer mode.&lt;/li&gt;
&lt;li&gt;Once started, the page would no longer react to your real mouse pointer, as if it was blocked behind a full page overlay.&lt;/li&gt;
&lt;li&gt;A virtual pointer would appear on the page instead.&lt;/li&gt;
&lt;li&gt;You would then be able to move the pointer anywhere on the page, and choose from a list of mouse events to simulate.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I even made a quick prototype back then, which you can see in action here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bug1036722.bmoattachments.org/attachment.cgi?id=8873802&quot; alt=&quot;Animation showing a virtual pointer being moved around in the page, and a mouse event menu being used to generate a click event&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I wonder if this is actually doable as a browser extension nowadays? You&#39;d have to block the page with some kind of overlay, emulate focus on the page at all times, and then synthesize mouse events. Seems fairly straightforward.&lt;/p&gt;
&lt;h3&gt;Idea: snapshoting the page&#39;s HTML and CSS&lt;/h3&gt;
&lt;p&gt;Here&#39;s an idea for a different approach that involves capturing the entire HTML and computed CSS of the page, and then displaying it in the browser.&lt;/p&gt;
&lt;p&gt;The pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s easy to implement, the approach is very simple and robust.&lt;/li&gt;
&lt;li&gt;It freezes the entire page in its current state, and nothing can change anymore.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It produces very large amounts of HTML and CSS.&lt;/li&gt;
&lt;li&gt;It loses the CSS rules, and replaces all styles with inline styles instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It probably can be improved though, I only spend a few minutes thinking about it. With that in mind, here&#39;s how it works:&lt;/p&gt;
&lt;p&gt;First, we need a bit of JavaScript to run on the page. This can be done by either running it from a &lt;strong&gt;Snippet&lt;/strong&gt; in Chrome/Edge DevTools (so you can reuse it later), or just running it from the &lt;strong&gt;Console&lt;/strong&gt; in any browser. Here is the code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IGNORED_TAGS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SCRIPT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LINK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;META&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getDOMCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DocumentFragment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; mirroredNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nodeType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ELEMENT_NODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IGNORED_TAGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Create the mirrored node.&lt;/span&gt;
            mirroredNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Mirror the attributes.&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; attr &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                mirroredNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Mirror the styles, skipping default styles.&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; computedStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mirroredComputedStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mirroredNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; style &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; computedStyle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                mirroredNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; computedStyle&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Mirror child nodes.&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; childNodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;childNodes&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; childNode &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; childNodes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;getDOMCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;childNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mirroredNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nodeType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEXT_NODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            mirroredNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTextNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mirroredNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mirroredNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;freezePage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fragment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getDOMCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Serialize the document fragment to HTML code.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; serializer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XMLSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serializeToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fragment&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Replace the content.&lt;/span&gt;
        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keyup&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Freezing the page ...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;freezePage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DONE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ready, focus the page and press F to freeze it&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this code has run, focus the webpage, get it into the state that you need, and immediately press &lt;kbd&gt;F&lt;/kbd&gt;. The entire state of the page is captured and the current page is replaced with this snapshot.&lt;/p&gt;
&lt;p&gt;As a result: the page doesn&#39;t change when you resize the browser window and the hover or focus styles are frozen in place. You can then inspect the page as you wish.&lt;/p&gt;
&lt;p&gt;Let me know &lt;a href=&quot;https://mas.to/@patrickbrosset&quot;&gt;on Mastodon&lt;/a&gt; if you think this could be useful, and how it could be improved.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Making puzzle pieces with CSS mask-image and SVG masks</title>
    <link href="https://patrickbrosset.com/articles/2023-12-06-making-puzzle-pieces-with-css-mask-image-and-svg-masks/"/>
    <updated>2023-12-06T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-12-06-making-puzzle-pieces-with-css-mask-image-and-svg-masks/</id>
    <content type="html">
      
        &lt;p&gt;This week, I was working on a little demo page to learn more about CSS masking with SVG. The page is a puzzle game where each piece is a single DOM element, and is masked with an SVG mask, to give it the shape of a puzzle piece.&lt;/p&gt;
&lt;p&gt;If you want to see the final result, you can check out the &lt;a href=&quot;https://patrickbrosset.com/lab/2023-12-06-puzzle&quot;&gt;demo page&lt;/a&gt; (&lt;em&gt;warning&lt;/em&gt;: this doesn&#39;t seem to work in all browsers. Use Firefox or Chrome/Edge 120+).&lt;/p&gt;
&lt;p&gt;I won&#39;t go into the details of how the game works here. However, I found out the hard way about how to make the SVG masks work. So, I thought I&#39;d share what I learned, in case it can help someone else.&lt;/p&gt;
&lt;h3&gt;The SVG shapes&lt;/h3&gt;
&lt;p&gt;The first thing I did was to create the SVG shapes that I wanted to use as masks. I didn&#39;t want to waste too much time on this because this wasn&#39;t the main point of the demo. So I opted for a simple shape that I made by using one &lt;code&gt;&amp;lt;rect&amp;gt;&lt;/code&gt; and 0 or more &lt;code&gt;&amp;lt;circle&amp;gt;&lt;/code&gt; elements around it. Here are a few examples of the shapes:&lt;/p&gt;
&lt;div style=&quot;background:black;padding:1rem;display:grid;gap:1rem;grid-template-columns:repeat(auto-fit, 100px);&quot;&gt;
  &lt;svg viewBox=&quot;0 0 80 80&quot;&gt;
    &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;60&quot; height=&quot;60&quot; fill=&quot;white&quot;&gt;&lt;/rect&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;10&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
  &lt;/svg&gt;
  &lt;svg viewBox=&quot;0 0 80 80&quot;&gt;
    &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;60&quot; height=&quot;60&quot; fill=&quot;white&quot;&gt;&lt;/rect&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;10&quot; r=&quot;10&quot; fill=&quot;black&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;70&quot; cy=&quot;40&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;10&quot; cy=&quot;40&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;70&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
  &lt;/svg&gt;
  &lt;svg viewBox=&quot;0 0 80 80&quot;&gt;
    &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;60&quot; height=&quot;60&quot; fill=&quot;white&quot;&gt;&lt;/rect&gt;
    &lt;circle cx=&quot;70&quot; cy=&quot;40&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;70&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
  &lt;/svg&gt;
  &lt;svg viewBox=&quot;0 0 80 80&quot;&gt;
    &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;60&quot; height=&quot;60&quot; fill=&quot;white&quot;&gt;&lt;/rect&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;10&quot; r=&quot;10&quot; fill=&quot;black&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;70&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;10&quot; cy=&quot;40&quot; r=&quot;10&quot; fill=&quot;white&quot;&gt;&lt;/circle&gt;
  &lt;/svg&gt;
  &lt;svg viewBox=&quot;0 0 80 80&quot;&gt;
    &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;60&quot; height=&quot;60&quot; fill=&quot;white&quot;&gt;&lt;/rect&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;10&quot; r=&quot;10&quot; fill=&quot;black&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;40&quot; cy=&quot;70&quot; r=&quot;10&quot; fill=&quot;black&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;70&quot; cy=&quot;40&quot; r=&quot;10&quot; fill=&quot;black&quot;&gt;&lt;/circle&gt;
    &lt;circle cx=&quot;10&quot; cy=&quot;40&quot; r=&quot;10&quot; fill=&quot;black&quot;&gt;&lt;/circle&gt;
  &lt;/svg&gt;
&lt;/div&gt;
&lt;p&gt;Each piece is a square, and each side of the square can either be flat (if it&#39;s a side of the puzzle), or have a little bump, or a little hole. I drew the bumps and holes with circles. Holes are black circles, so they blend in with the black background, giving the illusion of a hole. And bumps are white circles. We&#39;ll see later why these colors are important.&lt;/p&gt;
&lt;p&gt;If you&#39;re wondering, there are 81 total possible shapes (3 possible states per side of a shape: either a flat surface, a hole, or a bump).&lt;/p&gt;
&lt;h3&gt;The SVG masks&lt;/h3&gt;
&lt;p&gt;In order to be able to use the above shapes as masks from CSS, I needed to create SVG masks that could be accessed from CSS. I opted for just having the SVG code right in the document.&lt;/p&gt;
&lt;p&gt;To make the masks available, I used the &lt;code&gt;&amp;lt;defs&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;mask&amp;gt;&lt;/code&gt; elements, kind of like this (&lt;em&gt;don&#39;t copy this code just yet, we&#39;ll see later that there are two important things to change&lt;/em&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;defs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;60&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;60&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;70&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;70&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;60&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;60&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;70&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;70&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;defs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Masks colors&lt;/h3&gt;
&lt;p&gt;I&#39;m using the SVG shapes above as masks on DOM elements. A mask works by only allowing the parts of the element that are covered by the mask to be visible. For this to work, there needs to be information in the mask to say which parts of the element should be visible, and which parts should be hidden.&lt;/p&gt;
&lt;p&gt;This is achieved by using colors. In my masks above, anything that&#39;s white will be visible, and anything that&#39;s black will be hidden.&lt;/p&gt;
&lt;h3&gt;Mask IDs&lt;/h3&gt;
&lt;p&gt;There are a couple of ways to use the above masks from CSS. I could convert the SVG code to a data-uri, and use it inside a &lt;code&gt;url()&lt;/code&gt; function in the CSS &lt;code&gt;mask-image&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;But, it turns out I could also just refer to the ID of a mask element, if that element is available in the same document as the DOM elements I want to mask. Like this: &lt;code&gt;mask-image: url(#the-id-of-the-mask);&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, that&#39;s what I did. I added a unique  ID to each &lt;code&gt;&amp;lt;mask&amp;gt;&lt;/code&gt; element like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;defs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mask&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mask-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mask&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mask-2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;defs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Mask coordinates&lt;/h3&gt;
&lt;p&gt;This is the part that took me a long time to figure out. At first, I just couldn&#39;t see anything from the masked DOM elements, as if they were getting completely hidden by the masks.&lt;/p&gt;
&lt;p&gt;I finally figured out that the masks needed to be in a particular coordinate system to map well to the target DOM elements. To do this, I needed to use the &lt;code&gt;maskContentUnits&lt;/code&gt; attribute on the &lt;code&gt;&amp;lt;mask&amp;gt;&lt;/code&gt; elements, and set all of the shapes&#39; coordinates to be from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; (instead of from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;80&lt;/code&gt; in my case).&lt;/p&gt;
&lt;p&gt;Here&#39;s the new code:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;defs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mask&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mask-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;maskContentUnits&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;objectBoundingBox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.75&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.75&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.875&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.875&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mask&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mask-2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;maskContentUnits&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;objectBoundingBox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.75&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.75&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.875&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.875&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0.125&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;defs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Applying the mask to DOM elements with CSS masking&lt;/h3&gt;
&lt;p&gt;The final piece of the puzzle (pun intended) was to actually apply the SVG masks to my DOM elements. I did this by using the &lt;code&gt;mask-image&lt;/code&gt; CSS property, like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.piece-1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;mask-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;#mask-1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.piece-2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;mask-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;#mask-2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;That&#39;s it for this short article. If you&#39;re ever in the same situation as I was, hopefully you&#39;ll find this article and it will save you some time!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Navigate the unexpected: using Copilot in Edge and DevTools</title>
    <link href="https://patrickbrosset.com/articles/2023-12-14-navigate-the-unexpected-using-copilot-in-edge-and-devtools/"/>
    <updated>2023-12-14T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-12-14-navigate-the-unexpected-using-copilot-in-edge-and-devtools/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2023/12/14/navigate-the-unexpected-using-copilot-in-edge-and-devtools/">https://blogs.windows.com/msedgedev/2023/12/14/navigate-the-unexpected-using-copilot-in-edge-and-devtools/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Inside the all-new Edge DevTools user interface</title>
    <link href="https://patrickbrosset.com/articles/2023-12-20-inside-the-all-new-edge-devtools-user-interface/"/>
    <updated>2023-12-20T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2023-12-20-inside-the-all-new-edge-devtools-user-interface/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2023/12/20/inside-the-all-new-edge-devtools-ui/">https://blogs.windows.com/msedgedev/2023/12/20/inside-the-all-new-edge-devtools-ui/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Microsoft Edge and Interop 2024</title>
    <link href="https://patrickbrosset.com/articles/2024-02-01-microsoft-edge-and-interop-2024/"/>
    <updated>2024-02-01T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-02-01-microsoft-edge-and-interop-2024/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2024/02/01/microsoft-edge-and-interop-2024/">https://blogs.windows.com/msedgedev/2024/02/01/microsoft-edge-and-interop-2024/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Building an image performance overlay with the Performance API</title>
    <link href="https://patrickbrosset.com/articles/2024-03-12-building-an-image-performance-overlay-with-the-performance-api/"/>
    <updated>2024-03-12T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-03-12-building-an-image-performance-overlay-with-the-performance-api/</id>
    <content type="html">
      
        &lt;p&gt;I&#39;ve recently started to learn more about the Performance API that&#39;s available in web browsers, and wanted to share some of my learnings. I&#39;m far from being an expert, but I hope this post is a fun way to get into the world of performance metrics, and how you can use them to track the performance of your website.&lt;/p&gt;
&lt;p&gt;While I was looking at the (very good) &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Performance_API&quot;&gt;documentation of the Performance API on MDN&lt;/a&gt;, one thing caught my attention: the ability for the API to report the load time of elements on the page. It turns out that the &lt;code&gt;element&lt;/code&gt; performance metric can be used to know how long any image or text-containing element took to load and render.&lt;/p&gt;
&lt;p&gt;So I got the idea of using this API to build an on-page overlay that would appear on top of images on the page, only shown when using my site in development mode. The overlay would display the load and render times for each image and appear as a warning if the time is too long. I also got the idea of displaying a warning if the image&#39;s natural size is too large compared to the size it&#39;s actually being displayed at.&lt;/p&gt;
&lt;p&gt;For the rest of this post, let&#39;s go through the main steps required to build this overlay. I&#39;m applying this to my own website &lt;a href=&quot;https://devtoolstips.org/&quot;&gt;devtoolstips.org&lt;/a&gt;, but the steps below should be applicable to any website.&lt;/p&gt;
&lt;h2&gt;Browser support warning&lt;/h2&gt;
&lt;p&gt;Before we get started, a quick word about browser support. The Performance API is huge, in that it contains a lot of different interfaces, properties, and methods. While the majority of is supported in all browsers, the &lt;code&gt;element&lt;/code&gt; performance metric I&#39;ll be using here is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceElementTiming#browser_compatibility&quot;&gt;only supported in chromium-based browsers&lt;/a&gt; for now unfortunately.&lt;/p&gt;
&lt;h2&gt;Step 1 - Add HTML attributes to images&lt;/h2&gt;
&lt;p&gt;The first thing we need to do is to add the &lt;code&gt;elementtiming&lt;/code&gt; HTML attribute to the images that we want to track. By default, no element&#39;s load time is recorded by the Performance API, so we need to explicitly tell the browser which ones to track.&lt;/p&gt;
&lt;p&gt;On my site, I have pages that display lists of posts. Each post is represented by a card containing a title, some preview text, and an image. I want to track the load time of the image, so I&#39;ll add the &lt;code&gt;elementtiming&lt;/code&gt; attribute to the &lt;code&gt;img&lt;/code&gt; element:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tips&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
{%- for post in posts -%}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tip&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ post.url }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tip-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ post.data.title }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ post.url }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tip-image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ post.data.imageUrl }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ post.data.imageAlt }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;elementtiming&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;img&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tip-excerpt&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ post.data.excerpt }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
{%- endfor -%}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part in the above code snippet is the &lt;code&gt;elementtiming&lt;/code&gt; attribute on the &lt;code&gt;img&lt;/code&gt; tag. The pieces that start with &lt;code&gt;{%&lt;/code&gt; or &lt;code&gt;{{&lt;/code&gt; are from the template language I use with my &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; website to generate the list of posts.&lt;/p&gt;
&lt;h2&gt;Step 2 - Set up a PerformanceObserver&lt;/h2&gt;
&lt;p&gt;Now that the HTML code is ready, let&#39;s actually use the Performance API to retrieve the images timing information.&lt;/p&gt;
&lt;p&gt;There are two main ways to retrieve the performance metrics we care about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By using the &lt;code&gt;performance.getEntries()&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Or, by using a &lt;code&gt;PerformanceObserver&lt;/code&gt; instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using a &lt;code&gt;PerformanceObserver&lt;/code&gt; instance offers a few important advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PerformanceObserver&lt;/code&gt; observes performance metrics and dispatches them over time. Instead, using &lt;code&gt;performance.getEntries()&lt;/code&gt; will always return the entire list of entries since the performance metrics started being recorded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PerformanceObserver&lt;/code&gt; dispatches the metrics asynchronously, which means they don&#39;t have to block what the browser is doing.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;element&lt;/code&gt; performance metric type just doesn&#39;t work with the &lt;code&gt;performance.getEntries()&lt;/code&gt; method anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, let&#39;s create a &lt;code&gt;PerformanceObserver&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; perfObserver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For now, we&#39;re passing an empty callback function to the &lt;code&gt;PerformanceObserver&lt;/code&gt; constructor. Later, we&#39;ll change it to actually do something with the observed performance metrics.&lt;/p&gt;
&lt;p&gt;Now, let&#39;s start observing:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;perfObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;element&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing to notice in the above code snippet is the use of the &lt;code&gt;type: &amp;quot;element&amp;quot;&lt;/code&gt; property. That&#39;s what tells the observer to only observe the &lt;code&gt;element&lt;/code&gt; performance metrics. If you wanted to, instead, observe other perf metrics such as &lt;em&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/LayoutShift&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;&lt;/em&gt;, or &lt;em&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/LargestContentfulPaint&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;&lt;/em&gt;, then that&#39;s where you&#39;d specify it.&lt;/p&gt;
&lt;p&gt;The second very important thing above is the use of the &lt;code&gt;buffered: true&lt;/code&gt; property. Setting this to true means that we not only want to observe performance metrics being dispatched after we start observing, but we also want to get the performance metrics that were queued by the browser &lt;strong&gt;before&lt;/strong&gt; we started observing.&lt;/p&gt;
&lt;p&gt;That means we don&#39;t need to worry about running this piece of code right when the page loads. We can run it at a more convenient time, lazily, after the whole page has finished loading.&lt;/p&gt;
&lt;p&gt;Now let&#39;s work on the callback function that&#39;s passed when instantiating the &lt;code&gt;PerformanceObserver&lt;/code&gt; object. That&#39;s the function the browser will call whenever performance metrics are dispatched (as well as right from the start if there are queued metrics):&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; perfObserver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; renderTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; naturalWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; naturalHeight &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our callback receives a list of performance entries, so the code snippet above iterates over these entries. Here are the properties of each performance entry that we&#39;re interested in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;element&lt;/code&gt;: a reference to the image element which this entry applies to.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loadTime&lt;/code&gt; and &lt;code&gt;renderTime&lt;/code&gt;: the image load and render time, given as a high resolution timestamp (more on this later).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;naturalWidth&lt;/code&gt; and &lt;code&gt;naturalHeight&lt;/code&gt;: the width and height of the image resource.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we have the required data, it&#39;s time to display it.&lt;/p&gt;
&lt;h2&gt;Step 3 - Create and style the overlay&lt;/h2&gt;
&lt;p&gt;My idea was to display the performance metrics on each image, as an overlay of some kind. To do this, we&#39;ll need to make the data available in the DOM somewhere, ideally, on the images themselves, and then style the data.&lt;/p&gt;
&lt;p&gt;Let&#39;s use data attributes to store the metrics on each image, to avoid having to create new elements in the DOM, and so that we can retrieve the data from CSS by using the &lt;code&gt;attr()&lt;/code&gt; function. We can then display the data by using the &lt;code&gt;::before&lt;/code&gt; and &lt;code&gt;::after&lt;/code&gt; pseudo-elements.&lt;/p&gt;
&lt;p&gt;Note though that pseudo-elements don&#39;t get rendered inside images, because images are replaced elements inside which CSS doesn&#39;t apply. So, in the following code snippet, you&#39;ll notice that we actually use the image&#39;s parent element instead.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; perfObserver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; renderTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; naturalWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; naturalHeight &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Find the image parent element to store the data.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parentEl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.tip-image&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;parentEl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Check the display dimensions of the image in the page&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// to compare them with the natural dimensions.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; realWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetWidth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; realHeight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// If the loadTime + renderTime is too long,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// or if the image is too big compared to how it&#39;s displayed,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// then add a warning class to the parent element.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isPerformanceIssue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
      loadTime &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; renderTime &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
      naturalWidth &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; realWidth &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; naturalHeight &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; realHeight &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;perf-issue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isPerformanceIssue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Write the performance data to the DOM as data attributes.&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loadTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loadTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;renderTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;naturalWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; naturalWidth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;naturalHeight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; naturalHeight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;realWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; realWidth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    parentEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;realHeight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; realHeight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code snippet, we first get the image&#39;s parent element which we&#39;ll use to write the performance data.&lt;/p&gt;
&lt;p&gt;We then do some calculation to decide whether the image is a performance issue or not. We check if the load and render time aren&#39;t too long, and if the image&#39;s render size isn&#39;t much smaller than its natural size. We then apply a class to the element if there&#39;s a potential performance issue.&lt;/p&gt;
&lt;p&gt;Finally, we write the performance data to the DOM as data attributes. Data attributes are nice because they can be written and read by using the &lt;code&gt;element.dataset&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;Note that the load and render times are given to us as high resolution timestamps by the Performance API. A high resolution timestamp is nice because it represents a moment in the lifetime of the webpage that&#39;s very accurate. Much more accurate than if we had been running our own timers with &lt;code&gt;setTimeout&lt;/code&gt; or &lt;code&gt;Date.now&lt;/code&gt;. A high resolution timestamp is a number of milliseconds that&#39;s accurate up to 5 microseconds. In the code snippet above, we use &lt;code&gt;Math.round()&lt;/code&gt; to get rid of the decimal places, and make the numbers easier to read in the overlay.&lt;/p&gt;
&lt;p&gt;Alright, we have our data available in the DOM. Let&#39;s now display it by using CSS. As I mentioned earlier, we&#39;ll use the &lt;code&gt;attr()&lt;/code&gt; function to read the content of HTML attributes:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Make the image parent container be positioned, so
we can absolutely place the before/after pseudos. */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tip-image&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Position the pseudo-elements absolutely within
the container, and give them some default styles */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tip-image::before,
.tip-image::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.7rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; lime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* If the element has the perf-issue class,
then make the pseudo-elements red instead. */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tip-image.perf-issue::before,
.tip-image.perf-issue::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Display the load and render time in the
::before pseudo-element. */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tip-image[data-load-time]::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;load: &quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-load-time&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms render: &quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-render-time&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Display the image dimensions in the
::after pseudo-element. */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tip-image[data-natural-width]::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-natural-width&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; ⨉ &quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-natural-height&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; (vs. &quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-real-width&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; ⨉ &quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data-real-height&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code sample, because the load and render time are set in JavaScript by using &lt;code&gt;element.dataset.loadTime&lt;/code&gt; and &lt;code&gt;element.dataset.renderTime&lt;/code&gt;, they become available as the &lt;code&gt;data-load-time&lt;/code&gt; and &lt;code&gt;data-render-time&lt;/code&gt; HTML attributes, which, in turn, makes them available to CSS by using the &lt;code&gt;attr(data-load-time)&lt;/code&gt; and &lt;code&gt;attr(data.render-time)&lt;/code&gt; functions.&lt;/p&gt;
&lt;h2&gt;Step 4 - Test it&lt;/h2&gt;
&lt;p&gt;It&#39;s time to test our change. After a quick re-build of my Eleventy website, here&#39;s what I get in the browser:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/element-timing/image1.webp&quot; alt=&quot;My site in a browser window, red warnings appear as overlays at the top and bottom of each image, DevTools is opened and shows the HTML data attributes where the performance metrics are stored&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The data attributes are correctly written on the image&#39;s parent element. The pseudo-elements are present in the DOM. And the performance overlays appear on each image.&lt;/p&gt;
&lt;p&gt;Looks like our overlays are all red because the image resources are a lot bigger than the dimension they&#39;re displayed as. For example, the first image in the screenshot above is 2862 pixels wide, even if the element it&#39;s displayed in is only 201 pixels. The image is more than 10 times as big as it needs to be!&lt;/p&gt;
&lt;p&gt;This gives us a really good clue of how to make the image load and render faster. Ideally, it should match the dimensions that it will need to be on the site. To do this, we could simply resize the image files on the server, use a tool to do it automatically at build time, or use a CDN for serving images at the right size dynamically.&lt;/p&gt;
&lt;p&gt;The Google Lighthouse tool provides a similar warning, by the way, which you can read more about at &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images&quot;&gt;Properly size images&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that our load and render times don&#39;t seem to be too much out of the ordinary. The first image takes 103ms to load and 135ms to render which, given its size (2862px by 1410px) doesn&#39;t seem very surprising. But, it&#39;s important to keep in mind that these numbers were measured with a local server running on my development device. This represents an environment that none of my users will ever encounter. They&#39;ll more likely be using a lower-end device, on a less reliable internet connection.&lt;/p&gt;
&lt;p&gt;Knowing who our users are, and building empathy for them is a very important part of web development, and of performance testing. One way to start doing this is by using DevTools to slow things down. Here&#39;s one way to do it in Chromium-based browsers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In DevTools, open the Performance tool.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Capture settings&lt;/strong&gt; in the top-right corner of the tool (the cog icon).&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;CPU&lt;/strong&gt; choose a 4x or 6x slowdown to emulate a less capable device.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Network&lt;/strong&gt;, choose Fast 3G to emulate a slower internet connection.&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;Network&lt;/strong&gt; tool and select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&#39;s test again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/element-timing/image2.webp&quot; alt=&quot;My site in a browser window, red warnings appear on images and show very long load times, DevTools is opened on the side and shows the Network tool with the disabled cache and fast 3G settings&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This makes quite the difference. The first image on the page is now taking 13 seconds to load!&lt;/p&gt;
&lt;p&gt;Remember to always test for performance on the devices that your users (or, ideally, your next users) are using, not only on your high-end development machine.&lt;/p&gt;
&lt;h2&gt;Continue learning about the Performance API&lt;/h2&gt;
&lt;p&gt;This tutorial showed how to use the Performance API to create a performance overlay for images. This tutorial only really scratches the surface though. The Performance API is quite large and contains many more features that you may find interesting. To continue learning about it, take a look at &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Performance_API&quot;&gt;MDN&#39;s several guides&lt;/a&gt; to get a sense of the kinds of things you could be using the API for.&lt;/p&gt;
&lt;p&gt;More generally speaking, recording the page load and key user scenarios of your website is a very good practice that you should consider. It will help you understand how each new feature you ship impacts the overall user experience of your product. It can also help you track progress towards a performance target.&lt;/p&gt;
&lt;p&gt;I hope this post was useful to you.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Closing the loop - From developer signals to browser maker decisions</title>
    <link href="https://patrickbrosset.com/articles/2024-03-15-closing-the-loop-from-developer-signals-to-browser-maker-decisions/"/>
    <updated>2024-03-15T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-03-15-closing-the-loop-from-developer-signals-to-browser-maker-decisions/</id>
    <content type="html">
      
        &lt;h2&gt;Problem statement&lt;/h2&gt;
&lt;p&gt;The way I see it, our (web) industry has two problems when it comes to web developer feedback:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Web developers have a hard time keeping track with the web platform and sharing feedback&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There&#39;s a general lack of transparency about how the &amp;quot;sausage is made&amp;quot;, and the whole thing feels too intimidating and opaque for web developers to get involved and stay up to date.&lt;/p&gt;
&lt;p&gt;More specifically, when a web feature is not implemented in a browser, it&#39;s hard to know the reason for it to be missing and whether it will be implemented at some point, as well as where progress can be tracked.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Browser makers have a hard time basing new feature decisions on developer signals&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Surveying developers helps, but survey fatigue is a real thing, and we can&#39;t always ask developers about their needs across a wide variety of web development areas. Prioritization is often based on internal priorities, and gut feelings about what the ecosystem needs.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://wpt.fyi/interop&quot;&gt;Interop&lt;/a&gt; &lt;a href=&quot;https://github.com/web-platform-tests/interop/blob/main/2024/selection-process.md#focus-area-selection-process&quot;&gt;focus area selection process&lt;/a&gt; is a specific instance of this. The process seems opaque from the outside, and it&#39;s hard to know if clear signals from developers feed into the process.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Could a common language be part of the solution?&lt;/h2&gt;
&lt;p&gt;One thing that doesn&#39;t help both of the previous problems is the &lt;strong&gt;lack of a common language between web developers and browser makers to talk about web features&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I believe the following would help:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Agree on a nomenclature for the web.&lt;/p&gt;
&lt;p&gt;The two parties don&#39;t speak the same language. What&#39;s a feature? Do web developers think of features in terms of specifications? Do they think of them as individual browser increments? Even MDN and Can I Use don&#39;t always agree on what a feature is, how coarse or fine grain it is. Browser makers and web developers definitely don&#39;t always share the same understanding of what a feature is.&lt;/p&gt;
&lt;p&gt;A common language could provide much needed common grounds. If everyone starts calling features the same way, we get clarity on both sides. Web developers can more easily track the development of a feature. Browser makers can more easily track signals from developers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a virtuous circle where web features and how well they&#39;re supported is clearly documented, and where sending feedback about your needs is easy and rewarding.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;About a common nomenclature&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/web-platform-dx/web-features/&quot;&gt;WebDX web-features repository&lt;/a&gt; is shaping up to be a really useful catalog of web features, designed from a web developer&#39;s perspective, complete with hierarchical groups of features.&lt;/p&gt;
&lt;p&gt;While not that useful on its own, the catalog has the potential of becoming an interesting missing link when added to places that developers go to, and to places that browser makers communicate status on.&lt;/p&gt;
&lt;p&gt;For example, the feature names that the WebDX web-features repository defines are linked to specifications, and to &lt;a href=&quot;https://github.com/mdn/browser-compat-data/&quot;&gt;Browser Compatibility Data (BCD)&lt;/a&gt;. This is a good start.&lt;/p&gt;
&lt;p&gt;The feature catalog can also be used for a better information architecture on MDN, as well as to provide simplified browser compatibility information on MDN and Can I Use.&lt;/p&gt;
&lt;p&gt;On the other side of the spectrum, the WebDX web-features names are also starting to be used to tag &lt;a href=&quot;https://wpt.fyi/&quot;&gt;Web Platform Tests&lt;/a&gt;, and it would be great if they were also used by browser bug trackers, by standards position websites (&lt;a href=&quot;https://mozilla.github.io/standards-positions/&quot;&gt;Mozilla&lt;/a&gt;, &lt;a href=&quot;https://webkit.org/standards-positions/&quot;&gt;WebKit&lt;/a&gt;), or on &lt;a href=&quot;https://chromestatus.com/&quot;&gt;chromestatus.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully, with this kind of interconnectedness, when a developer asks themselves:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&#39;s going on with feature X?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They&#39;d be able to find the answer easily:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oh, it&#39;s spec&#39;d here, documented here, and implemented in browsers A and B, but not in browser C, because of this bug which is linked here and which I can comment or vote on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;About developer signals&lt;/h2&gt;
&lt;p&gt;Surveys are useful, and tell us a lot about developer needs. But referring to survey results over time or comparing between them isn&#39;t always easy. Using web feature identifiers in surveys might help a little bit too.&lt;/p&gt;
&lt;p&gt;But also, developer signals don&#39;t have to be in the form of surveys. Browser maker bug trackers often accept votes or, at least, comments. Mapping web features to their corresponding implementation bugs would provide an easy way for developers to go and vote on these bugs. Right now, I don&#39;t think many developers know where to go to signal their needs.&lt;/p&gt;
&lt;h2&gt;About rewarding developer signals&lt;/h2&gt;
&lt;p&gt;Going out of your way to tell a browser maker that you need a feature takes time and energy. We need to fix this by making it rewarding to do so.&lt;/p&gt;
&lt;p&gt;If developers&#39; feedback clearly feeds into browsers&#39; prioritization process, people are more likely to engage. It would also be great to make the Interop process more transparent. If vote/comment counts on bugs are used to feed into the Interop focus area selection process, developers are also more likely to engage.&lt;/p&gt;
&lt;h2&gt;The &lt;code&gt;impl_url&lt;/code&gt; field in BCD&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mdn/browser-compat-data/&quot;&gt;Browser Compat Data&lt;/a&gt; open-source project, on top of maintaining a database of browser compatibility data, also has some information about the browser bugs that are blocking the implementation of a feature.&lt;/p&gt;
&lt;p&gt;For example, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_MIDI_API#browser_compatibility&quot;&gt;Web MIDI API&lt;/a&gt; isn&#39;t implemented in Safari (at the time of writing), and the BCD data about it has a &lt;a href=&quot;https://github.com/mdn/browser-compat-data/blob/16df68556803e30b10b19782819aa432a031a30d/api/Navigator.json#L4024-L4027&quot;&gt;link&lt;/a&gt; to the &lt;a href=&quot;https://webkit.org/b/107250&quot;&gt;bug in WebKit&lt;/a&gt; that&#39;s tracking its implementation.&lt;/p&gt;
&lt;p&gt;This is great, because it means that other projects that use BCD, such as MDN itself, can show the lack of support &lt;strong&gt;and&lt;/strong&gt; point developers to the right place to signal their need for the feature:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/mdn-midi-safari.png&quot; alt=&quot;The Navigator.requestMIDIAccess() MDN page, showing that the feature is missing on Safari, and showing a link to the tracking WebKit bug&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I think this link has a lot of potential when it comes to &amp;quot;closing the loop&amp;quot;, and I&#39;ve recently spent some time adding more of these links to BCD.&lt;/p&gt;
&lt;p&gt;Adding links for existing features isn&#39;t straightforward. It takes a lot of research through bug trackers, standards positions, specs, mailing lists, etc. But it&#39;s definitely worth it because of the impact it will keep on having on web developers, who will now be one step closer to actually being able to influence the web platform.&lt;/p&gt;
&lt;p&gt;The web is this wonderful (mostly) open platform. Let&#39;s keep it as open and transparent as we can. Let&#39;s give everyone a voice, and a common language to speak with.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>My analysis of the State of HTML 2023 results</title>
    <link href="https://patrickbrosset.com/articles/2024-06-10-my-analysis-of-the-state-of-html-2023-results/"/>
    <updated>2024-06-10T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-06-10-my-analysis-of-the-state-of-html-2023-results/</id>
    <content type="html">
      
        &lt;p&gt;As someone who works on web browsers, I take web development survey results very seriously. They&#39;re a way for us, browser makers, to understand what you, web developers, need from us. The pain points you feel, the features you use, the features you need, and a bit of a general trend of the web development ecosystem.&lt;/p&gt;
&lt;p&gt;As far as surveys go, the State of &lt;em&gt;&amp;lt;thing&amp;gt;&lt;/em&gt; survey series is a really good one to keep an eye on. The &lt;a href=&quot;https://stateofjs.com/&quot;&gt;State of JS&lt;/a&gt;, &lt;a href=&quot;https://stateofcss.com/&quot;&gt;State of CSS&lt;/a&gt;, and &lt;a href=&quot;https://stateofhtml.com/&quot;&gt;State of HTML&lt;/a&gt; run yearly and allow us to compare results year over year. There are other places to get developer signals about the web out there, of course, but the State Of &lt;em&gt;&amp;lt;thing&amp;gt;&lt;/em&gt; is one of the major ones.&lt;/p&gt;
&lt;p&gt;For more information about how the State of &lt;em&gt;&amp;lt;thing&amp;gt;&lt;/em&gt; survey series started, see &lt;a href=&quot;https://sachagreif.com/&quot;&gt;Sacha Greig&lt;/a&gt;&#39;s &lt;a href=&quot;https://www.devographics.com/&quot;&gt;devographics website&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The State of HTML 2023&lt;/h2&gt;
&lt;p&gt;You can see the results for yourself on the &lt;a href=&quot;https://2023.stateofhtml.com/&quot;&gt;State of HTML 2023 website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Last year was the first time the State of HTML survey ran. Because it was the first time, the questions were a little more open-ended than other State of &lt;em&gt;&amp;lt;thing&amp;gt;&lt;/em&gt; surveys, simply because there were no previous surveys to compare to, and therefore more free-text answers were needed.&lt;/p&gt;
&lt;p&gt;This led to a lot of free-text answers that needed to be categorized, which was a lot of work. This explains that the results took a little longer to come out than the other State of &lt;em&gt;&amp;lt;thing&amp;gt;&lt;/em&gt; surveys.&lt;/p&gt;
&lt;p&gt;Note that &lt;em&gt;HTML&lt;/em&gt;, in State of HTML, really means: everything that&#39;s not already captured in State of JS and State of CSS. HTML, as a language, was not the only thing surveyed here.&lt;/p&gt;
&lt;h2&gt;My analysis&lt;/h2&gt;
&lt;p&gt;The sections below are based on the results of the survey, and are my own, personal, analysis of the results. I&#39;m only speaking for myself, and only focused on the areas of the survey that were of interest to me. You migth find other interesting insights in the survey results, so I encourage you to go check them out for yourself.&lt;/p&gt;
&lt;h3&gt;Interoperability issues&lt;/h3&gt;
&lt;p&gt;The lack of browser support for the features that people want is a common theme throughout the survey, and this is true of other web-related developer surveys. The web is fragmented, and this has always been a big pain for web developers. But one question in particular focuses on this specific topic: &lt;a href=&quot;https://2023.stateofhtml.com/en-US/usage/#html_interoperability_features&quot;&gt;Which existing HTML features or browser APIs are you unable to use because of browser differences or lack of support?&lt;/a&gt;. Here are some of the top issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Form input elements is the top group.&lt;/p&gt;
&lt;p&gt;It&#39;s actually presented as a group of 15 sub-features, none of which are as high as the other features in the results. But, combined as a form input group, they are the top issue.&lt;/p&gt;
&lt;p&gt;In particular, web developers complain about the lack of date/time pickers.
Additionally, many people requested a stylable &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Popover_API&quot;&gt;Popover API&lt;/a&gt; is high in the list too.&lt;/p&gt;
&lt;p&gt;Thankfully, the API has now shipped in all browsers (Firefox recently added support for it in Firefox 125, which became available in April 2024, after the survey ran).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There are many complaints about the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/dialog&quot;&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This was surprising to me at first, knowing that the element has been available across browsers since the beginning of 2022 (Safari and Firefox shipped it in March 2022 with 15.4 and 98, respectively).&lt;/p&gt;
&lt;p&gt;My understanding now, is that the element is known to have accessibility and usability issues. I think people generally think that it&#39;s not entirely usable across browsers yet. The &lt;a href=&quot;https://a11y-dialog.netlify.app/further-reading/dialog-element/&quot;&gt;dialog element&lt;/a&gt; on the a11y-dialog website summarizes the remaining problems well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/View_Transitions_API&quot;&gt;View Transitions&lt;/a&gt; is also very high in the list.&lt;/p&gt;
&lt;p&gt;Respondents seem to generally want View Transitions to be supported in multi-page apps (MPA) too (i.e. across page navigation). I can totally understand this. Using View Transitions to easily animate between DOM changes is nice, but support for MPA is real promise here. And, in many cases, might make it possible for web developers to ditch their client-side JS rendering frameworks in favour of server-side HTML rendering, which performs better.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The functional &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/:has&quot;&gt;&lt;code&gt;:has()&lt;/code&gt;&lt;/a&gt; CSS pseudo-class is also very high in the results.&lt;/p&gt;
&lt;p&gt;This is not surprising considering that it shipped in Firefox in December 2023 (Firefox 121), after the survey ran.&lt;/p&gt;
&lt;p&gt;But also, my feeling is that web developers tend to be very careful with using new features, even when those features are supposedly available in all browser engines. I think web developers have a sense of caution, where, even if a feature is implemented everywhere, they&#39;ll just wait for more time before they use it.&lt;/p&gt;
&lt;p&gt;The nature of the web platform is such that this is the safest approach to take. Unfortunately, without actually knowing what browsers your users have when accessing your website, it&#39;s also the &lt;em&gt;only&lt;/em&gt; approach you have. You can never be sure if all your users have up to date browsers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Progressive_web_apps&quot;&gt;Progressive Web Apps&lt;/a&gt; is in the top 7 on this question.&lt;/p&gt;
&lt;p&gt;This shows interest in PWAs, and frustration that developers still can&#39;t use them everywhere.&lt;/p&gt;
&lt;p&gt;One of the most frequent complaint is related to the PWA install prompt, especially that it&#39;s not possible to invite the installation of a PWA on all browsers.&lt;/p&gt;
&lt;p&gt;The other most frequent answer is about the lack of PWA support on iOS, and on Firefox on desktop OSes.&lt;/p&gt;
&lt;p&gt;More about PWAs in the &lt;a href=&quot;https://patrickbrosset.com/articles/2024-06-10-my-analysis-of-the-state-of-html-2023-results/#progressive-web-apps&quot;&gt;Progressive Web Apps&lt;/a&gt; section, below.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_grid_layout/Subgrid&quot;&gt;CSS Subgrid&lt;/a&gt; is high too.&lt;/p&gt;
&lt;p&gt;Also understandable, because it became available on all browsers only after Chrome/Edge implemented it in version 117, in September 2023, after the survey ran.&lt;/p&gt;
&lt;p&gt;Looking into the results more, it also became apparent that web developers want more/better documentation about subgrid, and aren&#39;t completely convinced that there is a need for this feature.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last one I want to highlight is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_components&quot;&gt;Web Components&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many of the complaints are about the lack of &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/template#shadowrootmode&quot;&gt;declarative shadow DOM&lt;/a&gt;, which allows you to define create a shadow root for you components without using JavaScript. The good news is, it&#39;s now available across browsers (as of Firefox 123, which shipped in February 2024).&lt;/p&gt;
&lt;p&gt;Respondents also complain about the lack of support for &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/CustomElementRegistry/define#defining_a_customized_built-in_element&quot;&gt;customized built-in elements&lt;/a&gt;, which allow you to extend any HTML element by using the &lt;code&gt;is&lt;/code&gt; attribute (instead of having to define your own custom element). This feature is still missing in Safari, because &lt;a href=&quot;https://github.com/WebKit/standards-positions/issues/97&quot;&gt;WebKit opposes the feature&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More on Web Components in the &lt;a href=&quot;https://patrickbrosset.com/articles/2024-06-10-my-analysis-of-the-state-of-html-2023-results/#web-components&quot;&gt;Web Components&lt;/a&gt; section, below.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Limited support&lt;/h3&gt;
&lt;p&gt;The survey contains another question, similar to the above: &lt;a href=&quot;https://2023.stateofhtml.com/en-US/usage/#html_functionality_features&quot;&gt;Which existing HTML features or browser APIs are you unable to use for other reasons (and why)?&lt;/a&gt;. This question led to answers that more or less confirm the previous question, with some of the top features being:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input element&lt;/li&gt;
&lt;li&gt;Dialog&lt;/li&gt;
&lt;li&gt;Form inputs&lt;/li&gt;
&lt;li&gt;Web Components&lt;/li&gt;
&lt;li&gt;Popover&lt;/li&gt;
&lt;li&gt;Stylable select/drop-down menu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But the question also led to some new insights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;
&lt;p&gt;Respondents complain about the element&#39;s poor accessibility, lack of animations, and lack of mutually exclusive accordion behavior.&lt;/p&gt;
&lt;p&gt;Indeed, by using the &lt;code&gt;name&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element, you can make a series of &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element act as an accordion where, when one is opened, the others are closed. Unfortunately, this feature isn&#39;t available on Firefox yet. You can &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1856460&quot;&gt;see the bug here&lt;/a&gt;, and maybe vote for it to be implemented.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sentiment on existing features&lt;/h3&gt;
&lt;p&gt;One nice thing that the State of HTML survey results allow us to do is to sort features by sentiment. For example, on the &lt;a href=&quot;https://2023.stateofhtml.com/en-US/features/#all_features&quot;&gt;HTML features and other browser APIs&lt;/a&gt; question, respondents were asked to say whether they have used, heard of, or never heard of a feature, but they could also optionally leave a positive, neutral, or negative sentiment about the feature. This is useful to understand the developer ergonomics of a feature. People could also leave free-form text comments, and going through those is sometimes quite eye-opening.&lt;/p&gt;
&lt;p&gt;For example, of the features that have the highest negative sentiment, and which people have used, the following features stand out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shadow DOM&lt;/li&gt;
&lt;li&gt;Defining Custom Elements&lt;/li&gt;
&lt;li&gt;Using Custom Elements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, developing and using Web Components feels hard, and people don&#39;t like it. More about this in &lt;a href=&quot;https://patrickbrosset.com/articles/2024-06-10-my-analysis-of-the-state-of-html-2023-results/#web-components&quot;&gt;Web Components&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are more things to be extracted from this question&#39;s results, in particular a bunch of app-related features that people commented about. I&#39;ll come back to these later in the post, when talking about PWA. See &lt;a href=&quot;https://patrickbrosset.com/articles/2024-06-10-my-analysis-of-the-state-of-html-2023-results/#progressive-web-apps&quot;&gt;Progressive Web Apps&lt;/a&gt; below.&lt;/p&gt;
&lt;h3&gt;Web Components&lt;/h3&gt;
&lt;p&gt;One of the question is about &lt;a href=&quot;https://2023.stateofhtml.com/en-US/features/web_components/&quot;&gt;Web Components&lt;/a&gt;. In it, developers were asked their experience, and sentiment about a number of Web Components-related features.&lt;/p&gt;
&lt;p&gt;The result is quite clear: there&#39;s a big need for a native component system, and developers are not happy with the current solutions, especially with the Shadow DOM feature.&lt;/p&gt;
&lt;p&gt;When it comes to making Web Components, respondents complain about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The lack of documentation, knowledge, best practices, and the fact that the technology is complex and is hard to learn.&lt;/li&gt;
&lt;li&gt;About the inconsistent browser support, generally, and also specifically about the lack of support for customized built-in components in Safari.&lt;/li&gt;
&lt;li&gt;The complexity, style encapsulation issues, and poor accessibility of Shadow DOM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When it comes to using Web Components, developers say that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Styling Web Components is hard.&lt;/li&gt;
&lt;li&gt;Getting them to work with frameworks like React is hard.&lt;/li&gt;
&lt;li&gt;Documentation was missing.&lt;/li&gt;
&lt;li&gt;Browser support was inconsistent.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Progressive Web Apps&lt;/h3&gt;
&lt;p&gt;There are two questions that I want to highlight here. The first is &lt;a href=&quot;https://2023.stateofhtml.com/en-US/features/#all_features&quot;&gt;HTML features and other browser APIs&lt;/a&gt;, which contains a bunch of PWA-related (or, really, app-related) features, which respondents have interesting things to say about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Share_API&quot;&gt;Web Share API&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;People complain about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The poor state of support for this API across browsers.&lt;/li&gt;
&lt;li&gt;The lack of good file sharing support.&lt;/li&gt;
&lt;li&gt;The fact that the API was too limited.&lt;/li&gt;
&lt;li&gt;And that the browser-based share dialogs weren&#39;t good enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://wicg.github.io/file-system-access/&quot;&gt;File System Access API&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This API extends the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/File_System_API&quot;&gt;File System API&lt;/a&gt; by allowing developers to programmatically open file pickers to get access to files on disk. The main complaint here is about the lack of browser support for this API.&lt;/p&gt;
&lt;p&gt;Interestingly, an origin-private version of this API, called &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/File_System_API/Origin_private_file_system&quot;&gt;Origin Private File System API&lt;/a&gt; (OPFS) is now available in all browsers (since 15.2 for Safari, 102 for Chrome and Edge, and 111 for Firefox). OPFS doesn&#39;t give you access to the file system, but instead to a place on disk for your website to store and read files. So it&#39;s a good step forward for file handling, but it doesn&#39;t solve the need that some web apps (like text editors) have to give access to any file on the user&#39;s disk.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window_Controls_Overlay_API&quot;&gt;Window Controls Overlay&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Two thirds of the people have never heard of this feature, so the feedback here is not very actionable. One interesting comment is about the fact that macOS gives native apps control over the position and the spacing of the window controls, and that this feature would be nice to have in PWAs too.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Badging_API&quot;&gt;Badging API&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Developers complain about browsers&#39; poor support of this API, which, to me, is mostly related to the poor support of PWAs in general.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The second question which highlights PWA-related features is &lt;a href=&quot;https://2023.stateofhtml.com/en-US/features/mobile-web-apps/#mobile_web_apps_pain_points&quot;&gt;What are your biggest pain points around making web apps that feel native?&lt;/a&gt;. This is a free-form question. Here are some of the top pain points that people noted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The top pain point for developers is Apple iOS&#39; lack of support for PWAs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Installability is high on the list too.&lt;/p&gt;
&lt;p&gt;People complain that the UX to install a PWA is bad. It&#39;s hard for users to use, and some simply don&#39;t know they can install PWAs at all. The lack of installability on iOS and on Firefox for desktop is a common complaint in the results.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There are some mentions of stores and how hard, or impossible, getting a PWA in an app store is. And how web stores are generally untrusted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The lack of cross-browser support also ranks high in the results.&lt;/p&gt;
&lt;p&gt;While this is mostly related to the installability topic, some specific features are also mentioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;File handling is a big one.&lt;/p&gt;
&lt;p&gt;Developers want to be able to handle files natively in their apps and complain about inconsistencies between browsers, and about the lack of real file system access API in non-chromium.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Developers need a real client-side database solution (such as SQLLite).&lt;/p&gt;
&lt;p&gt;This reminds me of an interesting Chrome experiment: &lt;a href=&quot;https://developer.chrome.com/blog/sqlite-wasm-in-the-browser-backed-by-the-origin-private-file-system&quot;&gt;SQLite Wasm in the browser backed by the Origin Private File System&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Storage APIs, Background Fetch API, and Push notifications are mentioned as features that suffer from cross-browser issues.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One of the pain point in this question is around the fact that PWAs don&#39;t look and/or feel &amp;quot;native&amp;quot;.&lt;/p&gt;
&lt;p&gt;No matter how many web apps everyone already use on a daily basis to do work, it seems that this complaint is still very much alive. Developers would like PWAs to be like native apps. Specifically, developers complain that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OS-native controls are missing and it&#39;s impossible to truly match the platform&#39;s look and feel.&lt;/li&gt;
&lt;li&gt;Animations are less fluid and more janky. Animating smoothly between pages is impossible (thankfully, View Transitions for page navigation is coming).&lt;/li&gt;
&lt;li&gt;Web apps are in this uncanny valley, where they almost feel like &amp;quot;real&amp;quot; apps, but not quite.&lt;/li&gt;
&lt;li&gt;Handling gestures, on touch screen, is very far from what you can do in a native app.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Developers also would like better docs.&lt;/p&gt;
&lt;p&gt;The technology feels hard to learn, and a lot of work, and docs are not always available or up to date.&lt;/p&gt;
&lt;p&gt;Over the past few years, I&#39;ve spent a lot of time making sure the PWA documentation on &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Progressive_web_apps&quot;&gt;MDN&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/microsoft-edge/progressive-web-apps-chromium/&quot;&gt;learn.microsoft.com&lt;/a&gt; was up to date, so I feel bad. But I can totally understand this, especially for developers who have worked on native apps before. On the web, there&#39;s no one vendor, there&#39;s no SDK, and docs have to accommodate all browsers, and usually only show the lowest common denominator.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Service Workers appears as a pain point too.&lt;/p&gt;
&lt;p&gt;Service Workers are too complicated. One person said that they&#39;re &lt;em&gt;&amp;quot;basically rocket science&amp;quot;&lt;/em&gt;. The following aspects are thought to be hard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing service workers in the first place.&lt;/li&gt;
&lt;li&gt;Updating service workers on new app versions.&lt;/li&gt;
&lt;li&gt;Figuring out the right caching strategy.&lt;/li&gt;
&lt;li&gt;Debugging service worker issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The final pain point I want to highlight is related to native API access.&lt;/p&gt;
&lt;p&gt;The replies are quite vague, and it seems like respondents generally wish that PWAs offered the same capabilities that native apps do. There are some specific mentions though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;File system access. Mostly that it&#39;s only in chromium, but also that even in chromium, you can&#39;t access all locations on disk.&lt;/li&gt;
&lt;li&gt;Accessing device functionalities, like camera.&lt;/li&gt;
&lt;li&gt;Lack of MIDI support.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Stylable select&lt;/h3&gt;
&lt;p&gt;Responses to multiple questions of the survey mention the ability to style &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; HTML elements. It seems like a lot of people are passionate about not inventing a new element, but extending the existing &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element instead.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://open-ui.org/&quot;&gt;Open UI Community Group&lt;/a&gt; has been working on this for a few years. It originally started as a new element called &lt;code&gt;&amp;lt;selectmenu&amp;gt;&lt;/code&gt;, to avoid breaking the existing &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element. It, later, got renamed to &lt;code&gt;&amp;lt;selectlist&amp;gt;&lt;/code&gt;, but is still a separate element. My understanding is that the latest discussions on the group are heading towards extending the existing &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element instead.&lt;/p&gt;
&lt;p&gt;In any case, many respondents say that they need this capability in the web platform. In fact, more generally, people say that they need to be able to style all form controls more than they can today.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;There are more responses and more comments that can be read, and more filtering that can be done on the survey results. The above are just the few things that stood out as interesting to me. I encourage you to go check out the &lt;a href=&quot;https://2023.stateofhtml.com/&quot;&gt;State of HTML 2023 results&lt;/a&gt; for yourself, and see what you can find in there.&lt;/p&gt;
&lt;p&gt;Looking forward the next State of &lt;em&gt;&amp;lt;thing&amp;gt;&lt;/em&gt; surveys.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Understanding CSS subgrid</title>
    <link href="https://patrickbrosset.com/articles/2024-06-10-understanding-css-subgrid/"/>
    <updated>2024-06-10T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-06-10-understanding-css-subgrid/</id>
    <content type="html">
      
        &lt;p&gt;Features of the web platform tend to move &lt;em&gt;very&lt;/em&gt; slowly. It can take years for them to mature from an idea, to a prototype, to a specification, and to a final implementation in all the major browsers.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_grid_layout/Subgrid&quot;&gt;CSS Subgrid&lt;/a&gt; feature is no exception. The very first browser to ever implement subgrid was Firefox, in 2019. And the specification had been around for at least two years before that.&lt;/p&gt;
&lt;p&gt;The feature only became available across all three major browser engines (Gecko, Webkit, and Blink) at the end of 2023, more specifically in September 2023 with the release of Chrome and Edge 117.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Yes, you can use CSS Subgrid in Chrome, Edge, Safari, and Firefox now&lt;/strong&gt;. &lt;em&gt;Keep in mind, though, that all of your users might not yet be using the latest versions of those browsers (or browser engines), check your site&#39;s usage carefully.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So what is CSS Subgrid, and when and how to use it?&lt;/p&gt;
&lt;h2&gt;Quick reminder about CSS grid&lt;/h2&gt;
&lt;p&gt;Before we dive into &lt;em&gt;subgrid&lt;/em&gt;, let&#39;s quickly remind ourselves of what &lt;em&gt;CSS grid&lt;/em&gt; is.&lt;/p&gt;
&lt;p&gt;CSS grid is a layout system that allows you to create complex two-dimensional layouts. You can define rows and columns, and place items in cells. The grid itself is only defined in CSS. There are no HTML elements that represent the the grid&#39;s columns or rows. You don&#39;t even need to have HTML elements for the cells. CSS grid lets you define the layout purely in CSS, and use HTML for the content only.&lt;/p&gt;
&lt;p&gt;Here is some HTML code that defines a container element with a few children:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notebook-pages&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use CSS grid to define a grid in the &lt;code&gt;.notebook-pages&lt;/code&gt; container, which automatically places the &lt;code&gt;.page&lt;/code&gt; child elements into grid cells. The snippet below shows one way to do this with CSS grid:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.notebook-pages&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* Turn the container element into a grid. */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/*
  Define columns by using the repeat() syntax.
  The grid container will have a variable number of columns,
  depending on the space available. Columns will at least be
  200px wide, but can grow to fill the available space.
  */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;auto-fill&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;minmax&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;200px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1fr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/* Add some gap between rows and columns. */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;gap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the result. Try resizing the browser window to see how the grid adapts to the available space:&lt;/p&gt;
&lt;style&gt;
.notebook-pages {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.5rem;
  padding: 0.5rem;
  height: 50vh;
  background: var(--background);
  border: 1px solid var(--text);
}
.notebook-pages .page {
  background: var(--text);
}
&lt;/style&gt;
&lt;div class=&quot;notebook-pages&quot;&gt;
  &lt;div class=&quot;page&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;page&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;page&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;page&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;page&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I won&#39;t go into more details about CSS grid here. To learn more about it, check out &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_grid_layout&quot;&gt;CSS grid layout&lt;/a&gt; at MDN.&lt;/p&gt;
&lt;h2&gt;The problem with nested elements&lt;/h2&gt;
&lt;p&gt;As shown in the previous example, the elements that are positioned in the grid are direct children of the grid container. In the example, these were the &lt;code&gt;.page&lt;/code&gt; elements. While this is very convenient, and sufficient in many cases, it can also be limiting.&lt;/p&gt;
&lt;p&gt;Consider the following example: you have a list of articles, and each article has a title, a publication date, and some text. You want to display these articles in a grid, with each article taking up a row in the grid, and where each row has two columns: one for the article&#39;s title and date, and one for the article&#39;s text, as shown below:&lt;/p&gt;
&lt;style&gt;
.articles-list {
  font-size: .9rem;
  display: grid;
  grid-template-columns: 1fr 3fr;
  gap: 0.25rem;
  padding: 0.25rem;
  background: color-mix(in srgb, var(--background) 90%, var(--text));
}
.articles-list .article {
  background: color-mix(in srgb, var(--background) 80%, var(--text));
  grid-column: span 2;
  display: grid;
  grid-template-columns: subgrid;
  grid-template-rows: min-content 1fr;
  padding: 0.25rem;
}
.articles-list p {
  margin: 0;
}
.articles-list .title {
  font-weight: bold;
  grid-column: 1;
  grid-row: 1;
}
.articles-list .date {
  font-style: italic;
  grid-column: 1;
  grid-row: 2;
  font-size: .8rem;
}
.articles-list .text {
  grid-column: 2;
  grid-row: 1 / span 2;
}
&lt;/style&gt;
&lt;div class=&quot;articles-list&quot;&gt;
  &lt;div class=&quot;article&quot;&gt;
    &lt;p class=&quot;title&quot;&gt;Lorem ipsum dolor sit amet&lt;/p&gt;
    &lt;p class=&quot;date&quot;&gt;2024-05-30&lt;/p&gt;
    &lt;p class=&quot;text&quot;&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque facilisis vitae turpis eget posuere. Vivamus sed convallis ante. Aliquam ex sapien, ultricies in diam vitae, maximus elementum augue. Quisque pulvinar ipsum ut felis aliquam, in pharetra leo molestie. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; In augue eros, elementum ut sollicitudin non, gravida eu justo.&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;article&quot;&gt;
    &lt;p class=&quot;title&quot;&gt;Cras a pellentesque est&lt;/p&gt;
    &lt;p class=&quot;date&quot;&gt;2024-06-04&lt;/p&gt;
    &lt;p class=&quot;text&quot;&gt;Cras a pellentesque est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec porttitor vehicula aliquam. Cras pulvinar neque in nisi placerat congue. Pellentesque maximus ex lorem, in convallis lacus cursus nec. Nam ullamcorper lectus vitae bibendum ultrices. Vestibulum dapibus consequat euismod. Nam laoreet pharetra lectus, ac auctor lorem rhoncus at. Sed magna ante, finibus sed diam et, maximus auctor massa. Proin volutpat lorem lacus, non tincidunt diam imperdiet ac. Nulla pharetra ex et efficitur eleifend. Duis urna dui, dignissim eget luctus sed, tempor in eros. Sed eleifend, leo id hendrerit placerat, nibh dolor maximus ipsum, vitae vehicula ligula leo et tortor. Etiam et lorem sed dolor dignissim luctus. Duis dignissim elementum urna, tempor bibendum diam volutpat sit amet. Donec vel euismod neque, nec feugiat nulla.&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;article&quot;&gt;
    &lt;p class=&quot;title&quot;&gt;Nam elementum pretium quam in vestibulum&lt;/p&gt;
    &lt;p class=&quot;date&quot;&gt;2024-07-21&lt;/p&gt;
    &lt;p class=&quot;text&quot;&gt;Nam elementum pretium quam in vestibulum. Aliquam vitae magna ac dolor placerat fringilla. Nunc nulla eros, mattis vel blandit a, pellentesque eget elit. Nullam a lectus pulvinar, laoreet neque eu, venenatis erat.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let&#39;s say each article comes from a CMS, and is represented by a &lt;code&gt;div&lt;/code&gt; element with the class &lt;code&gt;.article&lt;/code&gt;. This extra article parent element is useful for styling the article as a whole, but it&#39;s not necessary for the layout. In fact, it&#39;s even annoying because it means you can&#39;t define your grid layout on the parent element which contains the list of articles. That&#39;s because CSS grid only allows you to place direct children of the grid container into grid cells.&lt;/p&gt;
&lt;p&gt;Here are some ways around this limitation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define a grid layout on each &lt;code&gt;.article&lt;/code&gt; element individually. However, this might make it hard to align the articles in a consistent way across rows of the parent grid.&lt;/li&gt;
&lt;li&gt;Apply &lt;code&gt;display:contents&lt;/code&gt; to each &lt;code&gt;.article&lt;/code&gt; element, which makes the &lt;code&gt;.article&lt;/code&gt; element disappear from the rendered page, and its children take its place in the grid. However, this technique is known to have &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/display#display_contents&quot;&gt;accessibility issues&lt;/a&gt;: the element itself will no longer be presented by assistive technologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Enter CSS Subgrid&lt;/h2&gt;
&lt;p&gt;CSS Subgrid is a solution to this problem. CSS Subgrid allows you to create nested grids that inherit their parent grid&#39;s rows and columns definitions. Here is how you can use CSS Subgrid to solve the above problem with the articles list:&lt;/p&gt;
&lt;p&gt;First, define the grid layout on the parent element, &lt;code&gt;.articles-list&lt;/code&gt;, even if its direct children are not the elements you want to place in the grid:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.articles-list&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr 3fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code sample, the &lt;code&gt;.articles-list&lt;/code&gt; element is a grid container with two columns. The first column will be used for the article&#39;s title and date, and the second column for the article&#39;s text.&lt;/p&gt;
&lt;p&gt;Now, you can define a subgrid on the &lt;code&gt;.article&lt;/code&gt; elements, which will inherit the columns of the parent grid:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.articles-list .article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; span 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; subgrid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; min-content 1fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see from the code sample above, each &lt;code&gt;.article&lt;/code&gt; element spans the two columns of the parent grid. So it doesn&#39;t use these columns at all. Instead, it defines itself as a grid too (with &lt;code&gt;display: grid&lt;/code&gt;) and uses &lt;code&gt;grid-template-column: subgrid&lt;/code&gt; to inherit the columns of the parent grid. This is the key part of CSS Subgrid, the ability to refer to the parent grid&#39;s column or row templates, by using the &lt;code&gt;subgrid&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;The code sample also defines &lt;code&gt;grid-template-rows: min-content 1fr&lt;/code&gt; to create two rows in the &lt;code&gt;.article&lt;/code&gt; grid. The first row will be used for the title, and the second row for the date. The text of the article will span both rows, and will be displayed in the second column of the parent grid.&lt;/p&gt;
&lt;p&gt;Here is the complete code sample for the layout example from before:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;.articles-list&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .9rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr 3fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;gap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.articles-list .article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; span 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; subgrid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; min-content 1fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.articles-list p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.articles-list .title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.articles-list .date&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; italic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .8rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.articles-list .text&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 / span 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;articles-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Lorem ipsum dolor sit amet&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;2024-05-30&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque facilisis vitae turpis eget posuere. Vivamus sed convallis ante. Aliquam ex sapien, ultricies in diam vitae, maximus elementum augue. Quisque pulvinar ipsum ut felis aliquam, in pharetra leo molestie. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; In augue eros, elementum ut sollicitudin non, gravida eu justo.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Cras a pellentesque est&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;2024-06-04&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Cras a pellentesque est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec porttitor vehicula aliquam. Cras pulvinar neque in nisi placerat congue. Pellentesque maximus ex lorem, in convallis lacus cursus nec. Nam ullamcorper lectus vitae bibendum ultrices. Vestibulum dapibus consequat euismod. Nam laoreet pharetra lectus, ac auctor lorem rhoncus at. Sed magna ante, finibus sed diam et, maximus auctor massa. Proin volutpat lorem lacus, non tincidunt diam imperdiet ac. Nulla pharetra ex et efficitur eleifend. Duis urna dui, dignissim eget luctus sed, tempor in eros. Sed eleifend, leo id hendrerit placerat, nibh dolor maximus ipsum, vitae vehicula ligula leo et tortor. Etiam et lorem sed dolor dignissim luctus. Duis dignissim elementum urna, tempor bibendum diam volutpat sit amet. Donec vel euismod neque, nec feugiat nulla.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Nam elementum pretium quam in vestibulum&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;2024-07-21&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Nam elementum pretium quam in vestibulum. Aliquam vitae magna ac dolor placerat fringilla. Nunc nulla eros, mattis vel blandit a, pellentesque eget elit. Nullam a lectus pulvinar, laoreet neque eu, venenatis erat.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, we used the &lt;code&gt;subgrid&lt;/code&gt; value for the &lt;code&gt;grid-template-columns&lt;/code&gt; property, but it can also be used for the &lt;code&gt;grid-template-rows&lt;/code&gt; property. In fact, you can even use &lt;code&gt;subgrid&lt;/code&gt; for both columns and rows properties at the same time.&lt;/p&gt;
&lt;p&gt;Note that the parent grid&#39;s gaps are also inherited by the children subgrids, but you can override them by setting the &lt;code&gt;gap&lt;/code&gt; property on the child grid if you want to.&lt;/p&gt;
&lt;p&gt;And that&#39;s it, thanks for reading this short article. If you remember one thing here, remember that CSS Subgrid is now available across all three major browser engines. So, if you find yourself needing it, then double-check your usage stats to make sure your users have those latest versions at least, and then go ahead and use it!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Invasion of the border snappers</title>
    <link href="https://patrickbrosset.com/articles/2024-06-21-invasion-of-the-border-snappers/"/>
    <updated>2024-06-21T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-06-21-invasion-of-the-border-snappers/</id>
    <content type="html">
      
        &lt;h2&gt;CSS and device pixels&lt;/h2&gt;
&lt;p&gt;On the web, a CSS pixel might be different than a device pixel. While CSS lengths that are expressed in pixels can sometimes map directly to lengths on the device screen, this is not always the case. In fact, it&#39;s less and less the case.&lt;/p&gt;
&lt;p&gt;CSS pixels, as a concept, are a way to abstract the physical pixels of the device screen. This is useful to deal with the wide variety of screen resolutions available today. One CSS pixel is not necessarily equal to one device pixel. For example, on the monitor that I&#39;m using right now, the value of &lt;code&gt;window.devicePixelRatio&lt;/code&gt; is &lt;code&gt;1.5&lt;/code&gt;, which means that 1 CSS pixel is equal to 1.5 device pixels. This is a common value for high-density displays, but it can be different on other devices. For example, my iPhone has a &lt;code&gt;window.devicePixelRatio&lt;/code&gt; value of &lt;code&gt;3&lt;/code&gt;, where 1 CSS pixel is equal to 3 device pixels. If you think about it, this makes sense, you wouldn&#39;t want your button with &lt;code&gt;width:100px;&lt;/code&gt; to look much smaller on a higher pixel-density screen than it does on a lower pixel-density screen.&lt;/p&gt;
&lt;h2&gt;Enter the border snappers&lt;/h2&gt;
&lt;p&gt;This is all well and good, until your start using borders in CSS. In fact, what I&#39;ll describe next applies to the &lt;code&gt;border&lt;/code&gt;, &lt;code&gt;outline&lt;/code&gt;, and &lt;code&gt;column-rule-width&lt;/code&gt; CSS properties.&lt;/p&gt;
&lt;p&gt;When you specify a border width in CSS, the browser will try to render the border as closely as possible to the width you specified. However, in doing so, the browser may round the border width to the nearest device pixel value, which can result in borders that are slightly thicker or thinner than the width you wanted.&lt;/p&gt;
&lt;p&gt;Let&#39;s take an example: &lt;code&gt;border: 1px solid black&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On my 1.5 device pixel ratio monitor, this should result in a device pixel border of &lt;code&gt;1.5px&lt;/code&gt;, right? After all, this is what happens for all other CSS properties that accept lengths, like &lt;code&gt;width&lt;/code&gt;. However, what does happen instead is that the browser changes my authored length, when computing styles, to be &lt;code&gt;0.666667px&lt;/code&gt; instead. Once multiplied by my 1.5 device pixel ratio, this results in a border that&#39;s exactly 1 device pixel wide, which is smaller than what I wanted.&lt;/p&gt;
&lt;p&gt;Don&#39;t believe me? Take a look at the authored and computed styles values in DevTools:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/border-snapping/computed-devtools.png&quot; alt=&quot;The Elements tool in Edge DevTools, showing the authored style value of 1px, and the computed style value of 0.666667px&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Why does this happen? Because it was decided that borders (and outlines, and column rules) should look as crisp as possible. This is known as &lt;strong&gt;border snapping&lt;/strong&gt; (hence the play on word for this article&#39;s title, based on the movie &amp;quot;Invasion of the body snatchers&amp;quot;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Border snapping&lt;/strong&gt; is the process by which the browser engine changes the computed border width so that it aligns with the nearest device pixel value.&lt;/p&gt;
&lt;p&gt;This behavior has been standardized recently in the &lt;a href=&quot;https://drafts.csswg.org/css-values-4/#snap-a-length-as-a-border-width&quot;&gt;CSS Values and Units Module Level 4 spec&lt;/a&gt;, which you can find a discussion about in the
&lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/5210&quot;&gt;Define when border rounding happens, and to which properties it applies&lt;/a&gt; GitHub issue, and all browsers have implemented this behavior.&lt;/p&gt;
&lt;p&gt;Let&#39;s test this with this sample code:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inset 0 0 0 5px green&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px solid blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px solid red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above example draws a single &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element, which is &lt;code&gt;100px&lt;/code&gt; wide and tall and has, from the inside out, a &lt;code&gt;5px&lt;/code&gt; green inset shadow, a &lt;code&gt;5px&lt;/code&gt; blue border, and a &lt;code&gt;5px&lt;/code&gt; red outline.
On my 1.5 device pixel ratio monitor, in theory, this should result in a &lt;code&gt;150px&lt;/code&gt; wide element, with a &lt;code&gt;7.5px&lt;/code&gt; shadow, a &lt;code&gt;7.5px&lt;/code&gt; border, and a &lt;code&gt;7.5px&lt;/code&gt; outline. Here is the result:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/border-snapping/div.png&quot; alt=&quot;A div element with a 5px shadow, 5px border, and 5px outline&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The area that&#39;s inside the blue border is indeed &lt;code&gt;150px&lt;/code&gt; in width and height. Now let&#39;s zoom in on the colored lines around the element:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/border-snapping/border.png&quot; alt=&quot;Zoomed in on the shadow, border, and outline&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The green area, which corresponds to the shadow, is &lt;code&gt;5px * 1.5 = 7.5px&lt;/code&gt; tall, as expected (note how the half pixel is rendered as a semi-transparent pixel).&lt;/li&gt;
&lt;li&gt;However, the blue and red areas, which correspond to the border and outline, are &lt;code&gt;7px&lt;/code&gt; tall instead. This shows the border snapping behavior in action.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here, the rendering engine decided to snap the &lt;code&gt;7.5&lt;/code&gt; device pixel value to &lt;code&gt;7&lt;/code&gt;, so that the border, and the outline, would appear as crisp as possible.&lt;/p&gt;
&lt;p&gt;If your device&#39;s screen has a different device pixel ratio, you will see different results. Also, if your device pixel ratio is an integer, no snapping will need to occur. For example, on my iPhone, which has a device pixel ratio of &lt;code&gt;3&lt;/code&gt;, the shadow, border, and outline would all be &lt;code&gt;15px&lt;/code&gt; thick, and there would be no need to snap the border and outline values to the nearest device pixel.&lt;/p&gt;
&lt;h2&gt;When can border snapping be a problem?&lt;/h2&gt;
&lt;p&gt;Border snapping is useful in that it helps us to create clean user interfaces, but it can sometimes cause confusion, as illustrated by this Stack Overflow question: &lt;a href=&quot;https://stackoverflow.com/questions/42710882/css-border-1px-appear-as-0-667px-or-1px-depending-on-the-computer-display-res&quot;&gt;css border: 1px appear as 0.667px or 1px depending on the computer / display resolution (?)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But, I&#39;ve also seen cases where it became a layout problem. The scenario involved calculating an element&#39;s height based on various factors, including the thickness of the border. This was a problem because &lt;code&gt;height&lt;/code&gt; is not subject to border snapping, but &lt;code&gt;border&lt;/code&gt; is. So, if you&#39;re not careful and write something like &lt;code&gt;height: calc(3rem - 1px)&lt;/code&gt;, assuming that the border will be &lt;code&gt;1px&lt;/code&gt; thick, you might end up with a border that&#39;s &lt;code&gt;0.666667px&lt;/code&gt; thick instead, on some devices, and therefore a broken layout.&lt;/p&gt;
&lt;p&gt;If this ever happens to you, you can use the &lt;code&gt;box-sizing:border-box&lt;/code&gt; on the element, which will include the border in the element&#39;s dimensions, and therefore avoid the problem altogether.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>My web platform analysis of the State of JS 2023 results</title>
    <link href="https://patrickbrosset.com/articles/2024-06-27-my-web-platform-analysis-of-the-state-of-js-2023-results/"/>
    <updated>2024-06-27T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-06-27-my-web-platform-analysis-of-the-state-of-js-2023-results/</id>
    <content type="html">
      
        &lt;p&gt;Here&#39;s my quick summary of the &lt;a href=&quot;https://2023.stateofjs.com/&quot;&gt;State of JS 2023 results&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let me get this out of the way first: in this blog post, I don&#39;t care about frameworks, libraries, and build tools. I know, a large part of the State of JS series of surveys is about those, but I&#39;m more interested in the &lt;strong&gt;web platform&lt;/strong&gt; itself. So I&#39;m going to focus on the questions that are about the web platform.&lt;/p&gt;
&lt;h2&gt;Browser APIs interop&lt;/h2&gt;
&lt;p&gt;To no one&#39;s surprise, &lt;strong&gt;lack of interoperability&lt;/strong&gt; is still the number 1 pain point that developers have with browser APIs. 33% of the people who responded to the browser API pain point question did mention browser support.&lt;/p&gt;
&lt;p&gt;The second most common, albeit with only 6% of responses, was &lt;strong&gt;Safari&lt;/strong&gt;. Specifically, people shared their frustrations with things that were either missing or that behaved differently in Safari. Many people voiced their frustration about the fact that Safari is lagging behind and slowing down the web platform. Some of the examples people mentioned are: PWAs, WebCodecs, WebGL, Web USB, and Web Bluetooth. No surprises there either.&lt;/p&gt;
&lt;h2&gt;Static typing&lt;/h2&gt;
&lt;p&gt;The most common thing that people said they felt pain with is the &lt;strong&gt;lack of static typing&lt;/strong&gt; (33% of respondents). This is confirmed by another question about the missing JavaScript features where 57% of the respondents mentioned static typing.&lt;/p&gt;
&lt;p&gt;My intuition is that many people come from other languages that have static types, and they miss the authoring help this provides them when coding in JavaScript.&lt;/p&gt;
&lt;h2&gt;PWA APIs get used a lot&lt;/h2&gt;
&lt;p&gt;Out of a list of 12 specific browser APIs, such as Web Sockets, WebGL, Web Animations, Web RTC, WebXR, and more, &lt;strong&gt;PWA&lt;/strong&gt; was chosen by 50% of the respondents as a set of APIs that they have used. This is the second most popular API, after Web Sockets (60%).&lt;/p&gt;
&lt;p&gt;It&#39;s pretty cool to see that &lt;strong&gt;out of 20,000 people, 10,000 have used PWA APIs&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Dealing with dates&lt;/h2&gt;
&lt;p&gt;The pain of dealing with dates in JavaScript is real. 40% of the people who responded to the question about missing features mentioned the &lt;strong&gt;need for better date management&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Date management was also a common answer to a question that asked people to rank aspects of JavaScript that they struggled with the most.&lt;/p&gt;
&lt;p&gt;Thankfully, &lt;a href=&quot;https://tc39.es/proposal-temporal/docs/&quot;&gt;Temporal&lt;/a&gt; is coming (&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1839673&quot;&gt;Firefox bug&lt;/a&gt;, &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=223166&quot;&gt;Safari bug&lt;/a&gt;, &lt;a href=&quot;https://issues.chromium.org/issues/42201538&quot;&gt;Chromium bug&lt;/a&gt;).&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>A language model in your browser, accessible as a Web API?</title>
    <link href="https://patrickbrosset.com/articles/2024-07-04-a-language-model-in-your-browser-accessible-as-a-web-api/"/>
    <updated>2024-07-04T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-07-04-a-language-model-in-your-browser-accessible-as-a-web-api/</id>
    <content type="html">
      
        &lt;p&gt;AI, these days, is all about cloud services. You chat with a bot, you ask a voice assistant for something, etc. All of which happens to be powered by some AI model that&#39;s running on a server somewhere. But on-device AI is a thing too. It&#39;s just maybe less popular, less well-known. On-device AI refers to running AI models on the same device where the user is consuming the result of the model.&lt;/p&gt;
&lt;p&gt;Running an AI model on your device comes with important benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s private by default, meaning that whatever data you give the model (be it an image, or some text), it stays on your device, and never needs to be sent to a server.&lt;/li&gt;
&lt;li&gt;It costs nothing, apart from the electricity and data consumption needed to originally download the model and then run it. AI cloud services usually come with a price tag.&lt;/li&gt;
&lt;li&gt;It can work offline. If you have the model on your device, then you don&#39;t need an internet connection to use it.&lt;/li&gt;
&lt;li&gt;It can be faster in some cases. There&#39;s no network round trip to wait for. The model can generate whatever it needs to generate right away, with the caveat that it&#39;s probably much less powerful than a cloud model, and therefore might generate slower or more inaccurate results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&#39;s nothing very new here, and many apps, and devices already come with AI models built-in. In the world of web browsers, it&#39;s worth noting that some things are starting to happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firefox announced &lt;a href=&quot;https://hacks.mozilla.org/2024/05/experimenting-with-local-alt-text-generation-in-firefox-nightly/&quot;&gt;experimenting with local alt-text generation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Brave announced the ability to &lt;a href=&quot;https://brave.com/blog/byom-nightly/&quot;&gt;bring your own model&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;But also, web developers can already make use of models on web pages by using &lt;a href=&quot;https://webmachinelearning.github.io/webnn-intro/&quot;&gt;WebNN&lt;/a&gt;. This has also been possible for some time with WebGL and WebGPU too.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When it comes to web developers getting access to AI models, it still comes at the price of downloading your own models, and using frameworks such as ONNX web runtime to run them. What if there was a simpler way?&lt;/p&gt;
&lt;h2&gt;Simpler on-device AI for web pages?&lt;/h2&gt;
&lt;p&gt;That&#39;s what Google has started to experiment with. At Google I/O earlier this year, they announced their &lt;a href=&quot;https://developer.chrome.com/docs/ai/built-in&quot;&gt;&amp;quot;built-in&amp;quot; AI&lt;/a&gt; plans, which involve shipping a small language model (Gemini Nano) as part of Chrome, and making it available to web developers as a series of Web APIs. More recently, they published an &lt;a href=&quot;https://github.com/explainers-by-googlers/prompt-api&quot;&gt;explainer document for their Prompt API&lt;/a&gt;, which goes deeper into the details of how this works.&lt;/p&gt;
&lt;p&gt;The main benefits of this approach, over what you can already do with WebGL, WebGPU, and WebNN, are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The model is already provided for you by the browser, no need to download it yourself along with the necessary framework code. &lt;strong&gt;Note&lt;/strong&gt;: the model is downloaded on-demand, the first time you need it. That&#39;s a 1.7GB download which, obviously, doesn&#39;t come bundled with the browser installer.&lt;/li&gt;
&lt;li&gt;The model is shared across all web pages. With WebNN, each origin needs to download the model, and run it in its own context.&lt;/li&gt;
&lt;li&gt;You can just call a method to get generated text from the model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The drawback I can see far is that you don&#39;t get to choose the model.&lt;/p&gt;
&lt;p&gt;Now, this is all very new and experimental. The only API that Google has made available so far is the &lt;code&gt;prompt&lt;/code&gt; API, which allows you to access the model pretty directly. Here&#39;s a short code snippet showing the intended usage of the prompt API:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; session &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ai&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTextSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;promptStreaming&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; chunk &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  textarea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chunk&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can already use this today in Chrome Canary, by first enabling the &lt;code&gt;chrome://flags/#optimization-guide-on-device-model&lt;/code&gt; and &lt;code&gt;chrome://flags/#prompt-api-for-gemini-nano&lt;/code&gt; flags.&lt;/p&gt;
&lt;h2&gt;The risks&lt;/h2&gt;
&lt;p&gt;Exposing a language model directly to web pages is certainly exciting, but also comes with risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Interoperability&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Web APIs are designed to work across products and to endure the passage of time. Language models pose a fundamental challenge here in that models are non-deterministic. Different models respond differently to the same prompt and require different prompt engineering techniques. The results that a model generates now may be different tomorrow as the model evolves and will be different from another model. Can we even make an interoperable prompt API?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Safety&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I think websites are far easier to access than native apps, which require an install and, often, permissions. The risk of web apps making malicious use of AI is therefore greater.&lt;/p&gt;
&lt;p&gt;Should web browsers warn users and request their permissions first?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What do you think? I&#39;m personally reassured by this part of the explainer document:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Even more so than many other behind-a-flag APIs, the prompt API is an experiment, designed to help us understand web developers&#39; use cases to inform a roadmap of purpose-built APIs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Purpose-built APIs could be very useful to web developers, and would come with far fewer risks attached. Imagine being able to call &lt;code&gt;summarize(text)&lt;/code&gt; or &lt;code&gt;translate(text, &amp;quot;en-us&amp;quot;, &amp;quot;fr-fr&amp;quot;)&lt;/code&gt; from your webpage.&lt;/p&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;At this early stage, if you have feedback about any of this, I think the most useful thing to do is to engage with Google by submitting an issue on the &lt;a href=&quot;https://github.com/explainers-by-googlers/prompt-api/issues&quot;&gt;prompt API explainer repo&lt;/a&gt;.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Seamless SVG copy-paste on the web</title>
    <link href="https://patrickbrosset.com/articles/2024-07-11-seamless-svg-copy-paste-on-the-web/"/>
    <updated>2024-07-11T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-07-11-seamless-svg-copy-paste-on-the-web/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2024/07/11/seamless-svg-copy-paste-on-the-web/">https://blogs.windows.com/msedgedev/2024/07/11/seamless-svg-copy-paste-on-the-web/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>A release note for the web platform</title>
    <link href="https://patrickbrosset.com/articles/2024-09-04-a-release-note-for-the-web-platform/"/>
    <updated>2024-09-04T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-09-04-a-release-note-for-the-web-platform/</id>
    <content type="html">
      
        &lt;p&gt;tl;dr: the &lt;a href=&quot;https://www.w3.org/community/webdx/&quot;&gt;WebDX Community Group&lt;/a&gt; is working hard on cataloging the features of the web platform, and mapping them to their availability in browsers, which is making it possible to create a release note for the web platform. Check out our work in progress &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/release-notes/&quot;&gt;web platform release notes&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;As a web developer, do you know what&#39;s available for you to use on the web platform today? Also, do you know what&#39;s new since the last version of all major browsers? If you do, then please do share with the dev community. I think keeping track of what&#39;s available on the web has been a hard problem for a long time.&lt;/p&gt;
&lt;p&gt;Most dev marketing and dev relation teams tend to focus on the bleeding edge features. I get it, after product managers and engineers have spent major efforts in designing and implementing a new feature on the web, they want to call it a day, and pass the baton to devrels to handle the communication.&lt;/p&gt;
&lt;p&gt;Unfortunately, this way of communicating about the web platform isn&#39;t what developers really need. A feature is simply not a web platform feature until it exists in all the implementations (i.e. browsers) that users care about.&lt;/p&gt;
&lt;p&gt;Talking about bleeding edge features is still important. Talking early leads to early developer engagement, and therefore feedback which may help shape the feature. But the developers who can afford to engage early in the process are a minority. The majority of developers are busy solving their day to day issues, and may not always have the luxury to follow the latest features that a given browser just implemented.&lt;/p&gt;
&lt;p&gt;Sites like &lt;a href=&quot;https://caniuse.com/&quot;&gt;caniuse.com&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt; are amazing resources for developers. They provide a lot of information about what&#39;s available on the web platform. But, they are not shaped as a release note. They don&#39;t give you information about what&#39;s new this month, or what&#39;s coming soon.&lt;/p&gt;
&lt;p&gt;Other application platforms have release notes. If you develop apps for Android for example, you can check the different versions of Android, their release dates, and list of new features and bug fixes for each. Same on iOS, Windows, etc.&lt;/p&gt;
&lt;p&gt;The web platform is different. It doesn&#39;t have a version number, or just one core SDK. It&#39;s a multi-implementation platform that&#39;s, by nature, very fragmented. It grows bigger in multiple directions at the same time, without much concerted efforts from implementers to move in the same direction at the same time. This makes a release note for the web a hard problem to solve.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The recent work that we&#39;ve been doing on the &lt;a href=&quot;https://www.w3.org/community/webdx/&quot;&gt;WebDX Community Group&lt;/a&gt; is a step in the right direction. We are trying to create a community-driven effort to index all of the &lt;em&gt;features&lt;/em&gt; of the web platform, give them unique identifiers, and track their availability across all major browsers. This, in turn, lets us track what features are available in browsers in which versions, and at which dates. Based on this data, we can create a release note style document that tells you what&#39;s new on the web for any given month.&lt;/p&gt;
&lt;p&gt;If you want to see a preview of our work in progress, check out the &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/release-notes/&quot;&gt;list of monthly release notes&lt;/a&gt; on the web-features-explorer website.&lt;/p&gt;
&lt;p&gt;If we take &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/release-notes/july-2024/&quot;&gt;July 2024&lt;/a&gt; as an example, here&#39;s what happened:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/font-synthesis/&quot;&gt;The &lt;code&gt;font-synthesis&lt;/code&gt; CSS property&lt;/a&gt; became &lt;strong&gt;Baseline widely available&lt;/strong&gt;, which means it&#39;s been support for more than 30 months on all core browsers. This means that, since July 2024, using this property in your project should be very low risk. You should always check which browsers your actual users have, but your confidence in using this property should be high.&lt;/li&gt;
&lt;li&gt;Several other features became &lt;strong&gt;Baseline newly available&lt;/strong&gt;, which means that, since July 2024, they&#39;re now available across all core browsers. This means that if your users all have the latest versions of the core browsers, then you can safely use these features. But, again, always check which browsers, and which versions of these browsers your actual users have. In any case, it&#39;s a good time to at least learn about these features:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/alt-text-generated-content/&quot;&gt;Alt text for generated content&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/font-size-adjust/&quot;&gt;&lt;code&gt;font-size-adjust&lt;/code&gt;&lt;/a&gt; CSS property.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/parse-html-unsafe/&quot;&gt;Unsanitized HTML parsing methods&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/registered-custom-properties/&quot;&gt;Registered custom properties&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/relative-color/&quot;&gt;Relative colors&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/resizable-buffers/&quot;&gt;Resizable buffers&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The web-features-explorer website is generated nightly, based on the latest information that&#39;s available in the &lt;a href=&quot;https://github.com/web-platform-dx/web-features/&quot;&gt;web-features&lt;/a&gt; and &lt;a href=&quot;https://github.com/mdn/browser-compat-data/&quot;&gt;browser-compat-data&lt;/a&gt; repositories. You can keep an eye on the list of release notes to see what&#39;s coming in the current month.&lt;/p&gt;
&lt;p&gt;I hope this prototype of a web platform release note is useful for you to keep track of what&#39;s available, and what&#39;s coming. If you have any feedback, please open an issue on the &lt;a href=&quot;https://github.com/web-platform-dx/web-features-explorer&quot;&gt;web-features-explorer repo&lt;/a&gt;. And, ff you want to get involved, join the &lt;a href=&quot;https://www.w3.org/community/webdx/&quot;&gt;WebDX Community Group&lt;/a&gt;, and help us make this a reality.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Looking forward to an Interop 2025 that addresses your top needs </title>
    <link href="https://patrickbrosset.com/articles/2024-09-17-looking-forward-to-an-interop-2025-that-addresses-your-top-needs/"/>
    <updated>2024-09-17T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-09-17-looking-forward-to-an-interop-2025-that-addresses-your-top-needs/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2024/09/17/interop-2025-call-for-proposals/">https://blogs.windows.com/msedgedev/2024/09/17/interop-2025-call-for-proposals/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Smashing Conference NYC 2024</title>
    <link href="https://patrickbrosset.com/articles/2024-10-15-smashing-conference-nyc-2024/"/>
    <updated>2024-10-15T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-10-15-smashing-conference-nyc-2024/</id>
    <content type="html">
      
        &lt;p&gt;Last week, I attended my very first Smashing Conference event, and it was great! Here&#39;s my little write up about it.&lt;/p&gt;
&lt;p&gt;I was there for a few different reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, Microsoft sponsored the event, and we provided them with conference rooms at the NYC Microsoft office for the pre- and post-conference workshops. I was the Microsoft person on-site to help with logistics and make sure everything went smoothly.&lt;/li&gt;
&lt;li&gt;Second, I took part in the &lt;a href=&quot;https://smashingconf.com/ny-2024/jam-session&quot;&gt;pre-event lightning talk session&lt;/a&gt; on the first day, at the Figma office, and talked about on-device AI on the web platform there.&lt;/li&gt;
&lt;li&gt;Finally, I held a booth during the conference to talk about Microsoft Edge in general, &lt;a href=&quot;https://www.pwabuilder.com/&quot;&gt;PWABuilder&lt;/a&gt;, and on-device AI on the web more specifically.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The people&lt;/h2&gt;
&lt;p&gt;Meeting, and re-connecting with people was the most rewarding part of the conference. It&#39;s been really cool to chat with so many folks from the web community.&lt;/p&gt;
&lt;p&gt;Here are some of my highlights in no particular order, and I&#39;m also most likely forgetting a bunch of people, so I apologize in advance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chenhuijing.com/&quot;&gt;Chen Hui Jing&lt;/a&gt;, who I had not seen in a long time, and who&#39;s doing amazing work in the web monetization space.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/ioana_cis&quot;&gt;Ioana Chiorean&lt;/a&gt;, who I had somehow missed during our shared time at Mozilla. Thanks for the super fun discussions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tej.as/&quot;&gt;Tejas Kumar&lt;/a&gt;, who&#39;s been doing cool AI stuff on his website. I recommend listening to his podcast. Tejas is very passionate and his energy is contagious.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.quirksmode.org/about/&quot;&gt;Peter-Paul Koch&lt;/a&gt;, who I chatted with about the CSS Day and Performance.now() conferences.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/kevinpowell&quot;&gt;Kevin Powell&lt;/a&gt;, whose videos I really admire.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mstdn.social/@cariefisher&quot;&gt;Carie Fisher&lt;/a&gt;, who ran a super interesting accessibility workshop.&lt;/li&gt;
&lt;li&gt;The entire Smashing team, who were all very nice and welcoming: &lt;a href=&quot;https://www.linkedin.com/in/amanda-tamny-annandale-849510121&quot;&gt;Amanda Annandale&lt;/a&gt;, &lt;a href=&quot;https://www.imakewebsites.nl/&quot;&gt;Charis Rooda&lt;/a&gt;, &lt;a href=&quot;https://www.smashingmagazine.com/author/jarijn-nijkamp/&quot;&gt;Jarijn Nijkamp&lt;/a&gt;, &lt;a href=&quot;https://marionajones.net/&quot;&gt;Mariona Jones&lt;/a&gt;, and of course the one and only &lt;a href=&quot;https://www.linkedin.com/in/vitalyfriedman/&quot;&gt;Vitaly Friedman&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I even shook hands with &lt;a href=&quot;https://www.marcthiele.com/&quot;&gt;Marc Thiele&lt;/a&gt;, who organizes &lt;a href=&quot;https://beyondtellerrand.com/&quot;&gt;Beyond Tellerrand&lt;/a&gt;, and &lt;a href=&quot;https://timkadlec.com/&quot;&gt;Tim Kadlec&lt;/a&gt;, who&#39;s done amazing work during his time at &lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;WebPageTest&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The talks&lt;/h2&gt;
&lt;p&gt;Sadly, being at my booth for most of the conference, I couldn&#39;t attend the talks. I did pop in the room on a few occasions and caught a few snippets here and there. The talks seemed very interesting and high quality. The whole experience felt very professional and fun at the same time, with Vitaly being a great MC and keeping the energy up.&lt;/p&gt;
&lt;p&gt;Here are the main themes I gathered from the talks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A lot of talks focused on design systems. While this topic doesn&#39;t really apply to my work, it&#39;s good to know more about this, and it&#39;s reassuring that designers and developers have the tools and processes they need to communicate and build products together.&lt;/li&gt;
&lt;li&gt;Many of the talks also focused on web components.&lt;/li&gt;
&lt;li&gt;Others were about CSS, and some of the more advanced features the language has been getting lately.&lt;/li&gt;
&lt;li&gt;Finally, and this surprised me a little bit considering that this is a frontend/design conference, a lot of the talks were about AI. The speakers did a great job tying that back to frontend, UX, and design, and evaluating what the applications of AI are in those fields.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Smashing Conference team&lt;/h2&gt;
&lt;p&gt;I was very impressed by how organized and efficient the team was. Even better is that they were all super nice and friendly human beings. By the end of the event, it felt like being among friends!&lt;/p&gt;
&lt;p&gt;I highly recommend attending a conference organized by them, but also write blog posts on &lt;a href=&quot;https://www.smashingmagazine.com/&quot;&gt;Smashing Magazine&lt;/a&gt;, and propose talks or workshop at their &lt;a href=&quot;https://www.smashingmagazine.com/events/&quot;&gt;events&lt;/a&gt; if you can.&lt;/p&gt;
&lt;h2&gt;The venues&lt;/h2&gt;
&lt;p&gt;The workshops happened at the Microsoft office, near Times Square. I had never visited. The rooms were nice and spacious.&lt;/p&gt;
&lt;p&gt;The lightning talks were at the Figma office, which is super cute with all the colors and cool furniture. It was fun to use their big conf/kitchen room to present, mingle, and have some nice pizzas and drinks.&lt;/p&gt;
&lt;p&gt;The conference itself was at &lt;a href=&quot;https://www.newworldstages.com/&quot;&gt;New World Stages&lt;/a&gt;, also near Times Square. It&#39;s a nice theater, I liked the feel of it. The breaks area was small enough that people could just go from one booth to the next and chat with sponsors.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://marcthiele.com/photos/smashingconf-new-york-2024&quot;&gt;All photos onm Marc Thiele&#39;s website&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;New York City&lt;/h2&gt;
&lt;p&gt;Now, this is the good stuff. It was my first time in New York, and I immediately fell in love with the city. It&#39;s so photogenic, and has a lot more chaos and vibes than other US cities I&#39;ve visited. Perhaps because it&#39;s an older city than the ones I&#39;ve been to, but it does feel like it has a soul.&lt;/p&gt;
&lt;p&gt;Obviously, there&#39;s always something to do, but also always something to eat (what is it with US folks and food, y&#39;all are obsessed).&lt;/p&gt;
&lt;p&gt;I liked how different parts of the city were super different from one another. I stayed in Manhattan, but also visited Harlem and Brooklyn, attended a Sunday service in a church in Harlem, went up the One World Trade Center, visited the 9/11 memorial, ran in Central Park, took a boat to Staten Island, which passed by the Statue of Liberty, walked the High Line, visited the Whitney Museum, saw a Broadway show, ate a lot of good food, and so on.&lt;/p&gt;
&lt;p&gt;Here a few photos, &lt;a href=&quot;https://www.instagram.com/brosset.patrick/&quot;&gt;check out my Instagram for more&lt;/a&gt;:&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; alt=&quot;The inside of the shopping mall next to the 9/11 memorial museum&quot; src=&quot;https://patrickbrosset.com/assets/nyc/IMG_9198.webp&quot; style=&quot;width:unset;&quot; /&gt; 
&lt;img loading=&quot;lazy&quot; alt=&quot;The statue of liberty, seen from the Brooklyn Bridge park, at night&quot; src=&quot;https://patrickbrosset.com/assets/nyc/IMG_9249.webp&quot; style=&quot;width:unset;&quot; /&gt; 
&lt;img loading=&quot;lazy&quot; alt=&quot;The top of one the arches on the Brooklyn bridge, by night&quot; src=&quot;https://patrickbrosset.com/assets/nyc/IMG_9248.webp&quot; style=&quot;width:unset;&quot; /&gt; 
&lt;img loading=&quot;lazy&quot; alt=&quot;Night shot on a street, car and building lights, smoke coming out of the sewer&quot; src=&quot;https://patrickbrosset.com/assets/nyc/IMG_9222.webp&quot; style=&quot;width:unset;&quot; /&gt; 
&lt;img loading=&quot;lazy&quot; alt=&quot;The status of liberty, at sunset&quot; src=&quot;https://patrickbrosset.com/assets/nyc/IMG_9251.webp&quot; style=&quot;width:unset;&quot; /&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>State of CSS and State of HTML 2024</title>
    <link href="https://patrickbrosset.com/articles/2024-11-08-state-of-css-and-state-of-html-2024/"/>
    <updated>2024-11-08T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-11-08-state-of-css-and-state-of-html-2024/</id>
    <content type="html">
      
        &lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/sachagreif.bsky.social&quot;&gt;Sacha Greif&lt;/a&gt;&#39;s &amp;quot;State Of&amp;quot; surveys have become one of the most useful web development series of surveys. They &lt;a href=&quot;https://www.devographics.com/&quot;&gt;started&lt;/a&gt; out of Sacha&#39;s need to learn more about the JavaScript ecosystem, but have now expanded to include more parts of the web platform.&lt;/p&gt;
&lt;p&gt;As someone who works on browsers, I can assure you that the results to these surveys are read by browser vendors and used to inform our work. There are a few key questions in these surveys that speak directly to browser vendors, such as questions about the availability of features across browsers, their interoperability, and the pain points that developers feel.&lt;/p&gt;
&lt;p&gt;As a web developer, there aren&#39;t that many different things you can do to influence the web platform evolution, and taking the time to fill out these surveys is one of them. I know taking time out of your day job is hard, but unlike other means of influencing, this one is a low barrier to entry, and also a great opportunity to learn about new features too.&lt;/p&gt;
&lt;p&gt;With that said, let&#39;s jump into my analysis of the results of the State of CSS 2024 and the preliminary results of the State of HTML 2024.&lt;/p&gt;
&lt;p&gt;Caveat: for both of these surveys, I will only look at the few questions that relate to browser compatibility issues, missing features, and other pain points.&lt;/p&gt;
&lt;h2&gt;State of CSS 2024&lt;/h2&gt;
&lt;p&gt;Let&#39;s first take a look at the State of CSS survey.&lt;/p&gt;
&lt;h3&gt;Browser incompatibilities (&lt;a href=&quot;https://2024.stateofcss.com/en-US/usage/#css_interoperability_features&quot;&gt;results&lt;/a&gt;)&lt;/h3&gt;
&lt;p&gt;Participants were asked about which features they couldn&#39;t use because of the lack of support, or differences between browsers. The results are quite spread out with no clear winner (or, loser, in this case). The numbers seem fairly low overall too. Here are the top 7 responses (I cut off below 5% of the votes):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Anchor positioning&lt;/strong&gt;: 11%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container queries&lt;/strong&gt;: 10%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;:has()&lt;/code&gt;&lt;/strong&gt;: 9%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nesting&lt;/strong&gt;: 9%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View Transitions&lt;/strong&gt;: 9%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subgrid&lt;/strong&gt;: 7%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grid&lt;/strong&gt;: 6%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from the expected Anchor positioning and View Transition features, which are not yet available across all browsers, the key take away for me here is that developers tend to take a very conservative approach to using new CSS features.&lt;/p&gt;
&lt;p&gt;Indeed, Container queries, &lt;code&gt;:has()&lt;/code&gt;, Nesting, and Subgrid have been available across all browser engines since 2023. But, 2023 is basically yesterday, and it feels way to soon for people to feel safe using features that have only been available for a year. Not to mention the fact that many developers might not even be aware of these features yet.&lt;/p&gt;
&lt;p&gt;Reading through the free-form comments on this question also shows that web devs have been burnt in the past and have to account for large user bases, which don&#39;t always migrate to more recent versions of their browsers (this is especially true, it seems, for users on older iOS versions).&lt;/p&gt;
&lt;p&gt;Some comments also mention the frustration caused by hearing about new features much too early, when developers can&#39;t use them yet. This leads to developers feeling overwhelmed by the number of features that they have to memorize for years before they can use them.&lt;/p&gt;
&lt;p&gt;Now, Grid was surprising. It&#39;s been available across browsers since 2017, which feels like a long enough time for developers to use. Digging through the comments actually shows that developers were mostly asking about Masonry and Subgrid. So, this is more of a case of the survey not having the right categorization of the results, rather than a signal that Grid is not interoperable (that said, Grid has been part of the Interop project for the past few years, there are always more edge cases to fix).&lt;/p&gt;
&lt;p&gt;Respondents also left some interesting comments on specific features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Container queries was mentioned as being too complex.&lt;/li&gt;
&lt;li&gt;Some people mentioned bugs with &lt;code&gt;:has()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Some people have heard of the CSS Nesting&#39;s &amp;quot;relaxed syntax&amp;quot;, and want to wait for this before using it. This new nesting syntax was actually part of the Interop 2024 project, and we&#39;re getting it across all browsers this year.&lt;/li&gt;
&lt;li&gt;Subgrid is also mentioned as having bugs and performance problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Missing features (&lt;a href=&quot;https://2024.stateofcss.com/en-US/usage/#css_missing_features&quot;&gt;results&lt;/a&gt;)&lt;/h3&gt;
&lt;p&gt;Participants were also asked about which features they thought were missing from CSS.&lt;/p&gt;
&lt;p&gt;The numbers are also quite low here with no one feature that stands out. This is explained by some of the comments left on the question where people shared their feeling that there are already too many features to learn, to use, and to keep track of.&lt;/p&gt;
&lt;p&gt;That said, here are the top three features that people felt were missing from CSS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mixins&lt;/strong&gt;: people want what SCSS/Sass has, to be able to reuse styling code logic across their code base.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditional logic&lt;/strong&gt;: the ability to have if/else statements in CSS code.&lt;/li&gt;
&lt;li&gt;And, &lt;strong&gt;Masonry layout&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Other pain points (&lt;a href=&quot;https://2024.stateofcss.com/en-US/usage/#css_pain_points&quot;&gt;results&lt;/a&gt;)&lt;/h3&gt;
&lt;p&gt;This question is about whether developers are facing any other pain points when writing CSS.&lt;/p&gt;
&lt;p&gt;The top response here is absolutely no surprise: &lt;strong&gt;browser support&lt;/strong&gt;. It&#39;s not a surprise because the lack of cross-browser support (as well as low interoperability of features) has always been the top pain point in these surveys. Safari is mentioned in quite a few comments.&lt;/p&gt;
&lt;p&gt;But, the comments also contain an interesting key take away here related to design tools. This one resonates with some of what I heard at &lt;a href=&quot;https://patrickbrosset.com/articles/2024-10-15-smashing-conference-nyc-2024/&quot;&gt;SmashingConf&lt;/a&gt; earlier this year. Designers are seeing that CSS has gotten so many features that let you be creative on the web, but also that let you design specifically &lt;strong&gt;for&lt;/strong&gt; the web, that they&#39;re feeling held back by their design tools that don&#39;t &amp;quot;speak&amp;quot; CSS. This tells me that traditional graphic design and CSS are growing further apart and that there&#39;s room for CSS-first design tools.&lt;/p&gt;
&lt;p&gt;One final take away on this question is related to complexity, where some people mentioned that CSS was getting very complex to use.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In conclusion, here are some of the opportunities identified by the State of CSS 2024 survey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Anchor positioning, View Transitions, Masonry, mixins, and conditional logic are some the top requested CSS features.&lt;/li&gt;
&lt;li&gt;Developers have to wait years before they can use new features.&lt;/li&gt;
&lt;li&gt;DevRels announce features way too early. This doesn&#39;t mean they shouldn&#39;t, but they should also announce them again later when developers can actually use them.&lt;/li&gt;
&lt;li&gt;There&#39;s a feeling that CSS is getting kind of overwhelming and complex to use.&lt;/li&gt;
&lt;li&gt;There seems to be a need for CSS-first design tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;State of HTML 2024&lt;/h2&gt;
&lt;p&gt;At the time of writing, the results from the State of HTML 2024 have not been published yet. I&#39;ve been given access to the preliminary results, but won&#39;t be able to share links to specific survey sections, so you&#39;ll need to trust me. I don&#39;t expect there to be much changes in the final results though.&lt;/p&gt;
&lt;h3&gt;Browser interoperability&lt;/h3&gt;
&lt;p&gt;Participants were asked about which features they couldn&#39;t use because of the lack of support, or differences between browsers. Here, the Popover API is the clear winner, with close to 20% of respondents choosing this feature. But apart from this, other features trail off quickly below 10%.&lt;/p&gt;
&lt;p&gt;Below is the top 5 features that respondents said they couldn&#39;t use because of browser incompatibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Popover&lt;/strong&gt;: 19%&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;This API is now supposedly supported in all engines, but there&#39;s a known &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=267688&quot;&gt;issue in Safari iOS preventing the dismissal of Popovers when tapping outside of them&lt;/a&gt;. That said, the comments on this feature don&#39;t mention this issue specifically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Many people who left comments mentioned the lack of anchoring support for Popover. Indeed, Popovers can be very useful to implement tooltip-like UIs, but that&#39;s not very easy if you can&#39;t anchor the tooltips to the elements they relate to.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Anchor positioning&lt;/strong&gt;: 11%&lt;/p&gt;
&lt;p&gt;This feature is missing in &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1838746&quot;&gt;Firefox&lt;/a&gt; and in Safari, but hopefully coming soon.&lt;/p&gt;
&lt;p&gt;Multiple respondents mentioned that they needed this feature to position Popovers. So, the responses to this question are related to the previous question.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;View transitions API&lt;/strong&gt;: 8%&lt;/p&gt;
&lt;p&gt;No specific comments were left on this one. People just want it to be implemented.&lt;/p&gt;
&lt;p&gt;View Transitions within the same document are missing in Firefox but work is happening. But, really, what people need here is support for View Transitions across documents, which can help remove performance costly client-side single-page app frameworks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/strong&gt;: 6%&lt;/p&gt;
&lt;p&gt;This feature is widely available now, having been supported in all browser engines since March 2022. However, similar to how web devs find that using 2023 CSS features is too soon, some people commented that they couldn&#39;t use &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; because of the lack of support in older iOS versions.&lt;/p&gt;
&lt;p&gt;Respondents also mentioned a few different issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not being to animate the dialog appearance/disappearance. For this to work, developers need a set of other CSS features: &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/overlay&quot;&gt;&lt;code&gt;overlay&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@starting-style&quot;&gt;&lt;code&gt;@starting-style&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/transition-behavior&quot;&gt;&lt;code&gt;transition-behavior&lt;/code&gt;&lt;/a&gt;, and animating &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/display#animating_display&quot;&gt;&lt;code&gt;display&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/content-visibility#animating_and_transitioning_content-visibility&quot;&gt;&lt;code&gt;content-visibility&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Accessibility issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PWAs&lt;/strong&gt;: 6%&lt;/p&gt;
&lt;p&gt;Comments on this response mostly mention the lack of support for PWAs on iOS devices.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Limited functionality&lt;/h3&gt;
&lt;p&gt;Participants were also asked about features they couldn&#39;t use for other reasons. I&#39;ll list the top 8 responses below (&amp;gt; 5%). Note that the order isn&#39;t super meaningful here because the data seems to contain a bunch of incorrectly categorized responses. That said, I&#39;m less interested in which features came first than the reasons people said they couldn&#39;t use those features.&lt;/p&gt;
&lt;p&gt;Two key learnings for me here: many elements are not stylable enough, and some are too complex to use.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;: People complain that the element is not stylable enough (truly stylable selects can&#39;t come soon enough).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;input type=date&amp;gt;&lt;/code&gt;: Same thing here, date and time inputs aren&#39;t stylable enough. But also, they vary across browsers, are not configurable enough, and their default UX leaves much to be desired.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;: Respondents mentioned difficulties to style and to animate. They also said that the element was complex to use.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;datalist&amp;gt;&lt;/code&gt;: Here too, people mentioned the lack of ability to style the element.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;: Devs said these elements were a great way to implement accordion components, but that this was hard due to difficulties in styling and animating (e.g. animating to auto height).&lt;/li&gt;
&lt;li&gt;Popover: Respondents mentioned the lack of anchor positioning prevented them from using the API.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;select multiple&amp;gt;&lt;/code&gt;: People mentioned a poor accessibility and poor UX, especially on desktop.&lt;/li&gt;
&lt;li&gt;Web Components: Developers said web components were complex.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Missing elements&lt;/h3&gt;
&lt;p&gt;In this question, respondents were asked to sort a list of pre-determined missing elements, based on what they needed the most.&lt;/p&gt;
&lt;p&gt;Below is the top 7 elements, which all got above 20%. Scores to elements after that drop off.&lt;/p&gt;
&lt;p&gt;It&#39;s worth noting that the list of elements is exactly the same as last year&#39;s, in the same order.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data table&lt;/strong&gt;: 51%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tabs&lt;/strong&gt;: 40%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Switch/toggle&lt;/strong&gt;: 32%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skeleton loader&lt;/strong&gt;: 28%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context menu&lt;/strong&gt;: 27%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Carousel&lt;/strong&gt;: 26%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infinite scroll&lt;/strong&gt;: 24% (although several people commented against it, saying that infinite scroll was bad UX).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This question also got some interesting comments from people saying they didn&#39;t want anything new that would be too complex, but instead wanted better primitives to build their own components.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In conclusion, here are the opportunities I can see from the State of HTML 2024 survey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The top request is for real Popover support, with light dismiss on iOS, and with anchor positioning built-in.&lt;/li&gt;
&lt;li&gt;Support for top-layer animations would go a long way in letting people use Popovers and Dialogs, and therefore remove the need for costly JS library dependencies.&lt;/li&gt;
&lt;li&gt;Existing HTML elements need to be more easily stylable, especially &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input type=date&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;datalist&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;New elements are needed, especially data tables, tabs, and switches. Re-implementing your own currently requires a lot of work and JS code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Going forward&lt;/h2&gt;
&lt;p&gt;The Web platform is far from ever being done. New problem areas get identified all the time, and new solutions are needed for them. As these make their ways through standards and browser engines, it&#39;s important to keep an eye on how well they&#39;re being implemented, and how well they indeed solve the problems they were meant to solve.&lt;/p&gt;
&lt;p&gt;These State Of surveys are a tremendous tool to help us keep the pulse on what&#39;s happening in this world, and therefore to help us prioritize efforts to continue evolving the web platform in the right direction.&lt;/p&gt;
&lt;p&gt;Survey fatigue is real, but without signals from the people actually using the platform, those who build it can&#39;t do so effectively.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Even faster IndexedDB reads with getAllRecords</title>
    <link href="https://patrickbrosset.com/articles/2024-11-19-even-faster-indexeddb-reads-with-getallrecords/"/>
    <updated>2024-11-19T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-11-19-even-faster-indexeddb-reads-with-getallrecords/</id>
    <content type="html">
      
        &lt;p&gt;Reading large amounts of data in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot;&gt;IndexedDB&lt;/a&gt; can be slow at times. In this article, let&#39;s look at &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/IndexedDbGetAllEntries/explainer.md&quot;&gt;a proposal&lt;/a&gt; from the Microsoft Edge team that improves the performance &lt;strong&gt;and&lt;/strong&gt; ergonomics of reading IndexedDB data.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Help wanted&lt;/strong&gt;: If this is interesting to you, and you want to help make this a reality, please consider providing feedback on the proposal. The more feedback, the better chances the proposal has of matching your needs, and of being implemented in all browsers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Proposal&lt;/strong&gt;: &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/IndexedDbGetAllEntries/explainer.md&quot;&gt;IndexedDB: getAllRecords&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feedback&lt;/strong&gt;: &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/&quot;&gt;Review existing issues&lt;/a&gt; or &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new/choose&quot;&gt;open a new issue&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Reading a lot of data, fast&lt;/h2&gt;
&lt;p&gt;There are a few options available to read records from an IndexedDB store:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBCursor&quot;&gt;IDBCursor&lt;/a&gt; to read the records one at a time.&lt;/li&gt;
&lt;li&gt;Reading the entire store at once using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBObjectStore/getAll&quot;&gt;IDBObjectStore&#39;s getAll&lt;/a&gt; and/or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBObjectStore/getAllKeys&quot;&gt;IDBObjectStore&#39;s getAllKeys&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using the same methods as above, but providing &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBKeyRange&quot;&gt;IDBKeyRange&lt;/a&gt; objects along the way, to read in batches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&#39;ll ignore the first option because reading one record at a time can be slow if you have a lot of data, as it requires a lot of back and forth between your app&#39;s main thread and the thread where the IDB engine runs.&lt;/p&gt;
&lt;p&gt;We&#39;ll also ignore the second option because reading the entire store at once could freeze up your app while the data is being read, and cause memory problems.&lt;/p&gt;
&lt;p&gt;Let&#39;s go with the third option since it reduces the number of back and forth with the IDB engine, and lets you provide a better user experience. Let&#39;s see what an implementation might look like (thank you, Nolan Lawson, for the inspiration in &lt;a href=&quot;https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/&quot;&gt;Speeding up IndexedDB reads and writes&lt;/a&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readInBatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;db&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transaction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;readonly&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;objectStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNextBatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAllKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNextBatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keys &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// There&#39;s more to read. Define the next range.&lt;/span&gt;
      range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; IDBKeyRange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lowerBound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Do something with `keys` and `values` here.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// We&#39;re done reading.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code uses an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBKeyRange&quot;&gt;IDBKeyRange&lt;/a&gt; to read the data in batches. It also uses both &lt;code&gt;getAll&lt;/code&gt; and &lt;code&gt;getAllKeys&lt;/code&gt; in order to not only get the records, but their primary keys too.&lt;/p&gt;
&lt;p&gt;This is a good solution, but there are a couple of limitations with it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Having to use both &lt;code&gt;getAll&lt;/code&gt; and &lt;code&gt;getAllKeys&lt;/code&gt; together means that you have to make two requests to the IDB engine for each batch.&lt;/li&gt;
&lt;li&gt;This solution doesn&#39;t support reading in reverse order.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reading in reverse order&lt;/h2&gt;
&lt;p&gt;Unfortunately, there isn&#39;t a way to read in batches &lt;strong&gt;and&lt;/strong&gt; in reverse order at the same time. To read in reverse order, you&#39;ll have to use and &lt;code&gt;IDBCursor&lt;/code&gt;, which supports setting a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBObjectStore/openCursor#direction&quot;&gt;direction&lt;/a&gt; when moving to the next record. This means you&#39;ll have to read one record at a time. Here&#39;s an example of how you might do this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readReverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transaction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;readonly&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;objectStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cursor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cursor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Do something with `cursor.key` and `cursor.value` here.&lt;/span&gt;
      cursor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Done reading.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, doing the above can be slow if you have a lot of data.&lt;/p&gt;
&lt;h2&gt;Introducing getAllRecords()&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;getAllRecords&lt;/code&gt; method of an &lt;code&gt;IDBObjectStore&lt;/code&gt; (and &lt;code&gt;IDBIndex&lt;/code&gt;) is a &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/IndexedDbGetAllEntries/explainer.md&quot;&gt;proposal&lt;/a&gt; from the Microsoft Edge team that aims to address the limitations discussed above. It does so by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allowing you to read in batches.&lt;/li&gt;
&lt;li&gt;Allowing you to read in reverse order.&lt;/li&gt;
&lt;li&gt;Returning both the records and their primary keys in one request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes it possible for you to batch read data, in both directions, with the minimum amount of requests to the IDB engine.&lt;/p&gt;
&lt;p&gt;Here&#39;s what reading with &lt;code&gt;getAllRecords&lt;/code&gt; might look like:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readInBatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;db&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transaction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;readonly&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;objectStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNextBatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAllRecords&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        direction
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; records &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNextBatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;records&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// There could be more records, set a starting point for the next iteration.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lastRecord &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; records&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; direction &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prev&quot;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; IDBKeyRange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;upperBound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastRecord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IDBKeyRange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lowerBound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastRecord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Do something with `records` here, which gives access to both `key` and `value`.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Done reading.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above uses the proposed &lt;code&gt;getAllRecords()&lt;/code&gt; method on an &lt;code&gt;IDBObjectStore&lt;/code&gt; object. The method accepts a &lt;code&gt;query&lt;/code&gt; argument, just like &lt;code&gt;getAll&lt;/code&gt; or &lt;code&gt;getAllKeys&lt;/code&gt;, which you can use to read in batches. The method also accepts a &lt;code&gt;count&lt;/code&gt; and &lt;code&gt;direction&lt;/code&gt; argument, which you can use to set how many records you want to read, and in which order.&lt;/p&gt;
&lt;p&gt;The method returns a list of &lt;code&gt;IDBRecord&lt;/code&gt; objects, which contain both the &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; of the record (note that it also returns the &lt;code&gt;primaryKey&lt;/code&gt;, which in this case is equal to &lt;code&gt;key&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The method can also be used on an &lt;code&gt;IDBIndex&lt;/code&gt; object, in the same way, but in this case &lt;code&gt;key&lt;/code&gt; provides the index key while &lt;code&gt;primaryKey&lt;/code&gt; provides the primary key.&lt;/p&gt;
&lt;h2&gt;Demo app&lt;/h2&gt;
&lt;p&gt;To see a more complete code example, check out &lt;a href=&quot;https://github.com/MicrosoftEdge/Demos/tree/main/idb-getallrecords&quot;&gt;this demo I made&lt;/a&gt;. You can also &lt;a href=&quot;https://microsoftedge.github.io/Demos/idb-getallrecords/&quot;&gt;run the demo live&lt;/a&gt;, if you enable the feature first (see the next section).&lt;/p&gt;
&lt;p&gt;In my quick local tests (which I did by simulating a 6x CPU slowdown from DevTools), I got the following results:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;th&gt;Read with &lt;code&gt;getAll&lt;/code&gt; and &lt;code&gt;getAllKeys&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Read with &lt;code&gt;getAllRecords&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;2384ms&lt;/td&gt;
&lt;td&gt;1294ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;2896ms&lt;/td&gt;
&lt;td&gt;1678ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;3786ms&lt;/td&gt;
&lt;td&gt;1701ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 4&lt;/td&gt;
&lt;td&gt;1510ms&lt;/td&gt;
&lt;td&gt;3110ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 5&lt;/td&gt;
&lt;td&gt;2294ms&lt;/td&gt;
&lt;td&gt;2196ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 6&lt;/td&gt;
&lt;td&gt;1559ms&lt;/td&gt;
&lt;td&gt;1454ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 7&lt;/td&gt;
&lt;td&gt;3879ms&lt;/td&gt;
&lt;td&gt;1013ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 8&lt;/td&gt;
&lt;td&gt;2369ms&lt;/td&gt;
&lt;td&gt;1293ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2584ms&lt;/strong&gt; ❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1717ms&lt;/strong&gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;th&gt;Reverse read with a cursor&lt;/th&gt;
&lt;th&gt;Reverse read with &lt;code&gt;getAllRecords&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;4443ms&lt;/td&gt;
&lt;td&gt;1412ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;2764ms&lt;/td&gt;
&lt;td&gt;1474ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;6561ms&lt;/td&gt;
&lt;td&gt;1229ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 4&lt;/td&gt;
&lt;td&gt;3898ms&lt;/td&gt;
&lt;td&gt;2552ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 5&lt;/td&gt;
&lt;td&gt;5673ms&lt;/td&gt;
&lt;td&gt;1212ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 6&lt;/td&gt;
&lt;td&gt;6490ms&lt;/td&gt;
&lt;td&gt;1238ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 7&lt;/td&gt;
&lt;td&gt;4405ms&lt;/td&gt;
&lt;td&gt;2047ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 8&lt;/td&gt;
&lt;td&gt;6617ms&lt;/td&gt;
&lt;td&gt;1683ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5106ms&lt;/strong&gt; ❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1605ms&lt;/strong&gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Enable the feature for testing&lt;/h2&gt;
&lt;p&gt;A prototype of the &lt;code&gt;getAllRecords&lt;/code&gt; method is now available in Chromium, which means you can try it out for yourself in Chrome Canary or Edge Canary (to make sure you have the version that has the feature).&lt;/p&gt;
&lt;p&gt;To enable the feature, start the browser from the command line with the following additional parameter: &lt;code&gt;--enable-blink-features=IndexedDbGetAllRecords&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, on Windows, you might run the following command to start Chrome Canary with the feature enabled: &lt;code&gt;&amp;quot;%localappdata%&#92;Google&#92;Chrome SxS&#92;Application&#92;chrome.exe&amp;quot; --enable-blink-features=IndexedDbGetAllRecords&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I don&#39;t have an actual production use case to test, I highly encourage you to try the feature out yourself, in your own app, and see what the performance gains are for you.&lt;/p&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;If this is something that you could benefit from, please try it out and consider providing feedback to the Microsoft Edge team. The more feedback we get, the better chances the proposal has of matching your needs, and of being implemented in all browsers. To provide feedback:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read the proposal&lt;/strong&gt;: &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/IndexedDbGetAllEntries/explainer.md&quot;&gt;IndexedDB: getAllRecords&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leave feedback&lt;/strong&gt;: &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/&quot;&gt;Review existing issues&lt;/a&gt; or &lt;a href=&quot;https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new/choose&quot;&gt;open a new issue&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you, &lt;a href=&quot;https://github.com/SteveBeckerMSFT&quot;&gt;Steve Becker&lt;/a&gt;, for your help reviewing this article.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  
  <entry>
    <title>4 JS array methods that don&#39;t mutate the original array</title>
    <link href="https://patrickbrosset.com/articles/2024-12-16-4-js-array-methods-that-dont-mutate-the-original-array/"/>
    <updated>2024-12-16T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-12-16-4-js-array-methods-that-dont-mutate-the-original-array/</id>
    <content type="html">
      
        &lt;p&gt;Two and a half years ago, I wrote a short article titled &lt;a href=&quot;https://patrickbrosset.com/articles/2022-05-12-4-javascript-array-methods-you-need/&quot;&gt;4 JavaScript Array methods you didn&#39;t know you needed&lt;/a&gt;, which describes how useful the &lt;code&gt;Array.at&lt;/code&gt;, &lt;code&gt;Array.flat&lt;/code&gt;, &lt;code&gt;Array.some&lt;/code&gt;, &lt;code&gt;Array.every&lt;/code&gt; methods are. Today, while looking at the &lt;a href=&quot;https://2024.stateofjs.com/&quot;&gt;results to the State of JS 2024 survey&lt;/a&gt;, I realized there were a few more methods worth highlighting.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://2024.stateofjs.com/en-US/features/#array_features&quot;&gt;Array features question of the survey&lt;/a&gt;, participants were asked to rank a few other Array methods. That question made me discover a few methods I didn&#39;t know about, and when I looked at the comments that participants left on that question, it was clear that many other people didn&#39;t know about these methods either. So, let&#39;s review them now:  &lt;code&gt;Array.toSorted()&lt;/code&gt;, &lt;code&gt;Array.toReversed()&lt;/code&gt;, &lt;code&gt;Array.toSpliced()&lt;/code&gt;, and &lt;code&gt;Array.with()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At the time of writing, all of these methods are &lt;strong&gt;Baseline newly available&lt;/strong&gt;, which means they&#39;re now supported in all of the major browser engines. They haven&#39;t been supported for a long time yet, but they&#39;re here now.&lt;/p&gt;
&lt;h2&gt;Sort an array and return a copy with Array.toSorted()&lt;/h2&gt;
&lt;p&gt;You have probably already used, or at least heard of, the &lt;code&gt;Array.sort()&lt;/code&gt; method in the past. It sorts the elements of an array, &lt;em&gt;in place&lt;/em&gt;, by using a comparison function. While it&#39;s very useful, sometimes you don&#39;t want to change the original array. That&#39;s where &lt;code&gt;Array.toSorted()&lt;/code&gt; comes in:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Let&#39;s sort the array with `Array.toSorted()`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sortedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toSorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The method returns a new array with the sorted elements.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sortedArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [2, 4, 5, 10]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The original array remains unchanged.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; sortedArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more, see &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted&quot;&gt;Array.prototype.toSorted()&lt;/a&gt; on MDN.&lt;/p&gt;
&lt;h2&gt;Reverse an array and return a copy with Array.toReversed()&lt;/h2&gt;
&lt;p&gt;Similarly, you&#39;ve probably already used the &lt;code&gt;Array.reverse()&lt;/code&gt; method to reverse the order of an array&#39;s items. That method does the reversing of the items &lt;em&gt;in place&lt;/em&gt;, which means the original array is modified. If you don&#39;t want that to happen, you can now use the &lt;code&gt;Array.toReversed()&lt;/code&gt; method instead:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Let&#39;s reverse the array with `Array.toReversed()`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reversedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toReversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The method returns a new array with the reversed elements.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reversedArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [1, 2, 3]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The original array remains unchanged.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; reversedArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more, see &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed&quot;&gt;Array.prototype.toReversed()&lt;/a&gt; on MDN.&lt;/p&gt;
&lt;h2&gt;Remove or replace elements in an array and return a copy with Array.toSpliced()&lt;/h2&gt;
&lt;p&gt;Let&#39;s continue in the same vein. &lt;code&gt;Array.splice()&lt;/code&gt;, while I find its syntax hard to remember, is a very useful method to add, replace, or remove items from an array. But, again, it does so &lt;em&gt;in place&lt;/em&gt;, by changing the original array. What if you want to do this in a new copy of the array instead? Well, you&#39;ve guessed it, you can use &lt;code&gt;Array.toSpliced()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Let&#39;s remove the second and third items from the array with `Array.toSpliced()`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; splicedArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toSpliced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The method returns the result of the splice operation, as a new array.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;splicedArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [1, 2, 3, 4]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The original array remains unchanged.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; splicedArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more, see &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced&quot;&gt;Array.prototype.toSpliced()&lt;/a&gt; on MDN.&lt;/p&gt;
&lt;h2&gt;Create a new array with Array.with()&lt;/h2&gt;
&lt;p&gt;In JavaScript, you can easily change one item of an array by using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Property_accessors#bracket_notation&quot;&gt;bracket notation&lt;/a&gt;. For example, changing the second item of an array to &lt;code&gt;42&lt;/code&gt; can be done like this: &lt;code&gt;myArray[1] = 42&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Array.with()&lt;/code&gt; method lets you do the same thing exactly, but instead of changing the original array, it returns a new array with the change applied:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Let&#39;s change the second item of the array to `42` with `Array.with()`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The method returns a new array with the change applied.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [1, 42, 3]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The original array remains unchanged.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; newArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more, see &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/with&quot;&gt;Array.prototype.with()&lt;/a&gt; on MDN.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>My very short and incomplete analysis of the State of JS 2024 survey results</title>
    <link href="https://patrickbrosset.com/articles/2024-12-16-my-very-short-and-incomplete-analysis-of-the-state-of-js-2024-survey-results/"/>
    <updated>2024-12-16T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-12-16-my-very-short-and-incomplete-analysis-of-the-state-of-js-2024-survey-results/</id>
    <content type="html">
      
        &lt;p&gt;Here are my quick highlights from &lt;a href=&quot;https://2024.stateofjs.com/&quot;&gt;the State of JS 2024 survey results&lt;/a&gt;. There&#39;s much more data to be analyzed in the survey, but here are the things that stood out to me, from the perspective of someone working on a web browser engine:&lt;/p&gt;
&lt;h2&gt;There are many new features of the language that people don&#39;t know about, but find useful&lt;/h2&gt;
&lt;p&gt;For example, there are many new Array, String, or Set methods that could make developers&#39; lives easier, and might even make it possible to drop a library dependency. However, respondents said they didn&#39;t necessarily knew about these features, highlighting a lack of clear communication or documentation. And, when they did, they said they couldn&#39;t always use the new features because of browser support issues.&lt;/p&gt;
&lt;h2&gt;The lack of types in JavaScript is still a major pain point&lt;/h2&gt;
&lt;p&gt;When asked about pain points of the language, 32% or respondents mentioned the lack of static typing. This is confirmed by the fact that more than 80% of the respondents write at least half of their code in TypeScript, and a whopping 34% write all of their code in TypeScript. This ratio is on the rise too (in 2022, only 54% of the respondents wrote at least half of their code in TypeScript).&lt;/p&gt;
&lt;h2&gt;When using JavaScript in the browser, inconsistent browser support is still the main pain point&lt;/h2&gt;
&lt;p&gt;Nothing new under the sun. Survey after survey, developers still complain about inconsistencies between browsers. While the majority of the feedback is high-level, there are some comments that point to specific issues, such as: the lack of PWA support, the different module formats and runtimes, Safari being too far behind, and some specific features like File System.&lt;/p&gt;
&lt;h2&gt;Temporal cannot come soon enough&lt;/h2&gt;
&lt;p&gt;Temporal is the most anticipated new JavaScript proposal. When asked to rank some of the new language proposals, Temporal came out first, with 74% of the votes. The second, decorators, being far behind with 38%. This is also confirmed by the free-form responses to the language pain point question, where date management was mentioned in third place, after static typing and browser support.&lt;/p&gt;
&lt;h2&gt;Better documentation of browser APIs, based on real-world usage examples, are missing&lt;/h2&gt;
&lt;p&gt;This came out in the browser API features section of the survey. The lack of good documentation came out as the third most mentioned pain point. In this domain, MDN is the number one source of reliable reference documentation, but it seems like it&#39;s not enough and people are looking for longer guides, tutorials, and real-world usage examples.&lt;/p&gt;
&lt;p&gt;Unfortunately, there aren&#39;t a lot of comments that mention specific features for which documentation is lacking, but the following points came up: there are too many new APIs to document, when documentation exists, the examples are too contrived, better guides for IndexedDB or Service Workers are needed.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Évolution rapide et lente : le paradoxe de la plateforme Web</title>
    <link href="https://patrickbrosset.com/articles/2024-12-17-evolution-rapide-et-lente--le-paradoxe-de-la-plateforme-web/"/>
    <updated>2024-12-17T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2024-12-17-evolution-rapide-et-lente--le-paradoxe-de-la-plateforme-web/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.24joursdeweb.fr/2024/evolution-rapide-et-lente-le-paradoxe-de-la-plateforme-web">https://www.24joursdeweb.fr/2024/evolution-rapide-et-lente-le-paradoxe-de-la-plateforme-web</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Request for feedback: Incoming call notifications API for web apps </title>
    <link href="https://patrickbrosset.com/articles/2025-02-04-request-for-feedback-incoming-call-notifications-api-for-web-apps/"/>
    <updated>2025-02-04T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-02-04-request-for-feedback-incoming-call-notifications-api-for-web-apps/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/02/04/request-for-feedback-incoming-call-notifications-api/">https://blogs.windows.com/msedgedev/2025/02/04/request-for-feedback-incoming-call-notifications-api/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Better text rendering in Chromium-based browsers on Windows</title>
    <link href="https://patrickbrosset.com/articles/2025-02-12-better-text-rendering-in-chromium-based-browsers-on-windows/"/>
    <updated>2025-02-12T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-02-12-better-text-rendering-in-chromium-based-browsers-on-windows/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/blog/better-text-rendering-in-chromium-based-browsers-on-windows">https://developer.chrome.com/blog/better-text-rendering-in-chromium-based-browsers-on-windows</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Constructing ClipboardItems from strings and Promises</title>
    <link href="https://patrickbrosset.com/articles/2025-02-12-constructing-clipboarditems-from-strings-and-promises/"/>
    <updated>2025-02-12T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-02-12-constructing-clipboarditems-from-strings-and-promises/</id>
    <content type="html">
      
        &lt;p&gt;If you need to work with the system clipboard in your web app, either to read from or write to it, or both, then the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Clipboard_API&quot;&gt;Async Clipboard API&lt;/a&gt; is your friend. It makes it easy to read or write to the system clipboard, by using modern promise-based methods, and respecting the user&#39;s permissions.&lt;/p&gt;
&lt;p&gt;One thing just changed in Chromium-based browsers that&#39;s worth calling out. Up until this point, browsers such as Chrome or Edge only allowed &lt;code&gt;Blob&lt;/code&gt; objects to be written to the clipboard. This is how you would typically write text into the clipboard:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; blob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClipboardItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; blob &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clipboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Copied to clipboard&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even for simple plain text scenarios, you must first create a &lt;code&gt;Blob&lt;/code&gt; object. That&#39;s not an issue when dealing with other type of data, such as when copying an image or a file. But for plain text, it&#39;s a bit of a hassle.&lt;/p&gt;
&lt;h2&gt;Creating ClipboardItems from strings&lt;/h2&gt;
&lt;p&gt;Now, starting with Chrome 133 and Edge 133, you can simplify this code a little bit and just pass a string:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClipboardItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello string!&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clipboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Copied to clipboard&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might seem like a small change, but it&#39;s a nice quality of life improvement, and it brings Chromium up to par with other browsers, therefore improving interoperability.&lt;/p&gt;
&lt;h2&gt;Creating ClipboardItems from promises&lt;/h2&gt;
&lt;p&gt;But that&#39;s not all, the spec also allows for Promises to be passed to the &lt;code&gt;ClipboardItem&lt;/code&gt; constructor, and this now also works in Chromium:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchSomeText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Imagine this method asynchronously returns some text content,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// perhaps from a server, using fetch().&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// For the sake of this sample code, let&#39;s just return the text now.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello async!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClipboardItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchSomeText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clipboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Copied to clipboard&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might be very useful in cases where you need to fetch some content from a server first. The Promise you pass when constructing the &lt;code&gt;ClipboardItem&lt;/code&gt; can resolve to a string or a &lt;code&gt;Blob&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That&#39;s it for today, happy clipboarding!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Microsoft Edge and Interop 2025</title>
    <link href="https://patrickbrosset.com/articles/2025-02-13-microsoft-edge-and-interop-2025/"/>
    <updated>2025-02-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-02-13-microsoft-edge-and-interop-2025/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/02/13/microsoft-edge-and-interop-2025/">https://blogs.windows.com/msedgedev/2025/02/13/microsoft-edge-and-interop-2025/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>First catalog of web features completed by the WebDX Community Group</title>
    <link href="https://patrickbrosset.com/articles/2025-03-13-first-catalog-of-web-features-completed-by-the-webdx-community-group/"/>
    <updated>2025-03-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-03-13-first-catalog-of-web-features-completed-by-the-webdx-community-group/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.w3.org/blog/2025/first-catalog-of-web-features-completed-by-the-webdx-community-group/">https://www.w3.org/blog/2025/first-catalog-of-web-features-completed-by-the-webdx-community-group/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Minding the gaps: A new way to draw separators in CSS </title>
    <link href="https://patrickbrosset.com/articles/2025-03-19-minding-the-gaps-a-new-way-to-draw-separators-in-css/"/>
    <updated>2025-03-19T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-03-19-minding-the-gaps-a-new-way-to-draw-separators-in-css/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/03/19/minding-the-gaps-a-new-way-to-draw-separators-in-css/">https://blogs.windows.com/msedgedev/2025/03/19/minding-the-gaps-a-new-way-to-draw-separators-in-css/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Contextual logging with console.context()</title>
    <link href="https://patrickbrosset.com/articles/2025-04-22-contextual-logging-with-console.context/"/>
    <updated>2025-04-22T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-04-22-contextual-logging-with-console.context/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/04/22/contextual-logging-with-console-context/">https://blogs.windows.com/msedgedev/2025/04/22/contextual-logging-with-console-context/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Creating a more accessible web with Aria Notify</title>
    <link href="https://patrickbrosset.com/articles/2025-05-05-creating-a-more-accessible-web-with-aria-notify/"/>
    <updated>2025-05-05T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-05-05-creating-a-more-accessible-web-with-aria-notify/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/05/05/creating-a-more-accessible-web-with-aria-notify/">https://blogs.windows.com/msedgedev/2025/05/05/creating-a-more-accessible-web-with-aria-notify/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>A new way to style gaps in CSS</title>
    <link href="https://patrickbrosset.com/articles/2025-06-11-a-new-way-to-style-gaps-in-css/"/>
    <updated>2025-06-11T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-06-11-a-new-way-to-style-gaps-in-css/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/blog/gap-decorations">https://developer.chrome.com/blog/gap-decorations</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>How to convince your boss to sponsor Open Web Docs</title>
    <link href="https://patrickbrosset.com/articles/2025-06-18-how-to-convince-your-boss-to-sponsor-open-web-docs/"/>
    <updated>2025-06-18T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-06-18-how-to-convince-your-boss-to-sponsor-open-web-docs/</id>
    <content type="html">
      
        &lt;p&gt;Does your organization rely on &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt;?&lt;br /&gt;
Do you often need to check compatibility tables on MDN or &lt;a href=&quot;https://caniuse.com/&quot;&gt;caniuse.com&lt;/a&gt;?&lt;br /&gt;
Do you use developer tools like browser DevTools and VSCode in your daily work?&lt;/p&gt;
&lt;p&gt;If so, you rely on the work that &lt;a href=&quot;https://openwebdocs.org/&quot;&gt;Open Web Docs&lt;/a&gt; does very directly, and should consider sponsoring them. Here is how to convince your boss to sponsor OWD!&lt;/p&gt;
&lt;h2&gt;Is this for me?&lt;/h2&gt;
&lt;p&gt;Any organization which finds itself in either of the below cases has a highly strategic interest in the health of the web platform, and therefore in the availability of its developer documentation and compatibility data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Organizations which depend on the web platform to build their products. This is basically most organizations today, whether they&#39;re building websites, web apps, native apps that use web technologies, or even server-side applications that use web runtimes.&lt;/li&gt;
&lt;li&gt;Or organizations which directly participate in advancing the web platform (such as browser vendors).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What&#39;s MDN and OWD?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt; is the de facto center of gravity for web platform docs and compatibility data. It is critical infrastructure for the developer community on the web, and there is no comparable equivalent. This resource is vendor-agnostic, up to date, and high-quality.&lt;/p&gt;
&lt;p&gt;While MDN, the website itself, is owned and maintained by Mozilla, the content and data that appears on it is the result of multiple actors. One of &lt;a href=&quot;https://openwebdocs.org/content/reports/2024/&quot;&gt;the most prominent&lt;/a&gt; of which is &lt;a href=&quot;https://openwebdocs.org/&quot;&gt;Open Web Docs (OWD)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;OWD protects the health of MDN&#39;s content and data, while preserving the core value of vendor-agnosticism and insulating the community from potential risks that could be introduced by Mozilla&#39;s shifts of strategy with their website.&lt;/p&gt;
&lt;h2&gt;There&#39;s more to OWD than MDN&lt;/h2&gt;
&lt;p&gt;Furthermore, the content and data which OWD creates reaches far beyond the MDN Web Docs website:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compatiblity data is used to power caniuse.com, which is another critical resource for web developers.&lt;/li&gt;
&lt;li&gt;Content and data is integrated in more and more developer tooling solutions, such as &lt;a href=&quot;https://web.dev/blog/baseline-vscode&quot;&gt;VSCode&lt;/a&gt;, WebStorm, linters, build tools, and browser DevTools.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features/&quot;&gt;Baseline&lt;/a&gt;, which has grown to be a critical resource for web developers, is also based on MDN&#39;s compatibility data.&lt;/li&gt;
&lt;li&gt;AI agents, which developers rely more and more for coding, are also trained on MDN&#39;s documentation, and MCP servers that fetch content, compat data, and Baseline information are used to provide up-to-date information to these agents.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OWD not only writes docs and keeps data up to date, it also runs and maintains the infrastructure that ensures that it remains up to date.&lt;/p&gt;
&lt;h2&gt;What&#39;s in it for our organization?&lt;/h2&gt;
&lt;p&gt;Sponsoring OWD gets you the following benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It ensures that the content and data on MDN, and in many other places, continues to be high-quality and up to date.&lt;/li&gt;
&lt;li&gt;It can get your organization a seat on the OWD &lt;a href=&quot;https://openwebdocs.org/team/&quot;&gt;steering or governing committee&lt;/a&gt;, which allows you to partner directly with OWD on the next challenges for web documentation.&lt;/li&gt;
&lt;li&gt;It publicly demonstrates your commitment to the health of the web and the developer community.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Playing devil&#39;s advocate: what would happen if OWD were not to be sponsored?&lt;/h2&gt;
&lt;p&gt;Not sponsoring OWD comes with significant long-term risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MDN content and data slowly becoming outdated, lower quality, and not representative of the web platform as a whole. This would have a significant impact on developers productivity, and their ability to build on the web platform.&lt;/li&gt;
&lt;li&gt;Out of date compatibility data would break production websites, tools, and ultimately developer trusts in the web platform.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;But wait, couldn&#39;t we just write our own docs, or contribute to MDN directly?&lt;/h2&gt;
&lt;p&gt;Alternatives, such as writing your own technical documentation, or contributing to MDN directly, aren&#39;t as effective:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Documentation platforms other than MDN have nowhere near the same reach or credibility and have higher ongoing maintenance costs.&lt;/li&gt;
&lt;li&gt;Contributing to MDN directly is of course very much encouraged, but getting your engineers or PMs to write docs, or hiring your own writers with limited experience will have substantially worse return on investment than working with professional writers who already have extensive industry experience and deep connections with browser vendors and standards bodies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How do I become a sponsor?&lt;/h2&gt;
&lt;p&gt;One simple way is to donate money to ensure that OWD is able to continue its work. There are two ways to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On &lt;a href=&quot;https://opencollective.com/open-web-docs/&quot;&gt;OWD&#39;s Open Collective page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;On &lt;a href=&quot;https://github.com/sponsors/openwebdocs&quot;&gt;OWD&#39;s GitHub Sponsors page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But if your organization relies on the work OWD does in a longer term strategic way, you should seriously consider becoming a Silver, Gold, or Platinum sponsor. To learn all about it, check out the &lt;a href=&quot;https://openwebdocs.org/membership/&quot;&gt;Membership page&lt;/a&gt;.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>The Edge 2025 web platform top developer needs dashboard</title>
    <link href="https://patrickbrosset.com/articles/2025-06-26-the-edge-2025-web-platform-top-developer-needs-dashboard/"/>
    <updated>2025-06-26T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-06-26-the-edge-2025-web-platform-top-developer-needs-dashboard/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/06/26/the-edge-2025-web-platform-top-developer-needs-dashboard/">https://blogs.windows.com/msedgedev/2025/06/26/the-edge-2025-web-platform-top-developer-needs-dashboard/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>The Gap Strikes Back: Now Stylable</title>
    <link href="https://patrickbrosset.com/articles/2025-07-01-the-gap-strikes-back-now-stylable/"/>
    <updated>2025-07-01T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-07-01-the-gap-strikes-back-now-stylable/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://css-tricks.com/the-gap-strikes-back-now-stylable/">https://css-tricks.com/the-gap-strikes-back-now-stylable/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Brick by brick: Help us build CSS Masonry</title>
    <link href="https://patrickbrosset.com/articles/2025-07-23-brick-by-brick-help-us-build-css-masonry/"/>
    <updated>2025-07-23T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-07-23-brick-by-brick-help-us-build-css-masonry/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/blog/masonry-update">https://developer.chrome.com/blog/masonry-update</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>AI agents and the web - A proposal to keep developers in the loop</title>
    <link href="https://patrickbrosset.com/articles/2025-08-28-ai-agents-and-the-web-a-proposal-to-keep-developers-in-the-loop/"/>
    <updated>2025-08-28T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-08-28-ai-agents-and-the-web-a-proposal-to-keep-developers-in-the-loop/</id>
    <content type="html">
      
        &lt;p&gt;In this post, I&#39;ll introduce &lt;a href=&quot;https://github.com/webmachinelearning/webmcp&quot;&gt;WebMCP&lt;/a&gt;, a proposal to let you, web developers, control how AI agents interact with your web pages. If this proposal is interesting to you, please let me know. &lt;a href=&quot;https://patrickbrosset.com/articles/2025-08-28-ai-agents-and-the-web-a-proposal-to-keep-developers-in-the-loop/#feedback&quot;&gt;I&#39;m looking for early feedback&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;AI agents and the web&lt;/h2&gt;
&lt;p&gt;AI agents are sort of taking over the way we use computers. The way we do things on computers, whether it&#39;s doing tasks, or searching for information in documents or on the web, is changing. The promise here is that you should be able to just say what you want to do in plain language, and let an AI agent take it from there. I&#39;m personally not there yet, but things certainly seem to be headed in that direction.&lt;/p&gt;
&lt;p&gt;Part of this promise requires that AI agents be able to navigate to web pages, read content, and sometimes perform actions on them. It&#39;s not enough for the agent to respond to a question based on its own training data, it now also needs to interact with live content and do things on the user&#39;s behalf. And some of these things might need to happen on web pages.&lt;/p&gt;
&lt;p&gt;Sometimes, that means the AI agent accesses the website&#39;s backend, via some kind of remote service. But other times, the AI agent needs to actually use the browser like a user would, navigating and scrolling around, getting content, and performing actions. This might be by extracting text and meaning from the DOM tree, or the accessibility tree, or by processing screenshots.&lt;/p&gt;
&lt;p&gt;When it comes to agents performing actions on the web though, such as filling and submitting forms, things tend to get a little more complicated. Would the AI agent find the right fields and buttons by looking at the DOM? What if the developer used &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s instead of &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;s? Would the AI agent better understand what to do by looking at screenshots instead? Not sure.&lt;/p&gt;
&lt;p&gt;More importantly, should you, the developer of the page, have a say in how the AI agent is using that page?&lt;/p&gt;
&lt;p&gt;The Edge and Google teams believe so, and have been working together on a proposal called &lt;a href=&quot;https://github.com/webmachinelearning/webmcp&quot;&gt;WebMCP&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;WebMCP&lt;/h2&gt;
&lt;p&gt;At its heart, WebMCP lets you list the actions (called &amp;quot;tools&amp;quot;) that an AI agent can perform on a page, as JavaScript functions registered via a browser API.&lt;/p&gt;
&lt;p&gt;For example, if your web app is a media player, you could register actions such as previous, next, play, pause, or search with WebMCP, to let AI agents use these actions via the browser. This way, not only would a real person continue to be able to use your media player, by interacting with the UI as normal, but an AI agent would also be able to detect the actions that are available, and invoke them, by calling into your webapp&#39;s code directly.&lt;/p&gt;
&lt;p&gt;This would not only make it easier for the agent to know what to do, and how to do it, with fewer chances for mistakes than by using screenshots or the DOM, but it also puts you in the loop. You, as the web developer of the page, choose which actions you want to expose, what they&#39;re called, how they&#39;re described, what they do, and how they do it. It also lets you register different actions at different times, to reflect state changes on a web page for example.&lt;/p&gt;
&lt;p&gt;Note that WebMCP doesn&#39;t prevent AI agents from using other means of analyzing or interacting with your page.&lt;/p&gt;
&lt;p&gt;The MCP part in WebMCP stands for Model Context Protocol, which is a protocol that&#39;s now commonly used by AI agents to perform actions on behalf of users. There are many MCP servers nowadays, to do all sorts of things. The idea here, is to make the browser be an MCP server too, and expose actions that are available on the current web page, and registered via WebMCP.
This way, the AI agent which the user is using can query the browser&#39;s MCP server for the actions that are available on the current page, and invoke them as needed, to fulfill the user&#39;s request.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Let&#39;s take the example of a TODO list app, which lets users create, edit, complete, and delete tasks in a list.&lt;/p&gt;
&lt;p&gt;Without WebMCP, an AI agent would have to understand the meaning of the webpage and which fields to use by looking at the DOM or a screenshot of the page.&lt;/p&gt;
&lt;p&gt;With WebMCP, the AI agent is able to use the MCP protocol by connecting to the browser-provided MCP server, get a list of the available actions (or &amp;quot;tools&amp;quot;, in MCP jargon) exposed by the web page, and then use any of these tools to complete the user request.&lt;/p&gt;
&lt;p&gt;Here&#39;s what a &lt;code&gt;add-todo&lt;/code&gt; WebMCP tool would look like:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;provideContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Metadata about the tool, for the AI agent.&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;add-todo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Add a new todo item to the list&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;inputSchema&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The text of the todo item&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Implementation of the tool.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; text &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Code to add the todo item and update the UI.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* structured content response */&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
&lt;p&gt;Folks on the Edge team have started implementing a prototype, so we can get a feel for how it would really work in practice.&lt;/p&gt;
&lt;p&gt;In the following video, you can see me using VSCode and Edge side by side. I&#39;ve opened a personal project of mine in VSCode, and I&#39;m using the Copilot Chat sidebar, in agent mode. In the video, I&#39;m demonstrating how I might use this agent mode to do things on my project, like find colors, or image URLs, and so on, but also how I might communicate with the browser&#39;s MCP server to make it do things on the web.&lt;/p&gt;
&lt;p&gt;That&#39;s where the Edge window, next to VSCode in the video, comes into play. In it, I&#39;ve loaded my TODO list app. The agent in VSCode connects to the MCP server in Edge, which exposes the actions that the TODO list app registered. So, I can just ask my agent in VSCode to add the TODO items I need.&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=gbu9kyY2B60&lt;/p&gt;
&lt;div id=&quot;feedback&quot;&gt;&lt;/div&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;As I mentioned before, this is an early proposal for now. There&#39;s nothing more important in the life of an early proposal than feedback from people who might need to use this when it exists.&lt;/p&gt;
&lt;p&gt;So, if you&#39;re excited about this (in fact, even if you have an alergic reaction to it), or if you have different ideas for how to do something like this, then let me know on &lt;a href=&quot;https://mas.to/@patrickbrosset&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/patrickbrosset.com&quot;&gt;Bluesky&lt;/a&gt;, or &lt;a href=&quot;https://www.linkedin.com/in/patrickbrosset/&quot;&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Or, &lt;a href=&quot;https://github.com/webmachinelearning/webmcp/issues&quot;&gt;open an issue on the proposal&#39;s repository directly&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Any little bit of feedback can help.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Calling for Interop 2026 proposals </title>
    <link href="https://patrickbrosset.com/articles/2025-09-04-calling-for-interop-2026-proposals/"/>
    <updated>2025-09-04T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-09-04-calling-for-interop-2026-proposals/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/02/13/microsoft-edge-and-interop-2025/">https://blogs.windows.com/msedgedev/2025/02/13/microsoft-edge-and-interop-2025/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Demanding more from our AI coding tools</title>
    <link href="https://patrickbrosset.com/articles/2025-09-16-demanding-more-from-our-ai-coding-tools/"/>
    <updated>2025-09-16T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-09-16-demanding-more-from-our-ai-coding-tools/</id>
    <content type="html">
      
        &lt;p&gt;AI can often be pretty shitty, and this technology comes with very real risks for the world. But it seems like it&#39;s here to stay, and it also seems like developers really like using it as a tool for coding (see the &lt;a href=&quot;https://2024.stateofjs.com/en-US/other-tools/#ai_tools&quot;&gt;State of JS 2024&#39;s AI tools question&lt;/a&gt; and the &lt;a href=&quot;https://2025.stateofai.dev/en-US/opinions/&quot;&gt;State of AI 2025&#39;s opinions question&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;If we&#39;re to continue using AI as a coding tool, we absolutely should do this in a responsible manner, and we should demand more of this tool. We should find ways for it to start generating code that actually makes sense, adheres to best practices, is current, leads to accessible experiences, and even more.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;It&#39;s fair to say that Large Language Models (LLMs) often get stuff wrong. LLMs are very good at writing text which feels like it was written by an actual person, at least at first sight. But they can also be really bad at saying correct and up to date things. To be fair, they&#39;re trained on the entire web content. How often do you encounter correct and up to date content on the web?&lt;/p&gt;
&lt;p&gt;We need to bring the human back into the loop. Letting LLMs do all the work based on a prompt isn&#39;t going to cut it. We can do better. We shouldn&#39;t let a model do everything for us, especially when that model has been trained on millions of questionable quality, outdated, and contradictory content.&lt;/p&gt;
&lt;p&gt;Not all content on the web is bad though. There are a few corners of the web where good, dare I say great, content still exists. &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN Web Docs&lt;/a&gt; is one of these places. If you do any kind of web front-end work (which, if you&#39;re reading this site, you probably are), you most likely read MDN multiple times a day. It&#39;s one of the most valuable sets of docs for web developers out there. MDN content is manually written and reviewed, endlessly updated and corrected by passionate technical writers and &lt;a href=&quot;https://developer.mozilla.org/community&quot;&gt;a big community of contributors&lt;/a&gt;.
There are many other high-quality developer resources on the web too, including blogs, newsletters, Q&amp;amp;As, tips and tricks, release notes, and more.&lt;/p&gt;
&lt;p&gt;Sure, language models are trained on all this content, so you could argue that they already know the correct answers to web development-related questions. But LLMs are also trained on millions of other web development-related resources which freshness, quality, and relevance are sometimes questionable. Plus, that training is always outdated. Training models is very expensive, so doesn&#39;t happen very frequently.&lt;/p&gt;
&lt;h2&gt;Being responsible developers&lt;/h2&gt;
&lt;p&gt;As developers, the very least we can do is be mindful of the above problems, and question the output of our AI tools.&lt;/p&gt;
&lt;p&gt;Vibe coding our ways through every problem we face isn&#39;t a very responsible thing to do. If we&#39;re going to use these tools, we should at least verify and correct what they generate.&lt;/p&gt;
&lt;p&gt;To illustrate this, I used VS Code&#39;s Copilot in agent mode, and asked it to generate an TODO list web app. Maybe not a great example, knowing how often TODO lists are used in tutorials and docs (see &lt;a href=&quot;https://todomvc.com/&quot;&gt;TodoMVC&lt;/a&gt;), but I think it still illustrates my point.&lt;/p&gt;
&lt;p&gt;The result wasn&#39;t too bad at first sight. The app worked. You can see a screenshot of the app here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/ai-todo-list.png&quot; alt=&quot;The TODO list app. The header says TODO List. There&#39;s an input and button to add new tasks. And then three tasks below that.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But looking at it with a more critical eye revealed a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Color values were hard-coded throughout the file.&lt;/li&gt;
&lt;li&gt;Physical properties, such as &lt;code&gt;border-bottom&lt;/code&gt;, were used instead of the corresponding logical properties, such as &lt;code&gt;border-block-end&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A mix of &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;px&lt;/code&gt; values were used for margins and paddings.&lt;/li&gt;
&lt;li&gt;No dark theme was created.&lt;/li&gt;
&lt;li&gt;The layout didn&#39;t really use the extra space when available, and also didn&#39;t particularly work great on a very narrow screen.&lt;/li&gt;
&lt;li&gt;Marking a task as done wasn&#39;t accessible. It used a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; element and an &lt;code&gt;onclick&lt;/code&gt; event listener, which weren&#39;t exposed to assistive technologies in any way.&lt;/li&gt;
&lt;li&gt;The delete task button&#39;s hover state didn&#39;t have a high enough contrast ratio.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep in mind, this was for a very simple example, and I only reviewed the code for a few minutes. Things can easily get worse in more involved or sensitive situations.&lt;/p&gt;
&lt;p&gt;AI coding tools aren&#39;t silver bullets. We still need to be responsible developers and ensure the experiences we deliver are good, accessible, run fast, are &lt;a href=&quot;https://www.youtube.com/watch?v=24VZT855OhI&quot;&gt;secure&lt;/a&gt;, private, that the code adheres to our style guide and best practices, is maintainable and so on. So, like, actually do the work.&lt;/p&gt;
&lt;h2&gt;Demanding more&lt;/h2&gt;
&lt;p&gt;We should also demand more from our AI coding tools. An AI tool that spits out whatever the underlying LLM generated only based on a question isn&#39;t a great AI tool. We can do better than this.&lt;/p&gt;
&lt;p&gt;We need to start seeing LLMs for what they are: machines that are good at generating text based on some context. So let&#39;s use them for that and provide the correct, comprehensive, and up to date context.&lt;/p&gt;
&lt;p&gt;In my previous TODO list example, perhaps the problems I found could have been avoided if the AI tool knew about the importance of color contrast, coherent units, accessible user actions, or newer CSS properties.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt; might be useful here. Perhaps MCP can be used as an intermediate layer that&#39;s responsible for checking MDN, and other trusted sources, for best practices.&lt;/p&gt;
&lt;p&gt;Various experiments along those lines already exist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mcp.so/server/mdn-lookup&quot;&gt;MDN Lookup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://glama.ai/mcp/servers/integrations/mdn-web-docs&quot;&gt;MCP Servers for MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These MCP servers fetch live MDN docs based on a user&#39;s query and then provide either the response to the LLM for it to summarize or extract whatever the user wanted. This is a great start, but we need to go further than this.&lt;/p&gt;
&lt;p&gt;The true value lies in deeply understanding the user intent, and in finding the right information from the right places. This information might be spread across multiple pages. The key here is that it&#39;s no enough to fetch an API reference page from MDN and return it. The key is to understand the problem you&#39;re facing, and fetch relevant sections of docs from multiple trusted and up to date sources.&lt;/p&gt;
&lt;p&gt;Indeed, users of AI coding tools will rarely ask &lt;em&gt;what&#39;s the syntax of this property already?&lt;/em&gt; They&#39;re more likely to ask more intricate questions, which might require looking up multiple web features and responding in a way that points the user in the right direction, taking multiple factors into consideration, instead of merely spitting out an API reference page.&lt;/p&gt;
&lt;p&gt;Given the right context, LLMs can be pretty good at articulating a summarized, but also comprehensive response.&lt;/p&gt;
&lt;p&gt;To be clear, what I&#39;m saying is there needs to be two layers involved in an AI tool responding to a coding task:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first layer, which is &lt;em&gt;not&lt;/em&gt; (or rather &lt;em&gt;not only&lt;/em&gt;)provided by an LLM, takes care of understanding what the developer is looking for, and actually finding the corresponding resources.&lt;/li&gt;
&lt;li&gt;And the second layer is where the LLM really comes in. Once the right context was found, we can shove it down the LLM&#39;s throat and let it do its magic: providing a clear step-by-step guide of how to solve the issue, or even do the right code changes on its own.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Layer 1, above, takes a lot of work.&lt;/p&gt;
&lt;p&gt;I like the &lt;a href=&quot;https://learn.microsoft.com/training/support/mcp&quot;&gt;learn.microsoft.com MCP server&lt;/a&gt;, which Microsoft recently launched. This server uses a vector search and retrieval service that finds chunks of content, from the learn.microsoft.com site, which are relevant to the user query. This is a great way to provide the AI coding tool with the context it needs. Well, at least if what you&#39;re looking for is documented within one of Microsoft&#39;s documentation websites.&lt;/p&gt;
&lt;p&gt;I hope someone does the same for web development-related resources such as MDN, and others!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Test the clipboardchange event—a more efficient way to monitor the clipboard</title>
    <link href="https://patrickbrosset.com/articles/2025-09-23-test-the-clipboardchange-eventa-more-efficient-way-to-monitor-the-clipboard/"/>
    <updated>2025-09-23T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-09-23-test-the-clipboardchange-eventa-more-efficient-way-to-monitor-the-clipboard/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/blog/clipboardchange">https://developer.chrome.com/blog/clipboardchange</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Unlock text editing use cases with highlightsFromPoint and other FromPoint APIs </title>
    <link href="https://patrickbrosset.com/articles/2025-09-25-unlock-text-editing-use-cases-with-highlightsfrompoint-and-other-frompoint-apis/"/>
    <updated>2025-09-25T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-09-25-unlock-text-editing-use-cases-with-highlightsfrompoint-and-other-frompoint-apis/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/09/25/unlock-text-editing-use-cases-with-highlightsfrompoint/">https://blogs.windows.com/msedgedev/2025/09/25/unlock-text-editing-use-cases-with-highlightsfrompoint/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Perfecting Baseline</title>
    <link href="https://patrickbrosset.com/articles/2025-11-13-perfecting-baseline/"/>
    <updated>2025-11-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-11-13-perfecting-baseline/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://piccalil.li/blog/perfecting-baseline/">https://piccalil.li/blog/perfecting-baseline/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Masonry: Things You Won’t Need A Library For Anymore</title>
    <link href="https://patrickbrosset.com/articles/2025-12-02-masonry-things-you-wont-need-a-library-for-anymore/"/>
    <updated>2025-12-02T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-12-02-masonry-things-you-wont-need-a-library-for-anymore/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.smashingmagazine.com/2025/12/masonry-things-you-wont-need-library-anymore/">https://www.smashingmagazine.com/2025/12/masonry-things-you-wont-need-library-anymore/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>What&#39;s wrong with this HTML, and is it valid?</title>
    <link href="https://patrickbrosset.com/articles/2025-12-08-whats-wrong-with-this-html-and-is-it-valid/"/>
    <updated>2025-12-08T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-12-08-whats-wrong-with-this-html-and-is-it-valid/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://www.htmhell.dev/adventcalendar/2025/8/">https://www.htmhell.dev/adventcalendar/2025/8/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Making complex web apps faster</title>
    <link href="https://patrickbrosset.com/articles/2025-12-09-making-complex-web-apps-faster/"/>
    <updated>2025-12-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-12-09-making-complex-web-apps-faster/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2025/12/09/making-complex-web-apps-faster/">https://blogs.windows.com/msedgedev/2025/12/09/making-complex-web-apps-faster/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>My 2025 in numbers</title>
    <link href="https://patrickbrosset.com/articles/2025-12-31-my-2025-in-numbers/"/>
    <updated>2025-12-31T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2025-12-31-my-2025-in-numbers/</id>
    <content type="html">
      
        &lt;p&gt;Today is the last day of 2025, which seems like a good time to look back and think about what I&#39;ve done this year.
This year, more than ever, I&#39;ve continued to push for a web that&#39;s easier to build with, more capable, easier to discover, and better documented.
I&#39;m thankful to be able to do this work, something I enjoy and care deeply about.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/2025-pics/sunset.avif&quot; alt=&quot;Sun setting over the sea, dark clouds above, yellow sun, orange sky, dark sea below.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;I talked about web technologies, a lot&lt;/h2&gt;
&lt;p&gt;First in writing, by publishing 23 blog posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;9 on the &lt;a href=&quot;https://blogs.windows.com/msedgedev/&quot;&gt;Edge dev blog&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/02/04/request-for-feedback-incoming-call-notifications-api/&quot;&gt;Request for feedback: Incoming call notifications API for web apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/02/13/microsoft-edge-and-interop-2025/&quot;&gt;Microsoft Edge and Interop 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/03/19/minding-the-gaps-a-new-way-to-draw-separators-in-css/&quot;&gt;Minding the gaps: A new way to draw separators in CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/04/22/contextual-logging-with-console-context/&quot;&gt;Contextual logging with console.context()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/05/05/creating-a-more-accessible-web-with-aria-notify/&quot;&gt;Creating a more accessible web with Aria Notify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/06/26/the-edge-2025-web-platform-top-developer-needs-dashboard/&quot;&gt;The Edge 2025 web platform top developer needs dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/09/04/calling-for-interop-2026-proposals/&quot;&gt;Calling for Interop 2026 proposals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/09/25/unlock-text-editing-use-cases-with-highlightsfrompoint/&quot;&gt;Unlock text editing use cases with highlightsFromPoint and other FromPoint APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.windows.com/msedgedev/2025/12/09/making-complex-web-apps-faster/&quot;&gt;Making complex web apps faster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;5 on my own blog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2025-09-16-demanding-more-from-our-ai-coding-tools/&quot;&gt;Demanding more from our AI coding tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2025-08-28-ai-agents-and-the-web-a-proposal-to-keep-developers-in-the-loop/&quot;&gt;AI agents and the web - A proposal to keep developers in the loop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2025-06-18-how-to-convince-your-boss-to-sponsor-open-web-docs/&quot;&gt;How to convince your boss to sponsor Open Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2025-02-12-constructing-clipboarditems-from-strings-and-promises/&quot;&gt;Constructing ClipboardItems from strings and Promises&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;And this one.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4 on &lt;a href=&quot;https://developer.chrome.com/blog&quot;&gt;Chrome dev blog&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/clipboardchange&quot;&gt;Test the clipboardchange event—a more efficient way to monitor the clipboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/masonry-update&quot;&gt;Brick by brick: Help us build CSS Masonry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/gap-decorations&quot;&gt;A new way to style gaps in CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/better-text-rendering-in-chromium-based-browsers-on-windows&quot;&gt;Better text rendering in Chromium-based browsers on Windows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 on &lt;a href=&quot;https://www.smashingmagazine.com/&quot;&gt;Smashing Magazine&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2025/12/masonry-things-you-wont-need-library-anymore/&quot;&gt;Masonry: Things You Won’t Need A Library For Anymore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 on &lt;a href=&quot;https://www.htmhell.dev/&quot;&gt;HTMHell&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.htmhell.dev/adventcalendar/2025/8/&quot;&gt;What&#39;s wrong with this HTML, and is it valid?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 on &lt;a href=&quot;https://piccalil.li/&quot;&gt;Piccalilli&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/perfecting-baseline/&quot;&gt;Perfecting Baseline&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 on &lt;a href=&quot;https://css-tricks.com/&quot;&gt;CSS-Tricks&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/the-gap-strikes-back-now-stylable/&quot;&gt;The Gap Strikes Back: Now Stylable&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 on the &lt;a href=&quot;https://www.w3.org/blog/&quot;&gt;W3C blog&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/blog/2025/first-catalog-of-web-features-completed-by-the-webdx-community-group/&quot;&gt;First catalog of web features completed by the WebDX Community Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On social media:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I published about 120 posts on &lt;a href=&quot;https://bsky.app/profile/patrickbrosset.com&quot;&gt;Bluesky&lt;/a&gt; (and probably the same amount on &lt;a href=&quot;https://mas.to/@patrickbrosset&quot;&gt;Mastodon&lt;/a&gt; and &lt;a href=&quot;https://www.linkedin.com/in/patrickbrosset/&quot;&gt;LinkedIn&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/2025-pics/shadows.avif&quot; alt=&quot;Two persons walking away from the camera on a cobble street, sun is facing the camera, long shadows extend from the person&#39;s silouhettes. Black and white.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But also in person, by giving 4 talks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;2 lightning talks at Smashing Conf events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/slides/2025-10-CSS-Masonry.pdf&quot;&gt;Masonry - The pretty grid you didn’t see coming (Smashing Conference, NYC, October 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/slides/2025-09-CSS-Gap-Decorations.pdf&quot;&gt;Filling the Gap - Decorating Layouts with CSS (Smashing Conference, Freiburg, September 2025)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2 at W3C events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=QzfwNFIXOkM&quot;&gt;web-features and Baseline (AC 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/slides/breakouts-day-2025/&quot;&gt;web-features and Baseline - We&#39;re feature complete! What&#39;s next? (W3C Breakouts Day)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;I demonstrated what the web is capable of&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I built 15 little experiments on my site, here are those which I&#39;m most proud of/are the most useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/lab/navigating-the-web-platform&quot;&gt;Navigating the web platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/lab/css-gap-decoration-resources&quot;&gt;CSS gap decoration resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/lab/css-masonry-resources&quot;&gt;CSS Masonry resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/lab/web-features-finder&quot;&gt;Web features finder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://patrickbrosset.com/lab/accordion&quot;&gt;Implementing an accordion component in 2026 🪗&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I built 15 demos on &lt;a href=&quot;https://github.com/microsoftEdge/Demos&quot;&gt;the Edge Demos repo&lt;/a&gt;, supporting the web platform docs my team maintains.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/2025-pics/house-tree.avif&quot; alt=&quot;A blossoming tree in the foreground, partly hiding a house in the background.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;I documented web technologies and made them easier to find and use&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I created over 180 commits to &lt;a href=&quot;https://github.com/web-platform-dx/web-features&quot;&gt;the web-features repository&lt;/a&gt;, to help build a better, more comprehensive, catalog of all the features of the web platform.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I created about 10 commits to the &lt;a href=&quot;https://github.com/mdn/content&quot;&gt;mdn/content&lt;/a&gt; and &lt;a href=&quot;https://github.com/mdn/browser-compat-data/&quot;&gt;mdn/browser-compat-data&lt;/a&gt; repositories, to improve MDN web docs and browser compatibility tables.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I created over 140 commits to the &lt;a href=&quot;https://github.com/MicrosoftDocs/edge-developer&quot;&gt;MicrosoftDocs/edge-developer repository&lt;/a&gt;, to improve the &lt;a href=&quot;https://learn.microsoft.com/microsoft-edge/developer/&quot;&gt;web platform-related Edge technical documentation&lt;/a&gt;, in particular publishing &lt;a href=&quot;https://learn.microsoft.com/microsoft-edge/web-platform/release-notes/&quot;&gt;Edge Beta release notes&lt;/a&gt; every 4 weeks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I created about 200 commits both on the &lt;a href=&quot;https://github.com/web-platform-dx/web-features-mappings&quot;&gt;web-features-mappings&lt;/a&gt; and &lt;a href=&quot;https://github.com/web-platform-dx/web-features-explorer&quot;&gt;web-features-explorer&lt;/a&gt; repos, which together power &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/&quot;&gt;the Web Platform Features Explorer site&lt;/a&gt;, a tool to discover and learn about the web platform, via &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/release-notes/december-2025/&quot;&gt;monthly release notes&lt;/a&gt;, and &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/document-caretpositionfrompoint/&quot;&gt;detailed feature pages&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/2025-pics/kern.avif&quot; alt=&quot;A kern, which is a stack of stones, used on mountain paths, as indicators of the path to follow. The kern is photographed with the sun right behind it.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;I contributed to open source&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GitHub tells me that I&#39;ve made &lt;a href=&quot;https://github.com/captainbrosset/&quot;&gt;3187 contributions this year&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I&#39;ve also opened about 260 issues and pull requests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On the personal side&lt;/h2&gt;
&lt;p&gt;Because work is only a part of life, and can never be more important than your own well-being and loved ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I biked, ran, hiked, and skied over 1400km, totaling 32000m of elevation gain (and that&#39;s not counting the multiple sessions I do at the bouldering gym each week).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I listened to more than 55,000 minutes of music.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I took more than 5000 photos, some of which you&#39;ve seen in this blog.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/2025-pics/tree-light.avif&quot; alt=&quot;A montain ridge, cover in pine trees. One of the trees is brighter than the others.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Happy new year folks, see you in 2026!&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Fun with the web</title>
    <link href="https://patrickbrosset.com/articles/2026-01-06-fun-with-the-web/"/>
    <updated>2026-01-06T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-01-06-fun-with-the-web/</id>
    <content type="html">
      
        &lt;p&gt;Fun makes learning better!&lt;/p&gt;
&lt;p&gt;Kids learn by having fun, and adults do too, we just forgot about it. When you&#39;re having fun, your brain is more engaged, more open to learning new things. And yet, as adult professionals, we forget that sometimes. It&#39;s all serious business, and that&#39;s just sad.&lt;/p&gt;
&lt;p&gt;Fun is how I learned to code for the web in the first place. I didn&#39;t know any programming as a kid (apart from a bit of &lt;a href=&quot;https://en.wikipedia.org/wiki/Logo_(programming_language)&quot;&gt;Logo programming&lt;/a&gt; with a &lt;a href=&quot;https://en.wikipedia.org/wiki/Turtle_graphics&quot;&gt;Turtle&lt;/a&gt;), I just liked computers. And when the web came along, as a teenager, I was blown away by the access to infinite information and communities, and wanted to be part of it. That&#39;s why I started creating simple web pages using HTML, and then (much) later added CSS and JavaScript to the mix.&lt;/p&gt;
&lt;p&gt;If your story is a little bit like mine, do you remember the first time you built something for the web? That thrill of seeing your code come alive and displaying pixels on the screen?&lt;/p&gt;
&lt;p&gt;Somewhere along the way, many of us lost that joy. Deadlines, frameworks, best practices. These things tend to turn play into process. But here&#39;s the thing: fun isn&#39;t a luxury. It&#39;s how we learn best. It&#39;s how we grow. To me, the web is still the most playful platform out there. You can just open a text editor, write some code, and then play with that code live in the browser. No complex setup, no heavy tools, just you, your creativity, and appetite for learning something new that might, one day, prove useful.&lt;/p&gt;
&lt;p&gt;I still love doing things just for fun, things that aren&#39;t production ready, far from it, but that let me explore new things.&lt;/p&gt;
&lt;p&gt;For example, here&#39;s some flying math equations:&lt;/p&gt;
&lt;div class=&quot;flying-math&quot;&gt;
  &lt;div class=&quot;animation&quot;&gt;
    &lt;math&gt;
      &lt;mi&gt;π&lt;/mi&gt;
      &lt;mo&gt;&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;r&lt;/mi&gt;
        &lt;mn&gt;2&lt;/mn&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;msup&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;b&lt;/mi&gt;
      &lt;mi&gt;x&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;c&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;0&lt;/mn&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mo&gt;=&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mrow&gt;
              &lt;mo&gt;-&lt;/mo&gt;
              &lt;mi&gt;b&lt;/mi&gt;
            &lt;/mrow&gt;
            &lt;mo&gt;±&lt;/mo&gt;
            &lt;msqrt&gt;
              &lt;mrow&gt;
                &lt;msup&gt;
                  &lt;mi&gt;b&lt;/mi&gt;
                  &lt;mn&gt;2&lt;/mn&gt;
                &lt;/msup&gt;
                &lt;mo&gt;-&lt;/mo&gt;
                &lt;mrow&gt;
                  &lt;mn&gt;4&lt;/mn&gt;
                  &lt;mo&gt;⁢&lt;/mo&gt;
                  &lt;mi&gt;a&lt;/mi&gt;
                  &lt;mo&gt;⁢&lt;/mo&gt;
                  &lt;mi&gt;c&lt;/mi&gt;
                &lt;/mrow&gt;
              &lt;/mrow&gt;
            &lt;/msqrt&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mn&gt;2&lt;/mn&gt;
            &lt;mo&gt;⁢&lt;/mo&gt;
            &lt;mi&gt;a&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mi&gt;A&lt;/mi&gt;
        &lt;mo&gt;=&lt;/mo&gt;
        &lt;mfenced open=&quot;[&quot; close=&quot;]&quot;&gt;
          &lt;mtable&gt;
            &lt;mtr&gt;
              &lt;mtd&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mtd&gt;
              &lt;mtd&gt;
                &lt;mi&gt;y&lt;/mi&gt;
              &lt;/mtd&gt;
            &lt;/mtr&gt;
            &lt;mtr&gt;
              &lt;mtd&gt;
                &lt;mi&gt;z&lt;/mi&gt;
              &lt;/mtd&gt;
              &lt;mtd&gt;
                &lt;mi&gt;w&lt;/mi&gt;
              &lt;/mtd&gt;
            &lt;/mtr&gt;
          &lt;/mtable&gt;
        &lt;/mfenced&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;semantics&gt;
          &lt;mrow&gt;
            &lt;msubsup&gt;
              &lt;mo&gt;∫&lt;/mo&gt;
              &lt;mn&gt;1&lt;/mn&gt;
              &lt;mi&gt;t&lt;/mi&gt;
            &lt;/msubsup&gt;
            &lt;mfrac&gt;
              &lt;mrow&gt;
                &lt;mo&gt;ⅆ&lt;/mo&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mrow&gt;
              &lt;mi&gt;x&lt;/mi&gt;
            &lt;/mfrac&gt;
          &lt;/mrow&gt;
          &lt;annotation-xml encoding=&quot;MathML-Content&quot;&gt;
            &lt;apply&gt;
              &lt;int&gt;&lt;/int&gt;
              &lt;bvar&gt;
                &lt;ci&gt;x&lt;/ci&gt;
              &lt;/bvar&gt;
              &lt;lowlimit&gt;
                &lt;cn&gt;1&lt;/cn&gt;
              &lt;/lowlimit&gt;
              &lt;uplimit&gt;
                &lt;ci&gt;t&lt;/ci&gt;
              &lt;/uplimit&gt;
              &lt;apply&gt;
                &lt;divide&gt;&lt;/divide&gt;
                &lt;cn&gt;1&lt;/cn&gt;
                &lt;ci&gt;x&lt;/ci&gt;
              &lt;/apply&gt;
            &lt;/apply&gt;
          &lt;/annotation-xml&gt;
        &lt;/semantics&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math title=&quot;a x^2+b x+c=0&quot;&gt;
      &lt;mstyle&gt;
        &lt;mi&gt;
          a
        &lt;/mi&gt;
        &lt;msup&gt;
          &lt;mi&gt;
            x
          &lt;/mi&gt;
          &lt;mn&gt;
            2
          &lt;/mn&gt;
        &lt;/msup&gt;
        &lt;mo&gt;
          +
        &lt;/mo&gt;
        &lt;mi&gt;
          b
        &lt;/mi&gt;
        &lt;mi&gt;
          x
        &lt;/mi&gt;
        &lt;mo&gt;
          +
        &lt;/mo&gt;
        &lt;mi&gt;
          c
        &lt;/mi&gt;
        &lt;mo&gt;
          =
        &lt;/mo&gt;
        &lt;mn&gt;
          0
        &lt;/mn&gt;
      &lt;/mstyle&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mn&gt;
        2
      &lt;/mn&gt;
      &lt;mo&gt;
        ⁢
      &lt;/mo&gt;
      &lt;mi&gt;
        x
      &lt;/mi&gt;
      &lt;mo&gt;
        ×
      &lt;/mo&gt;
      &lt;mo&gt;
        -
      &lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mrow&gt;
          &lt;mi&gt;
            1
          &lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mn&gt;
          16
        &lt;/mn&gt;
      &lt;/mfrac&gt;
      &lt;mo&gt;
        +
      &lt;/mo&gt;
      &lt;msup&gt;
        &lt;mrow&gt;
          &lt;mo&gt;
            −
          &lt;/mo&gt;
          &lt;mn&gt;
            5
          &lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;mn&gt;
          2
        &lt;/mn&gt;
      &lt;/msup&gt;
      &lt;mo&gt;
        =
      &lt;/mo&gt;
      &lt;mn&gt;
        24
      &lt;/mn&gt;
      &lt;mo&gt;
        ⁤
      &lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mn&gt;
          15
        &lt;/mn&gt;
        &lt;mn&gt;
          16
        &lt;/mn&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mi&gt;E&lt;/mi&gt;
        &lt;mo&gt;=&lt;/mo&gt;
        &lt;mi&gt;m&lt;/mi&gt;
        &lt;msup&gt;
          &lt;mi&gt;c&lt;/mi&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/msup&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mo&gt;{&lt;/mo&gt;
      &lt;mo&gt;[&lt;/mo&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;b&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;c&lt;/mi&gt;
      &lt;mo&gt;)&lt;/mo&gt;
      &lt;mo&gt;-&lt;/mo&gt;
      &lt;mi&gt;d&lt;/mi&gt;
      &lt;mo&gt;}&lt;/mo&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mfenced&gt;
        &lt;mrow&gt;
          &lt;mi&gt; a &lt;/mi&gt;
          &lt;mo&gt; + &lt;/mo&gt;
          &lt;mi&gt; b &lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/mfenced&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mtable groupalign=&quot;{decimalpoint left left decimalpoint left left decimalpoint}&quot;&gt;
        &lt;mtr&gt;
          &lt;mtd&gt;
            &lt;mrow&gt;
              &lt;mrow&gt;
                &lt;mrow&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mn&gt; 8.44 &lt;/mn&gt;
                  &lt;mo&gt;&lt;/mo&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mi&gt; x &lt;/mi&gt;
                &lt;/mrow&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mo&gt; + &lt;/mo&gt;
                &lt;mrow&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mn&gt; 55 &lt;/mn&gt;
                  &lt;mo&gt;&lt;/mo&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mi&gt; y &lt;/mi&gt;
                &lt;/mrow&gt;
              &lt;/mrow&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mo&gt; = &lt;/mo&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mn&gt; 0 &lt;/mn&gt;
            &lt;/mrow&gt;
          &lt;/mtd&gt;
        &lt;/mtr&gt;
        &lt;mtr&gt;
          &lt;mtd&gt;
            &lt;mrow&gt;
              &lt;mrow&gt;
                &lt;!--mrow--&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mn&gt; 3.1 &lt;/mn&gt;
                &lt;mo&gt;&lt;/mo&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mi&gt; x &lt;/mi&gt;
                &lt;!--/mrow--&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mo&gt; - &lt;/mo&gt;
                &lt;mrow&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mn&gt; 0.7 &lt;/mn&gt;
                  &lt;mo&gt;&lt;/mo&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mi&gt; y &lt;/mi&gt;
                &lt;/mrow&gt;
              &lt;/mrow&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mo&gt; = &lt;/mo&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mrow&gt;
                &lt;mo&gt; - &lt;/mo&gt;
                &lt;mn&gt; 1.1 &lt;/mn&gt;
              &lt;/mrow&gt;
            &lt;/mrow&gt;
          &lt;/mtd&gt;
        &lt;/mtr&gt;
      &lt;/mtable&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;semantics&gt;
        &lt;mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;sin&lt;/mi&gt;
            &lt;mo&gt;⁡&lt;!--FUNCTION APPLICATION--&gt;&lt;/mo&gt;
            &lt;mfenced&gt;
              &lt;mi&gt;x&lt;/mi&gt;
            &lt;/mfenced&gt;
          &lt;/mrow&gt;
          &lt;mo&gt;+&lt;/mo&gt;
          &lt;mn&gt;5&lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;annotation-xml cd=&quot;mathmlkeys&quot; name=&quot;contentequiv&quot; encoding=&quot;MathML-Content&quot;&gt;
          &lt;apply&gt;
            &lt;plus&gt;&lt;/plus&gt;
            &lt;apply&gt;
              &lt;sin&gt;&lt;/sin&gt;
              &lt;ci&gt;x&lt;/ci&gt;
            &lt;/apply&gt;
            &lt;cn&gt;5&lt;/cn&gt;
          &lt;/apply&gt;
        &lt;/annotation-xml&gt;
        &lt;annotation encoding=&quot;application/x-tex&quot;&gt;
          &#92;sin x + 5
        &lt;/annotation&gt;
      &lt;/semantics&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;menclose notation=&quot;circle box&quot;&gt;
        &lt;mi&gt; x &lt;/mi&gt;
        &lt;mo&gt; + &lt;/mo&gt;
        &lt;mi&gt; y &lt;/mi&gt;
      &lt;/menclose&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mroot&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;64&lt;/mi&gt;
          &lt;mn&gt;4&lt;/mn&gt;
        &lt;/mroot&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mroot&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mi&gt;1&lt;/mi&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mn&gt;5&lt;/mn&gt;
        &lt;/mroot&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mroot&gt;
        &lt;mrow&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mo&gt;+&lt;/mo&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mo&gt;−&lt;/mo&gt;
            &lt;mn&gt;1&lt;/mn&gt;
          &lt;/mrow&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mfrac&gt;
      &lt;/mroot&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;f&lt;/mi&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;a&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mn&gt;1&lt;/mn&gt;
        &lt;mrow&gt;
          &lt;mn&gt;2&lt;/mn&gt;
          &lt;mi&gt;π&lt;/mi&gt;
          &lt;mi&gt;i&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/mfrac&gt;
      &lt;mo data-mjx-texclass=&quot;OP&quot;&gt;∮&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mrow&gt;
          &lt;mi&gt;f&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
          &lt;mi&gt;z&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
        &lt;/mrow&gt;
        &lt;mrow&gt;
          &lt;mi&gt;z&lt;/mi&gt;
          &lt;mo&gt;−&lt;/mo&gt;
          &lt;mi&gt;a&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/mfrac&gt;
      &lt;mi&gt;d&lt;/mi&gt;
      &lt;mi&gt;z&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;θ&lt;/mi&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mi&gt;φ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;θ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;φ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;−&lt;/mo&gt;
      &lt;mi&gt;sin&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;θ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;sin&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;φ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mn&gt;4&lt;/mn&gt;
        &lt;msup&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
            &lt;mfrac&gt;
              &lt;mn&gt;1&lt;/mn&gt;
              &lt;mn&gt;4&lt;/mn&gt;
            &lt;/mfrac&gt;
          &lt;/mrow&gt;
        &lt;/msup&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;msup&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
          &lt;mn&gt;9&lt;/mn&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
        &lt;/mrow&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mfrac&gt;
            &lt;mn&gt;1&lt;/mn&gt;
            &lt;mn&gt;2&lt;/mn&gt;
          &lt;/mfrac&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;msub&gt;
        &lt;mi&gt;log&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;x&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;ln&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;e&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;1&lt;/mn&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;V&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mn&gt;4&lt;/mn&gt;
        &lt;mn&gt;3&lt;/mn&gt;
      &lt;/mfrac&gt;
      &lt;mi&gt;π&lt;/mi&gt;
      &lt;msup&gt;
        &lt;mi&gt;r&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;msub&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;msub&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mo&gt;−&lt;/mo&gt;
      &lt;msub&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;msup&gt;
        &lt;mi&gt;sin&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;x&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;cos&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;y&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;1&lt;/mn&gt;
    &lt;/math&gt;
    &lt;div class=&quot;circle&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;msup&gt;
        &lt;mi mathvariant=&quot;normal&quot;&gt;∇&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;→&lt;/mo&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo&gt;⋅&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;F&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;→&lt;/mo&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;z&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;y&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo&gt;−&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;y&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;z&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;i&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;z&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo&gt;−&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;z&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;x&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;j&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;y&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;x&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo&gt;−&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;y&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;k&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mi&gt;B&lt;/mi&gt;
      &lt;mi&gt;C&lt;/mi&gt;
      &lt;mo&gt;△&lt;/mo&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mover&gt;
        &lt;mrow&gt;
          &lt;mi&gt;A&lt;/mi&gt;
          &lt;mi&gt;B&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mtext&gt;⌒&lt;/mtext&gt;
        &lt;/mrow&gt;
      &lt;/mover&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mover&gt;
        &lt;mrow&gt;
          &lt;mi&gt;A&lt;/mi&gt;
          &lt;mi&gt;B&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;←&lt;/mo&gt;
        &lt;/mrow&gt;
      &lt;/mover&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mn&gt;10&lt;/mn&gt;
      &lt;mo&gt;∵&lt;/mo&gt;
      &lt;mn&gt;5&lt;/mn&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;l&lt;/mi&gt;
      &lt;mi&gt;i&lt;/mi&gt;
      &lt;msub&gt;
        &lt;mi&gt;m&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;→&lt;/mo&gt;
          &lt;mi mathvariant=&quot;normal&quot;&gt;∞&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mi&gt;f&lt;/mi&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;4&lt;/mn&gt;
      &lt;mi&gt;π&lt;/mi&gt;
      &lt;msup&gt;
        &lt;mi&gt;r&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;sin&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;A&lt;/mi&gt;
        &lt;mi&gt;C&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;B&lt;/mi&gt;
        &lt;mi&gt;C&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;msup&gt;
        &lt;mi&gt;sin&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo&gt;−&lt;/mo&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;x&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;mi&gt;r&lt;/mi&gt;
      &lt;mi&gt;c&lt;/mi&gt;
      &lt;mi&gt;s&lt;/mi&gt;
      &lt;mi&gt;i&lt;/mi&gt;
      &lt;mi&gt;n&lt;/mi&gt;
      &lt;mi&gt;x&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;tan&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;θ&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;A&lt;/mi&gt;
        &lt;mi&gt;B&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;mi&gt;cot&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;B&lt;/mi&gt;
        &lt;mi&gt;A&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;sec&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;C&lt;/mi&gt;
        &lt;mi&gt;A&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;csc&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;C&lt;/mi&gt;
        &lt;mi&gt;B&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt; &lt;math&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;xx&lt;/mi&gt;
        &lt;mi&gt;yy&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;π&lt;/mi&gt;
      &lt;mo&gt;&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;r&lt;/mi&gt;
        &lt;mn&gt;2&lt;/mn&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;msup&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;b&lt;/mi&gt;
      &lt;mi&gt;x&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;c&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;0&lt;/mn&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mo&gt;=&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mrow&gt;
              &lt;mo&gt;-&lt;/mo&gt;
              &lt;mi&gt;b&lt;/mi&gt;
            &lt;/mrow&gt;
            &lt;mo&gt;±&lt;/mo&gt;
            &lt;msqrt&gt;
              &lt;mrow&gt;
                &lt;msup&gt;
                  &lt;mi&gt;b&lt;/mi&gt;
                  &lt;mn&gt;2&lt;/mn&gt;
                &lt;/msup&gt;
                &lt;mo&gt;-&lt;/mo&gt;
                &lt;mrow&gt;
                  &lt;mn&gt;4&lt;/mn&gt;
                  &lt;mo&gt;⁢&lt;/mo&gt;
                  &lt;mi&gt;a&lt;/mi&gt;
                  &lt;mo&gt;⁢&lt;/mo&gt;
                  &lt;mi&gt;c&lt;/mi&gt;
                &lt;/mrow&gt;
              &lt;/mrow&gt;
            &lt;/msqrt&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mn&gt;2&lt;/mn&gt;
            &lt;mo&gt;⁢&lt;/mo&gt;
            &lt;mi&gt;a&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mi&gt;A&lt;/mi&gt;
        &lt;mo&gt;=&lt;/mo&gt;
        &lt;mfenced open=&quot;[&quot; close=&quot;]&quot;&gt;
          &lt;mtable&gt;
            &lt;mtr&gt;
              &lt;mtd&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mtd&gt;
              &lt;mtd&gt;
                &lt;mi&gt;y&lt;/mi&gt;
              &lt;/mtd&gt;
            &lt;/mtr&gt;
            &lt;mtr&gt;
              &lt;mtd&gt;
                &lt;mi&gt;z&lt;/mi&gt;
              &lt;/mtd&gt;
              &lt;mtd&gt;
                &lt;mi&gt;w&lt;/mi&gt;
              &lt;/mtd&gt;
            &lt;/mtr&gt;
          &lt;/mtable&gt;
        &lt;/mfenced&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;semantics&gt;
          &lt;mrow&gt;
            &lt;msubsup&gt;
              &lt;mo&gt;∫&lt;/mo&gt;
              &lt;mn&gt;1&lt;/mn&gt;
              &lt;mi&gt;t&lt;/mi&gt;
            &lt;/msubsup&gt;
            &lt;mfrac&gt;
              &lt;mrow&gt;
                &lt;mo&gt;ⅆ&lt;/mo&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mrow&gt;
              &lt;mi&gt;x&lt;/mi&gt;
            &lt;/mfrac&gt;
          &lt;/mrow&gt;
          &lt;annotation-xml encoding=&quot;MathML-Content&quot;&gt;
            &lt;apply&gt;
              &lt;int&gt;&lt;/int&gt;
              &lt;bvar&gt;
                &lt;ci&gt;x&lt;/ci&gt;
              &lt;/bvar&gt;
              &lt;lowlimit&gt;
                &lt;cn&gt;1&lt;/cn&gt;
              &lt;/lowlimit&gt;
              &lt;uplimit&gt;
                &lt;ci&gt;t&lt;/ci&gt;
              &lt;/uplimit&gt;
              &lt;apply&gt;
                &lt;divide&gt;&lt;/divide&gt;
                &lt;cn&gt;1&lt;/cn&gt;
                &lt;ci&gt;x&lt;/ci&gt;
              &lt;/apply&gt;
            &lt;/apply&gt;
          &lt;/annotation-xml&gt;
        &lt;/semantics&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math title=&quot;a x^2+b x+c=0&quot;&gt;
      &lt;mstyle&gt;
        &lt;mi&gt;
          a
        &lt;/mi&gt;
        &lt;msup&gt;
          &lt;mi&gt;
            x
          &lt;/mi&gt;
          &lt;mn&gt;
            2
          &lt;/mn&gt;
        &lt;/msup&gt;
        &lt;mo&gt;
          +
        &lt;/mo&gt;
        &lt;mi&gt;
          b
        &lt;/mi&gt;
        &lt;mi&gt;
          x
        &lt;/mi&gt;
        &lt;mo&gt;
          +
        &lt;/mo&gt;
        &lt;mi&gt;
          c
        &lt;/mi&gt;
        &lt;mo&gt;
          =
        &lt;/mo&gt;
        &lt;mn&gt;
          0
        &lt;/mn&gt;
      &lt;/mstyle&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mn&gt;
        2
      &lt;/mn&gt;
      &lt;mo&gt;
        ⁢
      &lt;/mo&gt;
      &lt;mi&gt;
        x
      &lt;/mi&gt;
      &lt;mo&gt;
        ×
      &lt;/mo&gt;
      &lt;mo&gt;
        -
      &lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mrow&gt;
          &lt;mi&gt;
            1
          &lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mn&gt;
          16
        &lt;/mn&gt;
      &lt;/mfrac&gt;
      &lt;mo&gt;
        +
      &lt;/mo&gt;
      &lt;msup&gt;
        &lt;mrow&gt;
          &lt;mo&gt;
            −
          &lt;/mo&gt;
          &lt;mn&gt;
            5
          &lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;mn&gt;
          2
        &lt;/mn&gt;
      &lt;/msup&gt;
      &lt;mo&gt;
        =
      &lt;/mo&gt;
      &lt;mn&gt;
        24
      &lt;/mn&gt;
      &lt;mo&gt;
        ⁤
      &lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mn&gt;
          15
        &lt;/mn&gt;
        &lt;mn&gt;
          16
        &lt;/mn&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mi&gt;E&lt;/mi&gt;
        &lt;mo&gt;=&lt;/mo&gt;
        &lt;mi&gt;m&lt;/mi&gt;
        &lt;msup&gt;
          &lt;mi&gt;c&lt;/mi&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/msup&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mo&gt;{&lt;/mo&gt;
      &lt;mo&gt;[&lt;/mo&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;b&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;c&lt;/mi&gt;
      &lt;mo&gt;)&lt;/mo&gt;
      &lt;mo&gt;-&lt;/mo&gt;
      &lt;mi&gt;d&lt;/mi&gt;
      &lt;mo&gt;}&lt;/mo&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mfenced&gt;
        &lt;mrow&gt;
          &lt;mi&gt; a &lt;/mi&gt;
          &lt;mo&gt; + &lt;/mo&gt;
          &lt;mi&gt; b &lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/mfenced&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mtable groupalign=&quot;{decimalpoint left left decimalpoint left left decimalpoint}&quot;&gt;
        &lt;mtr&gt;
          &lt;mtd&gt;
            &lt;mrow&gt;
              &lt;mrow&gt;
                &lt;mrow&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mn&gt; 8.44 &lt;/mn&gt;
                  &lt;mo&gt;&lt;/mo&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mi&gt; x &lt;/mi&gt;
                &lt;/mrow&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mo&gt; + &lt;/mo&gt;
                &lt;mrow&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mn&gt; 55 &lt;/mn&gt;
                  &lt;mo&gt;&lt;/mo&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mi&gt; y &lt;/mi&gt;
                &lt;/mrow&gt;
              &lt;/mrow&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mo&gt; = &lt;/mo&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mn&gt; 0 &lt;/mn&gt;
            &lt;/mrow&gt;
          &lt;/mtd&gt;
        &lt;/mtr&gt;
        &lt;mtr&gt;
          &lt;mtd&gt;
            &lt;mrow&gt;
              &lt;mrow&gt;
                &lt;!--mrow--&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mn&gt; 3.1 &lt;/mn&gt;
                &lt;mo&gt;&lt;/mo&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mi&gt; x &lt;/mi&gt;
                &lt;!--/mrow--&gt;
                &lt;maligngroup&gt;&lt;/maligngroup&gt;
                &lt;mo&gt; - &lt;/mo&gt;
                &lt;mrow&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mn&gt; 0.7 &lt;/mn&gt;
                  &lt;mo&gt;&lt;/mo&gt;
                  &lt;maligngroup&gt;&lt;/maligngroup&gt;
                  &lt;mi&gt; y &lt;/mi&gt;
                &lt;/mrow&gt;
              &lt;/mrow&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mo&gt; = &lt;/mo&gt;
              &lt;maligngroup&gt;&lt;/maligngroup&gt;
              &lt;mrow&gt;
                &lt;mo&gt; - &lt;/mo&gt;
                &lt;mn&gt; 1.1 &lt;/mn&gt;
              &lt;/mrow&gt;
            &lt;/mrow&gt;
          &lt;/mtd&gt;
        &lt;/mtr&gt;
      &lt;/mtable&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;semantics&gt;
        &lt;mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;sin&lt;/mi&gt;
            &lt;mo&gt;&lt;/mo&gt;
            &lt;mfenced&gt;
              &lt;mi&gt;x&lt;/mi&gt;
            &lt;/mfenced&gt;
          &lt;/mrow&gt;
          &lt;mo&gt;+&lt;/mo&gt;
          &lt;mn&gt;5&lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;annotation-xml cd=&quot;mathmlkeys&quot; name=&quot;contentequiv&quot; encoding=&quot;MathML-Content&quot;&gt;
          &lt;apply&gt;
            &lt;plus&gt;&lt;/plus&gt;
            &lt;apply&gt;
              &lt;sin&gt;&lt;/sin&gt;
              &lt;ci&gt;x&lt;/ci&gt;
            &lt;/apply&gt;
            &lt;cn&gt;5&lt;/cn&gt;
          &lt;/apply&gt;
        &lt;/annotation-xml&gt;
        &lt;annotation encoding=&quot;application/x-tex&quot;&gt;
          &#92;sin x + 5
        &lt;/annotation&gt;
      &lt;/semantics&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;menclose notation=&quot;circle box&quot;&gt;
        &lt;mi&gt; x &lt;/mi&gt;
        &lt;mo&gt; + &lt;/mo&gt;
        &lt;mi&gt; y &lt;/mi&gt;
      &lt;/menclose&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mroot&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;64&lt;/mi&gt;
          &lt;mn&gt;4&lt;/mn&gt;
        &lt;/mroot&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mroot&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mi&gt;1&lt;/mi&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow&gt;
        &lt;mroot&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mn&gt;5&lt;/mn&gt;
        &lt;/mroot&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mroot&gt;
        &lt;mrow&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mo&gt;+&lt;/mo&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mo&gt;−&lt;/mo&gt;
            &lt;mn&gt;1&lt;/mn&gt;
          &lt;/mrow&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mfrac&gt;
      &lt;/mroot&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;f&lt;/mi&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;a&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mn&gt;1&lt;/mn&gt;
        &lt;mrow&gt;
          &lt;mn&gt;2&lt;/mn&gt;
          &lt;mi&gt;π&lt;/mi&gt;
          &lt;mi&gt;i&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/mfrac&gt;
      &lt;mo data-mjx-texclass=&quot;OP&quot;&gt;∮&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mrow&gt;
          &lt;mi&gt;f&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
          &lt;mi&gt;z&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
        &lt;/mrow&gt;
        &lt;mrow&gt;
          &lt;mi&gt;z&lt;/mi&gt;
          &lt;mo&gt;−&lt;/mo&gt;
          &lt;mi&gt;a&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/mfrac&gt;
      &lt;mi&gt;d&lt;/mi&gt;
      &lt;mi&gt;z&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;θ&lt;/mi&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mi&gt;φ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;θ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;φ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;−&lt;/mo&gt;
      &lt;mi&gt;sin&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;θ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;sin&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;φ&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mn&gt;4&lt;/mn&gt;
        &lt;msup&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
            &lt;mfrac&gt;
              &lt;mn&gt;1&lt;/mn&gt;
              &lt;mn&gt;4&lt;/mn&gt;
            &lt;/mfrac&gt;
          &lt;/mrow&gt;
        &lt;/msup&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;msup&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
          &lt;mn&gt;9&lt;/mn&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
        &lt;/mrow&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mfrac&gt;
            &lt;mn&gt;1&lt;/mn&gt;
            &lt;mn&gt;2&lt;/mn&gt;
          &lt;/mfrac&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;msub&gt;
        &lt;mi&gt;log&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;x&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;ln&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;e&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;1&lt;/mn&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;V&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mn&gt;4&lt;/mn&gt;
        &lt;mn&gt;3&lt;/mn&gt;
      &lt;/mfrac&gt;
      &lt;mi&gt;π&lt;/mi&gt;
      &lt;msup&gt;
        &lt;mi&gt;r&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;msub&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;msub&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mo&gt;−&lt;/mo&gt;
      &lt;msub&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;3&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;msup&gt;
        &lt;mi&gt;sin&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;x&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;cos&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;y&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;1&lt;/mn&gt;
    &lt;/math&gt;
    &lt;div class=&quot;circle&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;msup&gt;
        &lt;mi mathvariant=&quot;normal&quot;&gt;∇&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;→&lt;/mo&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo&gt;⋅&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;F&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;→&lt;/mo&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;z&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;y&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo&gt;−&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;y&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;z&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;i&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;z&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo&gt;−&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;z&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;x&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;j&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;y&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;x&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo&gt;−&lt;/mo&gt;
        &lt;mfrac&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;msub&gt;
              &lt;mi&gt;F&lt;/mi&gt;
              &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
                &lt;mi&gt;x&lt;/mi&gt;
              &lt;/mrow&gt;
            &lt;/msub&gt;
          &lt;/mrow&gt;
          &lt;mrow&gt;
            &lt;mi&gt;∂&lt;/mi&gt;
            &lt;mi&gt;y&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mfrac&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
      &lt;mi&gt;k&lt;/mi&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mi&gt;B&lt;/mi&gt;
      &lt;mi&gt;C&lt;/mi&gt;
      &lt;mo&gt;△&lt;/mo&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mover&gt;
        &lt;mrow&gt;
          &lt;mi&gt;A&lt;/mi&gt;
          &lt;mi&gt;B&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mtext&gt;⌒&lt;/mtext&gt;
        &lt;/mrow&gt;
      &lt;/mover&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mover&gt;
        &lt;mrow&gt;
          &lt;mi&gt;A&lt;/mi&gt;
          &lt;mi&gt;B&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;←&lt;/mo&gt;
        &lt;/mrow&gt;
      &lt;/mover&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mn&gt;10&lt;/mn&gt;
      &lt;mo&gt;∵&lt;/mo&gt;
      &lt;mn&gt;5&lt;/mn&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;l&lt;/mi&gt;
      &lt;mi&gt;i&lt;/mi&gt;
      &lt;msub&gt;
        &lt;mi&gt;m&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mi&gt;x&lt;/mi&gt;
          &lt;mo stretchy=&quot;false&quot;&gt;→&lt;/mo&gt;
          &lt;mi mathvariant=&quot;normal&quot;&gt;∞&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/msub&gt;
      &lt;mi&gt;f&lt;/mi&gt;
      &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
        &lt;mi&gt;x&lt;/mi&gt;
        &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
      &lt;/mrow&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;4&lt;/mn&gt;
      &lt;mi&gt;π&lt;/mi&gt;
      &lt;msup&gt;
        &lt;mi&gt;r&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mn&gt;2&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;sin&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;A&lt;/mi&gt;
        &lt;mi&gt;C&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;cos&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;B&lt;/mi&gt;
        &lt;mi&gt;C&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;msup&gt;
        &lt;mi&gt;sin&lt;/mi&gt;
        &lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;
          &lt;mo&gt;−&lt;/mo&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
      &lt;/msup&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;x&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;mi&gt;r&lt;/mi&gt;
      &lt;mi&gt;c&lt;/mi&gt;
      &lt;mi&gt;s&lt;/mi&gt;
      &lt;mi&gt;i&lt;/mi&gt;
      &lt;mi&gt;n&lt;/mi&gt;
      &lt;mi&gt;x&lt;/mi&gt;
    &lt;/math&gt;
    &lt;div class=&quot;segment&quot;&gt;&lt;/div&gt;
    &lt;math&gt;
      &lt;mi&gt;tan&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;θ&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;A&lt;/mi&gt;
        &lt;mi&gt;B&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;cot&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;B&lt;/mi&gt;
        &lt;mi&gt;A&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;sec&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;C&lt;/mi&gt;
        &lt;mi&gt;A&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
    &lt;math&gt;
      &lt;mi&gt;csc&lt;/mi&gt;
      &lt;mo data-mjx-texclass=&quot;NONE&quot;&gt;⁡&lt;/mo&gt;
      &lt;mi&gt;A&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;C&lt;/mi&gt;
        &lt;mi&gt;B&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt; &lt;math&gt;
      &lt;mfrac&gt;
        &lt;mi&gt;xx&lt;/mi&gt;
        &lt;mi&gt;yy&lt;/mi&gt;
      &lt;/mfrac&gt;
    &lt;/math&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/flying-math.css&quot; /&gt;
&lt;script src=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/flying-math.js&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
&lt;p&gt;Math isn&#39;t exactly seen as fun, but being able to render mathematical notations on web pages felt like a cool thing to learn about.&lt;/p&gt;
&lt;p&gt;Turns out &lt;a href=&quot;https://developer.mozilla.org/docs/Web/MathML&quot;&gt;MathML&lt;/a&gt;, the markup language for mathematical notations, is &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/mathml/&quot;&gt;available across all browsers&lt;/a&gt;. Building the above demo taught me how to use the language, which is really quite simple. Here&#39;s an identity formula:&lt;/p&gt;
&lt;math display=&quot;block&quot;&gt;
  &lt;msup&gt;
    &lt;mrow&gt;
      &lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;
      &lt;mi&gt;a&lt;/mi&gt;
      &lt;mo&gt;+&lt;/mo&gt;
      &lt;mi&gt;b&lt;/mi&gt;
      &lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;
    &lt;/mrow&gt;
    &lt;mn&gt;2&lt;/mn&gt;
  &lt;/msup&gt;
  &lt;mo&gt;=&lt;/mo&gt;
  &lt;msup&gt;
    &lt;mi&gt;a&lt;/mi&gt;
    &lt;mn&gt;2&lt;/mn&gt;
  &lt;/msup&gt;
  &lt;mo&gt;+&lt;/mo&gt;
  &lt;mn&gt;2&lt;/mn&gt;
  &lt;mi&gt;a&lt;/mi&gt;
  &lt;mi&gt;b&lt;/mi&gt;
  &lt;mo&gt;+&lt;/mo&gt;
  &lt;msup&gt;
    &lt;mi&gt;b&lt;/mi&gt;
    &lt;mn&gt;2&lt;/mn&gt;
  &lt;/msup&gt;
&lt;/math&gt;
&lt;p&gt;But you can display more complex things too:&lt;/p&gt;
&lt;math display=&quot;block&quot;&gt;
  &lt;munderover&gt;
    &lt;mo&gt;∑&lt;/mo&gt;
    &lt;mrow&gt;
      &lt;mi&gt;k&lt;/mi&gt;
      &lt;mo&gt;=&lt;/mo&gt;
      &lt;mn&gt;1&lt;/mn&gt;
    &lt;/mrow&gt;
    &lt;mrow&gt;
      &lt;mi&gt;n&lt;/mi&gt;
    &lt;/mrow&gt;
  &lt;/munderover&gt;
  &lt;msup&gt;
    &lt;mi&gt;k&lt;/mi&gt;
    &lt;mn&gt;3&lt;/mn&gt;
  &lt;/msup&gt;
  &lt;mo&gt;=&lt;/mo&gt;
  &lt;msup&gt;
    &lt;mrow&gt;
      &lt;mo&gt;(&lt;/mo&gt;
      &lt;munderover&gt;
        &lt;mo&gt;∑&lt;/mo&gt;
        &lt;mrow&gt;
          &lt;mi&gt;k&lt;/mi&gt;
          &lt;mo&gt;=&lt;/mo&gt;
          &lt;mn&gt;1&lt;/mn&gt;
        &lt;/mrow&gt;
        &lt;mrow&gt;
          &lt;mi&gt;n&lt;/mi&gt;
        &lt;/mrow&gt;
      &lt;/munderover&gt;
      &lt;mi&gt;k&lt;/mi&gt;
      &lt;mo&gt;)&lt;/mo&gt;
    &lt;/mrow&gt;
    &lt;mn&gt;2&lt;/mn&gt;
  &lt;/msup&gt;
&lt;/math&gt;
&lt;p&gt;It took me a while to figure out the flying animation, but using the &lt;code&gt;perspective&lt;/code&gt; CSS property and then animating the &lt;code&gt;translate&lt;/code&gt; property along the Z axis made it easy in the end.&lt;/p&gt;
&lt;p&gt;After spending way too much time making math fly across the screen, I realized something: there&#39;s a constant trickle of new features being add to the web all the time. So there&#39;s always something I&#39;ve never played with. What else have I been ignoring? What other elements, properties, or APIs are hiding, waiting to be abused for something they weren&#39;t really invented for?&lt;/p&gt;
&lt;p&gt;The HTML &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element caught my eye. It&#39;s been around for some time now, and I&#39;ve used it in simple demos, but never explored the various ways to open and close it, or how to style it. So, of course, I had to try and make a game with it: &lt;strong&gt;Whack a dialog&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Click start below, and then try to close as many of the popping dialogs as you can before they disappear again.&lt;/p&gt;
&lt;div class=&quot;whack-a-dialog&quot;&gt;
  &lt;div class=&quot;wrapper&quot;&gt;
    &lt;button id=&quot;start&quot;&gt;Start playing!&lt;/button&gt;
    &lt;button id=&quot;stop&quot;&gt;Stop&lt;/button&gt;
    &lt;p class=&quot;scores&quot;&gt;&lt;/p&gt;
    &lt;div class=&quot;game&quot;&gt;
      &lt;div class=&quot;hole&quot;&gt;
        &lt;dialog id=&quot;dialog1&quot;&gt;
          &lt;button tabindex=&quot;-1&quot; commandfor=&quot;dialog1&quot; command=&quot;close&quot; value=&quot;closed-by-player&quot;&gt;&lt;/button&gt;
        &lt;/dialog&gt;
      &lt;/div&gt;
      &lt;div class=&quot;hole&quot;&gt;
        &lt;dialog id=&quot;dialog2&quot;&gt;
          &lt;button tabindex=&quot;-1&quot; commandfor=&quot;dialog2&quot; command=&quot;close&quot; value=&quot;closed-by-player&quot;&gt;&lt;/button&gt;
        &lt;/dialog&gt;
      &lt;/div&gt;
      &lt;div class=&quot;hole&quot;&gt;
        &lt;dialog id=&quot;dialog3&quot;&gt;
          &lt;button tabindex=&quot;-1&quot; commandfor=&quot;dialog3&quot; command=&quot;close&quot; value=&quot;closed-by-player&quot;&gt;&lt;/button&gt;
        &lt;/dialog&gt;
      &lt;/div&gt;
      &lt;div class=&quot;hole&quot;&gt;
        &lt;dialog id=&quot;dialog4&quot;&gt;
          &lt;button tabindex=&quot;-1&quot; commandfor=&quot;dialog4&quot; command=&quot;close&quot; value=&quot;closed-by-player&quot;&gt;&lt;/button&gt;
        &lt;/dialog&gt;
      &lt;/div&gt;
      &lt;div class=&quot;hole&quot;&gt;
        &lt;dialog id=&quot;dialog5&quot;&gt;
          &lt;button tabindex=&quot;-1&quot; commandfor=&quot;dialog5&quot; command=&quot;close&quot; value=&quot;closed-by-player&quot;&gt;&lt;/button&gt;
        &lt;/dialog&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/whack-a-dialog.css&quot; /&gt;
&lt;script src=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/whack-a-dialog.js&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
&lt;p&gt;What&#39;s the point you ask? Learning through play, in order to gain self-confidence in using the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element!&lt;/p&gt;
&lt;p&gt;Now, I can say that I understand the ways in which dialogs can be opened and closed. Even better, I can do it without JavaScript code, by using the the &lt;code&gt;command&lt;/code&gt; and &lt;code&gt;commandfor&lt;/code&gt; attributes from the &lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/invoker-commands/&quot;&gt;Invoker commands API&lt;/a&gt;. I also know that it&#39;s possible to fully customize the default styling if dialogs, and I even learned a few tricks to animate them.&lt;/p&gt;
&lt;p&gt;Look, I know that many companies, especially those operating under tight budgets and client deadlines, can&#39;t really dedicate time to do silly things like this, or even prioritize a more serious form of learning. Investing in experimentation or skill-building often seems like a luxury they simply can&#39;t afford. This is sad because it doesn&#39;t help break the cycle of repeating the same patterns, using the same frameworks, and eventually shipping the same broken code.&lt;/p&gt;
&lt;p&gt;This isn&#39;t inevitable though. There are ways to carve out space for growth, even in constrained environments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Micro-learning during projects by trying something new on a real task.&lt;/li&gt;
&lt;li&gt;Super quick internal meetings for sharing discoveries.&lt;/li&gt;
&lt;li&gt;Leveraging online resources like &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt;, &lt;a href=&quot;https://web.dev/&quot;&gt;web.dev&lt;/a&gt;, and plenty other free content.&lt;/li&gt;
&lt;li&gt;Trying to convince your clients of the value of modern approaches to a problem, for example for performance benefits, accessibility improvements, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Learning doesn&#39;t always require a big budget, especially if it&#39;s based on letting people have fun and experiment on their own.&lt;/p&gt;
&lt;p&gt;Now let&#39;s play some Grid Pong!&lt;/p&gt;
&lt;p&gt;Click anywhere inside the game area below to start or to pause it. Or, with the keyboard, focus the game and then press &lt;kbd&gt;Enter&lt;/kbd&gt;. To play, use &lt;kbd&gt;W&lt;/kbd&gt; and &lt;kbd&gt;S&lt;/kbd&gt; to move the left paddle up and down, and &lt;kbd&gt;O&lt;/kbd&gt; and &lt;kbd&gt;L&lt;/kbd&gt; to move the right paddle up and down.&lt;/p&gt;
&lt;div class=&quot;pongrid&quot; tabindex=&quot;0&quot;&gt;
  &lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;top-wall&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;bottom-wall&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;middle-line&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;left-paddle&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;right-paddle&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;left-score&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;right-score&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;ball&quot;&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/pongrid.css&quot; /&gt;
&lt;script src=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/pongrid.js&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;button style=&quot;margin: 0 auto;display: block;font-family: inherit;font-size: inherit;&quot; onclick=&quot;document.querySelector(&#39;.pongrid .screen&#39;).classList.toggle(&#39;alternative-look&#39;);&quot;&gt;Change the look of the game!&lt;/button&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve been a huge fan of CSS grid for a long time, ever since it got implemented in browsers, back in 2015 (see my &lt;a href=&quot;https://patrickbrosset.com/articles/2015-08-26-the-future-of-layout-with-CSS--Grid-Layouts/&quot;&gt;future of layout with CSS Grid Layouts&lt;/a&gt; article from back then).&lt;/p&gt;
&lt;p&gt;Grid is made for layouts, but how fun is that? What if we could use it as a canvas instead? After all, grid lets you define any number of rows and columns in an area and then place items anywhere within them, by using a very handy coordinate system via &lt;code&gt;grid-row&lt;/code&gt; and &lt;code&gt;grid-column&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;m sure some day someone will display a runnable version of DOOM in a CSS grid, but for now, I limited myself to a playable version of the retro game Pong, which renders in a CSS grid.&lt;/p&gt;
&lt;p&gt;The ball and paddles are HTML elements that get re-positioned within the grid by using the &lt;code&gt;grid-row&lt;/code&gt; and &lt;code&gt;grid-column&lt;/code&gt; properties as you play the game. This is different to how most web games usually work, which is by running a draw loop which clears and then draws onto a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; at every frame. Here, the elements remain in the DOM all the time, and only their position in the grid changes.&lt;/p&gt;
&lt;p&gt;Open DevTools (&lt;kbd&gt;F12&lt;/kbd&gt;) to see for yourself. Right-click the game and select &lt;strong&gt;Inspect&lt;/strong&gt; to inspect the game DOM structure. If you enable the grid overlay, you should see something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/pong-grid.png&quot; alt=&quot;The above game, with its CSS grid highlighted. Vertical and horizontal lines are visible as an overlay and show the pixels of the game.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One cool thing I learned while making this demo is the shorthand syntax for &lt;code&gt;grid-area&lt;/code&gt; allows you to position elements respective to grid line numers. I&#39;ve only been using the property for named areas until now. But now, I know that &lt;code&gt;grid-row-start: 1; grid-row-end: 3; grid-column-start: 2; grid-column-end: 4;&lt;/code&gt; can be shortened to &lt;code&gt;grid-area: 1 / 2 / 3 / 4;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Having fun pays off.&lt;/p&gt;
&lt;p&gt;Play has a funny way of turning complex concepts into instincts. CSS math is one of those complex concepts: &lt;code&gt;cos()&lt;/code&gt;, &lt;code&gt;sin()&lt;/code&gt;, &lt;code&gt;tan()&lt;/code&gt;, transforms, paths… using these can get abstract very quickly. But when you&#39;re building for fun, trying to create something nice without constraints, these abstractions become tools, like paint brushes.&lt;/p&gt;
&lt;p&gt;Instead of trying to understand the theory first, and then create something very specific, you chase a visual daydream, something that isn&#39;t clearly defined, and you learn as you go. While creative coding for fun, you inevitably bump into aspects of the web platform that you tweak and experiment with until you get the effect you want, and that&#39;s what makes it stick.&lt;/p&gt;
&lt;p&gt;Here is a kaleidoscope built with CSS. No canvas, just &lt;code&gt;clip-path&lt;/code&gt;, &lt;code&gt;tan()&lt;/code&gt;, &lt;code&gt;scale&lt;/code&gt;, &lt;code&gt;transform&lt;/code&gt;, and some CSS calculations. A little toy which taught me a lot about geometry in CSS.&lt;/p&gt;
&lt;div class=&quot;kaleidoscope&quot;&gt;
  &lt;div class=&quot;wrapper animated&quot;&gt;
    &lt;div class=&quot;blades&quot;&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;blade&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;controls&quot;&gt;
      &lt;label for=&quot;size-control&quot;&gt;Image size
        &lt;input type=&quot;range&quot; min=&quot;10&quot; max=&quot;500&quot; value=&quot;192&quot; id=&quot;size-control&quot; /&gt;
      &lt;/label&gt;
      &lt;label for=&quot;position-control&quot;&gt;Image position
        &lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;500&quot; value=&quot;350&quot; id=&quot;position-control&quot; /&gt;
      &lt;/label&gt;
      &lt;label for=&quot;rotation-control&quot;&gt;Image Rotation
        &lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;360&quot; value=&quot;0&quot; id=&quot;rotation-control&quot; /&gt;
      &lt;/label&gt;
      &lt;label for=&quot;number-control&quot;&gt;Number of blades
        &lt;input type=&quot;range&quot; min=&quot;8&quot; max=&quot;30&quot; value=&quot;10&quot; step=&quot;2&quot; id=&quot;number-control&quot; /&gt;
      &lt;/label&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/kaleidoscope.css&quot; /&gt;
&lt;script src=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/kaleidoscope.js&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
&lt;p&gt;At the end of the day though, I still suck at doing any kind of complex maths in CSS. If you want to get your mind blown, check out &lt;a href=&quot;https://codepen.io/thebabydino&quot;&gt;Ana Tudor&lt;/a&gt;, &lt;a href=&quot;https://codepen.io/t_afif&quot;&gt;Temani Afif&lt;/a&gt;, or &lt;a href=&quot;https://codepen.io/amit_sheen&quot;&gt;Amit Sheen&lt;/a&gt; on CodePen. These folks are on another level.&lt;/p&gt;
&lt;p&gt;What I love about these experiments is how often they reveal hidden layers deep within what would otherwise appear as ordinary parts of the web.&lt;/p&gt;
&lt;p&gt;CSS box-shadow is an example of one of these parts which, at first sight, may seem familiar, almost boring. But it also comes with hidden complexity and extra capabilities that, at least for me, only playing for no other reason than fun could reveal. Among them: the &lt;code&gt;box-shadow&lt;/code&gt; syntax, the ability to layer multiple shadows, the way they spread from each other, and the ability to animate shadows.&lt;/p&gt;
&lt;p&gt;So, here is a demo that&#39;s all about taking the smallest possible unit: a single CSS property, and seeing how weird I could make it:&lt;/p&gt;
&lt;div class=&quot;shadows&quot;&gt;
  &lt;div class=&quot;wrapper&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/shadows.css&quot; /&gt;
&lt;script src=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/shadows.js&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
&lt;p&gt;There are twenty circles in the above visual. Each is a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element with rounded corners and is randomly positioned. Each has three different shadows applied, and the shadows are animated. For extra fun, I used &lt;code&gt;mix-blend-mode&lt;/code&gt; to get some interesting interactions where the circles overlap, and you can also click the area to colorize it.&lt;/p&gt;
&lt;p&gt;Playing with this taught me a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I finally remember the syntax for &lt;code&gt;box-shadow&lt;/code&gt;. It is: &lt;code&gt;box-shawdow: &amp;lt;x offset&amp;gt; &amp;lt;y offset&amp;gt; &amp;lt;blur radius&amp;gt; &amp;lt;spread radius&amp;gt; &amp;lt;color&amp;gt;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I love that you can reset the blur radius to draw a shape around any element. For example &lt;code&gt;box-shadow: 0 0 0 10px red&lt;/code&gt; draws a 10px red &amp;quot;border&amp;quot; around an element.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I also now understand how multiple shadows stack. Subsequent shadows are drawn below previous ones. And the spread value is relative to the size of the previous shadow too.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mix-blend-mode&lt;/code&gt; is really fun when drawing abstract stuff like this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These experiments aren&#39;t just toys. They&#39;re reminders that the web platform is alive and kicking, full of features and capabilities that are just waiting to be discovered. They&#39;re also a reminder that we can all be creative people, given the right mindset and the right tools, and that learning through play is a powerful way to grow as a developer.&lt;/p&gt;
&lt;p&gt;Take an hour to play. Try something weird. Learn something new. If you do make something fun, share it with others, on your personal website, on social media, on codepen, or with your colleagues.&lt;/p&gt;
&lt;p&gt;Alright, one last silly experiment before I wrap this up. Meet a new special kind of popup hell!&lt;/p&gt;
&lt;div class=&quot;popup-hell&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/popup-hell.css&quot; /&gt;
&lt;script src=&quot;https://patrickbrosset.com/assets/fun-with-the-web-embedded-demos/popup-hell.js&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
&lt;p&gt;Try moving the red popup above. And click below to make it even worse:&lt;/p&gt;
&lt;p&gt;&lt;button style=&quot;margin: 0 auto;display: block;font-family: inherit;font-size: inherit;&quot; onclick=&quot;document.querySelector(&#39;.popup-hell&#39;).classList.toggle(&#39;no-clip&#39;);&quot;&gt;Make it even more hellish!&lt;/button&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-platform-dx.github.io/web-features-explorer/features/anchor-positioning/&quot;&gt;Anchor positioning&lt;/a&gt; is very new and not quite ready for prime time yet. To me, this means it&#39;s the perfect time to learn more about it. I&#39;ve seen countless messages on social media, talks, and demos about anchor positioning already, but I have never tried it myself.&lt;/p&gt;
&lt;p&gt;In this demo, there&#39;s one hundred popups (remember the good old days of popup hell on the web?), and they&#39;re all chained together using anchor positioning. They&#39;re randomly tethered to other popups, and randomly placed respective to their anchors. When you move the red popup around, all other popups randomly follow it because they&#39;re indirectly anchored to it via other popups, possibly through multiple levels of anchoring.&lt;/p&gt;
&lt;p&gt;This is obviously not something you&#39;d ever need to do in a real project. But in trying to achieve the exact look I was after, I learned a lot about anchor positioning. It made me realize that you can tether multiple elements to the same anchor, it made me use properties like &lt;code&gt;anchor-name&lt;/code&gt;, &lt;code&gt;position-anchor&lt;/code&gt;, the &lt;code&gt;anchor()&lt;/code&gt; function, and it also made me aware of other capabilities, such as the &lt;code&gt;position-try&lt;/code&gt; property, but also the &lt;code&gt;anchor&lt;/code&gt; HTML attribute.&lt;/p&gt;
&lt;h2&gt;Don&#39;t forget to play&lt;/h2&gt;
&lt;p&gt;The web is an amazingly playful app platform. That&#39;s what it was to me some 25 odd years ago, and that&#39;s what it still is. A platform where a text editor and a browser of your choice is all you need to start creating. No gatekeepers, no approval processes.&lt;/p&gt;
&lt;p&gt;Every silly experiment, demo, and useless project I&#39;ve shown here and others I&#39;ve worked on over the years have taught me something new about the web, and have kept my passion for it alive.&lt;/p&gt;
&lt;p&gt;The web is still magic, and it still matters. Even today when it feels like AI is replacing how people consume and create content.&lt;/p&gt;
&lt;p&gt;Go create something ridiculous, learn by playing, keep the web weird and wonderful.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Microsoft Edge and Interop 2026</title>
    <link href="https://patrickbrosset.com/articles/2026-02-12-microsoft-edge-and-interop-2026/"/>
    <updated>2026-02-12T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-02-12-microsoft-edge-and-interop-2026/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2026/02/12/microsoft-edge-and-interop-2026/">https://blogs.windows.com/msedgedev/2026/02/12/microsoft-edge-and-interop-2026/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Embrace the chaos — The shape and flow of masonry layouts</title>
    <link href="https://patrickbrosset.com/articles/2026-02-23-embrace-the-chaos-the-shape-and-flow-of-masonry-layouts/"/>
    <updated>2026-02-23T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-02-23-embrace-the-chaos-the-shape-and-flow-of-masonry-layouts/</id>
    <content type="html">
      
        &lt;p&gt;Masonry is a cool looking layout that can create visually stunning designs. It&#39;s not for every use case, that&#39;s for sure, but it has its place in a web designer&#39;s toolbox, and people have been wanting to do this for a long time.&lt;/p&gt;
&lt;p&gt;The good news is, masonry is finally getting implemented for real in browsers, and should be usable very soon thanks to &lt;code&gt;display: grid-lanes;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In this article, I want to explore how masonry relates to other types of layouts, and to do this, I&#39;ll focus on something I don&#39;t see being talked about often: shape and flow.&lt;/p&gt;
&lt;h2&gt;Shape and flow of a layout&lt;/h2&gt;
&lt;p&gt;The shape of a layout is the visual structure that it creates for its items. For example, if a layout container makes its children elements appear in columns, then the shape of that layout is column-based.&lt;/p&gt;
&lt;p&gt;The flow of a layout is the path which items follow to appear within the layout&#39;s structure. For example, in a column-shape layout, the items might fill each column one by one, or they might fill rows first, going left to right.&lt;/p&gt;
&lt;p&gt;When using a CSS layout on a webpage, what&#39;s more important to you? The visual shape of the layout or the way that the items flow within that shape?&lt;/p&gt;
&lt;p&gt;Both are important, right?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The final shape of the layout is important because that&#39;s what users see and that&#39;s what designers want.&lt;/li&gt;
&lt;li&gt;The flow of the items is also important because that&#39;s how users read and interact with the content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, ideally, any type of CSS layout should allow you to define and customize, to some extent, both of these aspects.&lt;/p&gt;
&lt;h2&gt;Layout types&lt;/h2&gt;
&lt;p&gt;Let&#39;s consider the following layout types (I&#39;m ignoring tables, and positioning techniques like absolute/fixed in this article):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Normal flow&lt;/li&gt;
&lt;li&gt;Flexbox&lt;/li&gt;
&lt;li&gt;Grid&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then compare this to masonry.&lt;/p&gt;
&lt;p&gt;They all have their specific shapes and ways of handling the flow of items through the layout.&lt;/p&gt;
&lt;h3&gt;Normal flow&lt;/h3&gt;
&lt;p&gt;Normal flow is the default layout type in CSS that&#39;s used anytime you don&#39;t specify a different layout.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Shape&lt;/em&gt;: the normal flow layout has no predefined shape beyond being a box, like any element on the web. A box, nothing more, nothing less. Normal flow doesn&#39;t impose any specific structure to its items.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Flow&lt;/em&gt;: the way items flow through the layout depends on the items themselves, not the layout shape (which doesn&#39;t exist). Block-level items stack vertically, one after the other. Inline-level items flow horizontally, wrapping to the next line when they reach the edge of the container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Normal flow is how the web got started, it&#39;s great for documents where different sections are stacked on top of each other, and text is the main content.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/normal-flow.avif&quot; alt=&quot;Illustration of the normal flow layout with a grey outline box that contains red block and inline elements&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Flexbox&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Shape&lt;/em&gt;: just like normal flow, flexbox doesn&#39;t have a predefined shape and doesn&#39;t impose any specific structure to its items beyond its size and rectangular shape.&lt;/p&gt;
&lt;p&gt;At least, as long as you don&#39;t use &lt;code&gt;flex-wrap&lt;/code&gt;. If you do and if you have enough items, then flexbox creates multiple flex lines to place these items. This creates a structure which your items have to follow.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Flow&lt;/em&gt;: the way items flow through a flexbox layout is determined by the &lt;code&gt;flex-direction&lt;/code&gt; property. If set to &lt;code&gt;column&lt;/code&gt;, items stack vertically, one after the other. If set to &lt;code&gt;row&lt;/code&gt;, items flow horizontally.&lt;/p&gt;
&lt;p&gt;Flexbox also supports &lt;code&gt;row-reverse&lt;/code&gt; and &lt;code&gt;column-reverse&lt;/code&gt; values, which changes the starting edge of the layout.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/flexbox-shape-flow.avif&quot; alt=&quot;Illustration of the flexbox layout with three examples: a column direction flex container, a row direction flex container, and a wrapping row flex container.&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Grid&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Shape&lt;/em&gt;: grid is different from the two previous layout types in that it does impose a very specific shape, or structure, to its items. Grid lets you define a two-dimensional shape by using the &lt;code&gt;grid-template-columns&lt;/code&gt; and &lt;code&gt;grid-template-rows&lt;/code&gt; properties. With this, you create rows and columns of specific sizes, and items can&#39;t escape this structure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Flow&lt;/em&gt;: the way items flow through a grid layout is highly customizable.&lt;/p&gt;
&lt;p&gt;You can either place all items in specific cells by using &lt;code&gt;grid-column&lt;/code&gt;, &lt;code&gt;grid-row&lt;/code&gt;, or &lt;code&gt;grid-area&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Or you can decide how the items flow through the cells by using the &lt;code&gt;grid-auto-flow&lt;/code&gt; property. When not set, the default value makes the items fill each row one by one before going down to the next row.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/grid-shape-flow.avif&quot; alt=&quot;Illustration of the grid layout with two examples: a grid with with a row auto flow, and one with a column auto flow.&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Masonry&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Shape&lt;/em&gt;: masonry is similar to grid in that it imposes a specific shape to the items, but it&#39;s also similar to flexbox in that it only does this along one dimension. The shape is either made up of columns or rows, depending on whether you define &lt;code&gt;grid-template-columns&lt;/code&gt; or &lt;code&gt;grid-template-rows&lt;/code&gt;. This creates lanes where items are placed, and which they can&#39;t really escape.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Flow&lt;/em&gt;: the way items flow through a masonry layout is determined by the masonry algorithm.&lt;/p&gt;
&lt;p&gt;This algorithm picks the right lane for each item based on which lane has the most space available. Because of this, you don&#39;t really get to decide how items flow through a masonry layout. It&#39;s imposed by the packing algorithm, and depends on the shape of the layout.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/masonry-shape-flow.avif&quot; alt=&quot;Illustration of masonry showing a vertical column-based masonry layout and items of varying heights being placed in the shortest column first, and a row-based horizontal layout, with varying width items.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The masonry packing algorithm&lt;/h2&gt;
&lt;p&gt;Let&#39;s go back to the algorithm which masonry uses to place items in the layout.&lt;/p&gt;
&lt;p&gt;This algorithm places each item one after the other, and at each step, finds the lane where the most space remains.&lt;/p&gt;
&lt;p&gt;For example, in a vertical masonry layout that already has 19 items, the 20th item will be placed in the column where the total height of all items is the shortest. In the illustration below, that&#39;s the second column from the left:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/shortest-column.avif&quot; alt=&quot;Masonry layout with five columns and 19 items. Columns contain colored and numbered blocks of varying height. Column 2 has the shortest items and is indicated with an arrow.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In case of ties, the lane that&#39;s closest to the starting edge will win. In a left-to-right layout, the leftmost lane wins ties; in a right-to-left layout, the rightmost lane wins ties. You can also influence this by using the &lt;code&gt;flow-tolerance&lt;/code&gt; property, but let&#39;s not get into that here.&lt;/p&gt;
&lt;p&gt;It&#39;s pretty interesting to think of this algorithm as being &lt;em&gt;opportunistic&lt;/em&gt;. In masonry, the emphasis is on optimizing to reduce empty space, rather than on controlling the order of items.&lt;/p&gt;
&lt;p&gt;For example, it&#39;s easy to end up in situations where items which follow each other in the DOM appear far apart visually, or even in seemingly random order:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/8-9-10-11.avif&quot; alt=&quot;A close up view of the previous layout with items 8, 9, 10, and 11 appearing in a seemingly random order.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There may very well be cases, though, where items of a masonry layout logically follow each other in a natural reading sequence, especially if items are of similar sizes. But that&#39;s probably the exception rather than the norm:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/15-16-17.avif&quot; alt=&quot;A close up view of the previous layout with items 15, 16, and 17 appearing almost next to each other horizontally.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Is masonry more like grid or flexbox?&lt;/h2&gt;
&lt;p&gt;Masonry is its own thing, but finding similarities with other layout types can be helpful. Indeed, masonry is still largely being defined by the CSS Working Group, and there are parts of it which are still up for debate. Resolving the remaining open questions take a deep understanding of other layout types, and existing CSS properties because reusing existing syntax and being consistent can make a big difference for developers.&lt;/p&gt;
&lt;p&gt;So let&#39;s try to find consistency with other types of layouts.&lt;/p&gt;
&lt;h3&gt;Is masonry like grid?&lt;/h3&gt;
&lt;p&gt;Well, first of all, masonry and grid have a pretty similar display type name:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;display: grid;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display: grid-lanes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But they&#39;re also similar in that you define the shape of both layouts by using &lt;code&gt;grid-template-columns&lt;/code&gt; or &lt;code&gt;grid-template-rows&lt;/code&gt;. You can also place items in specific lanes of a masonry layout by using &lt;code&gt;grid-column&lt;/code&gt; and &lt;code&gt;grid-row&lt;/code&gt;, just like you can use these properties to place items in specific cells in a grid layout.&lt;/p&gt;
&lt;p&gt;It also kind of looks like a grid, at least sometimes, and if most of your items are about the same size. If items are of very distinct sizes however, the grid-like appearance quickly breaks down:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://patrickbrosset.com/assets/looks-like-a-grid.avif&quot; alt=&quot;Two masonry layouts. On the left, one with items that are approximately of the same size, leading to a layout that almost looks like a grid where items are aligned along both axes. On the right, one with items of very different sizes, leading to a layout that does not look like a grid at all.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, unlike grid, masonry doesn&#39;t have a notion of cells. You can&#39;t place items in specific cells, only in specific &lt;em&gt;lanes&lt;/em&gt;. And when you do, the items are placed at the end of that lane, following other items that might already be there.&lt;/p&gt;
&lt;p&gt;Also, as we&#39;ve seen before with masonry, you don&#39;t get to decide how items flow through the layout. The flow is always determined by the packing algorithm, which places items in the shortest lane available at each step.&lt;/p&gt;
&lt;p&gt;But anyway, let&#39;s entertain the idea that masonry is similar to grid.&lt;/p&gt;
&lt;p&gt;In a grid, you describe the shape of the layout by using &lt;code&gt;grid-template-columns&lt;/code&gt; and &lt;code&gt;grid-template-rows&lt;/code&gt;. If you want two columns and ten rows, you use &lt;code&gt;grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(10, auto);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This defines the scaffold of the shape you want to see. You get to define it explicitly, with keywords that make sense: &lt;code&gt;columns&lt;/code&gt; and &lt;code&gt;rows&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And then, you can change the flow of the items within that shape as we&#39;ve seen before, either by positioning items explicitely by using &lt;code&gt;grid-column&lt;/code&gt; and &lt;code&gt;grid-row&lt;/code&gt;, or, by using the &lt;code&gt;grid-auto-flow&lt;/code&gt; property:&lt;/p&gt;
&lt;p&gt;In grid, flow and shape are independent, and you get to define both separately. That&#39;s because the &lt;code&gt;grid-template-*&lt;/code&gt; properties completely describe the shape of the layout, and the &lt;code&gt;grid-auto-flow&lt;/code&gt; property only describes the flow.&lt;/p&gt;
&lt;p&gt;Masonry is kind of like that but different. For starters, you only define one dimension of the shape, not two. So you either use &lt;code&gt;grid-template-columns&lt;/code&gt; or &lt;code&gt;grid-template-rows&lt;/code&gt;, but not both. And once the lanes are defined, you don&#39;t get to change the flow of the items because it&#39;s determined by the masonry packing algorithm.&lt;/p&gt;
&lt;p&gt;The only aspect of the flow you can change is the starting edge of the layout: which lane gets filled first in case of ties.&lt;/p&gt;
&lt;h2&gt;So is masonry more like flexbox then?&lt;/h2&gt;
&lt;p&gt;Flexbox and masonry share quite a few similarities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They&#39;re both one-dimensional layouts meaning that you only define one dimension of the layout shape, not two.&lt;/li&gt;
&lt;li&gt;You don&#39;t get to define the flow of items beyond the starting edge and the direction of the layout.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In flexbox, you use the &lt;code&gt;flex-direction&lt;/code&gt; property to define both the shape of the layout and the starting edge. For example, &lt;code&gt;row&lt;/code&gt; creates a horizontal row of items that start from the left edge of the layout container (in western left-to-right languages), and &lt;code&gt;row-reverse&lt;/code&gt; creates the same shape but starts placing items from the right edge instead.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;flex-direction&lt;/code&gt; property also supports the &lt;code&gt;row-reverse&lt;/code&gt; and &lt;code&gt;column-reverse&lt;/code&gt; values, letting you change the starting edge of the layout.&lt;/p&gt;
&lt;p&gt;Finally, flexbox also supports the &lt;code&gt;flex-wrap&lt;/code&gt; property which lets you reverse the wrapping direction for multi-line flex containers, which in a way is also a way to set the starting edge of the layout.&lt;/p&gt;
&lt;p&gt;So with flexbox, you get to define the shape of the layout only (row, column, wrapping) and the edges from which items start to be placed. But, you don&#39;t get to define the flow of the items beyond that.&lt;/p&gt;
&lt;p&gt;Masonry is kind of similar to flexbox in that you only get to define one dimension of the layout. The layout is either made up of columns or rows.&lt;/p&gt;
&lt;p&gt;And then, you can decide which of the edges should the items start to be placed from. But you don&#39;t get to choose how they actually stack up beyond that. The packing algorithm always determines the flow of items.&lt;/p&gt;
&lt;p&gt;This makes flexbox and masonry pretty similar in my opinion. Does that mean that masonry should be made consistent with flexbox and therefore support &lt;code&gt;grid-lanes-direction&lt;/code&gt; and &lt;code&gt;grid-lanes-wrap&lt;/code&gt;?&lt;/p&gt;
&lt;h2&gt;Overall though, masonry is its own thing&lt;/h2&gt;
&lt;p&gt;Overall though, masonry is its own thing. It shares a few aspects of both grid and flexbox, but it also has some unique characteristics.&lt;/p&gt;
&lt;p&gt;Masonry kind of feels chaotic, both from a developer&#39;s and a user&#39;s perspective.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;From a developer&#39;s perspective:&lt;/p&gt;
&lt;p&gt;It&#39;s hard to control the reading order of items, because the packing algorithm is opportunistic and depends on the sizes of the items already placed in the layout.&lt;/p&gt;
&lt;p&gt;If you have very precise control over the number of lanes and the exact size of your items, perhaps you might be able to customize the reading order to what you want. But in that case, wouldn&#39;t you be using a CSS grid instead?&lt;/p&gt;
&lt;p&gt;The whole point of masonry is that it&#39;s easy to use for cases where you don&#39;t really care about the order of the items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And from a user&#39;s perspective:&lt;/p&gt;
&lt;p&gt;It&#39;s also hard to know in which order to read items. An English reader might try to start from the top left and go right, then down. But because of the way items are packed, there might not always be a clear path to follow, and the user might have to jump around the layout to find the next item.&lt;/p&gt;
&lt;p&gt;That&#39;s why masonry is usually best for image galleries, portfolios, and other cases where the visual arrangement is more important than the reading order.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a developer, you don&#39;t really control the flow, just the direction of packing. And as a user you don&#39;t really &lt;em&gt;see&lt;/em&gt; the flow either, it looks chaotic and you don&#39;t know which order to scan items in. There&#39;s no clear reading order. So why trying to set one?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Embrace the chaos!&lt;/strong&gt;&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>WebMCP updates, clarifications, and next steps</title>
    <link href="https://patrickbrosset.com/articles/2026-02-23-webmcp-updates-clarifications-and-next-steps/"/>
    <updated>2026-02-23T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-02-23-webmcp-updates-clarifications-and-next-steps/</id>
    <content type="html">
      
        &lt;p&gt;Six months have pased since I wrote about &lt;a href=&quot;https://patrickbrosset.com/articles/2025-08-28-ai-agents-and-the-web-a-proposal-to-keep-developers-in-the-loop/&quot;&gt;WebMCP&lt;/a&gt;. Both an eternity in the world of AI, and a blink of an eye in the world of web standards.&lt;/p&gt;
&lt;p&gt;In my earlier post, I introduced the general idea: letting you, web developers, register tools on your pages so AI agents can use them. The proposal is still evolving, and I wanted to provide a quick update, as well as some corrections to my original post.&lt;/p&gt;
&lt;p&gt;If you&#39;ve heard about WebMCP recently, that&#39;s likely because Google Chrome &lt;a href=&quot;https://developer.chrome.com/blog/webmcp-epp&quot;&gt;announced their early preview&lt;/a&gt;. Keep in mind, however, that &lt;a href=&quot;https://github.com/webmachinelearning/webmcp/blob/main/docs/proposal.md&quot;&gt;WebMCP&lt;/a&gt; isn&#39;t from a single vendor. It&#39;s Microsoft and Google working closely together through &lt;a href=&quot;https://www.w3.org/groups/wg/webmachinelearning/&quot;&gt;the Web Machine Learning Working Group at W3C&lt;/a&gt;. That&#39;s many people collaborating to shape how AI agents will soon interact with the web.&lt;/p&gt;
&lt;h2&gt;WebMCP and MCP: clearing up the confusion&lt;/h2&gt;
&lt;p&gt;In my first post, I said that the browser acted as an &lt;em&gt;MCP server&lt;/em&gt;. That&#39;s not exactly right. I was simplifying how WebMCP relates to the Model Context Protocol.&lt;/p&gt;
&lt;p&gt;The spirit of it is correct. The browser does become an agent-accessible interface to the page. But the reality is more nuanced:&lt;/p&gt;
&lt;p&gt;MCP is three layers stacked together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Primitives&lt;/strong&gt;: the tools, resources, prompts, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data layer&lt;/strong&gt;: how the MCP client and server talk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transport layer&lt;/strong&gt;: how messages move around.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;WebMCP only really cares about that first layer. A WebMCP tool looks almost exactly like an MCP tool. Same name, same description, same input schema, same implementation function.&lt;/p&gt;
&lt;p&gt;But the browser handles the other two layers for you. The spec even says this outright:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Implementation of the data layer to arbitrate access to these primitives for an Agent is left to the browser.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why does this matter? A few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The browser can stay compatible as MCP evolves. You don&#39;t break when the protocol changes.&lt;/li&gt;
&lt;li&gt;The browser can enforce web-specific security policies, for keeping users safe.&lt;/li&gt;
&lt;li&gt;You don&#39;t implement MCP yourself. You just write JavaScript functions. That&#39;s it.&lt;/li&gt;
&lt;li&gt;The API feels natural for web development.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So what is WebMCP really? &lt;strong&gt;It&#39;s an API for exposing tools from a webpage. The browser then translates those tools into MCP format when talking to agents&lt;/strong&gt;. Under the hood, the browser is doing the protocol work for you.&lt;/p&gt;
&lt;p&gt;The spec uses the term &lt;strong&gt;Model context provider&lt;/strong&gt;. Through &lt;code&gt;navigator.modelContext.provideContext()&lt;/code&gt; (and other APIs), your webpage provides context. It provides the tools that an AI agent then uses, but it&#39;s the browser that talks to the agent.&lt;/p&gt;
&lt;h2&gt;What&#39;s new since August 2025?&lt;/h2&gt;
&lt;p&gt;As a result of the collaboration, the spec keeps getting better. It&#39;s still in draft form as of early 2026, but the core API design is taking shape. Here are the highlights of what&#39;s new since August 2025:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;API naming&lt;/strong&gt;: The API now lives at &lt;code&gt;window.navigator.modelContext&lt;/code&gt;. Earlier versions called it &lt;code&gt;window.agent&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User interaction built-in&lt;/strong&gt;: The spec now has &lt;code&gt;agent.requestUserInteraction()&lt;/code&gt; to let tools ask the browser to get user confirmation before doing something. This is to keep the human in the loop, and prevent an AI agent from completing a purchase without asking you first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Smarter tool management&lt;/strong&gt;: You still have &lt;code&gt;navigator.modelContext.provideContext()&lt;/code&gt; to set all tools at once, but now there&#39;s also &lt;code&gt;registerTool()&lt;/code&gt; and &lt;code&gt;unregisterTool()&lt;/code&gt;. This is great for single-page apps where you want to enable or disable specific tools based on what&#39;s happening in your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MCP relationship spelled out&lt;/strong&gt;: The spec now has a whole section on &amp;quot;&lt;a href=&quot;https://github.com/webmachinelearning/webmcp/blob/main/docs/proposal.md#intersection-with-mcp&quot;&gt;Intersection with MCP&lt;/a&gt;&amp;quot;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What&#39;s next?&lt;/h2&gt;
&lt;p&gt;AI agents are already using the web, that much is true. On the Edge team, we care about keeping not only the human in the loop, but developers too. That&#39;s why we care about WebMCP and will continue to collaborate with the community to improve it.&lt;/p&gt;
&lt;p&gt;The best way to make it great is by testing it with real scenarios and then providing informed feedback. Google&#39;s early preview is a great step towards this, and stay tuned for more news from the Edge team as well!&lt;/p&gt;
&lt;p&gt;In the meantime, if you have ideas, feedback, concerns, or just want to get involved, please take a look at the &lt;a href=&quot;https://github.com/webmachinelearning/webmcp/blob/main/docs/proposal.md&quot;&gt;proposal&lt;/a&gt; and reach out, for example by &lt;a href=&quot;https://github.com/webmachinelearning/webmcp/issues&quot;&gt;opening a new issue&lt;/a&gt; or jumping into existing discussions.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Making keyboard navigation effortless</title>
    <link href="https://patrickbrosset.com/articles/2026-03-05-making-keyboard-navigation-effortless/"/>
    <updated>2026-03-05T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-03-05-making-keyboard-navigation-effortless/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2026/03/05/making-keyboard-navigation-effortless/">https://blogs.windows.com/msedgedev/2026/03/05/making-keyboard-navigation-effortless/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Using CSS animations as state machines to remember focus and hover states with CSS only</title>
    <link href="https://patrickbrosset.com/articles/2026-03-09-using-css-animations-as-state-machines-to-remember-focus-and-hover-states-with-css-only/"/>
    <updated>2026-03-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-03-09-using-css-animations-as-state-machines-to-remember-focus-and-hover-states-with-css-only/</id>
    <content type="html">
      
        &lt;p&gt;Recently, I was searching for a way to style an element depending on whether it was ever focused.&lt;/p&gt;
&lt;p&gt;I wanted to do this without JavaScript, just with CSS, because I was going to use it in a demo about the new &lt;a href=&quot;https://blogs.windows.com/msedgedev/2026/03/05/making-keyboard-navigation-effortless/&quot;&gt;&lt;code&gt;focusgroup&lt;/code&gt; web platform feature&lt;/a&gt;. Focusgroup handles a lot of keyboard navigation logic for you, for free, with only an HTML attribute. So adding a bunch of JavaScript code to track past focus states to that demo felt like a step back.&lt;/p&gt;
&lt;p&gt;I kept thinking about this problem, but couldn&#39;t find a solution. See, storing a click state is easy. You can use a checkbox element and then apply styles based on whether its &lt;code&gt;:checked&lt;/code&gt; pseudo-class matches. You can also hide the checkbox itself and only show its corresponding &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element if you prefer. Heck, you can even put your checkbox anywhere you want and then use the &lt;code&gt;:has()&lt;/code&gt; pseudo-class to style other elements based on that checkbox&#39;s state.&lt;/p&gt;
&lt;p&gt;But, I couldn&#39;t find anything similar for tracking if an element had been focused. And then, it hit me: what if I used a CSS animation for this?&lt;/p&gt;
&lt;h2&gt;Using CSS animations as state machines&lt;/h2&gt;
&lt;p&gt;CSS animations are basically state machines. They can change the value of any property over time. The trick is advancing the time to the right point, in order to reach the state you want to remember, and then keep it there.&lt;/p&gt;
&lt;p&gt;Fortunately, CSS animations come with two very useful properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;animation-play-state&lt;/code&gt;, which we can use to pause the animation in its initial state.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;animation-fill-mode: forwards&lt;/code&gt;, which we can use to keep the animation in its end state after it finishes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s use these properties to set up our animation:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.remember-focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; remember-focus&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .00001s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-timing-function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-fill-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; forwards&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-play-state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; paused&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, using the shorthand syntax:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.remember-focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; remember-focus .00001s linear forwards paused&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.remember-focus&lt;/code&gt; class sets an animation on the element, keeping it paused for now, but filling forwards so it retains the end state once it runs.&lt;/p&gt;
&lt;p&gt;Notice the weirdly short animation duration of &lt;code&gt;.00001s&lt;/code&gt;. That&#39;s because we want the animation to reach its end state immediately after starting. The duration needs to be short enough to be effectively instant to the user.&lt;/p&gt;
&lt;p&gt;Whenever we&#39;re ready to change the state, all we have to do is play the animation. Let&#39;s say we want to do this when the element receives the user focus:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.remember-focus:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-play-state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; running&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now all we need to do is define the animation itself, via the &lt;code&gt;@keyframes&lt;/code&gt; rule. Let&#39;s use background colors for now, to make things simple:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; remember-focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there we have it. By default, the element on which the &lt;code&gt;.remember-focus&lt;/code&gt; class is applied will have a red background. And then, when it receives focus, the animation will run and immediately change the background color to blue. Because of &lt;code&gt;animation-fill-mode: forwards&lt;/code&gt;, the element will stay blue even after it loses focus.&lt;/p&gt;
&lt;p&gt;The cool thing is that that state of the animation is associated to its element, so even if multiple elements have the &lt;code&gt;.remember-focus&lt;/code&gt; class, each one will remember its own focus state independently.&lt;/p&gt;
&lt;h2&gt;Demos&lt;/h2&gt;
&lt;p&gt;Here&#39;s a live demo of the code we&#39;ve just seen. Click or tab to focus the box: the color changes. The color stays even if click somewhere else or tab to another element:&lt;/p&gt;
&lt;style&gt;
.remember-focus {
  padding: .5rem;
  border: 2px dashed black;
  inline-size: max-content;
  color: white;
  font-weight: bold;
  font-size: 1.1rem;
  animation: remember-focus .00001s linear forwards paused;
}
.remember-focus:focus {
  animation-play-state: running;
}
@keyframes remember-focus {
  from {
    background: red;
  }
  to {
    background: blue;
  }
}
&lt;/style&gt;
&lt;div class=&quot;remember-focus&quot; tabindex=&quot;0&quot;&gt;Click or tab to focus me&lt;/div&gt;
&lt;p&gt;This technique works with the &lt;code&gt;:hover&lt;/code&gt; pseudo-class as well. Just change the selector to &lt;code&gt;.remember-focus:hover&lt;/code&gt; and the animation will run on hover instead of focus.&lt;/p&gt;
&lt;p&gt;Here&#39;s a demo that uses hover instead of focus, and has multiple elements with the same class. Try hovering over the boxes below to see them change color:&lt;/p&gt;
&lt;style&gt;
.hover-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: repeat(5, 1fr);
  gap: 1px;
  max-block-size: 50vh;
  aspect-ratio: 1;
}
.hover-grid div {
  animation: remember-hover .00001s linear forwards paused;
}
.hover-grid div:hover {
  animation-play-state: running;
}
@keyframes remember-hover {
  from {
    background: red;
  }
  to {
    background: blue;
  }
}
&lt;/style&gt;
&lt;div class=&quot;hover-grid&quot;&gt;
  &lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;
  &lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;
  &lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;
  &lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;
  &lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Animating other properties&lt;/h2&gt;
&lt;p&gt;Of course changing colors is just a simple thing you can do with this. But, CSS animations are capable of changing any property, including custom properties, and even properties that can&#39;t be animated.&lt;/p&gt;
&lt;p&gt;For example, to display an icon next to an element that was focused, you could use the technique to animate the &lt;code&gt;content&lt;/code&gt; property of the element&#39;s &lt;code&gt;::after&lt;/code&gt; pseudo, even if &lt;code&gt;content&lt;/code&gt; is not animatable.&lt;/p&gt;
&lt;p&gt;Try it yourself: focus the first box below, and then use tab to navigate to the next boxes. You&#39;ll see the icon next to the boxes you&#39;ve already focused change:&lt;/p&gt;
&lt;style&gt;
.checks {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}
.checks .check-on-focus {
  padding: .5rem;
  border: 2px dashed black;
  inline-size: max-content;
  cursor: pointer;
  animation: check-on-focus .00001s linear forwards paused;
}
.checks .check-on-focus:hover {
  background: #eee;
}
.checks .check-on-focus::after {
  content: &quot;🐰&quot;;
  margin-inline-start: .25rem;
  animation: check-on-focus .00001s linear forwards paused;
}
.checks .check-on-focus:focus {
  animation-play-state: running;
}
.checks .check-on-focus:focus::after {
  animation-play-state: running;
}
@keyframes check-on-focus {
  to {
    content: &quot;😺&quot;;
    background: #c7ff6e;
  }
}
&lt;/style&gt;
&lt;div class=&quot;checks&quot;&gt;
  &lt;div class=&quot;check-on-focus&quot; tabindex=&quot;0&quot;&gt;Box 1&lt;/div&gt;
  &lt;div class=&quot;check-on-focus&quot; tabindex=&quot;0&quot;&gt;Box 2&lt;/div&gt;
  &lt;div class=&quot;check-on-focus&quot; tabindex=&quot;0&quot;&gt;Box 3&lt;/div&gt;
  &lt;div class=&quot;check-on-focus&quot; tabindex=&quot;0&quot;&gt;Box 4&lt;/div&gt;
  &lt;div class=&quot;check-on-focus&quot; tabindex=&quot;0&quot;&gt;Box 5&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Using style container queries as an &lt;code&gt;if&lt;/code&gt; statement&lt;/h2&gt;
&lt;p&gt;Let&#39;s conclude by refactoring the code a little bit so it&#39;s easier to reuse in multiple places.&lt;/p&gt;
&lt;p&gt;First, let&#39;s use the technique to swap the value of a custom property called &lt;code&gt;--was-focused&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.track-focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--was-focused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; false&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; track-focus .00001s linear forwards paused&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.track-focus:focus-within&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-play-state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; running&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; track-focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;--was-focused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; true&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can use this by applying the &lt;code&gt;.track-focus&lt;/code&gt; class to any element we want to track the focus state of. For example, a couple of form labels with inputs in them:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;my-form&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;track-focus&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Your name
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;track-focus&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Your email
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, to actually use the &lt;code&gt;--was-focused&lt;/code&gt; property, we can use a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/Guides/Containment/Container_size_and_style_queries#container_style_queries&quot;&gt;container style query&lt;/a&gt;. This way, we can conditionally apply styles to the element itself or to any of its descendants, depending on whether it was ever focused:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@container&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;--was-focused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; true&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; lightgreen&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the result:&lt;/p&gt;
&lt;style&gt;
.my-form {
  padding: 1rem;
  border: 2px dashed black;
}
.track-focus {
  --was-focused: false;
  animation: track-focus .00001s linear forwards paused;
}
.track-focus:focus-within {
  animation-play-state: running;
}
@keyframes track-focus {
  to { --was-focused: true; }
}

@container style(--was-focused: true) {
  input {
    background: lightgreen;
  }
}
&lt;/style&gt;
&lt;form class=&quot;my-form&quot;&gt;
  &lt;label class=&quot;track-focus&quot; for=&quot;name&quot;&gt;
    Your name
    &lt;input type=&quot;text&quot; id=&quot;name&quot; /&gt;
  &lt;/label&gt;
  &lt;label class=&quot;track-focus&quot; for=&quot;email&quot;&gt;
    Your email
    &lt;input type=&quot;text&quot; id=&quot;email&quot; /&gt;
  &lt;/label&gt;
&lt;/form&gt;
&lt;p&gt;That&#39;s it. Let me know if you find a cool use case for this technique, or if you have other ideas to improve it.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Make custom elements behave with scoped registries</title>
    <link href="https://patrickbrosset.com/articles/2026-03-09-make-custom-elements-behave-with-scoped-registries/"/>
    <updated>2026-03-09T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-03-09-make-custom-elements-behave-with-scoped-registries/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/blog/scoped-registries">https://developer.chrome.com/blog/scoped-registries</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Abusing customizable selects</title>
    <link href="https://patrickbrosset.com/articles/2026-03-11-abusing-customizable-selects/"/>
    <updated>2026-03-11T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-03-11-abusing-customizable-selects/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://css-tricks.com/abusing-customizable-selects/">https://css-tricks.com/abusing-customizable-selects/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Monitor and improve your web app’s load performance </title>
    <link href="https://patrickbrosset.com/articles/2026-03-17-monitor-and-improve-your-web-apps-load-performance/"/>
    <updated>2026-03-17T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-03-17-monitor-and-improve-your-web-apps-load-performance/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://blogs.windows.com/msedgedev/2026/03/17/monitor-and-improve-your-web-apps-load-performance/">https://blogs.windows.com/msedgedev/2026/03/17/monitor-and-improve-your-web-apps-load-performance/</a></p>
      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Install web apps with the new HTML install element</title>
    <link href="https://patrickbrosset.com/articles/2026-05-13-install-web-apps-with-the-new-html-install-element/"/>
    <updated>2026-05-13T00:00:00Z</updated>
    <id>https://patrickbrosset.com/articles/2026-05-13-install-web-apps-with-the-new-html-install-element/</id>
    <content type="html">
      
        <p>Read the full article at <a href="https://developer.chrome.com/blog/install-element-ot">https://developer.chrome.com/blog/install-element-ot</a></p>
      
    </content>
  </entry>
  
</feed>
