Matthew Roach All articles from matthewroach.me 2021-12-31T00:00:00Z https://matthewroach.me/ Matthew Roach feed@matthewroach.me 2021 2021-12-31T00:00:00Z https://matthewroach.me/2021/ <blockquote> <p>Well, a global pandemic</p> </blockquote> <p>Not much changed from <a href="https://matthewroach.me/2020">2020</a>. At times it felt like things were getting better, but as we all know that was short lived. Three vacinations received in 2021, how many will there be in 2022?</p> <p>Some items that happened in what felt like a blur of a year, in no particular order -</p> <ul> <li>πŸ‘¨β€πŸ’» Started off the year by switching jobs after the move last year not working out as I expected</li> <li>🚴 Bought a new road bike and enjoyed the early spring sun, even got Pamela out on the open roads a couple of times, even when there was no cake involved</li> <li>πŸ– Enjoyed a family holiday to Edinburgh that included my parents</li> <li>🚘 Switched to an electric car, first a Vauxhall Corsa-e, then to an Volkswagen ID.3</li> <li>πŸŽ‰ Now a parent to a teenager</li> <li>πŸ‘ My son started playing field hockey for the men’s 2nd team</li> <li>πŸ‘ I started playing hockey again, after playing it when I was younger (20+ years ago). Been amazing being able to play alongside my son, and watch how fast he is progressing.</li> <li>πŸ‘©β€πŸŽ“ My wife finished her studies and started a new job with her qualifications</li> </ul> <p>2022 - who knows what’s going to happen…</p> A Themeable Design System 2021-12-09T00:00:00Z https://matthewroach.me/a-themeable-design-system/ <p>I've been involved with design systems for a good number of years. Even before design systems, there where styleguides. Remember those?</p> <p>Design Systems, Styleguides, pattern libraries, etc. They are all great tools/systems and having created and consumed a variety of these systems over the years I've recently come up against a problem where the &quot;traditional&quot; systems where not flexible enough.</p> <p>If you go look at any of the big tech companies desgin system(s) they have published you'll notice they are all aimed at the companies usage, which is great. If you are building an integration on top of shopify using their design system polaris to maintain consistency from your integration to shopify's application is great.</p> <p>Even some big names provide &quot;themes&quot; on top of their systems. If the company has a few identities you are able to use the correct theme for the relevant company.</p> <p>These are all great, but -</p> <p><strong>What if you wanted to provide as much flexibility as possible?</strong></p> <p><strong>What if you didn't know who was going to using the system?</strong></p> <p><strong>What if you wanted the ability for the system to be flexible enough to apply a companies own style to it?</strong></p> <p>These are just a few of the questions I was faced with answering recently. How can we provide a core set of components, that are ultimately fully customisable at the core? But also provide an &quot;out-of-the-box&quot; theme.</p> <p>Based on these questions and more, I came up with following solution which I am going to walk you through.</p> <h2>Technology</h2> <p>For the solution as it is to be used in part of an exisiting ecosystem. The technology stack wasn't completely open. React was a hard requirment, the styling was a bit more open - it was either going to be a CSS-in-JS approach or CSS. I opted for CSS, but with Sass as the integration layer, and the end result being a standalone CSS file.</p> <h2>System Overview</h2> <p>The system is broken down to three parts</p> <ul> <li>Theme</li> <li>Components</li> <li>Blocks</li> </ul> <p>The core of the system is a set of <strong>components</strong>, think of these as the core. The <strong>components</strong> would be used as the building elements of <strong>blocks</strong>, example of such components -</p> <ul> <li>Paragraph</li> <li>Image</li> <li>Caption</li> <li>Date</li> <li>Link</li> </ul> <p>These <strong>components</strong> are very presentational, and are a means to provide a consistent interface to commonly used <strong>components</strong> of the system. The <strong>components</strong> are not limited to outputting text, images, etc, a <strong>component</strong> can also be a layout component.</p> <p><strong>Blocks</strong> are used within the system to provide an entry point and exit point. The entry point being the internal system, and the exit point being the published page. A <strong>block</strong> is a composed component of functionality that is made up primarily of <strong>components</strong>. An example of a <strong>block</strong> would be</p> <ul> <li>Promo Item</li> <li>List of articles</li> <li>Search Results list</li> </ul> <p><strong>Theme</strong> the theme is what brings it all together and holds the styling information. The theme is a collection of Sass maps that hold the CSS properties and values for a <strong>component</strong>, a <strong>block</strong> and also the ability for <strong>component</strong> within a <strong>block</strong> to be styled.</p> <h2>System Breakdown</h2> <h3>Components</h3> <p>To understand how it all fits together and what is involved at each level, I am going to run through a basic example.</p> <p>The paragraph <strong>component</strong> looks as follows</p> <pre><code class="language-javascript">import React from &quot;react&quot;; import PropTypes from &quot;prop-types&quot;; const Paragraph = ({ children }) =&gt; &lt;p className=&quot;c-paragraph&quot;&gt;{children}&lt;/p&gt;; Paragraph.propTypes = { children: PropTypes.string.isRequired, }; export default Paragraph; </code></pre> <p>Very simple and basic React component that takes children and output wraps that in a <code>p</code> tag, where we apply a class of <code>c-paragraph</code>.</p> <p>The class applied to the component is exposed via a Sass file which looks as follows</p> <pre><code class="language-css">.c-paragraph { @include component-properties(&quot;paragraph&quot;); } </code></pre> <p>This would the bare minimum for a component Sass file. It looks a little bare, and not doing anything at the moment. This is because the styling will come from the <strong>theme</strong>. We are using a mixin provided by the system that would output the CSS properties and values for a <code>paragraph</code></p> <h3>Blocks</h3> <p>As mentioned earlier, <strong>blocks</strong> are composed of <strong>components</strong>. An very simple <strong>block</strong> could look like</p> <pre><code class="language-javascript">import React from &quot;react&quot;; import PropTypes from &quot;prop-types&quot;; import { Paragraph } from &quot;components&quot;; const FakeStory = { description: &quot;One of the highest paved roads in Europe, this mountain pass makes for a thrilling road trip into the misty mountains above the Adriatic and Ionian seas.&quot;, }; const Promo = () =&gt; ( &lt;div className=&quot;b-promo&quot;&gt; &lt;Paragraph&gt;{FakeStory.description}&lt;/Paragraph&gt; &lt;/div&gt; ); export default Promo; </code></pre> <p>As you can see it's very basic. The promo <strong>block</strong> is only using a single <strong>component</strong> in this example, but it could use any number of the core <strong>components</strong> if needed. The <strong>block</strong> has a wrapping <code>div</code> with a class <code>b-promo</code> this is then exposed via a <strong>block</strong> Sass file to allow styling of the <strong>block</strong> but also <strong>components</strong> inside a block.</p> <p>The Sass file for a block is as follows</p> <pre><code class="language-css">.b-promo { @include block-components(&quot;promo&quot;); @include block-properties(&quot;promo&quot;); } </code></pre> <p>Similar to <strong>components</strong>, we are making use of mixins to load in the <code>promo</code> block styles, but also load in the styles for <strong>components</strong> within a <strong>block</strong>. How the <strong>component</strong> styles work will be explained below as part of the <strong>theme</strong>.</p> <h3>Theme</h3> <p>The <strong>theme</strong> is where the styling is controlled for a <strong>component</strong>, a <strong>block</strong> and for <strong>components</strong> within a <strong>block</strong>.</p> <p>The <strong>theme</strong> is utilising Sass to extend the capabilites of CSS by providing some mixins and better variable management. The output from the Sass files is a CSS file that is utilising CSS Variables. The primary reason to use Sass is the power it provides with being able to doing looping and provide functions (mixins).</p> <p>Within the <strong>theme</strong> there are multiple Sass maps, following the design token approach it's broken down into four distinct areas</p> <ul> <li>Global</li> <li>Alias</li> <li>Components</li> <li>Blocks</li> </ul> <h4>Global</h4> <p>Global tokens are sets of tokens that are the core to the system. Color pallettes, font sizing, font weight, spacing, etc. Each token in the global tokens map is output as a CSS Variable prefixed with <code>--global-</code>.</p> <pre><code class="language-css">$global: ( &quot;gray-50&quot;: rgb(218 218 218) ); </code></pre> <p>is then converted into the following CSS</p> <pre><code class="language-css">:root { --global-gray-50: rgb(218 218 218); } </code></pre> <h4>Alias</h4> <p>Alias tokens are technically still global tokens, but provide the ability to have a friendly name attached to a given global token. For example, <code>primary-color</code> would be an alias token, and the value of that token would reference a global token. Each token in the alias map is output as a CSS variable - these are not prefixed at all.</p> <pre><code class="language-css">$alias: ( &quot;primary-color&quot;: rgb(100 100 100) ); </code></pre> <p>is then converted into the following CSS</p> <pre><code class="language-css">:root { --primary-color: rgb(100 100 100); } </code></pre> <h4>Components</h4> <p>Component and block tokens are a little different to the global and alias set up, as these are nested Sass maps that follow the CSS property, and value set up.</p> <p>For components the nested Sass map is structured with the first key inside the map being a <strong>component</strong> name. If you remember from before there is a Sass mix that looks up the <strong>component</strong> tokens by name. Meaning the <strong>component</strong> name will need to match that which is set in the components .scss file.</p> <pre><code class="language-css">$tokens: ( components: ( &quot;paragraph&quot;: () ) ); </code></pre> <p>Then inside the <code>paragraph</code> map we have the ability to use CSS properties and values just as if you were writing CSS, allowing you do something like</p> <pre><code class="language-css">'paragraph': ( 'font-size': 16px, 'font-weight': bold, ), </code></pre> <p>If we bring back the code from above of how we write the Sass for the paragraph component we can see how they work together</p> <pre><code class="language-css">.c-paragraph { @include component-properties(&quot;paragraph&quot;); } </code></pre> <p>With the above code and the above example the generated CSS would be</p> <pre><code class="language-css">.c-paragraph { font-size: 16px; font-weight: bold; } </code></pre> <p>Obviously that's not great to use hard coded values as the value items in these maps as you loose the power of the token system. Knowing how the previous global and alias tokens are converted you can reference those as the values</p> <pre><code class="language-css">'paragraph': ( 'font-size': var(--global-font-size-200), 'font-weight': var(--global-font-weight-700), ), </code></pre> <p>this would then make use of the CSS variable in the outputting CSS as follows</p> <pre><code class="language-css">:root { --c-paragraph-font-size: var(--global-font-size-200); --c-paragraph-font-weight: var(--global-font-weight-700); } .c-paragraph { font-size: var(--c-paragraph-font-size); font-weight: var(--c-paragraph-font-weight); } </code></pre> <h4>Blocks</h4> <p>The blocks part of the theme works in a similar way to components the <code>@include block-properties('promo');</code> Sass mixing does pretty much the same as the <code>component-properties</code> version, the only difference is it's looking for the named item within the <code>blocks</code> map.</p> <pre><code class="language-css">$tokens: ( blocks: ( &quot;promo&quot;: () ) ); </code></pre> <p>The added ability you have within a block is the ability to change the style of a <strong>component</strong> differently to that of the base <code>components</code> styling. As we mentioned before <strong>blocks</strong> are composed of <strong>components</strong>, this means by default and using the CSS cascade all components will have the same styling when used within a <strong>block</strong>. But what if you want to make the <code>paragraph</code> component within the <code>promo</code> block to be styled slightly different? Say we wanted to increase the <code>font-size</code> thats where the Sass mixin <code>@include block-components('promo');</code> comes in.</p> <p>Given we have the need to update the <code>paragraph</code> font size within a <code>promo</code> block the blocks map has the ability for us to define <strong>component</strong> styles for a given block.</p> <pre><code class="language-css">$tokens: ( blocks: ( 'promo: ( 'components': ( 'paragraph': ( 'font-size': 18px, ), ), ), ), ); </code></pre> <p>To break down the nested map it can be a little easier read from the inside out. What the map is saying is for the <code>paragraph</code> component inside a <code>block</code> set the <code>font-size</code> to <code>18px</code>. How this is translated to CSS is by using the power of CSS variables, as previously mentioned we are outputting to CSS variables at all times meaning that we are able redfined the paragraph font size CSS variable within the promo block CSS.</p> <p>Reminder of how the block Sass file is</p> <pre><code class="language-css">.b-promo { @include block-components(&quot;promo&quot;); @include block-properties(&quot;promo&quot;); } </code></pre> <p>Based on the above example of setting the <code>paragraph</code> <code>font-size</code> to <code>18px</code> within a <code>promo</code> block the CSS would be output as</p> <pre><code class="language-css">.b-promo { --c-paragraph-font-size: 18px; } </code></pre> <h2>Working Examples</h2> <p>Code is available on GitHub - <a href="https://github.com/matthewroach/theme-components">Theme Component Repo</a></p> <p>It's all demo'd via storybook which is published - <a href="https://theme-components.notio.xyz/?path=/story/blocks-promo--default-story">Theme Components Storybook</a></p> JAMStack URL Shortener with Netlify, 11ty and GitHub 2021-02-07T00:00:00Z https://matthewroach.me/jamstack-url-shortener-with-netlify-11ty-and-github/ <h2>Why...</h2> <p>I wanted a place where I can &quot;bookmark&quot; things I come across on the internet. Either for reading later, sharing or to keep a record of it for future reference.</p> <p>For the sharing part I wanted to a way to use a shortened style link</p> <p>Also, I want to own all this data. Along with not having to maintain a web application.</p> <p><strong>What if I could do all this with static site generation tool 11ty, and Netlify?</strong></p> <h2>How...</h2> <p>Well turns out it's very possible, and much easier than you might think.</p> <p>Broken down into the following parts</p> <ul> <li>Netlify to host the site and handle the URL redirection</li> <li>Eleventy to handle the website output and redirect file</li> <li>GitHub issues as a UI to add items</li> <li>GitHub actions to handle adding the content from the issue to the site</li> </ul> <p>And the output of all this is my own URL shortner/bookmark &quot;application&quot; - https://roach.link</p> <h2>Netlfiy</h2> <p>For the Netlify part it's very straight forward. The provide a way to host a site, and also a mechanism for handling redirects. Setting up a site is pretty straight forward from their UI with a few customisable options. For the <a href="https://www.netlify.com/blog/2019/01/16/redirect-rules-for-all-how-to-configure-redirects-for-your-static-site/">redirect</a> part as long as you have a <code>_redirects</code> file in the directory you want to serve your site from they take care of the rest. The also allow you have use a <code>netlify.toml</code> file.</p> <h2>Eleventy (11ty)</h2> <p>For the <a href="https://www.11ty.dev/">11ty</a> part I went for a very similar set up to my personal site (this site) set up. Each link would be a markdown file inside a <code>posts</code> directory that looks like this</p> <pre><code class="language-markdown">--- title: &quot;Matthew Roach&quot; date: &quot;2021-01-24&quot; permalink: &quot;b/view/&quot; link: &quot;https://matthewroach.me&quot; tags: [&quot;personal&quot;, &quot;blog&quot;] --- Personal website of Matthew Roach </code></pre> <p>The frontmatter data is where the majority of the needed data is housed. You may notice the <code>permalink</code> seems a little odd why is it <code>b/view</code>? The <code>b</code> part is the URL shortner unique ID, and the view part is used to build the output for each post to be <code>roach.link/b/view</code> allowing you to view the individual bookmark item (single view page still work in progress).</p> <p>Hang on.. You said unique ID... That's going to be fun to maintain I hear you ask. Well it would be if you were trying to do it by hand. But I am not, I am using an <code>npm</code> package called <a href="https://www.npmjs.com/package/bijective-link-shortener">bijective-link-shortener</a> to increment them each time. Which is handled with GitHub as you'll see later.</p> <h3>Redirect file</h3> <p>Without the ability to do redirection a URL shortner isn't much use, and as previously mentioned Netlfiy allows you to do redirects if you provide either a <code>_redirects</code> or <code>netlify.toml</code> file in the root of the published site. And with 11ty this is made super simple to generate, as with 11ty you can configure it to output the input file to any output file (give or take), by setting the <code>permalink</code> to be the output.</p> <p>Given all the bookmarks are all going to be individual files in a folder I set up a new collection in the <code>.eleventy.js</code> config file named <code>allLinks</code> and reversed the collection so the newest would come first.</p> <p>With a file named <code>_redirects.11ty.js</code> in my <code>src</code> directory I can consume the <code>allLinks</code> collection and make 11ty render out the links in a format needed for Netlify. The <code>_redirects.11ty.js</code> looks as follows</p> <pre><code class="language-javascript">class Redirects { data() { return { permalink: &quot;netlify.toml&quot;, }; } render(data) { return data.collections.allLinks .map( (l) =&gt; ` [[redirects]] from = &quot;${l.url.split(&quot;/&quot;)[1]}&quot; // permalink is in format {shortLink}/view to = &quot;${l.data.link}&quot;` ) .join(&quot;&quot;); } } module.exports = Redirects; </code></pre> <p>Broken down the file is looping over the <code>allLinks</code> collection, and setting up the Netlify redirect file in the format they require by using the permalink data (first part) and the link from each posts frontmatter data. As as the <code>permalink</code> of this file is set to <code>netlify.toml</code> it will create that file based on the <code>render</code> method.</p> <p>The output file looks as follows</p> <pre><code>[[redirects]] from = &quot;b&quot; to = &quot;https://matthewroach.me&quot; </code></pre> <h2>GitHub</h2> <p>GitHub is where I am hosted the source code and then have Netlify linked to the repo to build the site and publish on each commit to the <code>main</code> branch.</p> <h3>Issues</h3> <p>But GitHub is also the primary way I add new data to the site. Rather than having to create a new file and fill in all the frontmater data, and do a host of git commands I wanted a way I could actually use this &quot;application&quot; with minimal effort. Turns out GitHub issues is a <s>great</s> perfectly acceptable way to be the UI for adding new bookmarks... That is if you are willing to make a small adjustment.</p> <p>So, GitHub issues provides all but one of the interface items I needed. This is how I mapped the GitHub issue UI to the frontmatter data</p> <ul> <li>Issue Comment = The post body</li> <li>Labels = Tags</li> <li>Title = Title and link</li> </ul> <p>Title = title and link? That seems odd. Well turns out the issue UI doesn't really have a field or place I could enter the URL for the item I want to bookmark. So I went with the format of making the title as follows:</p> <pre><code>Matthew Roach Blog::https://matthewroach.me </code></pre> <p>The two colons has no meaning other than for when the GitHub action runs and splits the title using <code>title.split('::')</code> to parse it into two parts. The first part is the Title of the Site, and the second is then used in the link frontmatter data field.</p> <h3>Actions</h3> <p><a href="https://github.com/features/actions">GitHub actions</a> is the glue that makes all the previous steps work together. Without actions the process of adding bookmarks would be very manual. Actions are a way for you to automate workflows from your GitHub repository. Based on an action you can configure an action to run and do &quot;things&quot;. Those &quot;things&quot; are what makes this all possible.</p> <p>As mentioned I am using GitHub issues as a UI to add new bookmarks. Broken down this works as follows</p> <ol> <li>Issue <ol> <li>Add a new issue to my roach.link repository</li> <li>Once I am happy with the description and tags</li> <li>Mark the issue as closed</li> </ol> </li> <li>GitHub action runs on issue closed event <ol> <li>The action is triggered and starts its &quot;steps&quot;</li> <li>Action checks out code, sets up node and makes the issue object available on an environment variable</li> <li>Then a node command runs a script <code>node new-item-from-issue.js</code> 1. Within the script I have access to the issue object by doing <code>JSON.parse(process.env.ISSUE_CONTEXT)</code> 2. Script checks how many posts are currently in the <code>posts</code> folder, and using the package <code>bijective-link-shortener</code> gets the next short link value 3. Using the data from the issue object and the short link value from the previous step I use <code>transformAndWriteToFile</code> from the package <code>json-to-frontmatter-markdown</code> to create a new markdown file in the format noted above</li> <li>Add and commit is then run from within the action to add the newly created file to the repo</li> </ol> </li> <li>Netlify is triggered due to a new commit on the <code>main</code> branch <ol> <li>Netlify runs its build steps I have configured</li> </ol> </li> <li>New version of site is live at <a href="https://roach.link/">https://roach.link</a> <ol> <li>Homepage is updated</li> <li>New redirect added - example: <a href="https://roach.link/b">https://roach.link/b</a></li> </ol> </li> </ol> VSCode focus between terminal and code 2021-01-30T00:00:00Z https://matthewroach.me/vscode-focus-between-terminal-and-code/ <p> I recently began to utilise the integrated terminal for VSCode more. And being a big fan of using my keyboard to switch around different parts is huge part of my work flows. One of my sticking points with the terminal in VSCode is by default you can not switch into and out of the terminal easily. </p> <p> For example if I am editing some code, then need to switch to the terminal to run some commands, and then switch back to the code window. I got so used to using command + tab to switch windows. Wanting to try and use VSCode more it turns out this was a sticking point of my flow. With some digging and trial and error it turns out you can setting up a custom keybinding to handle this. </p> <p> As there was already a default key binding to open a terminal, once opened you can not easily switch back. But with VSCode and key bindings you are able to use the <code>when</code> key to tell a key binding "when" something can be run. This allowed me to set the same key binding used to focus terminal as to focus back to the editor. Below is instructions and the key binding JSON to achieve this on a Mac. </p> <h2>Instructions for Mac</h2> <ol> <li>Code > Preferences > Keyboard Shortcuts</li> <li> This opens a GUI for keyboard shortcuts - click on the tab "Keyboard Shortcuts", and an icon with a file and arrow will appear (title - Open Keyboard Shortcuts JSON) </li> <li>Copy paste the below JSON into the file and save</li> <li>Done</li> </ol> <pre><code>[ { "key": "ctrl+`", "command": "workbench.action.terminal.focus" }, { "key": "ctrl+`", "command": "workbench.action.focusActiveEditorGroup", "when": "terminalFocus" } ]</code></pre> <hr /> <p> Another VSCode tip - <a href="https://matthewroach.me/vscode-zoom-only-font-not-whole-editor/">VSCode zoom only font not whole editor</a>. </p> Web Share API - Progressively enhanced web component 2021-01-24T00:00:00Z https://matthewroach.me/web-share-api-progressively-enhanced-web-component/ <p><em>How I implemented a share button at the bottom of my blog posts on this site using the Web Share API and web components. Scroll to the bottom and see if your browser supports the Web Share API... You'll see a share button.</em></p> <p>Being able to hook into native functionality when its available is a great especially when you are able to do it in a progressively enhanced manner. A great example of this is the <a href="https://www.w3.org/TR/web-share/">Web Share API</a>. It is quite a new API that is still in working draft at the W3C. Even though it is still in working draft and not a common API available on all browsers. The main point is that <strong>it is</strong> available on <strong>some</strong> <a href="https://caniuse.com/?search=share">browsers</a>. This allows you to start using it, and by allow the users of those devices and browsers to have an extra feature available to them. Aka - Progressive enhancement. While this allows you to use a new feature, its also a way for browsers and the W3C to get people testing the features, get excited and for it to become more than a working draft.</p> <p>As with all technical web docs, a great starting point is MDN Web Docs, and the docs for the Web Share API is no different. The API is exposed to you via JavaScript as <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share">Navigator.share()</a>.</p> <p>Broken down the Web Share API is one asynchronous method call, <em>if its available</em> in the users browsers. The function takes an object containing the data to share.</p> <pre><code class="language-javascript">navigator.share({}); </code></pre> <p>The data you pass to the function is</p> <ul> <li><strong>url</strong> - A URL string referring to a resource being shared</li> <li><strong>title</strong> - The title of the document being shared. May be ignored by the target</li> <li><strong>text</strong> - Arbitrary text that forms the body of the message being shared. (not used in my examples)</li> <li><strong>files</strong> - Files to be shared (not covered in this post)</li> </ul> <p>In its most basic example you can just pass the url and title to the <code>share()</code> method and it will work for supported browsers. (Try copy and pasting the following into console of dev tools of a supported browser).</p> <pre><code class="language-javascript">navigator.share({ url: &quot;http://matthewroach.me/web-share-api-progressively-enhanced-web-component/&quot;, title: &quot;Web Share API - Progressively enhanced web component&quot;, }); </code></pre> <p>While this is all great and all, I've mentioned progressively enhanced quite a few times already, and that this API is not available in all browsers. Being able to implement this as an enhanced feature is very neat, and if you pair it with web components you can have a fully isolated and truly enhanced feature for some of your users. Web components (<a href="https://caniuse.com/custom-elementsv1">customElements</a>) pairs well here as they are available in the browsers that have support for the Web Share API.</p> <h2>The button</h2> <p>We are dealing with an action, so we use a <code>button</code>, and as the button should only be shown if the browser supports the Web Share API its default state is <code>hidden</code>. As the Web Share API is a JavaScript based API and we can only detect it in the users browser we will control the visibility of the button via JavaScript. This also means if you are browsing without JavaScript enabled you are none the wiser.</p> <pre><code class="language-html">&lt;button hidden is=&quot;s-hare&quot; a-title=&quot;Web Share API - Progressively enhanced web component&quot; a-url=&quot;http://matthewroach.me/web-share-api-progressively-enhanced-web-component/&quot; &gt; Share &lt;/button&gt; </code></pre> <p>You maybe wondering what the extra attributes on the button are. the <code>is</code> and the <code>a-title</code> and <code>a-url</code> are.</p> <ul> <li>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-is"><code>is</code></a> global HTML attribute: Allows you to specify that a standard HTML element should behave like a registered custom built-in element.</li> <li>The <code>a-title</code> and <code>a-url</code> are custom attributes I decided to use for the component to read them for when the share API is called. Remember from above to share you need a url and title passed to the method.</li> </ul> <h2>The JavaScript</h2> <p>All the code that makes the button.. Pop.. Or well work in situations where it is able to. The progressive enhanced experience.</p> <p>Below is the full JavaScript class and custom element define call to set up the button code from above to work.. Side note, view source to see it on my site.</p> <p>As I used the <code>is</code> attribute on the <code>button</code> element our class extends the <code>HTMLButtonElement</code></p> <p>In the <code>constructor</code> I call a class method <code>canWebShare</code> - if this returns false. Browsers that don't support the Web Share API nothing more happens. But if your browsers has the Web Share API available <code>navigator.share</code>, the code will call <code>showButton</code> which actually shows the button and adds a click event listener to it.</p> <p><strong>Accessibility Tip:</strong> As I have extended the native HTML <code>button</code> you only need to set up the click event as the button element will fire this event when the user uses either the enter or space key on their keyboard. It will also catch the mouse click event too.</p> <p>Now the event listener is added the button is all set for user interaction, and this is where the <code>a-title</code> and <code>a-url</code> attributes come into play. When a user activates the button the event listener function will use the values of these attributes to pass the data to the <code>navigator.share</code> method. Remember this method call returns a promise, you can use <code>await</code> here.. And guess what no need to polyfil for this as the browsers that support Web Share also support <code>await</code></p> <p>The share dialog is part of the users operating system (OS) and from when the user clicks the button you have passed the share off to the OS.</p> <pre><code class="language-javascript">class Share extends HTMLButtonElement { constructor() { // Always call super first in constructor self = super(); if (this.canWebShare()) { this.showButton(); } } canWebShare() { return navigator.share !== undefined; } showButton() { self.removeAttribute(&quot;hidden&quot;); self.addEventListener(&quot;click&quot;, self.shareEvent); } async shareEvent() { try { await navigator.share(self.shareData()); } catch (err) { alert(&quot;Sorry, sharing failed - you could try again&quot;); } } shareData() { const shareData = { title: self.getAttribute(&quot;a-title&quot;), url: self.getAttribute(&quot;a-url&quot;) || window.location.href, }; if (self.getAttribute(&quot;a-text&quot;)) { shareData.text = self.getAttribute(&quot;a-text&quot;); } return shareData; } } customElements.define(&quot;s-hare&quot;, Share, { extends: &quot;button&quot; }); </code></pre>