<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Carme Mias</title>
    <description>The latest articles on DEV Community by Carme Mias (@carmemias).</description>
    <link>https://dev.to/carmemias</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F810966%2Fa138633f-a0e5-451a-8e17-8acf4d0d85c5.jpeg</url>
      <title>DEV Community: Carme Mias</title>
      <link>https://dev.to/carmemias</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/carmemias"/>
    <language>en</language>
    <item>
      <title>Intro to accessibility testing</title>
      <dc:creator>Carme Mias</dc:creator>
      <pubDate>Sun, 18 May 2025 10:59:06 +0000</pubDate>
      <link>https://dev.to/carmemias/intro-to-accessibility-testing-2igl</link>
      <guid>https://dev.to/carmemias/intro-to-accessibility-testing-2igl</guid>
      <description>&lt;p&gt;You’ve probably already heard we should make all our front-end work accessible. It helps make sure everyone can have the best experience when using the digital services, products, or platforms we've created.&lt;/p&gt;

&lt;p&gt;But how do we know whether our work is accessible? The most efficient way is to make testing part of our routine.&lt;/p&gt;

&lt;p&gt;In this post I'll share some freeby tools and strategies that I use from my local dev environment and that you can start using today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser extensions
&lt;/h2&gt;

&lt;p&gt;First, I check the page being tested with a browser extension. This is the best way to catch the most obvious issues.&lt;/p&gt;

&lt;p&gt;The three browser extensions recommended by gov.uk are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/wave-evaluation-tool/jbbplnpkjmmeebjpijfedlgcdilocofh?utm_source=chrome-ntp-icon" rel="noopener noreferrer"&gt;WAVE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.deque.com/axe/" rel="noopener noreferrer"&gt;axe DevTools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/arc-toolkit/chdkkkccnlfncngelccgbgfmjebmkmce?utm_source=chrome-ntp-icon" rel="noopener noreferrer"&gt;ArcToolkit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although it is not a browser extension, the &lt;strong&gt;&lt;em&gt;Lighthouse&lt;/em&gt;&lt;/strong&gt; tab in the Chrome DevTools is also useful and will give you similar results.&lt;/p&gt;

&lt;p&gt;It's important to keep an eye out for false positives!&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard navigation
&lt;/h2&gt;

&lt;p&gt;Next, I check that the page can be navigated using only a keyboard with the following key combinations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Tab&lt;/em&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;em&gt;Shift + Tab&lt;/em&gt;&lt;/strong&gt; to move focus to next or previous focussable element&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Spacebar&lt;/em&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;em&gt;Shift + spacebar&lt;/em&gt;&lt;/strong&gt; to scroll down or up a page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Return&lt;/em&gt;&lt;/strong&gt; key to click a focussed element&lt;/li&gt;
&lt;li&gt;Arrow keys to navigate within menus and lists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you move around the page, you should look out for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can you tell which element, if any, has focus?&lt;/li&gt;
&lt;li&gt;Can you reach and interact with all enabled, clickable elements (buttons, links, form fields)?&lt;/li&gt;
&lt;li&gt;Is the order in which these clickable elements get focus logical? or does focus jump around?&lt;/li&gt;
&lt;li&gt;Is focus lost at any point? This may happen if the focussed element is hidden.&lt;/li&gt;
&lt;li&gt;If there is a modal, external widget, or iframe in the page, can you navigate in and out of it? Can you open and close it as expected?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Screen readers
&lt;/h2&gt;

&lt;p&gt;Testing with a screen reader, is slightly less straight-forward as we must also think about which browser we’re using for the tests.&lt;/p&gt;

&lt;p&gt;The recommended browser pairings for the three main screen readers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;JAWS&lt;/em&gt;&lt;/strong&gt; with IE or Chrome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;NVDA&lt;/em&gt;&lt;/strong&gt; with Firefox&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;VoiceOver&lt;/em&gt;&lt;/strong&gt; with Safari&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Which screen reader?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JAWS&lt;/em&gt;&lt;/strong&gt; is the most widely used screen reader in real life. However, it is proprietary software and its license is not cheap. As a consequence, most developers will only be able to test against it if their workplace owns a license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;NVDA&lt;/em&gt;&lt;/strong&gt; is Open Source and the second most popular screen reader, although a version exists for Windows only. So if you work with a Mac or Linux laptop you may need to install it in a virtual machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;VoiceOver&lt;/em&gt;&lt;/strong&gt; comes built-in with iOS so, again, if you work with another operating system, you’ll need to use a virtual machine.&lt;/p&gt;

&lt;p&gt;The recommendation is to test your work against as many screen readers as you can get hold of as they sometimes give different results.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use them?
&lt;/h3&gt;

&lt;p&gt;Each screen reader has its own combination of keystrokes, so I’d advice downloading a cheatsheet and spending some time becoming familiar with them.&lt;/p&gt;

&lt;p&gt;You can find all the &lt;a href="https://dequeuniversity.com/screenreaders/" rel="noopener noreferrer"&gt;screen reader cheatsheets&lt;/a&gt; on the Deque website.&lt;/p&gt;

&lt;p&gt;Once you’ve got some practice under your belt, you’re ready to start testing your frontend work.&lt;/p&gt;

&lt;p&gt;Here’s the steps and what to check for each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Page navigation:

&lt;ul&gt;
&lt;li&gt;Is the main site navigation useable? Does it include a &lt;strong&gt;&lt;em&gt;skip to content&lt;/em&gt;&lt;/strong&gt; link?&lt;/li&gt;
&lt;li&gt;If there is a secondary navigation element, is its purpose announced?&lt;/li&gt;
&lt;li&gt;Are all regions, headings, and links well defined and findable?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;If there is a form:

&lt;ul&gt;
&lt;li&gt;Can you interact with all its elements as expected?&lt;/li&gt;
&lt;li&gt;Test also the unhappy path. If a warning or error is displayed, does the screen reader announce it?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Read the whole page through.

&lt;ul&gt;
&lt;li&gt;Is any information missed?&lt;/li&gt;
&lt;li&gt;Is there any repetition?&lt;/li&gt;
&lt;li&gt;Is anything read that could lead to confusion?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;I realise this is quite a lot of information. And that it can potentially also be perceived as quite a lot of extra work!&lt;/p&gt;

&lt;p&gt;What worked best for me was to add the checks to my workflow one at a time, in the order mentioned above. Once I was familiar and comfortable with one, I added the next.&lt;/p&gt;

&lt;p&gt;Just think of the time saved by not having to go back to fix accessibility issues! Plus the satisfaction of delivering top quality work. Priceless!&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>developers</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to migrate a Rails app from Bootstrap to Bulma</title>
      <dc:creator>Carme Mias</dc:creator>
      <pubDate>Sat, 15 Feb 2025 16:54:26 +0000</pubDate>
      <link>https://dev.to/carmemias/how-to-migrate-a-rails-app-from-bootstrap-to-bulma-b02</link>
      <guid>https://dev.to/carmemias/how-to-migrate-a-rails-app-from-bootstrap-to-bulma-b02</guid>
      <description>&lt;p&gt;The first thing to do is remove the Bootstrap-related gems from the Rails app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uninstall Bootstrap
&lt;/h2&gt;

&lt;p&gt;From the terminal, &lt;code&gt;cd&lt;/code&gt; into your project’s folder and type, for example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem uninstall bootstrap-sass bootstrap-will_paginate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then run &lt;code&gt;bundle install&lt;/code&gt; to update the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Bulma
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://bulma.io/documentation/start/installation/" rel="noopener noreferrer"&gt;the installation docs&lt;/a&gt;, if you are happy to use the Bulma styles as they come, the last step you'll need to do is add the pre-compiled Bulma stylesheet to your document header:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Otherwise if, like me, you want the option to customise the styles, you’ll need to add the Bulma npm package into your project.&lt;/p&gt;

&lt;p&gt;Before doing that though, if your Rails app doesn’t have them yet, you’ll need to install &lt;code&gt;nvm&lt;/code&gt;, &lt;code&gt;nodejs&lt;/code&gt;, and &lt;code&gt;npm&lt;/code&gt;. Follow &lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;the Node official instructions&lt;/a&gt; for the best way of doing this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the cssbundling-rails gem
&lt;/h2&gt;

&lt;p&gt;Once your environment is ready, you can install the &lt;code&gt;cssbunding-rails&lt;/code&gt; gem. This gem saved me so much time!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cssbunding-rails&lt;/code&gt; can be used to install multiple CSS frameworks, not just Bulma. Check out &lt;a href="https://github.com/rails/cssbundling-rails" rel="noopener noreferrer"&gt;the gem repo&lt;/a&gt; to find the whole list. It will also configure the app to compile the CSS styles locally.&lt;/p&gt;

&lt;p&gt;To install &lt;code&gt;cssbunding-rails&lt;/code&gt; and the Bulma package, run the following from the terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle add cssbundling-rails&lt;br&gt;
bin/rails css:install:bulma&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note that running &lt;code&gt;bundle add&lt;/code&gt; is equivalent to running &lt;code&gt;gem install&lt;/code&gt; followed by &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update all classes
&lt;/h2&gt;

&lt;p&gt;The last remaining step is to migrate all the existing CSS classes in the app templates, views and partials from Bootstrap to Bulma.&lt;/p&gt;

&lt;p&gt;A bit tedious but necessary.&lt;/p&gt;

&lt;p&gt;And, do not forget to update your tests too!&lt;/p&gt;

&lt;p&gt;After that, congratulations! All’s done!&lt;/p&gt;

&lt;h2&gt;
  
  
  A gotcha
&lt;/h2&gt;

&lt;p&gt;Or perhaps not?&lt;/p&gt;

&lt;p&gt;After the migration, the app run locally but my integration tests were failing with a weird error:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActionView::Template::Error: &lt;br&gt;
Error: Invalid CSS after "...-s), var(--l));": expected "}", was "--00-l: var(--bulma" on line 14482:46 of stdin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It turns out I still had &lt;code&gt;sassc-rails&lt;/code&gt; in the &lt;code&gt;Gemfile&lt;/code&gt;. This compiler gem does not recognise some of the more up-to-date Sass included in Bulma and was causing the errors.&lt;/p&gt;

&lt;p&gt;In any case, &lt;code&gt;sassc-rails&lt;/code&gt; was no longer needed as &lt;code&gt;cssbundling-rails&lt;/code&gt; does it all for us. So the solution, in the end, was easy:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem uninstall sassc-rails&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;Commit, push. All done!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>css</category>
      <category>frontend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why are we so rubbish at accessibility?</title>
      <dc:creator>Carme Mias</dc:creator>
      <pubDate>Sat, 11 Jan 2025 08:32:25 +0000</pubDate>
      <link>https://dev.to/carmemias/why-are-we-so-rubbish-at-accessibility-596g</link>
      <guid>https://dev.to/carmemias/why-are-we-so-rubbish-at-accessibility-596g</guid>
      <description>&lt;p&gt;Anybody working in the tech industry would agree that, as software developers, we pride ouselves on building beautiful, secure products properly.&lt;/p&gt;

&lt;p&gt;We take care of the details. We keep ourselves and our code up-to-date with the latest news and versions. We patch, review, comment, refactor, and marvel at the sight of dry, clean code and fast response times.&lt;/p&gt;

&lt;p&gt;So, please tell me, why are we so rubbish at accessibility?&lt;/p&gt;

&lt;h2&gt;
  
  
  Yes, we are rubbish
&lt;/h2&gt;

&lt;p&gt;Strong words, I know, but in my opinion completely justified. Otherwise, how do you explain the terrible &lt;a href="https://webaim.org/projects/million/" rel="noopener noreferrer"&gt;WebAIM Million report&lt;/a&gt; results, year after year?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;95.9% of the top million website homepages have accessibility errors. The average is 56.8 errors per page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“Ah, but accessibility is hard“, you may say.&lt;/p&gt;

&lt;p&gt;To which my answer is: “Not all of it is“.&lt;/p&gt;

&lt;p&gt;The most common failures, as identified by the report, definitely are not the hardest to fix. According to the latest report, the most common accessibility errors are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Low colour contrast (81% of homepages)&lt;/li&gt;
&lt;li&gt;Missing alternative text for images (54.5% of homepages)&lt;/li&gt;
&lt;li&gt;Missing form input labels (48.6% of homepages)&lt;/li&gt;
&lt;li&gt;Empty links (44.6% of homepages)&lt;/li&gt;
&lt;li&gt;Empty buttons (28.2% of homepages)&lt;/li&gt;
&lt;li&gt;Missing document language (17.1% of homepages)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are clever people, in general 😉. Surely we can do better!&lt;/p&gt;

&lt;p&gt;The documentation is definitely all out there and, let me tell you, it is not rocket science.&lt;/p&gt;

&lt;h2&gt;
  
  
  But why?
&lt;/h2&gt;

&lt;p&gt;Unfortunately, we can only guess what the reasons behind such dyre report results may be.&lt;/p&gt;

&lt;p&gt;Perhaps it could be due to time constraints, and being overstretched. Something has to give and, if it was ever in the roadmap, accessibility tends to be easy to drop, right?&lt;/p&gt;

&lt;p&gt;If I was feeling generous, I may think it was due to lack of awareness. But this just smells of a lazy excuse. We are well informed folks, we have to be if we are any good at our jobs.&lt;/p&gt;

&lt;p&gt;It could be lack of interest. There’s an adrenaline rush to cracking that gnarly bug, or getting your well researched and spiked architecture design approved. Double checking whether that form is accessible or that table understandable? Nah, boring!&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s not all on us
&lt;/h2&gt;

&lt;p&gt;OK, I have been a bit harsh. I agree.&lt;/p&gt;

&lt;p&gt;Accessibility work is not only on us: graphic designers, copy writers, social media experts, product onwers, and ultimately company execs should also be on top of it.&lt;/p&gt;

&lt;p&gt;In reality most of us know that &lt;strong&gt;accessibility is everybody’s work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But as developers, we are the last link in the chain. We build the actual stuff that gets served to the general public and our customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s take ownership
&lt;/h2&gt;

&lt;p&gt;We can make a difference. We can change the outcome if we care enough to give it our attention.&lt;/p&gt;

&lt;p&gt;Any software developer worth their salt would want to make sure 100% of their audience has a smooth, and enjoyable experience when using their product. Right?&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://researchbriefings.files.parliament.uk/documents/CBP-9602/CBP-9602.pdf" rel="noopener noreferrer"&gt;a report from the UK House of Commons Library&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;25% of the UK population report having a disability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, you’ve read correctly.&lt;/p&gt;

&lt;p&gt;So, let’s stop ignoring the 25% of our audience who are disabled.&lt;/p&gt;

&lt;p&gt;This 2025, put your time to good use by learning more about  accessibility and aim to embed it into your daily workflow. &lt;/p&gt;

&lt;p&gt;Your career, end users, and company’s bottom line will thank you for it.&lt;/p&gt;

&lt;p&gt;Here’s to a happy, fruitful 2025! 🎉&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>software</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Copy HTML content with navigator clipboard</title>
      <dc:creator>Carme Mias</dc:creator>
      <pubDate>Wed, 13 Dec 2023 08:20:52 +0000</pubDate>
      <link>https://dev.to/carmemias/copy-html-content-with-navigator-clipboard-25</link>
      <guid>https://dev.to/carmemias/copy-html-content-with-navigator-clipboard-25</guid>
      <description>&lt;p&gt;We have often seen “Copy” buttons for plain text. Thanks to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard"&gt;navigator.clipboard‘s API&lt;/a&gt;, we can also programatically copy and paste HTML content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The copy code
&lt;/h2&gt;

&lt;p&gt;Let’s imagine we have a bit of HTML content that includes links and other basic HTML code. We want our end users to be able to copy it exactly as it is and use it elsewhere.&lt;/p&gt;

&lt;p&gt;For this exercise, we will be working with a simple HTML page with a section with &lt;code&gt;id=”copy-content”&lt;/code&gt; that contains the HTML to be copied, and a “Copy to clipboard” button, with &lt;code&gt;id=”copy-button”&lt;/code&gt;. The code for this example can be found in &lt;a href="https://gist.github.com/carmemias/c79f68cd7b65d331da31e3acd6cd7574"&gt;this gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Initially, the javascript that makes it all happen looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;copyButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;copyContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;copyContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;addHtmlToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addHtmlToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ClipboardItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="p"&gt;})];&lt;/span&gt;

  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copied to clipboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed to copy to clipboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we add a “click” event to the “Copy to clipboard” button so that, when the button is clicked, the &lt;code&gt;copyContent&lt;/code&gt; function is executed.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;copyContent&lt;/code&gt; function, gets the HTML content we want to copy and passes it to the &lt;code&gt;addHtmlToClipboard&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;addHtmlToClipboard&lt;/code&gt; function then takes this content, converts it into an HTML &lt;code&gt;Blob&lt;/code&gt; and creates a new &lt;code&gt;ClipboardItem&lt;/code&gt; with it. This is then writen into the clipboard using &lt;code&gt;navigator.clipboard.write&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the clipboard API succedes, the content is ready to be pasted elsewhere. For example, in a Word document or an email.&lt;/p&gt;

&lt;p&gt;Note the inline CSS it is also copied but not that which is in a separate file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firefox gotcha
&lt;/h2&gt;

&lt;p&gt;Unfortunately the &lt;code&gt;ClipboardItem&lt;/code&gt; funtionality is not yet supported by all browsers, notably Firefox.&lt;/p&gt;

&lt;p&gt;As a compromise, we can change the code so that at least the text content is copied to clipboard if the full functionality cannot be used. Here is the updated code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;copyButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;copyContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;copyContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ClipboardItem&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;addHtmlToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;addTextToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addHtmlToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ClipboardItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="p"&gt;})];&lt;/span&gt;

  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copied html to clipboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed to copy html to clipboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// removes all HTML tags and empty spaces at either end&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sanitize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/?[\w&lt;/span&gt;&lt;span class="sr"&gt; ="'-:;&lt;/span&gt;&lt;span class="se"&gt;\/\.]&lt;/span&gt;&lt;span class="sr"&gt;+&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTextToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copied text to clipboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed to copy text to clipboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what has changed? First of all, after getting the content to be copied, the &lt;code&gt;copyContent&lt;/code&gt; function checks whether the &lt;code&gt;ClipboardItem&lt;/code&gt; functionality is available.&lt;/p&gt;

&lt;p&gt;If it is, the &lt;code&gt;addHtmlToClipboard&lt;/code&gt; function is run just as before. If it isn’t available, the new &lt;code&gt;addTextToClipboard&lt;/code&gt; function is run instead.&lt;/p&gt;

&lt;p&gt;This new &lt;code&gt;addTextToClipboard&lt;/code&gt; takes the content to be copied, removes all traces of HTML code from it (via the &lt;code&gt;sanitize&lt;/code&gt; function) and then writes it as text into the clipboard using &lt;code&gt;navigator.clipboard.writeText&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From the callbacks, instead of &lt;code&gt;console.log&lt;/code&gt;s, you could for example trigger a success notification or failure warning.&lt;/p&gt;

&lt;p&gt;Try it out to see it working!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>navigator</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
