<?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: Till Sanders</title>
    <description>The latest articles on DEV Community by Till Sanders (@tillsanders).</description>
    <link>https://dev.to/tillsanders</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%2F556863%2F34c52e67-cf06-46e2-ad7b-0d104b93c755.jpeg</url>
      <title>DEV Community: Till Sanders</title>
      <link>https://dev.to/tillsanders</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tillsanders"/>
    <language>en</language>
    <item>
      <title>Automatically combine your scattered documentation with a few lines in GitLab CI</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Mon, 28 Feb 2022 13:00:27 +0000</pubDate>
      <link>https://dev.to/tillsanders/automatically-combine-your-scattered-documentation-with-a-few-lines-in-gitlab-ci-1mlh</link>
      <guid>https://dev.to/tillsanders/automatically-combine-your-scattered-documentation-with-a-few-lines-in-gitlab-ci-1mlh</guid>
      <description>&lt;h1&gt;
  
  
  Our documentation lives in scattered Readme's
&lt;/h1&gt;

&lt;p&gt;At FinanzRitter, we have a lot of repositories. Some do small things, some do big things. All are documented in Readme files, written in Markdown. That works really well for us, because it allows us to keep the documentation where the code lives. However, when we're &lt;strong&gt;onboarding new developers&lt;/strong&gt; we struggle to get them up to speed because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;all the &lt;strong&gt;information is scattered&lt;/strong&gt; and we don't have much documentation that would explain where everything is and how it connects to work together.&lt;/li&gt;
&lt;li&gt;We're also &lt;strong&gt;missing pieces on why we structured things a certain way&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;And lastly, we have &lt;strong&gt;no place for things that don't really belong anywhere&lt;/strong&gt;: code snippets, how-tos etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  We need a system to combine all Readme's and a little more
&lt;/h1&gt;

&lt;p&gt;With this struggle, we set out to find a solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We &lt;em&gt;don't&lt;/em&gt; want to separate our documentation from our code base.&lt;/li&gt;
&lt;li&gt;We &lt;em&gt;don't&lt;/em&gt; want to maintain the same documentation in two different places.&lt;/li&gt;
&lt;li&gt;We &lt;em&gt;don't&lt;/em&gt; want to put a lot of extra work into it.&lt;/li&gt;
&lt;li&gt;We &lt;em&gt;do&lt;/em&gt; want the ability to add documentation that does not belong to a specific repository but rather explains overarching concepts.&lt;/li&gt;
&lt;li&gt;We &lt;em&gt;do&lt;/em&gt; want to have one tool to rule them all. One place where everything comes together.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Why we chose Outline
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;I'm not affiliated in any way with Outline.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.getoutline.com/" rel="noopener noreferrer"&gt;Outline&lt;/a&gt; is, as they put it: „Your team’s knowledge base“. It's an online documentation tool built on the Markdown syntax. We chose it over competitors like Confluence, Notion and Slite because Outline is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple, easy to use and minimalistic in design. It does one thing and does it well.&lt;/li&gt;
&lt;li&gt;open source (&lt;a href="https://github.com/outline/outline/blob/main/LICENSE" rel="noopener noreferrer"&gt;not strictly FOSS&lt;/a&gt;), &lt;a href="https://www.getoutline.com/changelog" rel="noopener noreferrer"&gt;built in public&lt;/a&gt; and can be self-hosted.&lt;/li&gt;
&lt;li&gt;has a solid API.&lt;/li&gt;
&lt;li&gt;has a fair price structure starting at 10 USD / month for 10 users (not per user)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Using the API to push Readme's to Outline
&lt;/h1&gt;

&lt;p&gt;Outline supports collections (we have, for example a collection for frontend documentation, for backend documentation, infrastructure and onboarding). Collections contain pages and subpages. So you have plenty of options to organize things.&lt;/p&gt;

&lt;p&gt;We integrated our Readme's into Outline using their API and a job in our GitLab pipelines. The pipeline will take the contents of the Readme and push them to the Outline API, overwriting a given page completely. With a little more work it should also be possible to only overwrite a certain section of a page, but we opted for completely overwriting them for ease-of-use and safety.&lt;/p&gt;

&lt;p&gt;So for example, our 'Frontend' collection contains a page called 'Data Management', explaining the overall concept of handling data on the client. It has three sub-pages, one for every repository we have that plays a role in managing the data. Those are updated automatically to mirror the Readme's on GitLab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7wycv8aqxhd3praj5ua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7wycv8aqxhd3praj5ua.png" alt="Structure of our Frontend collection"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend (Collection)
 → Data Management (Page, written in the Outline editor)
    → DataCollection (Subpage, synchronized from the repo's Readme on GitLab)
    → DataStore (Subpage, synchronized from the repo's Readme on GitLab)
    → DataVersion (Subpage, synchronized from the repo's Readme on GitLab)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;We add a &lt;strong&gt;new &lt;code&gt;document&lt;/code&gt; stage&lt;/strong&gt; to our GitLab CI pipeline, as well as a &lt;code&gt;document&lt;/code&gt; job. This should run after a release job.&lt;/li&gt;
&lt;li&gt;This job reads the &lt;code&gt;README.md&lt;/code&gt; from your repository. And &lt;strong&gt;replaces the headline&lt;/strong&gt; (which is expected to be &lt;code&gt;# Your Repository&lt;/code&gt;) &lt;strong&gt;with a warning&lt;/strong&gt;: &lt;em&gt;„This page automatically mirrors the documentation of *Your Repository&lt;/em&gt;. Don’t make changes here or they will be lost with the next release.”* This marks the page as being automatically mirrored to Outline and prevents us from loosing manual changes. We don't have a two-way synchronization here!&lt;/li&gt;
&lt;li&gt;Lastly, we &lt;strong&gt;sent the modified Markdown&lt;/strong&gt; inside a JSON object to the Outline API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2ba5qjghntgwvsljx55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2ba5qjghntgwvsljx55.png" alt="A warning in the mirrored documentation"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up the integration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Create a new page in Outline
&lt;/h2&gt;

&lt;p&gt;Create a new page / subpage in Outline and collect its alias from the URL. For example, an alias might look like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;how-to-set-up-outline-integration-nkOq0DuntG&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Get an API Token
&lt;/h2&gt;

&lt;p&gt;Create an API token in the settings: &lt;code&gt;https://your_organization.getoutline.com/settings/tokens&lt;/code&gt; and save it to your GitLab repository or group as a new &lt;a href="https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project" rel="noopener noreferrer"&gt;CI environment variable&lt;/a&gt;. We're naming it &lt;code&gt;OUTLINE_AUTH_TOKEN&lt;/code&gt;. It will now be available to your job. You should also make it protected. If you do so, make sure the job is only running on protected branch- or tag-pipelines or it will fail because of the missing API token.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Add the job to your pipeline
&lt;/h2&gt;

&lt;p&gt;Add the following job at the end of your &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;document&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;document&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# depending on your base image, you probably need to install jq&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt update &amp;amp;&amp;amp; apt install -y jq&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;ORIGINAL=$(cat README.md)&lt;/span&gt;
    &lt;span class="s"&gt;WARNING=':::info&lt;/span&gt;
    &lt;span class="s"&gt;This page automatically mirrors the documentation of [Your Repository](https://gitlab.com/your_username/your_repository). Don’t make changes here or they **will be lost** with the next release.&lt;/span&gt;
    &lt;span class="s"&gt;:::&lt;/span&gt;
    &lt;span class="s"&gt;\'&lt;/span&gt;
    &lt;span class="s"&gt;DOCUMENT=$(echo "${ORIGINAL/\# Your Repository/$WARNING}" | jq -Rs .)&lt;/span&gt;
    &lt;span class="s"&gt;curl https://your_username.getoutline.com/api/documents.update \&lt;/span&gt;
      &lt;span class="s"&gt;-X POST \&lt;/span&gt;
      &lt;span class="s"&gt;-H "authorization: Bearer $OUTLINE_AUTH_TOKEN" \&lt;/span&gt;
      &lt;span class="s"&gt;-H 'content-type: application/json' \&lt;/span&gt;
      &lt;span class="s"&gt;-H 'accept: application/json' \&lt;/span&gt;
      &lt;span class="s"&gt;--data-raw "{\"id\": \"your_page_alias\", \"text\": ${DOCUMENT}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to adapt the job to your repository. You need to replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The name (&lt;code&gt;Your Repository&lt;/code&gt;) and URL of your GitLab repository (&lt;code&gt;https://gitlab.com/your_username/your_repository&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The sub-domain or URL of your Outline instance (&lt;code&gt;https://your_username.getoutline.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The page alias (&lt;code&gt;your_page_alias&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Possibly the name of your Readme. We use &lt;code&gt;README.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, the &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#rules" rel="noopener noreferrer"&gt;rules&lt;/a&gt; or &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#only--except" rel="noopener noreferrer"&gt;only/except&lt;/a&gt; (depending on what you are using) should match the configuration of your release job(s). So the documentation is only published when a release has been built.&lt;/p&gt;

&lt;p&gt;You also might want to add &lt;code&gt;allow_failure: false&lt;/code&gt; to the release job(s), if they have a manual trigger. Otherwise, the document stage will run regardless of their success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Outline has a feature that allows 'Collaborative editing' in real-time with multiple people. It is disabled by default (February 2022) and can be enabled in the &lt;em&gt;Features&lt;/em&gt; section of the settings. If active, the integration does not work. I haven't digged deeper into this. Maybe someone has a solution?&lt;/li&gt;
&lt;li&gt;Using the script above, you can only publish one Markdown file. If you have multiple, you need to add multiple jobs or adapt the script accordingly. &lt;/li&gt;
&lt;li&gt;This is currently GitLab-only. It sure would be nice to have A GitHub Action for other folks. If someone creates one, please let me know!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feedback for Outline
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your API documentation is good, thank you! Maybe it can be improved regarding the collaborative editing?&lt;/li&gt;
&lt;li&gt;Your WYSIWYG editor often does not behave as I'm used to, especially in regards to lists. It's not buggy, but it could use some fine-tuning.&lt;/li&gt;
&lt;li&gt;It would be amazing to have an integration like this natively!&lt;/li&gt;
&lt;li&gt;Your design is really clean, I enjoy using Outline very much!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gitlab</category>
      <category>productivity</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>Let's stop using [a-zA-Z]+</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Mon, 08 Mar 2021 19:27:20 +0000</pubDate>
      <link>https://dev.to/tillsanders/let-s-stop-using-a-za-z-4a0m</link>
      <guid>https://dev.to/tillsanders/let-s-stop-using-a-za-z-4a0m</guid>
      <description>&lt;p&gt;If you, like me, regularly (see what I did here?) validate alphanumeric fields using Regex, you probably learned to do it 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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Till&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/gu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is technically correct, of course. And it's what most validation libraries will do when you tell them a field is &lt;code&gt;alpha&lt;/code&gt; / &lt;code&gt;alphanumeric&lt;/code&gt; / etc.&lt;/p&gt;

&lt;p&gt;However, I have a problem with this approach and a lot (!) of other people do, too. Because I'm from Germany. More specifically, from a town called Lüdenscheid. And Lüdenscheid won't match the regular expression above because of the Umlaut. Same applies for languages like French, Spanish, Czech, just to name a few.&lt;/p&gt;

&lt;p&gt;So how can we as developers be more inclusive towards languages other than English? Do we have to include all possible variations of the latin alphabet? That's a common suggestion, but of course, it doesn't scale well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Luckily, Unicode has us covered:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lüdenscheid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\p&lt;/span&gt;&lt;span class="sr"&gt;{Letter}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Mark}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/gu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;\p&lt;/code&gt; flag allows us to pick a so called &lt;a href="http://www.regular-expressions.info/unicode.html#category"&gt;&lt;em&gt;Unicode Character Category&lt;/em&gt;&lt;/a&gt;. In Unicode, all characters are sorted into categories that we can use in our regular expression. The &lt;code&gt;Letter&lt;/code&gt; category includes letters from all kinds of languages, not just A-Z. But it does not include, e.g. &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;$&lt;/code&gt; which is important for security. The &lt;code&gt;Mark&lt;/code&gt; category – as &lt;a href="https://dev.to/lionelrowe"&gt;lionelrowe&lt;/a&gt; pointed out in the comments (thanks) – contains combining marks. In Unicode, a letter like &lt;code&gt;ü&lt;/code&gt; can be either one or two combined code points. So depending on how the character is coded, we need the &lt;code&gt;Mark&lt;/code&gt; category.&lt;/p&gt;

&lt;h3&gt;
  
  
  More details on the Mark category
&lt;/h3&gt;

&lt;p&gt;If we omit the &lt;code&gt;Mark&lt;/code&gt; category and run the following Regex: &lt;code&gt;'Lüdenscheid'.match(/[\p{Letter}]+/gu)&lt;/code&gt; it will match &lt;code&gt;Lüdenscheid&lt;/code&gt;, if the &lt;code&gt;ü&lt;/code&gt; is encoded as a single character. On the other hand, if the &lt;code&gt;ü&lt;/code&gt; is encoded as a letter-mark-combination (&lt;code&gt;u +   ̈&lt;/code&gt;), the regex will only match &lt;code&gt;Lu&lt;/code&gt;, because it will stop at the &lt;code&gt;̈&lt;/code&gt; mark.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/mdn-javascript_builtins_regexp_property_escapes"&gt;Browser support&lt;/a&gt; for this feature is good, IE (not Edge) being the only exclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Match only letters&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lüdenscheid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\p&lt;/span&gt;&lt;span class="sr"&gt;{Letter}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Mark}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/gu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Match letters and spaces&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pražští filharmonici&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\p&lt;/span&gt;&lt;span class="sr"&gt;{Letter}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Mark}&lt;/span&gt;&lt;span class="se"&gt;\s]&lt;/span&gt;&lt;span class="sr"&gt;+/gu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Match letters and hyphens&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Île-de-France&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\p&lt;/span&gt;&lt;span class="sr"&gt;{Letter}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Mark}-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/gu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Match letters hyphens and spaces&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Île-de-France&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\p&lt;/span&gt;&lt;span class="sr"&gt;{Letter}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Mark}&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/gu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>regex</category>
      <category>a11y</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Tell us what you don't know!</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Sun, 07 Feb 2021 18:59:25 +0000</pubDate>
      <link>https://dev.to/tillsanders/tell-us-what-you-don-t-know-k6b</link>
      <guid>https://dev.to/tillsanders/tell-us-what-you-don-t-know-k6b</guid>
      <description>&lt;p&gt;Too often do we take part in discussions about topics we actually don't no much about. Especially on the internet. And especially during the pandemic. I would have loved to have heard the following sentence more often: "I do not know".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We're all experts at something, but none of us are experts at everything. So please join me in this little practice of telling us – but even more importantly yourself – what you no nothing about.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can be something you still want to learn but also something you will probably never learn. This can be about programming, obviously, but doesn't have to be.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus challenge
&lt;/h1&gt;

&lt;p&gt;As a bonus, think about people who do have the skills you find yourself missing. Or think about people, maybe your colleagues, that you believe are more successful than you are. Focus on your feelings towards them. Do you admire them? Or do you maybe also experience some envy? Just ponder on these emotions for a while and see how they mix. Simply being aware of these feelings will help you become more professional. Just acknowledge that you don't know everything. There will always be someone who can do something better than you can. And that's okay. It's perfectly fine. &lt;/p&gt;

&lt;p&gt;Allow yourself to be passionate about your work but aim for a professional detachment. &lt;strong&gt;You are not your work.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>exercise</category>
    </item>
    <item>
      <title>Deploy Nuxt.js on DigitalOcean App Platform in 5 minutes or less</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Wed, 03 Feb 2021 00:52:03 +0000</pubDate>
      <link>https://dev.to/tillsanders/deploy-nuxt-js-on-digitalocean-app-platform-in-5-minutes-or-less-2dij</link>
      <guid>https://dev.to/tillsanders/deploy-nuxt-js-on-digitalocean-app-platform-in-5-minutes-or-less-2dij</guid>
      <description>&lt;p&gt;I recently find myself deploying more and more projects using the DigitalOcean App Platform because it is so damn quick. I love writing Dockerfiles and deploy my containers to a Kubernetes cluster, but with a Platform-as-a-Service solution such as AWS Elastic Beanstalk or the aforementioned solution by DigitalOcean which is even easier to use, I feel like I really don't need to. I've used Elastic Beanstalk in the past, but the pricing always was a bit too high for small projects. With the great usability and pricing of the DigitalOcean App Platform, I started deploying side-projects there. Today I tried deploying a Nuxt installation to App Platform for the first time and found a small hurdle, that somehow wasn't really documented in any tutorial. So here we go!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I assume, that you have basic devop knowledge, including DNS servers, environment variables, and stuff.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1. The container
&lt;/h1&gt;

&lt;p&gt;That's easily the best part. &lt;strong&gt;You don't need one,&lt;/strong&gt; because you can use the default Node container provided to you by DigitalOcean 👍&lt;/p&gt;

&lt;h1&gt;
  
  
  2. The repository
&lt;/h1&gt;

&lt;p&gt;DigitalOcean App Platform deploys your apps right from their respective git repositories. So, assuming you already have a Nuxt.js project in a GitHub or GitLab repository, you can jump right in: &lt;a href="https://cloud.digitalocean.com"&gt;Go to your DigitalOcean Admin Panel&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Set up your DigitalOcean App
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Hit the big green 'Create' button and start creating a new 'app'.&lt;/li&gt;
&lt;li&gt;Connect your GitHub or GitLab account if you haven't already. Select the repository containing your Nuxt.js project below and click 'next'.&lt;/li&gt;
&lt;li&gt;Choose a name and region and proceed to the next step. Here, we have a few settings to make. DigitalOcean should have detected the Node environment.&lt;/li&gt;
&lt;li&gt;We want to deploy it as a Web Service.&lt;/li&gt;
&lt;li&gt;You can set the environment variables if you need any.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  4. Getting health checks right
&lt;/h1&gt;

&lt;p&gt;This is the only problem I stumbled upon. To get health checks working, we need to change the default run command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The build command should be &lt;code&gt;npm run build&lt;/code&gt; – fine!&lt;/li&gt;
&lt;li&gt;The run command should be &lt;code&gt;npm start&lt;/code&gt; by default, but we need to change it to &lt;code&gt;npm start -- --hostname 0.0.0.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The default Nuxt.js port is &lt;code&gt;3000&lt;/code&gt; so we need to either change the port in the app platform setting, or set the run command to &lt;code&gt;npm start -- --hostname 0.0.0.0 --port 8080&lt;/code&gt;. Chose what you like better. I went with 3000.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  5. Set up your domain
&lt;/h1&gt;

&lt;p&gt;Finish the setup and wait for the deployment to complete. With the health checks set up properly, there shouldn't be any issues. Once that is done, you can make more adjustments to your app settings, like connecting your domain.&lt;/p&gt;

&lt;p&gt;I hope this was helpful 🚀&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>nuxt</category>
    </item>
    <item>
      <title>I'm tired of building APIs</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Sat, 23 Jan 2021 17:22:06 +0000</pubDate>
      <link>https://dev.to/tillsanders/i-m-tired-of-building-apis-aj4</link>
      <guid>https://dev.to/tillsanders/i-m-tired-of-building-apis-aj4</guid>
      <description>&lt;p&gt;There. I said it. Building APIs is getting harder. When I first started building services with Laravel 3, things were different, simpler, more enjoyable.&lt;/p&gt;

&lt;h1&gt;
  
  
  So what changed?
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Authentication got more complex
&lt;/h2&gt;

&lt;p&gt;Easily the biggest hurdle when building an API is authentication. Back in the days I would simply generate an API key and be done with it. Now we're shifting to microservices and JWT before most people even understood the complications of that. Getting authentication right (secure + easy to use) has gotten so hard, that I would not recommend anyone to build their own. It just takes too long and you'll probably get it wrong anyway. So we're using services like Keycloak or Auth0 that introduce their own kind of complexities (deployment, data privacy, et cetera). &lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/tillsanders" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CcyRvLL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--G0AOm2Rc--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/556863/34c52e67-cf06-46e2-ad7b-0d104b93c755.jpeg" alt="tillsanders image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/tillsanders/how-to-deploy-a-free-auth0-alternative-to-digitalocean-in-5-minutes-2ili" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to deploy a free Auth0 alternative to DigitalOcean in 5 minutes&lt;/h2&gt;
      &lt;h3&gt;Till Sanders ・ Jan 18 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#selfhosted&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Authorization is complex (still)
&lt;/h2&gt;

&lt;p&gt;This has always been difficult. There are systems and libraries ready to go and they're great. My problem with most authorization solutions is though that they have users and roles and groups, but no concept of ownership. Oftentimes I want to authorize an action depending on who a given object belongs to. &lt;/p&gt;

&lt;h2&gt;
  
  
  REST vs. GraphQL
&lt;/h2&gt;

&lt;p&gt;I was very content with REST. It just made a lot of sense. But now, all I can see are the problems it has compared to GraphQL which is so great on the client side and such a pain on the server side. At this point they both feel like a bad choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL vs. NoSQL
&lt;/h2&gt;

&lt;p&gt;Same as with REST. MySQL felt so logical to me. But when you're designing a slightly more complex data structure, you end up with lot's of tables, very quickly. Most of the time this also leads to more models, more APIs, more tests, etc. MongoDB on the other side is so much fun when it comes to nested objects. But somehow I still feel uneasy leaving normalized data and my neatly organized migrations behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Too big frameworks vs. too small
&lt;/h2&gt;

&lt;p&gt;Laravel is great! But it also became a bit overwhelming. It has always felt magical, but also difficult to understand what's going on behind the scenes. I don't think it has become harder to understand or to learn, there is just more of it. Sometimes I would prefer it to be a little smaller. That's when I look at Node.js frameworks like &lt;a href="https://adonisjs.com/"&gt;Adonis&lt;/a&gt;, &lt;a href="https://nestjs.com/"&gt;Nest&lt;/a&gt; or &lt;a href="https://www.fastify.io/"&gt;Fastify&lt;/a&gt;. I feel like I can make more choices there. With frameworks like Fastify I can come up with my own architecture, which I really enjoy. But I'm missing a solid authentication solution in the Node.js world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;I'm actually not sure if testing just wasn't a thing in Laravel 3, or if my junior dev ass just ignored it, but I didn't do any automated testing. Nowadays I do, of course, and I wouldn't stop doing it, but testing APIs feels so repetitive and time consuming. I need tests, but when I'm working on a side-project I would rather work on the actual software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;I used to just synchronize my code to some server using Transmit. I even recall writing a very angry e-mail to ExpanDrive once because it messed up my files, which was a great pain because I wasn't using Git back then. But aside from that deployment was simple. Today containerization makes deployment so much better and so much more complicated at the same time. I love it and it scares me at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casting and DTOs
&lt;/h2&gt;

&lt;p&gt;With Laravel 3 I would simply return an object I got from the database, it would then be converted to JSON automatically and wrapped in an HTTP response. And while that still works, of course, I now find myself writing DTOs for every model, at least for Create and Update, sometimes for more specific actions. I also spend time casting some properties back and forth because JSON is quite limited when it comes to typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security &amp;amp; Law
&lt;/h2&gt;

&lt;p&gt;CORS. CSRF. HTTP-only cookies. CORS. SSL certificates. GDPR and other privacy laws. CORS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion – I'm the problem.
&lt;/h1&gt;

&lt;p&gt;Yes, many things changed and many got more complicated. But there are also a lot more tools and platforms to chose from. Great things happened that made my work a lot easier (like Chrome DevTools or &lt;a href="https://directus.io/"&gt;Directus&lt;/a&gt;). So what's really the problem here? I'm afraid after all these years I simply got to a point on the learning curve that is high enough to see all these things properly. &lt;/p&gt;

&lt;p&gt;When I started web development I simply couldn't see many of these issues. And now that I do, I cannot ignore them. And sometimes, yes, that is tiring. Becoming more professional at what you do doesn't mean it's getting any easier. You just get better at doing it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Copyright of the title image belongs to the BBC.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>devjournal</category>
      <category>healthydebate</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to deploy a free Auth0 alternative to DigitalOcean in 5 minutes</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Mon, 18 Jan 2021 19:35:57 +0000</pubDate>
      <link>https://dev.to/tillsanders/how-to-deploy-a-free-auth0-alternative-to-digitalocean-in-5-minutes-2ili</link>
      <guid>https://dev.to/tillsanders/how-to-deploy-a-free-auth0-alternative-to-digitalocean-in-5-minutes-2ili</guid>
      <description>&lt;p&gt;Many of you might have already used a service like Auth0 to hand-off user authentication and authorization to a dedicated service. I think this is a sane solution to the ever-growing problem of getting authentication right. Technologies like &lt;a href="https://auth0.com/de/"&gt;OAuth&lt;/a&gt; and &lt;a href="https://jwt.io/"&gt;JWT&lt;/a&gt; sound great, but they are very easy to get wrong with mistakes that are &lt;a href="https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/"&gt;very hard&lt;/a&gt; to discover. Also, implementing authentication again and again is hardly fun. So, Auth0 or Firebase are nice solutions to hit the ground running, but some projects (or budgets for that matter) require self-hosted solutions, like &lt;a href="https://www.keycloak.org/"&gt;Keycloak&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Keycloak as a free, self-hosted authentication server
&lt;/h1&gt;

&lt;p&gt;As the project describes itself, Keycloak is an "open source identity and access management [tool] for modern applications and services", which allows you to "add authentication to applications and secure services with minimum fuss. No need to deal with storing users or authenticating users. It's all available out of the box. You'll even get advanced features such as User Federation, Identity Brokering and Social Login.".&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would you want this?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Save money in the long run. Auth0 starts at 23 USD / month for 1.000 users.&lt;/li&gt;
&lt;li&gt;Building your own solution is hard. Very. And not nearly as good.&lt;/li&gt;
&lt;li&gt;Login + Registration for a new service set up in minutes.&lt;/li&gt;
&lt;li&gt;E-Mail verification is built-in.&lt;/li&gt;
&lt;li&gt;2FA is built-in.&lt;/li&gt;
&lt;li&gt;Social logins for sites like Facebook, Twitter, LinkedIn, Instagram, GitHub, GitLab – only a few clicks away.&lt;/li&gt;
&lt;li&gt;Bring your own theme!&lt;/li&gt;
&lt;li&gt;Use as a SSO (Single Sign On) solution for multiple services&lt;/li&gt;
&lt;li&gt;Connect to existing LDAP or Active Directory services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I wanted to build something like this, it would take me months and I still wouldn't know it's safe.&lt;/p&gt;

&lt;p&gt;Sounds great? It is. Let's give it a spin, shall we?&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploy to DigitalOcean App Platform
&lt;/h1&gt;

&lt;p&gt;Recently, DigitalOcean launched the PaaS solution, called &lt;a href="https://www.digitalocean.com/products/app-platform/"&gt;DigitalOcean App Platform&lt;/a&gt;. Since we want to take the hassle out of authentication, this seems like a perfect fit to deploy our own authentication server.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I assume, that you have basic devop knowledge, including DNS servers, environment variables, databases, docker, and stuff.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The container
&lt;/h2&gt;

&lt;p&gt;Luckily, Keycloak &lt;a href="https://quay.io/repository/keycloak/keycloak-x?tag=latest&amp;amp;tab=tags"&gt;provides a container&lt;/a&gt;, optimized and ready to go. It's rather new and called 'keycloak-x'. You can read more about it &lt;a href="https://github.com/keycloak/keycloak-containers/tree/master/server-x"&gt;here&lt;/a&gt;. And more about it's configuration &lt;a href="https://github.com/keycloak/keycloak-community/blob/master/design/keycloak.x/configuration.md"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Your repository
&lt;/h2&gt;

&lt;p&gt;DigitalOcean App Platform deploys your apps right from their respective git repositories. You can provide a Dockerfile or use the readily available environments provided by DO. Since we already have a container, you can go ahead and create an empty repository for this project on GitHub or GitLab. All you need in this repository is a simple Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM quay.io/keycloak/keycloak-x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Set up a database
&lt;/h2&gt;

&lt;p&gt;You can configure a development database in step 4, create a new database in an existing database server, or create a new database server now.&lt;/p&gt;

&lt;p&gt;Sign into your DO account and create a new database server. We will use MySQL in this example. Configure your cluster as you like. We're going with the smallest configuration. Once your database server is deployed, add a new database called &lt;code&gt;keycloak&lt;/code&gt; and a new user, also called &lt;code&gt;keycloak&lt;/code&gt;. Copy all credentials for the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Set up your DigitalOcean App
&lt;/h2&gt;

&lt;p&gt;Hit the big green &lt;a href="https://cloud.digitalocean.com/apps/new"&gt;'Create' button&lt;/a&gt; and start creating a new 'app'. Connect your GitHub or GitLab account if you haven't already. Select the repository below and click 'next'. Choose a name and region and proceed to the next step. Here, we have a few settings to make. DigitalOcean should have detected the Dockerfile. We want to deploy it as a Web Service with the following environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KEYCLOAK_ADMIN=yourusername
DB_VENDOR=mysql
DB_ADDR=
DB_PORT=
DB_DATABASE=keycloak
DB_USER=keycloak
DB_PASSWORD=
KC_PROXY_MODE=edge
KC_METRICS_ENABLED=true
KC_HTTP_ENABLED=true
KC_HOSTNAME_FRONTEND_URL=https://auth.example.com/
KC_HOSTNAME_ADMIN_URL=https://auth.example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, make sure to provide the correct details for your database connection and set your own keycloak username and a strong password. Note that we're disabling https here since the SSL connection will be terminated by DO. Also, make sure to add a trailing &lt;code&gt;/&lt;/code&gt; to the hostname urls!&lt;/p&gt;

&lt;p&gt;Port is 8080 and health checks should work fine with TCP.&lt;br&gt;
&lt;em&gt;(Actually, although health check urls are enabled using &lt;code&gt;KC_METRICS_ENABLED&lt;/code&gt;, I didn't manage to get HTTP health checks working yet. So if you see something that I didn't, please leave a comment.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and deploy!&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Configure your very own keycloak server
&lt;/h2&gt;

&lt;p&gt;By now, your keycloak instance should be up and running. I would recommend adding another domain in the settings of your DigitalOcean app. You don't have to, though. Just make sure &lt;code&gt;KC_HOSTNAME_FRONTEND_URL&lt;/code&gt; and &lt;code&gt;KC_HOSTNAME_ADMIN_URL&lt;/code&gt; match your generated or custom domain and your DNS servers are configured accordingly. &lt;/p&gt;

&lt;p&gt;You can access your installation under the assigned or your custom domain. Simply log in with your admin credentials.&lt;/p&gt;

&lt;p&gt;If you need some help getting started with keycloak, I can recommend this video: &lt;a href="https://www.youtube.com/watch?v=duawSV69LDI"&gt;https://www.youtube.com/watch?v=duawSV69LDI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, I had a problem where I couldn't access the login screen of the realm I created in keycloak. All I ever got was an alert that keycloak could not be initialized and a 403. It turned out eventually, that I needed to set the Web Origins setting of my client to a wildcard: &lt;code&gt;*&lt;/code&gt; to allow access from any origin. But that was simply a beginner's mistake.&lt;/p&gt;

&lt;p&gt;Hope you had an easy time following along. Leave a comment below and tell me how it went and what you're planning to do with it!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>webdev</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>ESLint not working in VSCode? Help build a troubleshooting checklist!</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Mon, 11 Jan 2021 19:35:33 +0000</pubDate>
      <link>https://dev.to/tillsanders/eslint-not-working-in-vscode-help-build-a-troubleshooting-checklist-fdc</link>
      <guid>https://dev.to/tillsanders/eslint-not-working-in-vscode-help-build-a-troubleshooting-checklist-fdc</guid>
      <description>&lt;p&gt;I spend far too much time fighting configuration issues in my toolchain. Most issues I even encounter frequently. Help me build a troubleshooting checklist. Simply add other possible configuration mistakes in the comments and I will add them to the article.&lt;/p&gt;

&lt;h1&gt;
  
  
  Start here
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Did you run &lt;code&gt;npm install&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Stupid, I know. But I forget to do this quite a lot when coming back to a shared project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did you try turning it off and on again?
&lt;/h2&gt;

&lt;p&gt;Sometimes it helps to restart VSCode and I've even had problems that were solved with restarting my computer, though that is rather rare and might also have been related to the TypeScript server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did you install ESLint + Plugins + Presets?
&lt;/h2&gt;

&lt;p&gt;Take a look into your &lt;code&gt;eslintrc&lt;/code&gt; configuration file and make a list of all plugins and presets. Than make sure, they as well as ESLint itself are in your projects &lt;code&gt;devDependencies&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is your npm script set up?
&lt;/h2&gt;

&lt;p&gt;Do you have a &lt;code&gt;lint&lt;/code&gt; script in your &lt;code&gt;package.json&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint --ext .js --ignore-path .gitignore ."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Did you tell ESLint, which directory to lint?
&lt;/h2&gt;

&lt;p&gt;I recently encountered a project where the lint script in the &lt;code&gt;package.json&lt;/code&gt; was missing the dot at the very end which tells ESLint to start linting in the same directory as the configuration file is in. This was not easily detected, since ESLint didn't throw any warning but just quit silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did you make sure, ESLint is linting the correct file extensions?
&lt;/h2&gt;

&lt;p&gt;In case you're using TypeScript, Vue.js or other non-standard JavaScript files, make sure to tell ESLint about your file extensions by using the &lt;code&gt;--ext&lt;/code&gt; flag like so &lt;code&gt;--ext .js,.ts,.vue&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have more &lt;code&gt;.eslintrc&lt;/code&gt; configuration files?
&lt;/h2&gt;

&lt;p&gt;Since &lt;code&gt;.eslintrc&lt;/code&gt; configuration files automatically extend configuration files in higher directories, this can cause weird issues. Make sure you don't have any additional configuration files anywhere in your project or in the directories above, that you are unaware of. Sometimes, I accidentally scaffold a project in my home directory and forget to delete the hidden &lt;code&gt;.eslintrc.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When looking for more &lt;code&gt;.eslintrc&lt;/code&gt; files, remember, that they might be invisible to your file explorer. Use &lt;code&gt;ls -a&lt;/code&gt; to show hidden files in your terminal.&lt;/p&gt;

&lt;p&gt;Additionally, you can stop ESLint from searching further up the directory tree by adding the &lt;code&gt;root&lt;/code&gt; option to the &lt;code&gt;.eslintrc&lt;/code&gt; in your project root directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learn more about &lt;a href="https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy"&gt;Configuration Cascading and Hierarchy&lt;/a&gt; in the official documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have the ESLint plugin installed and enabled in VSCode?
&lt;/h2&gt;

&lt;p&gt;Maybe you've just set up a new VSCode installation or something's gone wrong syncing your settings. Make sure, the ESLint plugin is installed and also enabled in VSCode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did you set up library execution?
&lt;/h2&gt;

&lt;p&gt;The ESLint plugin requires permission to execute the local ESLint installation from your &lt;code&gt;node_modules&lt;/code&gt;. Open the command palette (&lt;code&gt;Ctrl / Cmd + Shift + P&lt;/code&gt;) and select &lt;code&gt;ESLint: Manage Library Execution&lt;/code&gt; to open the dialog for your project and confirm with 'Accept'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did you maybe disable the rule you're testing?
&lt;/h2&gt;

&lt;p&gt;Sometimes we mess projects up. Some might use Standard JS, others the AirBnB preset. Some projects disable certain rules. Make sure, you're not testing a rule that isn't even enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are you having trouble with the TypeScript integration?
&lt;/h2&gt;

&lt;p&gt;Make sure to install the TypeScript plugin and configure it accordingly: &lt;a href="https://github.com/typescript-eslint/typescript-eslint"&gt;https://github.com/typescript-eslint/typescript-eslint&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have confusing overrides?
&lt;/h2&gt;

&lt;p&gt;You might have added some overrides (note: not 'overwrites' – another mistake I made once) for test files or similar. Remove them from your config for a minute to see if that changes anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the ESLint plugin configured as expected?
&lt;/h2&gt;

&lt;p&gt;Open your VSCode preferences and search for ESLint. There's a host of settings, so make sure, they are all set as you want them to.&lt;/p&gt;

&lt;p&gt;Most important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate: Should contain all languages you want to lint&lt;/li&gt;
&lt;li&gt;Node Path&lt;/li&gt;
&lt;li&gt;Run: Wether to run on save or continuously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, be aware, that you might have accidentally overwritten these settings for your workspace or even only one project in your workspace. You can access these overwrites from the VSCode settings panel by simply clicking on 'Workspace' or 'Folder' below the search bar.&lt;/p&gt;

&lt;h1&gt;
  
  
  Nothing is helping? How to find the solution yourself!
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Update all ESLint related dependencies
&lt;/h2&gt;

&lt;p&gt;Maybe you've stumbled upon a bug and maaaaybe it has already been fixed? Use &lt;code&gt;npm outdated&lt;/code&gt; to find outdated dependencies in your project and just try to update ESLint, your plugins and presets. If you've installed ESlint globally, take care to use the correct installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are your plugins and presets still maintained?
&lt;/h2&gt;

&lt;p&gt;Some plugins or presets might not get any updates. Take a look at their repositories and issue trackers. Maybe someone there is even experiencing the same problems?&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the message you're seeing even coming from ESLint?
&lt;/h2&gt;

&lt;p&gt;The origin might also be in other tools like TypeScript or Prettier. You can see which tool is generating the message in VSCode at the end of the message in round brackets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take a look at the output panel in VSCode
&lt;/h2&gt;

&lt;p&gt;Open the command palette by pressing &lt;code&gt;Ctrl / Cmd + Shift + P&lt;/code&gt; and select 'ESLint: Show Output Channel'. If ESLint throws any errors, they should appear here. That's always a good starting point for further debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try setting up your ESLint config from scratch
&lt;/h2&gt;

&lt;p&gt;Remove everything ESLint related and start from scratch using the &lt;code&gt;eslint --init&lt;/code&gt; command that helps you set everything up.&lt;/p&gt;

&lt;h1&gt;
  
  
  Add your own troubleshooting tips in the comments!
&lt;/h1&gt;

&lt;p&gt;I will add them to the article so whenever you encounter something new, make sure to leave a comment ;)&lt;/p&gt;

</description>
      <category>eslint</category>
      <category>webdev</category>
      <category>linting</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Boy, was it hard to implement proper keyboard shortcuts</title>
      <dc:creator>Till Sanders</dc:creator>
      <pubDate>Sun, 10 Jan 2021 02:14:48 +0000</pubDate>
      <link>https://dev.to/tillsanders/boy-was-it-hard-to-implement-proper-keyboard-shortcuts-4d72</link>
      <guid>https://dev.to/tillsanders/boy-was-it-hard-to-implement-proper-keyboard-shortcuts-4d72</guid>
      <description>&lt;p&gt;I'm currently implementing a complete redesign of our primary administration interface. This web application is used by our customer service every day to read, write and process heaps of information and complex data structures. When you're designing an interface for such trained users, priorities shift a great deal. &lt;strong&gt;These users prefer speed over simplicity,&lt;/strong&gt; so you start to seriously optimize things like navigation. And the easiest, maybe even the most important way to speed up navigation is by providing &lt;strong&gt;keyboard shortcuts.&lt;/strong&gt; Most websites don't really use custom keyboard shortcuts because they take too much time to learn. But for users who spend many hours a day working with a web application, keyboard shortcuts are indispensable. And so my journey began...&lt;/p&gt;

&lt;p&gt;
  TL;DR
  &lt;p&gt;I built a polyfill for the &lt;code&gt;accessKeyLabel&lt;/code&gt; property that reveals the keyboard shortcut assigned by the browser and based on the &lt;code&gt;accessKey&lt;/code&gt; property assigned to an interactive element. Browsers use different shortcuts for this and the &lt;code&gt;accessKeyLabel&lt;/code&gt; property is the best way to improve the discoverability of custom shortcuts in web applications. Native support is missing in Chrome, IE and Edge. My AccessKeyLabelPolyfill is bridging the gap with less than 1 KB. &lt;a href="https://github.com/tillsanders/access-key-label-polyfill" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://tillsanders.github.io/access-key-label-polyfill/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; &lt;/p&gt;

&lt;/p&gt;

&lt;h1&gt;
  
  
  Using the &lt;code&gt;accesskey&lt;/code&gt; property to quickly add keyboard shortcuts
&lt;/h1&gt;

&lt;p&gt;Since HTML 4, we've had the power to add custom keyboard shortcuts to our webpage using the &lt;a href="https://developer.mozilla.org/de/docs/Web/HTML/Globale_Attribute/accesskey" rel="noopener noreferrer"&gt;&lt;code&gt;accesskey&lt;/code&gt;&lt;/a&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;accesskey=&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Do crazy stuff&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Simply by adding this property to a button, I've set up a keyboard shortcut. In most browsers, pressing &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Alt&lt;/code&gt; + &lt;code&gt;D&lt;/code&gt; will now trigger this button. Great! So I just added this to a number of buttons in our application and called it a day? Unfortunately not. See, what modifier keys (&lt;code&gt;Ctrl&lt;/code&gt;, &lt;code&gt;Alt&lt;/code&gt;, &lt;code&gt;Shift&lt;/code&gt;, etc.) will be combined to a shortcut is not standardized. And I wish that was the only problem.&lt;/p&gt;
&lt;h1&gt;
  
  
  Browsers don't use the same key to make the shortcuts
&lt;/h1&gt;

&lt;p&gt;So, as I said, most browsers use &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Alt&lt;/code&gt;. At least on macOS. Except for Firefox before version 14. Back then, it only used &lt;code&gt;Ctrl&lt;/code&gt;. On Windows, the same browsers generally use only the &lt;code&gt;Alt&lt;/code&gt; key. Except for Firefox, which seems to strive for cross-platform consistency. Nice. These are, however, only the browsers whose behaviour was documented on MDN. As you know there are loads more. And at least some even allow to customise their behaviour. This means that the actual shortcut depends on the browser, it's version, your operating system and possibly your personal settings. But don't worry, it get's worse.&lt;/p&gt;
&lt;h1&gt;
  
  
  Users are unaware of keyboard shortcuts on websites
&lt;/h1&gt;

&lt;p&gt;Let's face it. Most people are completely dumbfounded when it comes to keyboard shortcuts. If you're ever trying to impress people, just show them the &lt;code&gt;Tab&lt;/code&gt; key. But when they're working with the same application every day, we should try to educate them to help speed things up. So now that we have keyboard shortcuts thanks to &lt;code&gt;accesskey&lt;/code&gt;, how can we make them easily discoverable?&lt;/p&gt;
&lt;h1&gt;
  
  
  Proven technique to indicate keyboard shortcuts
&lt;/h1&gt;

&lt;p&gt;For some reason, I'm seeing this technique less and less in modern systems – probably due to aesthetic reasons. But it has been around for decades and I doubt that it get's any more elegant than this. The idea is simple and every little as space-saving as it is helpful: &lt;strong&gt;just underline the character that is the access key.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;accesskey=&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;u&amp;gt;&lt;/span&gt;D&lt;span class="nt"&gt;&amp;lt;/u&amp;gt;&lt;/span&gt;o crazy stuff&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By using a consistent scheme (like &lt;code&gt;Ctrl&lt;/code&gt;+ &lt;code&gt;Alt&lt;/code&gt; / &lt;code&gt;Alt&lt;/code&gt;) and underlining the access key, we give users a subtle hint or reminder which character provides access to a specific action. Neat.&lt;/p&gt;
&lt;h1&gt;
  
  
  Underlining the access key is not enough
&lt;/h1&gt;

&lt;p&gt;We surely can improve the discoverability of our shortcuts even more. Right now, the biggest issue is that users are probably unfamiliar with the default keyboard shortcut of their browser. So why don't we add a small reminder. For big buttons with enough space, we could add a small text below. For others, we could add tooltips or use the &lt;code&gt;title&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;accesskey=&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Do crazy stuff&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Hint: Use Ctrl + Alt + D to quickly access this button!&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Great! But as we learned in the beginning, browsers use different modifier keys for our shortcuts. How do we handle this?&lt;/p&gt;
&lt;h1&gt;
  
  
  The &lt;code&gt;accessKeyLabel&lt;/code&gt; API reveals the exact shortcut
&lt;/h1&gt;

&lt;p&gt;Thankfully, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/accessKeyLabel" rel="noopener noreferrer"&gt;&lt;code&gt;accessKeyLabel&lt;/code&gt;&lt;/a&gt; API of HTML 5 will tell us the exact keyboard shortcut the browser assigned to our button. That's great! We can access this property in a Vue.js component and add it to our hint easily. No magic required.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chrome does not support &lt;code&gt;accessKeyLabel&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Well, I must admit – I didn't see that one coming. While &lt;code&gt;accesskey&lt;/code&gt; is supported perfectly, Chrome, Internet Explorer (okay, no surprise...), Edge, Safari for iOS &amp;lt; v13 and more don't (yet) support &lt;code&gt;accessKeyLabel&lt;/code&gt;. &lt;a href="https://caniuse.com/?search=accessKeyLabel" rel="noopener noreferrer"&gt;That's a bummer.&lt;/a&gt; Firefox and Safari for macOS however, have good support. But it's still not enough to rely on.&lt;/p&gt;
&lt;h1&gt;
  
  
  Use my polyfill
&lt;/h1&gt;

&lt;p&gt;As it turns out, there is a &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=393466&amp;amp;q=accesskeylabel&amp;amp;can=2" rel="noopener noreferrer"&gt;ticket&lt;/a&gt; in the bug tracker of the chromium project that's been calling to add support for the &lt;code&gt;accessKeyLabel&lt;/code&gt; property since 2014. Unfortunately, other things seem to have a higher priority, so until native support arrives in all major browser, we need a polyfill.&lt;/p&gt;

&lt;p&gt;
  What is a polyfill?
  &lt;p&gt;A polyfill is a script that patches native functionality that has not yet arrived in certain browsers or will not arrive because of old age. Polyfills generally aim to provide the exact same functionality and API as the original specification. Some things, however, can only be partially implemented using polyfills. What's great about polyfills is that they allow us to use new technologies without needing to worry too much about "older" browsers. The disadvantage is, of course, that we're adding more code to our website that might slow it down a little bit more. That being said, most polyfills are actually quite small.&lt;/p&gt;

&lt;/p&gt;

&lt;p&gt;My search for a polyfill was surprisingly unsuccessful. I stumbled upon a jQuery plugin, but wanted something vanilla JavaScript.&lt;/p&gt;

&lt;p&gt;Sometimes, to provide great user experience and accessibility, you have to go to great lengths. I tend to do that. So here is my very first polyfill. It is vanilla JavaScript and weighs under 1 KB: &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/tillsanders" rel="noopener noreferrer"&gt;
        tillsanders
      &lt;/a&gt; / &lt;a href="https://github.com/tillsanders/access-key-label-polyfill" rel="noopener noreferrer"&gt;
        access-key-label-polyfill
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Polyfills the accessKeyLabel property to improve discoverability of native keyboard shortcuts for your website. Tiny and dependency-free.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;AccessKeyLabelPolyfill&lt;/h1&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Polyfills the accessKeyLabel property to improve discoverability of native keyboard shortcuts for
your website. Tiny (&amp;lt; 1KB) and dependency-free.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the &lt;a href="https://developer.mozilla.org/de/docs/Web/HTML/Globale_Attribute/accesskey" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;accesskey&lt;/code&gt; HTML property&lt;/a&gt;
you can easily provide keyboard shortcuts for the buttons of your web application.&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-c1"&gt;accesskey&lt;/span&gt;="&lt;span class="pl-s"&gt;D&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;Do crazy stuff&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;button&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-c"&gt;&amp;lt;!-- Can be activated using Ctrl + Alt + D --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Sadly, most users don't even know these shortcuts can be used, even if the web developer provided
them. To make these shortcuts more discoverable you might decide to use the &lt;code&gt;title&lt;/code&gt; property, a
tooltip or some other UI element to display the keyboard shortcut.&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-c1"&gt;accesskey&lt;/span&gt;="&lt;span class="pl-s"&gt;D&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;Do crazy stuff&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;button&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;small&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;Hint: Use Ctrl + Alt + D to quickly access this button!&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;small&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;A good idea, but browsers use different combinations of modifier keys. But…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tillsanders/access-key-label-polyfill" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Installation and usage of the AccessKeyLabelPolyfill
&lt;/h1&gt;

&lt;p&gt;You can install the polyfill using npm or yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i access-key-label-polyfill
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add access-key-label-polyfill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then import and execute 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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;installAccessKeyLabelPolyfill&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;access-key-label-polyfill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;installAccessKeyLabelPolyfill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Reading the &lt;code&gt;accessKeyLabel&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;I integrated this into a Vue.js button component that also has some neat methods to automatically underline the first occurrence of the &lt;code&gt;accessKey&lt;/code&gt; in the button caption, but you might use it any way you like. You can read the documentation of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/accessKeyLabel" rel="noopener noreferrer"&gt;&lt;code&gt;accessKeyLabel&lt;/code&gt; property on MDN&lt;/a&gt;, because the API of the polyfill is exactly the same. But it's quite simple, so here is an example:&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;label&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#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;accessKeyLabel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The polyfill will first check if &lt;code&gt;accessKeyLabel&lt;/code&gt; is supported natively and exit if so. Otherwise it will try to detect the browser and operating system and return the correct keyboard shortcut as a string that can be displayed in a &lt;code&gt;&amp;lt;small&amp;gt;&lt;/code&gt; tag, a tooltip, or the &lt;code&gt;title&lt;/code&gt; attribute. Get creative!&lt;/p&gt;

&lt;p&gt;If you're using the polyfill with Vue.js, React, Svelte, Angular or other frameworks, remember to only load it once!&lt;/p&gt;

&lt;h1&gt;
  
  
  Try the demo!
&lt;/h1&gt;

&lt;p&gt;You can visit the demo to try the plugin right in your browser. The demo will also tell you wether the polyfill detected native support or not. &lt;a href="https://tillsanders.github.io/access-key-label-polyfill/" rel="noopener noreferrer"&gt;Try the demo!&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The state of keyboard shortcuts in web applications
&lt;/h1&gt;

&lt;p&gt;Today's odyssey into the &lt;code&gt;accessKeyLabel&lt;/code&gt; property revealed to me the many issues we face regarding shortcuts in web applications. Not only are the shortcuts &lt;strong&gt;not standardised&lt;/strong&gt; and support for the simple &lt;code&gt;accessKeyLabel&lt;/code&gt; &lt;strong&gt;API is missing in most browsers&lt;/strong&gt;, but I was unable to discover the shortcut in Chrome for Android. Maybe I was due to a lack of a proper Android tablet for testing or maybe &lt;code&gt;accesskey&lt;/code&gt; is simply not supported. To me, it's unclear how access keys &lt;strong&gt;might interfere with screen readers&lt;/strong&gt;, browser shortcuts or even other applications. There is also no standard on how browsers handle &lt;strong&gt;conflicting access keys&lt;/strong&gt;. But what's easily &lt;strong&gt;the biggest issue is usage&lt;/strong&gt;. It is rare to see a web application use these features, even the complex ones that would benefit greatly from it. By taking the time of writing and publishing this polyfill, I hope to make keyboard shortcuts in web applications more accessible to you all by bridging the gap in browser support.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>html</category>
      <category>polyfill</category>
      <category>browser</category>
    </item>
  </channel>
</rss>
