<?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: Christian Kaindl</title>
    <description>The latest articles on DEV Community by Christian Kaindl (@christiankaindl).</description>
    <link>https://dev.to/christiankaindl</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%2F22709%2F1e659ade-abd1-42f1-a529-043fa09bfbd5.jpg</url>
      <title>DEV Community: Christian Kaindl</title>
      <link>https://dev.to/christiankaindl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christiankaindl"/>
    <language>en</language>
    <item>
      <title>A WebExtension Guide</title>
      <dc:creator>Christian Kaindl</dc:creator>
      <pubDate>Tue, 19 Dec 2017 10:37:49 +0000</pubDate>
      <link>https://dev.to/christiankaindl/a-webextension-guide-36ag</link>
      <guid>https://dev.to/christiankaindl/a-webextension-guide-36ag</guid>
      <description>&lt;p&gt;&lt;em&gt;For those who don't know, WebExtension is a new Add-on architecture/specification that can be used even across browsers (to some extend). It is a browser native API interface with which you can write extensions with just web technologies. The core of the WebExtension API is meant to be standardized for ease of development in the future. For this article, I will be writing in the context of extension development for Firefox but some things may also be true for other browsers.&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;

&lt;p&gt;At the heart and at the start of an extension is its &lt;strong&gt;manifest&lt;/strong&gt;. The manifest is a JSON file that contains all sorts of information about the Add-on: Name, version, description, permissions, UI elements, icons, etc.&lt;/p&gt;

&lt;p&gt;To create the simplest WebExtension that is possibly possible this is what a &lt;code&gt;manifest.json&lt;/code&gt; could look like:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Super Awesome Extension 👓"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&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;The 3 JSON keys shown above are the only mandatory keys in your manifest. But, before you add some fancy functionality to it, let me show you how you can actually get to install a local Add-on in the browser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to 'about:debugging' using your URL bar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fhido749a512hux1vuz59.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fhido749a512hux1vuz59.gif" alt="Visiting 'about:debugging'" width="837" height="740"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click 'Load Temporary Add-on' and choose the manifest.json from your local Add-on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fsr2jjgm5o5spox2mwn00.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fsr2jjgm5o5spox2mwn00.gif" alt="Loading a temporary Add-on" width="837" height="740"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your extension will now be displayed in Firefox' Add-on page ('about:addons' in the URL bar or hamburger menu on the top right and then 'Add-ons') and is ready to use! That is all you need to know to get you Add-on up 'n running. You can go and try it out right now, I'll wait here. Note: When you make any changes to you Add-on during development, don't forget to hit the &lt;code&gt;Reload&lt;/code&gt; button on the &lt;code&gt;about:debugging&lt;/code&gt; page.&lt;/p&gt;




&lt;p&gt;If you want to see potential errors and/or &lt;code&gt;log&lt;/code&gt; statements from your Add-on, there is a separate debug window for that (you will need it).&lt;/p&gt;

&lt;p&gt;To open it, go to 'about:debugging' (as shown above) and load your Add-on (also shown above). From there, check the 'Enable Add-on Debugging' checkbox and then click on 'Debug' for your Add-on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F2akbblefuhetmrbdglbi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2akbblefuhetmrbdglbi.gif" alt="Opening the debug window" width="1169" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nearly&lt;/em&gt; all debugging information will be displayed in this window, we will see later why only &lt;em&gt;nearly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext" rel="noopener noreferrer"&gt;Web-ext tool&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Debugging" rel="noopener noreferrer"&gt;Debugging on MDN&lt;/a&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  JavaScript!
&lt;/h1&gt;

&lt;p&gt;For WebExtensions, all APIs live in the &lt;code&gt;browser&lt;/code&gt; namespace. This means, that your scripts have access to an JavaScript object called &lt;code&gt;browser&lt;/code&gt; which has methods for all default APIs and for all APIs that you have granted permissions to through the manifest. So, for example, if you would want to open a new tab from one of your scripts you could write &lt;code&gt;browser.tabs.create({});&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; Google Chrome uses the non-standard &lt;code&gt;chrome&lt;/code&gt; namespace for its APIs. &lt;code&gt;browser&lt;/code&gt; uses JavaScript promises as return values for asynchronous APIs, &lt;code&gt;chrome&lt;/code&gt; uses callbacks. For cross-browser extensions there is a polyfill for the &lt;code&gt;browser&lt;/code&gt; namespace that you can use in Google Chrome. Firefox supports both &lt;code&gt;browser&lt;/code&gt; and &lt;code&gt;chrome&lt;/code&gt; to make it easier to port extensions from Google Chrome. If you develop a new extension with cross-browser compatibility, use the &lt;code&gt;browser&lt;/code&gt; namespace with the polyfill for Google Chrome.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are three types of scripts in the WebExtension world: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Background Scripts&lt;/strong&gt; are the highest level of script your extension can run. It has access to all default APIs and all APIs that you have granted through your manifest (such scripts are called 'privileged scripts'). Background Scripts run &lt;strong&gt;independent&lt;/strong&gt; of of any tabs (in fact they even have their own content process in the new Firefox), meaning that it starts its life on browser start-up (or Add-on installation) and ends its life when you close the browser (or uninstall the Add-on). The Background Script also runs across multiple browser instances (e.g. you have more than one Firefox window open at the same time). So, Background Scripts are your go to choice for 1) heavy lifting (there is an API to detect if the user is idle, which could be a good time for heavy tasks! &lt;em&gt;(Please, do not do crypto mining. Ups.)&lt;/em&gt; ), 2) enure features that work independent of websites, and 3) use privileged APIs. From Background Scripts you can also register to events such as website visits, and then programmatically insert a script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content Scripts&lt;/strong&gt; are either inserted programmatically (e.g. through a Background Script) or inserted automatically (using the &lt;code&gt;content_scripts&lt;/code&gt; manifest key) into a website. Once in, they can directly interact with the site's DOM. Content Scripts are not privileged scripts, that means they have only limited access to WebExtension APIs. Note: If you are unsure what your options are, you can find all APIs that are available to Content Scripts &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#WebExtension_APIs" rel="noopener noreferrer"&gt;in this MDN article&lt;/a&gt; or you can always use a &lt;code&gt;console.info(browser);&lt;/code&gt; statement and inspect its attached methods in the console. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scripts from WebExtension UIs&lt;/strong&gt;. These include pages from Sidebars, Page and Browser Action popups and Developer Tools. They share a similarity with Content Scripts in that they get terminated as soon as the popup/panel/sidebar is closed. Their debugging information is displayed in the debug window alongside your Background Scripts, and they are also privileged scripts. The fact that scripts that are executed within popups only live as long as the popup is open is a limiting factor. So, depending on your use case, it is very likely that you will also need a Background Script page. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;You can access the scope of other priviledged scripts with &lt;code&gt;browser.extension.getViews()&lt;/code&gt;, or the scope of the background script(s) only with &lt;code&gt;browser.extension.getBackgroundPage()&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API" rel="noopener noreferrer"&gt;Differences between &lt;code&gt;browser&lt;/code&gt; and &lt;code&gt;chrome&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension" rel="noopener noreferrer"&gt;Anatomy of an extension overview&lt;/a&gt; (Background Scripts, Content Scripts, Sidebars, popups, option pages)&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts" rel="noopener noreferrer"&gt;More on Content Scripts&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;The number of APIs you can use is limited by 1) the permissions that you granted through the manifest and 2) if the script in question is either a privileged script (Background Scripts, Sidebar, Popups) or a Content Script.&lt;/p&gt;

&lt;p&gt;Content Scripts can only use &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#WebExtension_APIs" rel="noopener noreferrer"&gt;certain WebExtension APIs&lt;/a&gt;, but have also access to the DOM of the page they were injected in.&lt;/p&gt;

&lt;p&gt;Privileged scripts have access to all WebExtension APIs (except for these that require permission, in that case only if you have specified the corresponding manifest key). In case of privileged scripts that have a HTML page with them (Sidebar, Popups) they have access to their DOM. Background Scripts can also access their own DOM, but it won't be rendered anywhere.&lt;/p&gt;

&lt;p&gt;To get permissions in addition to the default ones, you can include them in the manifest and are then shown to users when they install the Add-on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjn45987dgj8a9ty3g5b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjn45987dgj8a9ty3g5b5.png" alt="Permissions popup on install" width="573" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With added permissions, icons and a description, your manifest may look like this:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Super Awesome Extension 👓"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Super awesome Add-on for super awesome people."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&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;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo_small.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo_small.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"48"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo_small.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"64"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"128"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo.svg"&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;"permissions"&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="s2"&gt;"tabs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://awesome.com/*/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"downloads"&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  UIs
&lt;/h1&gt;

&lt;p&gt;Your Add-on can fully operate without &lt;em&gt;any&lt;/em&gt; user interface elements (e.g. only a Background Script) but there is a good chance you will need some. Luckily the browser helps us with this. Here's a list of UI elements you can use in your Add-on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Browser Action&lt;/strong&gt;. This is an icon that is displayed in the toolbar, right next to other toolbar icons. When the icon gets clicked, an event is fired on your Background Script which you can subscribe to using &lt;code&gt;browser.browserAction.onClicked.addListener()&lt;/code&gt;. Optionally you can also choose to display a popup when the icon gets clicked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Page Action&lt;/strong&gt;. This is an icon that is displayed in the &lt;em&gt;URL bar&lt;/em&gt;. In difference to the Browser Action, it is only displayed on certain websites (that you can define). When the icon gets clicked, an event is fired on your Background Script which you can subscribe to using &lt;code&gt;browser.pageAction.onClicked.addListener()&lt;/code&gt;. Optionally you can also choose to display a popup when the icon gets clicked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sidebar&lt;/strong&gt;. This adds a sidebar (default on the left) and displays a normal HTML page (just as you would do with a normal website). In case of Firefox, threre is a global sidebar which has a dropdown for switching between sidebars. If you use this UI element, you side bar gets added to that dropdown.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dev Tools&lt;/strong&gt; panel. Yes, if you want to enhance Firefox' Developer Tools, you can.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content Script&lt;/strong&gt;. This is not directly a user interface component but nonetheless, it is a way to interact with you users. As explained further down, Content Scripts are injected into websites and can directly interact with the site's DOM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notification&lt;/strong&gt;. Does what it says.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Address bar&lt;/strong&gt; suggestion. This let's you listen for inputs in the address bar and then suggest links or actions. Pretty cool.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browser Actions, Page Actions, Content Scripts (optionally), Sidebars, and Dev Tools are defined in the manifest and can then interact with your extension's (background) scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging popups
&lt;/h2&gt;

&lt;p&gt;If you use a popup you will soon wonder how you can debug it, because it &lt;em&gt;closes itself when it loses focus&lt;/em&gt;. That is, when you want to switch to the debug window while having the popup open, it will not work as you want. To overcome this we can "pin" the popups to where they are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On your previously opened debug window, click the 'Disable popup auto hide' button. This ensures that the popups always stay on top (and do not close themselves as they normally would). &lt;em&gt;Note that it literally always stays atop, even when you switch to a different application&lt;/em&gt;. &lt;strong&gt;Don't forget to disable this option again when you are done, (it is even saved across restarts), otherwise you will end up with &lt;a href="https://thepracticaldev.s3.amazonaws.com/i/kdx4eocf670zfiwbmmrs.png" rel="noopener noreferrer"&gt;lots of popups&lt;/a&gt;&lt;/strong&gt;_.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fv8t628siyv0s1btu6c7p.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fv8t628siyv0s1btu6c7p.gif" alt="Enabling 'Disable popup auto hide'" width="536" height="265"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the button to the left from the previous, choose your popup target:&lt;br&gt;
&lt;em&gt;(The button says 'Select an iframe as the currently targeted document')&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fyssfwe5ret5bpdbp2gfm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fyssfwe5ret5bpdbp2gfm.gif" alt="Selecting your popup HTML from the 'Select an iframe as the currently targeted document' dropdown" width="760" height="305"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can now inspect popups without a hassle.&lt;/p&gt;




&lt;p&gt;Let's add a sample Page Action to our manifest:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Super Awesome Extension 👓"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Super awesome Add-on for super awesome people."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&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;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo_small.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo_small.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"48"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo_small.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"64"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"128"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/logos/logo.svg"&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;"permissions"&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="s2"&gt;"tabs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://awesome.com/*/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"downloads"&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;"page_action"&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;"browser_style"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_icon"&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;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/assets/page-action/page-action-16.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"19"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/assets/page-action/page-action-19.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/assets/page-action/page-action-32.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"38"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/assets/page-action/page-action-38.svg"&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;"default_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Awesome Page Action!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/page-action/index.html"&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="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;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/User_experience_best_practices" rel="noopener noreferrer"&gt;UI best practices&lt;/a&gt;&lt;br&gt;
&amp;gt; UI elements: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/browser_action" rel="noopener noreferrer"&gt;Browser Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/page_action" rel="noopener noreferrer"&gt;Page Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/sidebar_action" rel="noopener noreferrer"&gt;Sidebar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/devtools_page" rel="noopener noreferrer"&gt;Dev Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/content_scripts" rel="noopener noreferrer"&gt;Content Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/notifications" rel="noopener noreferrer"&gt;Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/user_interface/Omnibox" rel="noopener noreferrer"&gt;Address bar suggestion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Content Scripts only have access to limited APIs, but sometimes you need functionality that only privileged scripts can offer from within a Content Script. This is where the Messaging API comes in. The Messaging API is a simple way to communicate with your scripts. It doesn't matter if it is a Background Script, a Content Script, a popup or a sidebar.&lt;/p&gt;

&lt;p&gt;To get started, you can use the following APIs:&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="c1"&gt;// Sends a message to all privileged scripts (can be used by Content Scripts, but can't be used to send to Content Scripts)&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;optionalID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

&lt;span class="c1"&gt;// Sends a message to a Content Script (from a privileged script)&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;optionalObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

&lt;span class="c1"&gt;// Receive messages sent via methods above&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: you can not communicate between background scripts because they all share the same scope (like a website). You can include multiple, but they are all executed in the same background context&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If I use &lt;code&gt;runtime.sendMessage()&lt;/code&gt; from a Content Script, then all privileged scripts (such as Background Scripts) will receive this message. But, if I have to send two different messages for two different tasks, how can I know which one is which?&lt;/p&gt;

&lt;p&gt;You can also send objects in messages, so this is how I solved this issue:&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="cm"&gt;/* EXAMPLE */&lt;/span&gt;


&lt;span class="cm"&gt;/* contentScript.js */&lt;/span&gt;

&lt;span class="c1"&gt;// Send a message. In this case, we send an object&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notification&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Include a 'type' property so the receiving scripts know what to do&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Some data you want to send&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ==================&lt;/span&gt;


&lt;span class="cm"&gt;/* background.js */&lt;/span&gt;

&lt;span class="c1"&gt;// Listen for messages&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Handle received messages&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&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="nx"&gt;message&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="c1"&gt;// "notification"&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="nx"&gt;message&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="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;// "example.com"&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="nx"&gt;message&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="nx"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// Array[1, 2, 3, 4, 5]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By sending an object and giving it a &lt;code&gt;type&lt;/code&gt; property, I can easily distinguish the notification from other messages the script may get. &lt;em&gt;(Using &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;data&lt;/code&gt; are just made-up properties, I could have used anything instead)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For more advanced communication needs, you can also use &lt;code&gt;runtime.connect()&lt;/code&gt; and &lt;code&gt;tabs.connect()&lt;/code&gt; which lets you build up a long term communication channel where you always know who sent the message.&lt;/p&gt;

&lt;p&gt;Additionally you have the option to send a return value back to the sender:&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="cm"&gt;/* EXAMPLE */&lt;/span&gt;


&lt;span class="cm"&gt;/* contentScript.js */&lt;/span&gt;

&lt;span class="c1"&gt;// Send a message. Assuming async function context, otherwise we would get a promise back&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;returnValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notification&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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;// ==================&lt;/span&gt;


&lt;span class="cm"&gt;/* background.js */&lt;/span&gt;

&lt;span class="c1"&gt;// Listen for messages&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Handle received messages&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Do something with received message...&lt;/span&gt;

  &lt;span class="c1"&gt;// Return a "response" and fulfill the promise for the message sender&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;42&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;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API" rel="noopener noreferrer"&gt;JavaScript API overview&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/sendMessage" rel="noopener noreferrer"&gt;runtime.sendMessage()&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage" rel="noopener noreferrer"&gt;runtime.onMessage()&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/sendMessage" rel="noopener noreferrer"&gt;tabs.sendMessage()&lt;/a&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  Publishing
&lt;/h1&gt;

&lt;p&gt;For you to be able to &lt;em&gt;permanently&lt;/em&gt; install a Firefox extension you first have to sign it. This can be done on Mozilla's Add-on website. To get your Add-on used by people (a.k.a. publishing it) you have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upload it to &lt;strong&gt;Mozilla's Add-on site&lt;/strong&gt;. This is probably the choice for most people. Your extension gets reviewed and is then listed and searchable on the site and you also get handy usage statistics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distribute it yourself&lt;/strong&gt;. On addons.mozilla.org (AMO) you have the option to only sign your extension and not have it listed on the site. You can then distribute the signed Add-on yourself (e.g. from your website). To provide updates to the extension you then have to provide an &lt;code&gt;update_url&lt;/code&gt; manifest key.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Notes about AMO
&lt;/h3&gt;

&lt;p&gt;AMO only uses the icons from your manifest on &lt;em&gt;&lt;a href="https://discourse.mozilla.org/t/manifest-specified-icons-on-amo/17443" rel="noopener noreferrer"&gt;first submission&lt;/a&gt;&lt;/em&gt; for their Add-on pages. This means if you want to change your icon that is displayed on AMO after that, you can only change it through their web interface. If you first uploaded your Add-on without an icon and then added them later via an update, you also have to add them manually through AMO (I really hope this changes sometime in the future. &lt;em&gt;This only applies to the icons used for the AMO website, not for Firefox' Add-on manager or other places&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Also, I suggest you to have a well-fed 'More Information' panel. It really helps people with your Add-on and leaves a good impression on visitors. Provide a website (if your Add-on is open source, leave a link to the repository (e.g. GitHub)), support E-Mail, support website (e.g. bug tracker, forums, or 'issues' page on GitHub). Furthermore, you can add a license (MIT, GPL, etc.) and a Privacy Policy (this you will need if your Add-on gets bigger). Everything of these mentioned information can be added through the AMO web interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Frs9fz8hrhys0zg1e8pqm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Frs9fz8hrhys0zg1e8pqm.png" alt="More Information panel" width="677" height="796"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;One last note, you can also have Beta releases of your Add-on on AMO. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A file with a version ending with a|alpha|b|beta|pre|rc and an optional number is detected as beta."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if your manifest's &lt;code&gt;version&lt;/code&gt; is "1.2" and it is a pre-release version you can add one of the mentioned letters, e.g. "1.2a". AMO will then show you this option when uploading it: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ftaxt8yfosfppp3thm0af.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ftaxt8yfosfppp3thm0af.png" alt="Upload a beta release" width="524" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Publishing_your_WebExtension" rel="noopener noreferrer"&gt;More info on publishing&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://addons.mozilla.org/en-US/firefox/" rel="noopener noreferrer"&gt;Mozilla's Add-on site (AMO)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Notes on self distributing
&lt;/h3&gt;

&lt;p&gt;When you choose the self distributing route, you will very likely need a &lt;code&gt;update_url&lt;/code&gt; key in your manifest. This is done in the &lt;code&gt;gecko&lt;/code&gt; key in the &lt;code&gt;applications&lt;/code&gt; key, as shows below:&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;"applications"&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;"gecko"&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;"update_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/updates.json"&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="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;The specified URL is a update manifest. This is a JSON file specifically used to tell the browser what the newest version is and where to get it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Alternative_distribution_options" rel="noopener noreferrer"&gt;More on self-distribution&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/applications" rel="noopener noreferrer"&gt;'applications' manifest key&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/Updates" rel="noopener noreferrer"&gt;Update Manifest&lt;/a&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  More stuff
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Internationalization (i18n)&lt;/strong&gt;&lt;br&gt;
Depending on the scope of your Add-on you may want to translate it to other languages, so that more users can use it in their native language. This is possible with WebExtensions. I suggest you to read &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to get started, but the TL;DR is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All locales are located in a folder called &lt;code&gt;_locales&lt;/code&gt; that is in the Add-on's root directory&lt;/li&gt;
&lt;li&gt;Every language has its own folder within the &lt;code&gt;_locales&lt;/code&gt; folder that is named with its &lt;a href="https://r12a.github.io/app-subtags/" rel="noopener noreferrer"&gt;appropriate language short code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;In each language folder is a file called &lt;code&gt;messages.json&lt;/code&gt; that contains all the strings for that language&lt;/li&gt;
&lt;li&gt;You can get a localized string by calling &lt;code&gt;browser.i18n.getMessage('yourStringYouWantLocalised')&lt;/code&gt; from a script. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that you have to translate your UIs (HTML pages) yourselves. This means, when an HTML page is loaded you will need some mechanism to &lt;em&gt;apply&lt;/em&gt; the translated strings to your HTML. With my Add-on I solved this problem by giving each element in my HTML that is translatable a &lt;code&gt;data-translate&lt;/code&gt; attribute. When the HTML page is loaded I have a small helper function called &lt;code&gt;translate()&lt;/code&gt; that replaces the text contents of every element with a &lt;code&gt;data-translate&lt;/code&gt; attribute. The function uses the value of &lt;code&gt;data-translate&lt;/code&gt; to get the corresponding translated string that are specified in the &lt;code&gt;messages.json&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forums&lt;/strong&gt;&lt;br&gt;
If you sometimes feel out of luck and can't find a way around, there is also an official AMO forum. I have no idea how I initially found it, as it is not something that is not easy to find.&lt;br&gt;
You can go and have a look &lt;a href="https://discourse.mozilla.org/c/add-ons" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Settings page&lt;/strong&gt;&lt;br&gt;
You may find yourself in a position where you want to make a settings page for your Add-on. The first step is to specify an &lt;code&gt;options_ui&lt;/code&gt; key in your manifest. In that you can put a &lt;code&gt;page&lt;/code&gt; key and specify an HTML page like so:&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;"options_ui"&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;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"settings/options.html"&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="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;Now when you visit your Add-on's page in the Add-on manager, you will see your specified HTML page at the bottom. In here you can add some form elements to let the user choose their preferences. Two things to note: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You will have to actually save the settings. This can be done with the Storage API: &lt;code&gt;browser.storage.set()&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;When loading the settings HTML page you will have to initialize the input elements to their stored values. To get values from storage you can use &lt;code&gt;browser.storage.get()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://discourse.mozilla.org/c/add-ons" rel="noopener noreferrer"&gt;AMO forums&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage" rel="noopener noreferrer"&gt;Storage API&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Implement_a_settings_page" rel="noopener noreferrer"&gt;Settings page guide&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://github.com/christiankaindl/trello-super-powers/blob/master/CONTRIBUTE.md#translating-ui-elements" rel="noopener noreferrer"&gt;My approach to translation&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;a href="https://github.com/christiankaindl/trello-super-powers/blob/master/translate.js" rel="noopener noreferrer"&gt;Link to my &lt;code&gt;translate()&lt;/code&gt; helper function&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;In Firefox 57, Mozilla dropped legacy extension support completely in favor of this new architecture. Before WebExtensions - as a developer making Firefox extensions - you could also use browser internal stuff. This led to a poor compatibility experience as things could easily break with updates and oftentimes there was no documentation for these internal functions. Mozilla tried to ease the development by providing an SDK, but it still wasn't perfect. So nowadays, writing a WebExtension is the only way to get a working Firefox Add-on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Phew! If you made it this far, congratulations! Writing this article was not easy but also lots of fun. If you have any question or I left something unclear, please leave a comment and I will gladly answer. Thank you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;There is much more possible with WebExtensions than I could have possibly show in this post, so be sure to check out all the APIs and manifest keys &lt;a href="https://developer.mozilla.org/de/Add-ons/WebExtensions" rel="noopener noreferrer"&gt;on MDN&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Add-on I was talking about in the beginning (and that is shown in the GIFs) is &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/trello-super-powers/" rel="noopener noreferrer"&gt;Trello Super Powers&lt;/a&gt;. It is a simple extension that adds some nifty features to your Trello boards. And by the way, it is open source, so you can check it out &lt;a href="https://github.com/christiankaindl/trello-super-powers" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webextensions</category>
      <category>guide</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Use hyphenation on Dev.to?</title>
      <dc:creator>Christian Kaindl</dc:creator>
      <pubDate>Tue, 19 Dec 2017 09:46:08 +0000</pubDate>
      <link>https://dev.to/christiankaindl/use-hyphenation-on-devto-5a5h</link>
      <guid>https://dev.to/christiankaindl/use-hyphenation-on-devto-5a5h</guid>
      <description>

&lt;p&gt;I think it would be cool to have articles use hyphenation, so their lines are more similar in length.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;hyphens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&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;This can be done with CSS today and has basic support from all major browsers. And hey, if a browser does not support it, it's fine, sentences will be displayed just fine. I guess that is the definition of progressive enhancement :)&lt;br&gt;
With a simple @-rule it could be used together with &lt;code&gt;text-align: justify&lt;/code&gt;! &amp;lt;-- This looks totally awesome and professional. #cutting-edge :)&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/hyphens"&gt;Hyphenation documentation on MDN&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kuNiH9ZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/aqgkktjaq5yuvdk87xf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kuNiH9ZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/aqgkktjaq5yuvdk87xf2.png" alt="Hyphenation example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently read an article where it was used and I was surprised this is already possible, so I think it would give a really nice experience to most people even though it is only a small addition ❤&lt;/p&gt;

&lt;p&gt;What does everybody else think on this?&lt;/p&gt;


</description>
      <category>suggestion</category>
      <category>cuttingedge</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Bases Explained Part 1 - Introduction</title>
      <dc:creator>Christian Kaindl</dc:creator>
      <pubDate>Thu, 14 Dec 2017 11:22:43 +0000</pubDate>
      <link>https://dev.to/christiankaindl/bases-explained-part-1---introduction-c84</link>
      <guid>https://dev.to/christiankaindl/bases-explained-part-1---introduction-c84</guid>
      <description>&lt;p&gt;&lt;em&gt;In this series, Bases Explained, I want to dive into the various aspects of bases. At the end of this series, you will be able to know what they are, how to convert between them and why they are &lt;strong&gt;awesome&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I suspect all of you have at least once touched upon the mathematical term "base". Things like binary and hexadecimal are probably familiar to most of you, but how good do you really know them? I mean, who can't use a hex color code, right? Well, some time ago I explored bases a bit more and I want to share my learnings with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Positional notation
&lt;/h2&gt;

&lt;p&gt;At the heart of the concept of bases is the positional notation. That means, depending on the position of a digit within a number, its value is different. &lt;code&gt;1&lt;/code&gt; from &lt;code&gt;100&lt;/code&gt; is different than &lt;code&gt;1&lt;/code&gt; from &lt;code&gt;1000&lt;/code&gt;. The more a digit is on &lt;em&gt;the left side&lt;/em&gt; of a number, the higher its value.&lt;/p&gt;

&lt;p&gt;This principle is the same throughout all bases. Base 3, 7, 4, 8, 342. They all share this common theme.&lt;/p&gt;




&lt;p&gt;The term &lt;em&gt;base&lt;/em&gt; in the context of numerical systems actually means "How many digits are used in this number system?" That is, base 2 has 2 digits, base 16 has 16 digits, base 10 has 10, and so on. The digits always range from &lt;code&gt;0&lt;/code&gt; to one less than the base (e.g. 0-9 in base 10).&lt;/p&gt;

&lt;p&gt;With these two things in mind - positional notation and number of digits - we have the building blocks to find out &lt;em&gt;how&lt;/em&gt; bases work. As we'll see later, it is just mind blowing when you try to completely think in a different base system, or to imaging that you always thought in a certain base system. Would you see the world differently?&lt;/p&gt;

&lt;p&gt;There are infinitely large amounts of bases out there, so it is definitely time to find out. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bases are important
&lt;/h2&gt;

&lt;p&gt;There are a number (ʕ•ᴥ•ʔ) of reasons why different bases are used for different jobs. It is very easy to think that bases are something &lt;em&gt;abstract&lt;/em&gt; or only &lt;em&gt;math related&lt;/em&gt;, but the reality is they are not only pretty damn useful, but they also bring a mental value once understood. In the end, we use numbers all the time, oftentimes without even realizing. So being aware of the power that this fundamental concept of our numerical system has can really step up your life-game.&lt;/p&gt;

&lt;p&gt;Some applications of bases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Electronics&lt;/strong&gt;&lt;br&gt;
Computers use, at their lowest level, base 2 systems to process data. This is due to a restriction in the hardware, e.g. logic gates are either on or off; nothing in-between. It is downright incredible that with just enough &lt;em&gt;zeros&lt;/em&gt; and &lt;em&gt;ones&lt;/em&gt;, you can have so much flexibility that all these crazy technologies that we use today can exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;br&gt;
Storage devices leverage higher base systems, such as base 16, because it allows them to save much more data at less length. Compare &lt;code&gt;10000000&lt;/code&gt;2 to &lt;code&gt;80&lt;/code&gt;16. They represent the same data, the latter one is just much shorter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's fun&lt;/strong&gt;&lt;br&gt;
Simple and plain, it's worth playing around with. And getting your head to think in other base systems is also cool. Finding patterns in conversions to other base systems is even more fun! Take the number 100 for example. You can take this number and interpret it in different base systems. 1002, 1003, 1004, etc.&lt;/p&gt;

&lt;p&gt;Each of them translate to a different value (when viewed from base 10). 1002 = 410, 1003 = 910, 1004 = 1610. It is really fascinating to see just how relative our base ten system is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cultures&lt;/strong&gt;&lt;br&gt;
Different cultures have historically counted in different base systems. It is a mere coincident that our western culture uses the base 10 notation (some say we chose base 10 because we have ten fingers). Other cultures have used other systems as well such as base 12. That is most likely where words like "a dozen" (meaning 12) come from. But also, some languages such as english have even unique names for the 11&lt;sup&gt;th&lt;/sup&gt; and 12&lt;sup&gt;th&lt;/sup&gt; digit: eleven and twelve.&lt;/p&gt;




&lt;p&gt;I want to dive into the math of bases a bit deeper in Part 2, e.g. how to convert between them.&lt;/p&gt;

&lt;p&gt;Fun fact: you can &lt;a href="https://www.youtube.com/watch?v=1SMmc9gQmHQ"&gt;count to 1 thousand using your two hands&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PS: Bonus points for everyone who can decipher the numbers in the header image.&lt;/p&gt;

</description>
      <category>math</category>
      <category>binary</category>
      <category>bases</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Things the web can do</title>
      <dc:creator>Christian Kaindl</dc:creator>
      <pubDate>Wed, 15 Nov 2017 09:02:47 +0000</pubDate>
      <link>https://dev.to/christiankaindl/things-the-web-can-do-afl</link>
      <guid>https://dev.to/christiankaindl/things-the-web-can-do-afl</guid>
      <description>&lt;p&gt;Sometimes I stumble upon a rarely discussed feature or watch a CSS talk that has only 451 views or I am looking for a specific feature on MDN and see some awesome JavaScript API. I love these times, it feels great to explore new things that I don't know about yet. But at the same time I always think something like: "Why hadn't I heard of this before" or "Wow, Everyone should know this!" or both of them. &lt;/p&gt;

&lt;p&gt;So, that's exactly what this post is about. Awesome features that I think are less talked about:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HTML&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ins&lt;/code&gt; tag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;CSS&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;element()&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;border-radius&lt;/code&gt; slash(&lt;code&gt;/&lt;/code&gt;)-syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Intl&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Performance&lt;/code&gt; interface&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Let's start with...&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Performance&lt;/code&gt; interface
&lt;/h3&gt;

&lt;p&gt;Yes, you read that right. The browser has a native interface that let's you do precise performance measurements. The important part is &lt;strong&gt;precise&lt;/strong&gt;, as it let's you do measurements simply not possible with normal JS methods (like &lt;code&gt;Date.now()&lt;/code&gt;). It lets you measure time to &lt;em&gt;microseconds&lt;/em&gt;. Microseconds. Not Milliseconds, but a thousand of that!&lt;/p&gt;

&lt;p&gt;You can also do all sorts of things like &lt;code&gt;marks&lt;/code&gt; and &lt;code&gt;highResTimeStamp&lt;/code&gt;s and gives you a better alternative to &lt;code&gt;Date.time()&lt;/code&gt; with &lt;code&gt;Performance.now()&lt;/code&gt;. Definitely worth a look!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance" rel="noopener noreferrer"&gt;MDN documentation for the &lt;code&gt;Performance&lt;/code&gt; interface&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;Intl&lt;/code&gt; object
&lt;/h3&gt;

&lt;p&gt;Ever did a project where you had to deal with number input, or had to display a date in the right format? Well, JavaScripts &lt;code&gt;Intl&lt;/code&gt; object is for you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Intl object is the namespace for the ECMAScript Internationalization API, which provides language sensitive string comparison, number formatting, and date and time formatting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl" rel="noopener noreferrer"&gt;MDN documentation for the &lt;code&gt;Intl&lt;/code&gt; object&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;border-radius&lt;/code&gt; slash(&lt;code&gt;/&lt;/code&gt;)-syntax
&lt;/h3&gt;

&lt;p&gt;Ok, so when I first saw this, I could not believe my eyes! I was using Enki to make my daily knowledge workout and then:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv7bg9r2coutcsy8m14hb.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv7bg9r2coutcsy8m14hb.jpg" alt="Screenshot from Enki"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can curve the x and y axis of a corner independently. That is awesome! Why? I immediately thought about an article I read some time ago (and gladly I had bookmarked it!) about optical illusions:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F687qj6ovi6d01rxgy2o5.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F687qj6ovi6d01rxgy2o5.png" alt="Snippet from "&gt;&lt;/a&gt;&lt;br&gt;
(from &lt;a href="https://medium.muz.li/optical-effects-9fca82b4cd9a" rel="noopener noreferrer"&gt;https://medium.muz.li/optical-effects-9fca82b4cd9a&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Further down a comment wrote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...] In the olden days we had to slice up corner images and use a lot of markup for rounded corners on elements, then border radius made it a lot easier, now to get optically correct rounded corners we’d have to go back to complex markup and background images.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But maybe we can use this slash-syntax to create these 'rounded' corners that also appear round! (&lt;em&gt;Edit: Sadly its not possible with the slash-syntax, but it's interesting anyway&lt;/em&gt; )&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius" rel="noopener noreferrer"&gt;MDN on the border-radius &lt;code&gt;/&lt;/code&gt;-syntax&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;element()&lt;/code&gt; function
&lt;/h3&gt;

&lt;p&gt;This is yet another awesome feature CSS has up its sleeve.&lt;/p&gt;

&lt;p&gt;Vincent De Oliveira wrote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To put it simply, this function renders any part of a website as a live image. A. Live. Image! As you see a DOM element rendered right in the browser, you’ll get an image of it. Every changes to that element will be immediately seen in real-time in the image, even text selection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of the possibilities that &lt;code&gt;element()&lt;/code&gt; would enable are thumbnails/previews and mirror-like effects. Sadly though this CSS feature only works in Firefox and can thus not be used for production purposes. Hopefully other browser vendors will follow the early efforts from Firefox at some point in the future!&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fz6gjnfh3t25xk2lu50pq.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fz6gjnfh3t25xk2lu50pq.png" alt="Demo of the CSS  raw `element()` endraw  function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Credit Vincent De Oliveira)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://iamvdo.me/en/blog/css-element-function" rel="noopener noreferrer"&gt;Read Vincent De Oliveira's full article&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;ins&lt;/code&gt; tag
&lt;/h3&gt;

&lt;p&gt;The HTML &lt;code&gt;ins&lt;/code&gt; tag represents something that is/was inserted into the current document.&lt;/p&gt;

&lt;p&gt;From MDN:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The HTML &lt;code&gt;&amp;lt;ins&amp;gt;&lt;/code&gt; element represents a range of text that has been added to a document.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a useful tag if you are picky about your markup (as you should be!) because you can use it with an additional &lt;code&gt;cite&lt;/code&gt; and/or &lt;code&gt;datetime&lt;/code&gt; attribute. &lt;code&gt;cite&lt;/code&gt; let's you add an URI which explains the change and &lt;code&gt;datetime&lt;/code&gt; let's you add a timestamp for when the change happened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins" rel="noopener noreferrer"&gt;More on the &lt;code&gt;ins&lt;/code&gt; element on MDN&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;output&lt;/code&gt; tag
&lt;/h3&gt;

&lt;p&gt;Similar to &lt;code&gt;ins&lt;/code&gt;, &lt;code&gt;output&lt;/code&gt; should be used for content that is the result of an equation. Nothing special, but still cool!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output" rel="noopener noreferrer"&gt;More on the &lt;code&gt;output&lt;/code&gt; element on MDN&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;That's it! Thank you for reading :)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>web</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>Coding and Education</title>
      <dc:creator>Christian Kaindl</dc:creator>
      <pubDate>Sun, 05 Nov 2017 12:02:20 +0000</pubDate>
      <link>https://dev.to/christiankaindl/coding-and-education-1lg</link>
      <guid>https://dev.to/christiankaindl/coding-and-education-1lg</guid>
      <description>&lt;p&gt;About two years ago I started learning HTML. I was sitting at home in front of my computer, looking at some W3Schools tutorials. When I first started with HTML I asked a friend of mine how it works but could not make much sense out of what he said, but managed to crawl my way through. Then, after HTML came CSS. Same pattern. W3Schools tutorials and (from today's perspective) laughable attempts to create anything with it that seemed useful.&lt;/p&gt;

&lt;p&gt;Two years later, I am happily enthusiastic about the web. I learned a lot about HTML, CSS and also JavaScript along the way and I now know that this is &lt;em&gt;my&lt;/em&gt; way to go. I learned to love the web so much I often cannot stop talking about it, but not only because I think it is an &lt;em&gt;absolutely mind-bogglingly amazing&lt;/em&gt; platform, but also because I see it is missing in my everyday world. &lt;/p&gt;

&lt;p&gt;I work in an education project - there's not much "software development" going on - and this field of technology, almost everything that has to do with coding, or computers or technical things seem so far away for the people I work with every day. In some sense, this whole "software development" thing seems to have such a huge bubble, and often I wish that it wouldn't. Coding - and everything that appends to it - is &lt;em&gt;such a useful&lt;/em&gt; mental property to have. It is, in my opinion, not about the ability to create something - although that sure is also useful :) - but more about the value that it has for someone's thinking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think everyone should learn how to program a computer, because it teaches you how to think.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Steve Jobs, 1995&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, that is true not only for "technical" things like coding but also for other professions that are &lt;em&gt;not taught in school&lt;/em&gt; (actually also the ones that are taught in school!). The bare presence of such highly specialized fields often makes people think "This is only a thing for [insert group of people that are underrepresented in most general education systems]". &lt;/p&gt;

&lt;p&gt;I see the focus is changing in a new direction, people are now actively looking for alternatives to this kind of gaps in teaching. One does not have to be an expert in every field, but I'd argue that is it from &lt;em&gt;high value&lt;/em&gt; to have an understanding and feeling about why a certain group of jobs are important to our society and myself. That is vital in my eyes. How could a society ever work when there are job groups that most people don't really know why they are important for them. That is what education should essentially do.&lt;/p&gt;

&lt;p&gt;It is a bit like the code-comments debates - that so many exist of: "Comments should explain the &lt;strong&gt;Why&lt;/strong&gt;, not the &lt;em&gt;What&lt;/em&gt;". And I think for a majority of the time that is spent in early education this is exactly what is important. If you learn to value other peoples work &lt;em&gt;by default&lt;/em&gt;, I think you, as a child/teenager/adult, will have it easier when looking for something you want to do in your life. Also you can have the trust that people will be able to look at what you do and find out why it is also from importance for them - and if not, have a conversation about it.&lt;/p&gt;

&lt;p&gt;Now you can see one problem with this different approach: We would have to change the way we teach society (not only our children) and this would mean to drastically alter the institutions that are responsible for education. Because: I personally think it is &lt;em&gt;hard&lt;/em&gt; - if not &lt;em&gt;really hard&lt;/em&gt; - to understand the vital importance of a groups existence if this group is not present. Meaning that a random person that tells e.g. a child that engineers are important won't have a direct, deep, impact to it. The work, ethic and enthusiasm from that group, let them be engineers, coders, managers or whatever group you like, has to be somehow present for someone to really believe that this group's work is valuable.&lt;/p&gt;

&lt;p&gt;Schools nowadays, at least where I live, are very isolated environments. A huge portion of job-fields are underrepresented or even completely absent. So there is this strange isolated thing called school (schools aren't bad, they just can be improved) which mostly tries to solve the &lt;em&gt;What&lt;/em&gt; and is oftentimes very old-school (pun not intended) in what it teaches. Now, here's the thing, and why this has to do with a coder/hacker community: I see that communities like dev.to and platforms like the web, are missing as a mental value for a lot of people. A huge industry has formed over the last decades, but education systems have not yet adapted fast enough.&lt;/p&gt;

&lt;p&gt;And this kinda hurts me. Really, really, deeply, because - and this is the actual reason for this writing - the web is &lt;strong&gt;&lt;em&gt;so&lt;/em&gt;&lt;/strong&gt; awesome. I cannot emphasize how much I love it. Just think about it: A platform that has evolved - and this is truly amazing - over the last &lt;strong&gt;25 years&lt;/strong&gt;, maybe now the biggest shared resources of humanity; With an unbelievably amazing, open environment, has now (from 2016 and ongoing) reached a point where it is mature enough so you can write &lt;strong&gt;first class, native like, blazing fast&lt;/strong&gt; awesome applications like you could never before. PWAs - instant 'ad hoc' experiences on the fly. And unlike platforms like Android and iOS (and macOS and Windows and ...) there are &lt;strong&gt;4&lt;/strong&gt; (!) major implementations of the technology stack! WHAT THE FUCK! &lt;em&gt;How amazing is that&lt;/em&gt;?. 25 years, so far, have formed the web in a way that no one could have &lt;em&gt;ever&lt;/em&gt; designed it to. It is a gem by every means and is having an exciting future with mature &lt;strong&gt;decentralization&lt;/strong&gt; technologies right around the corner. &lt;/p&gt;

&lt;p&gt;And the community... I cannot thank everyone - the countless &lt;strong&gt;open source projects&lt;/strong&gt; that drive powerful innovation and that I discover more of every day, &lt;strong&gt;communities&lt;/strong&gt; that are so welcoming and countless people that helped make it a great platform - enough... It is just really beautiful! And I want to deepen my involvement and want to contribute to this platform and its communities as much as I can. That's why I cannot stop being enthusiastic about &lt;em&gt;it&lt;/em&gt;, and that is why I cannot stop talking about &lt;em&gt;it&lt;/em&gt;, and that is why I think &lt;em&gt;it&lt;/em&gt; has a valuable mental value - even to people that do not actively participate in it - for everyone. And that I see is greatly missing in society. It is one part of the hundreds of parts that can ultimately change how we perceive and act in this precious (very precious) world.&lt;/p&gt;

&lt;p&gt;And that is the reason I wrote this. &amp;lt;3&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I think there is a lot more to say and I would love to have some conversations about education and coding (maybe in the comments c: ). I tried not to write my viewpoint as an absolute, but in the end lines are scarce and it is difficult to always bring that thought down as I meant it. Also, education often is a hot topic so I would love to extend myself a little more if something is unclear.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web</category>
      <category>education</category>
      <category>learning</category>
      <category>writing</category>
    </item>
    <item>
      <title>The strange &lt;img&gt; gap in HTML</title>
      <dc:creator>Christian Kaindl</dc:creator>
      <pubDate>Sat, 23 Sep 2017 10:27:43 +0000</pubDate>
      <link>https://dev.to/christiankaindl/the-strange-img-gap-in-html</link>
      <guid>https://dev.to/christiankaindl/the-strange-img-gap-in-html</guid>
      <description>&lt;p&gt;There are many frustrations that I have had in my short dev game so far, and one of them crossed my eyes very recently again while I was working on a small project of mine.&lt;/p&gt;

&lt;p&gt;It is this strange gap when using the HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag that made me go wild already quite a few times:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F07g1drtbyjcqwy7o7o1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F07g1drtbyjcqwy7o7o1l.png" alt="Image gap" width="53" height="65"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But not this time!&lt;/p&gt;

&lt;h1&gt;
  
  
  The solution
&lt;/h1&gt;

&lt;p&gt;The reasoning and the solution to this problem is actually really simple and has to do with the text of the box.&lt;/p&gt;

&lt;p&gt;Wait, what? There is no text in the picture!&lt;/p&gt;

&lt;p&gt;Brace yourself:&lt;/p&gt;

&lt;p&gt;As it turns out, &lt;code&gt;img&lt;/code&gt; elements are by default inline elements. This means they basically flow the same as text. As such the bottom edge of the image is aligned with the baseline of the font that is specified with the &lt;code&gt;font-size&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Mystery solved! Here's how you can fix this issue:&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1
&lt;/h2&gt;

&lt;p&gt;On your image, set &lt;code&gt;vertical-align&lt;/code&gt; to &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;text-top&lt;/code&gt;, &lt;code&gt;text-bottom&lt;/code&gt; or &lt;code&gt;middle&lt;/code&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 2
&lt;/h2&gt;

&lt;p&gt;Set your image to &lt;code&gt;display: block&lt;/code&gt; (Then you also have to specify a width for your image or otherwise everything will fall into the next line)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit:&lt;/em&gt; You can also fix this issue while using more modern techniques. So, setting &lt;code&gt;display&lt;/code&gt; to &lt;code&gt;flex&lt;/code&gt; or &lt;code&gt;grid&lt;/code&gt; (also needs a width to be set), or their inline counterparts &lt;code&gt;inline-flex&lt;/code&gt; and &lt;code&gt;inline-grid&lt;/code&gt; will also work while giving more flexibility as &lt;code&gt;display: block&lt;/code&gt; (and also work when applied to the image container).&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3
&lt;/h2&gt;

&lt;p&gt;Don't use images all together (makes your site faster)&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 4
&lt;/h2&gt;

&lt;p&gt;Maybe this is exactly what you want! Aligning an image or small icon within your text flow on the baseline couldn't be easier!&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 5
&lt;/h2&gt;

&lt;p&gt;Set the image container (parent) to &lt;code&gt;line-height: 0&lt;/code&gt; (does not work when the container is an &lt;code&gt;inline&lt;/code&gt; element) or &lt;code&gt;font-size: 0&lt;/code&gt; (always works)&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 6
&lt;/h2&gt;

&lt;p&gt;On the image, set &lt;code&gt;float&lt;/code&gt;  to &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;inline-end&lt;/code&gt; or &lt;code&gt;inline-start&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;In the end the "mysterious image gap" isn't that mysterious at all, and still, it is quite interesting where such simple frustrations can lead. After all, I have a more thorough understanding how inline elements behave now.&lt;/p&gt;

&lt;p&gt;Here are some more resources if this wasn't enough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://gtwebdev.com/workshop/gaps/image-gap.php" rel="noopener noreferrer"&gt;http://gtwebdev.com/workshop/gaps/image-gap.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Images%2C_Tables%2C_and_Mysterious_Gaps" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Images%2C_Tables%2C_and_Mysterious_Gaps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;or just search for "html img gap" or "img tag gap" in your favorite search engine and let the thousand Stack Overflow issues sink in your mind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading :)&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Edit:&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to find out more about the details of HTML layout&lt;br&gt;
You may also want to check out the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML" rel="noopener noreferrer"&gt;HTML reference&lt;/a&gt; on the Mozilla Developer Network.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Some further reading:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Cheatsheet" rel="noopener noreferrer"&gt;HTML Inline/Block elements cheatsheet&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://www.smashingmagazine.com/2017/12/understanding-css-layout-block-formatting-context/" rel="noopener noreferrer"&gt;Block Formatting Context&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/display" rel="noopener noreferrer"&gt;CSS &lt;code&gt;display&lt;/code&gt; documentation&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://quirksmode.org/css/css2/display.html" rel="noopener noreferrer"&gt;Article on the CSS &lt;code&gt;display&lt;/code&gt; values&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>html</category>
      <category>web</category>
      <category>css</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
