<?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: Codista</title>
    <description>The latest articles on DEV Community by Codista (@codista_).</description>
    <link>https://dev.to/codista_</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%2F361809%2F479c8eb5-c399-4bef-bab2-a3006ab19750.jpg</url>
      <title>DEV Community: Codista</title>
      <link>https://dev.to/codista_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codista_"/>
    <language>en</language>
    <item>
      <title>Build a "Neon Mode" for your Website</title>
      <dc:creator>Codista</dc:creator>
      <pubDate>Fri, 15 May 2020 07:48:39 +0000</pubDate>
      <link>https://dev.to/codista_/neon-mode-dark-ui-with-a-twist-h80</link>
      <guid>https://dev.to/codista_/neon-mode-dark-ui-with-a-twist-h80</guid>
      <description>&lt;p&gt;We recently added a new dark mode to &lt;a href="https://www.codista.com/" rel="noopener noreferrer"&gt;our website&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Dark modes have become a very popular feature for apps and operating systems in recent years, and many users prefer the inverted colors because they're easier on the eyes. Our new dark UI looks gorgeous and has a few extra tricks up its sleeve too - here's how we made it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the Mood
&lt;/h2&gt;

&lt;p&gt;First things first: the basis of most dark modes on the web these days are CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*" rel="noopener noreferrer"&gt;custom properties&lt;/a&gt; - they let us define all our colors as variables we can change at runtime. There are many &lt;a href="https://hankchizljaw.com/wrote/create-a-user-controlled-dark-or-light-mode/" rel="noopener noreferrer"&gt;good tutorials&lt;/a&gt; on how to use them already, so we'll skip over that. For an existing site, the initial switch to custom properties can be a bit cumbersome, but find &amp;amp; replace is very helpful 😉.&lt;/p&gt;

&lt;p&gt;We are working with Sass here, so it's a good idea to define the color schemes as &lt;a href="https://sass-lang.com/documentation/at-rules/mixin" rel="noopener noreferrer"&gt;mixins&lt;/a&gt;, so they're easier to reuse in different selectors. When it comes to naming the properties, try to stay away from visual names like "light" or "gray" - the other color scheme might invert that or use different hues, and things can get confusing.&lt;/p&gt;

&lt;p&gt;In our case, we went with a "color scale" from background to foreground, and three highlight colors for our brand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// define the color vars as a mixin so we can reuse it&lt;/span&gt;
&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;color-scheme-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;--color-scale-0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#001e2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--color-scale-25&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#143044&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--color-scale-50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#576975&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--color-scale-75&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#e6f1f8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--color-scale-100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="na"&gt;--color-brand-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#7a27ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--color-brand-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#26ffae&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--color-brand-tertiary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ed667b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// default to light colors...&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;color-scheme-light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...except if no theme is set and the system preference is dark&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root:not&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;color-scheme-dark&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;// override defaults through the data-theme attribute.&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dark'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;color-scheme-dark&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;We apply the light color scheme per default, and then override that based on other conditions. It's a good idea to check for the &lt;a href="https://web.dev/prefers-color-scheme/" rel="noopener noreferrer"&gt;system preference&lt;/a&gt; in the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query, but still give users the choice to toggle modes themselves, to adjust for whatever situation they're currently in.&lt;/p&gt;

&lt;p&gt;To do that, we need to expose the dark mode variable through our website's UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the Light Switch
&lt;/h2&gt;

&lt;p&gt;The light switch to toggle modes is just a simple button. We can define it as a thing with an on/off state by using &lt;code&gt;role="switch"&lt;/code&gt; and the &lt;code&gt;aria-checked&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;We put an SVG icon in there that has an accessible title and two paths for the "on" and "off" icons respectively. We show/hide these in CSS based on the documents &lt;code&gt;data-theme&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="c"&gt;&amp;lt;!-- hook into js-darkmode-toggle via Javascript later --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lightswitch js-darkmode-toggle"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"switch"&lt;/span&gt; &lt;span class="na"&gt;aria-checked=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lightswitch__icon"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;toggle dark mode&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- include two different icon paths, then display the one matching the current state in CSS --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lightswitch__icon__on"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M9 20...1.512z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lightswitch__icon__off"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M9 20...6h-2z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wire up the Power
&lt;/h2&gt;

&lt;p&gt;The switch looks good, but still needs some Javascript to actually do something. The main task here is to check which mode of the UI the user is currently seeing, and then to toggle the &lt;code&gt;data-theme&lt;/code&gt; attribute to trigger the changes in CSS. We use a cookie to save the current theme setting, so we can persist the choice when somebody navigates through multiple pages, or even closes their browser and comes back later.&lt;/p&gt;

&lt;p&gt;If no cookie is set, we fall back to the system default again by checking the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query.&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;class&lt;/span&gt; &lt;span class="nc"&gt;DarkMode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// define some properties&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookieTimeoutDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookieName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;darkMode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleBtn&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;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.js-darkmode-toggle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// get initial setting, add event listeners&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPreference&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getPreference&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// read if the user has selected a theme before,&lt;/span&gt;
    &lt;span class="c1"&gt;// otherwise fall back to media query for system defaults.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookieName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;systemPreference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;systemPreference&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="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// switch between light and dark&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// set the data attribute to trigger CSS changes&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// update the cookie to save the user preference&lt;/span&gt;
    &lt;span class="nx"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookieName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookieTimeoutDays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// toggle the button state for screen readers&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&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;// this feature only makes sense if CSS custom properties are supported, &lt;/span&gt;
&lt;span class="c1"&gt;// so check for that first before we kick things off&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CSS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;CSS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var(--fake-var)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DarkMode&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;To see the result, try clicking the light switch button on &lt;a href="https://www.codista.com" rel="noopener noreferrer"&gt;codista.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it Click
&lt;/h2&gt;

&lt;p&gt;A nice little detail here is the small "clicking" sound the toggle makes. It makes it feel a lot more natural, like a real switch we can flick on or off. A good resource for audio clips like this is &lt;a href="https://freesound.org/people/lebaston100/sounds/192275/" rel="noopener noreferrer"&gt;freesound.org&lt;/a&gt; - it only requires a free account and has tons of material.&lt;/p&gt;

&lt;p&gt;We can add the sounds as &lt;code&gt;audio&lt;/code&gt; elements to the DOM and &lt;code&gt;preload&lt;/code&gt; them so they can play instantly, which is crucial for the effect to work.&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;audio&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"click1.mp3"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"js-sound-off"&lt;/span&gt; &lt;span class="na"&gt;preload=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/audio&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;audio&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"click2.mp3"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"js-sound-on"&lt;/span&gt; &lt;span class="na"&gt;preload=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/audio&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooking an extra method into the &lt;code&gt;toggle()&lt;/code&gt; function, we can then select the appropriate sound and play it every time the button is pressed.&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="nf"&gt;playSound&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;soundOn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;soundOff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// always start the sound from beginning, in case it's&lt;/span&gt;
  &lt;span class="c1"&gt;// still playing from a previous action&lt;/span&gt;
  &lt;span class="nx"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&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;h2&gt;
  
  
  Turn up the Glow
&lt;/h2&gt;

&lt;p&gt;So far so good, but our darkmode could still use a little more spark. A nice thing about dark backgrounds is that it opens up more possibilities to play with &lt;a href="https://www.smashingmagazine.com/2009/05/30-examples-of-masterful-lighting-effects-in-web-design/" rel="noopener noreferrer"&gt;light in a design&lt;/a&gt;. It's not just about inverting the colors - we can give certain elements special treatment to make them stand out even more in the dark.&lt;/p&gt;

&lt;p&gt;In our case, the Codista brand design has lots of bright, synthetic colors that are perfect for a "neon" effect:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/mxbck/embed/MWaqBXG?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Achieving that look is quite easy: we can layer a few different &lt;code&gt;text-shadows&lt;/code&gt; on top of large type, like headlines. The effect works by setting the text color to white, fuzzing the edges a bit with some white text-shadow, then adding the neon color on top with increasing amounts of blur. It's a good idea to define the blur size in relative &lt;code&gt;em&lt;/code&gt; units here, so the glow will scale with the font-size.&lt;/p&gt;

&lt;p&gt;We can set up a Sass mixin again to call this with different color variables later, i.e. to switch colors between primary and secondary headlines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;neon-glow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.033em&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.08em&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.1em&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.2em&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.3em&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5em&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dark'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;neon-glow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;color-brand-primary&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;neon-glow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;color-brand-secondary&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The glow effect also works with other UI elements, like cards or call-to-action links. The exact implementation depends on the design - for the Codista website, we leveraged &lt;code&gt;border&lt;/code&gt; and &lt;code&gt;box-shadow&lt;/code&gt; properties to create neon edges and glows. It's best not to overdo the effect though - we use it for interactions like hover or focus.&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%2Fi%2F6pco730rddeg4wakf01m.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6pco730rddeg4wakf01m.jpg" alt="a blog post card and a big link both glowing in a "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Flicker Animation
&lt;/h2&gt;

&lt;p&gt;For the final touch, we gave our headlines the typical neon sign look where one of the letters is short-circuited and flickers. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/mxbck/embed/jObvpvY?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To make that work, we first select some elements to apply the animation to. It works best with large type and should be used sparingly, so we went with the page's &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; only.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;DarkMode.js&lt;/code&gt; class, we can now run a function to wrap some of the letters of the h1 in a &lt;code&gt;span&lt;/code&gt;, which we can then target for animation in CSS later.&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;function&lt;/span&gt; &lt;span class="nf"&gt;setFlickerAnimation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// get all elements that should be animated&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animatedElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&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;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.js-darkmode-flicker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;animatedElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// helper function to wrap random letters in &amp;lt;span&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapRandomChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iterations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludedChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludedIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// run for the number of letters we want to wrap&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// make sure we don't wrap a space or punctuation char&lt;/span&gt;
      &lt;span class="c1"&gt;// or hit the same letter twice&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;excludedIndexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;randIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;excludedChars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;span class="flicker"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;excludedIndexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;randIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// replace the plain text content in each element&lt;/span&gt;
  &lt;span class="nx"&gt;animatedElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flickerChars&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flickerChars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapRandomChars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The letters are chosen at random, so the effect is a bit different every time!&lt;/p&gt;

&lt;p&gt;We can now select the &lt;code&gt;.flicker&lt;/code&gt; class in CSS and apply a keyframe animation that toggles the element's opacity. We're going for a hard on/off flickering here rather than a slower transition, so the increments between the keyframe steps are very short.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Attention: people with epilepsy or photosensitivity can have issues with blinking/flickering text.&lt;/strong&gt; So it's very important to listen for signals like the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; media query to disable the effect. We can do that by setting the &lt;code&gt;animation-duration&lt;/code&gt; property to zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// keyframe animation to abruptly toggle&lt;/span&gt;
&lt;span class="c1"&gt;// the letter's opacity value.&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="nt"&gt;flicker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;19&lt;/span&gt;&lt;span class="nc"&gt;.999&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;22&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;62&lt;/span&gt;&lt;span class="nc"&gt;.999&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;64&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;64&lt;/span&gt;&lt;span class="nc"&gt;.999&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;72&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;20&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;21&lt;/span&gt;&lt;span class="nc"&gt;.999&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;63&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;63&lt;/span&gt;&lt;span class="nc"&gt;.999&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;65&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;71&lt;/span&gt;&lt;span class="nc"&gt;.999&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.33&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;// only run in dark mode. for every other letter, offset the animation &lt;/span&gt;
&lt;span class="c1"&gt;// and reverse its direction, so the flickering appears more random.&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dark'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.flicker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flicker&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="n"&gt;forwards&lt;/span&gt; &lt;span class="n"&gt;alternate&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;even&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;animation-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;alternate-reverse&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;// if the user prefers reduced motion,&lt;/span&gt;
&lt;span class="c1"&gt;// disable the animation entirely.&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0s&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transition-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0s&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, that's all folks! We love our new buzzing neon mode, and we hope you do too.&lt;/p&gt;

</description>
      <category>darkmode</category>
      <category>css</category>
      <category>design</category>
    </item>
    <item>
      <title>Adding TikTok Embed Support To Wagtail</title>
      <dc:creator>Codista</dc:creator>
      <pubDate>Mon, 27 Apr 2020 11:38:27 +0000</pubDate>
      <link>https://dev.to/codista_/adding-tiktok-embed-support-to-wagtail-3hfi</link>
      <guid>https://dev.to/codista_/adding-tiktok-embed-support-to-wagtail-3hfi</guid>
      <description>&lt;p&gt;TikTok videos are a lot of fun to watch. People get super creative with the limited amount of time they have. No wonder some of our clients requested being able to embed TikTok videos into their CMS pages, mainly blog articles.&lt;/p&gt;

&lt;p&gt;The Wagtail docs do cover &lt;a href="https://docs.wagtail.io/en/v2.8.1/advanced_topics/embeds.html#custom-embed-finders"&gt;adding custom embeds&lt;/a&gt; to &lt;a href="https://docs.wagtail.io/en/v2.8.1/advanced_topics/embeds.html#customising-embed-providers"&gt;your configuration&lt;/a&gt;, but at first glance it's not completely straight forward. So we hope to clear things up a bit, by example, in a couple of minutes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding support for TikTok oEmbed
&lt;/h1&gt;

&lt;p&gt;First, a big and loud "thank you!" to TikTok for &lt;a href="https://developers.tiktok.com/doc/Embed"&gt;supporting the oEmbed standard&lt;/a&gt;. There are platforms that don't and it makes embedding less trivial, but let's not get into that.&lt;/p&gt;

&lt;p&gt;First, fire up your Django settings (&lt;code&gt;settings.py&lt;/code&gt;) and then add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CUSTOM OEMBED PROVIDERS
&lt;/span&gt;&lt;span class="n"&gt;tiktok_provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://www.tiktok.com/oembed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"urls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;# 'https://www.tiktok.com/@memmesson/video/6797323084085890309'
&lt;/span&gt;        &lt;span class="s"&gt;r"^http(?:s)?://(?:www\.)?tiktok\.com/@\w+/video/\d+$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This specifies the provider itself. It also tells Wagtail which URLs are considered valid, via the regular expression pattern above.&lt;/p&gt;

&lt;p&gt;Now, we need to add that provider to the list of valid providers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;WAGTAILEMBEDS_FINDERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# Register tiktok
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"wagtail.embeds.finders.oembed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"providers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tiktok_provider&lt;/span&gt;&lt;span class="p"&gt;],},&lt;/span&gt;
    &lt;span class="c1"&gt;# Handles all other oEmbed providers the default way
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"wagtail.embeds.finders.oembed"&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;That's it! If you now fire up any CMS page that supports embeds, you can simply paste the direct TikTok video link and the embed will load right away.&lt;/p&gt;

</description>
      <category>tiktok</category>
      <category>embed</category>
      <category>wagtail</category>
      <category>django</category>
    </item>
    <item>
      <title>Taking beautiful screenshots of your Slack app</title>
      <dc:creator>Codista</dc:creator>
      <pubDate>Fri, 17 Apr 2020 15:40:04 +0000</pubDate>
      <link>https://dev.to/codista_/taking-beautiful-screenshots-of-your-slack-app-454</link>
      <guid>https://dev.to/codista_/taking-beautiful-screenshots-of-your-slack-app-454</guid>
      <description>&lt;p&gt;Hey there fellow Slack developer! I guess you are also in the process of wanting to take some great looking screenshots of your brand new creation in action.&lt;/p&gt;

&lt;p&gt;So, you fire up your Slack app, type in the magic words, or maybe click some magic buttons, and the content you want to picture for all eternity appears.&lt;/p&gt;

&lt;p&gt;But.&lt;/p&gt;

&lt;p&gt;It is in the Slack App. So… Whatever you are screenshotting might appear in the context of something else entirely.&lt;/p&gt;

&lt;p&gt;Let's look at a modal as example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--twyyY8tr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8v0eiofw9whoc3j2637c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--twyyY8tr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8v0eiofw9whoc3j2637c.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doesn't look all too friendly, eh? The darkened background, the lines of text hidden in it,…Would be a lot nicer if it would be white.&lt;/p&gt;

&lt;p&gt;Presenting your app the way it deserves&lt;/p&gt;

&lt;p&gt;Our little quick tip builds up on one realization: Slack is a web app. Hence, you can also open it in the browser. Seeing where we are going with this?&lt;/p&gt;

&lt;p&gt;Yup. That also means we can use the Web Developer Tools available in any browser to selectively hide, show and modify content to our liking! As a matter of fact, this is how we take the screenshots for our &lt;a href="https://www.codista.com/en/blog/tack-march-20-update/"&gt;Tack Release Notes&lt;/a&gt;, which we then run through &lt;a href="https://squoosh.app/"&gt;Squoosh&lt;/a&gt;, to further slim them down.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/S3CvjaidGF4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The above video is in Firefox, but you can do this in Chrome or Safari as well.&lt;/p&gt;

&lt;p&gt;Now, what you can see me doing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I have our development workspace open.&lt;/li&gt;
&lt;li&gt;I try to create the state I want to screenshot.&lt;/li&gt;
&lt;li&gt;Then I do a right click, anywhere on the page, and hit Inspect Element. Which in turn fires up the Developer Tools. Now I see the HTML and CSS, and can manipulate both freely.&lt;/li&gt;
&lt;li&gt;I remove the darkened background from the modal. You can see my mouse clicking there.&lt;/li&gt;
&lt;li&gt;Afterwards I hide the entire Slack Chat UI. This works by selecting it in the HTML part of the window and then hitting the H key on my keyboard.
Note that I move my cursor inside the Web Developer Tool Window. This keeps highlighting the section represented by the HTML I am hovering over. Super helpful!
I close the tools and am ready to take a screenshot.&lt;/li&gt;
&lt;li&gt;Now, you can do anything with this. You could change the shadow (via the CSS property &lt;code&gt;box-shadow&lt;/code&gt;), make the window smaller, highlight something a bit better etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this without having to fire up any image manipulation program (what a long way to say "Photoshop").&lt;/p&gt;

</description>
      <category>slack</category>
      <category>backgroundremoval</category>
      <category>webapp</category>
      <category>screenshots</category>
    </item>
    <item>
      <title>Wagtail multi-language and internationalization</title>
      <dc:creator>Codista</dc:creator>
      <pubDate>Wed, 08 Apr 2020 09:47:15 +0000</pubDate>
      <link>https://dev.to/codista_/wagtail-multi-language-and-internationalization-2gkf</link>
      <guid>https://dev.to/codista_/wagtail-multi-language-and-internationalization-2gkf</guid>
      <description>&lt;p&gt;WordPress is a well known content management system. Clients often contact us and ask whether we could build a website for them using WordPress. While it is a good solution for simple projects, we often recognize that our clients have very custom requirements that require another, more robust solution.&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://wagtail.io/"&gt;Wagtail&lt;/a&gt; comes into play. Wagtail is a Python/Django powered open source content management system to build high-quality websites and web applications.&lt;/p&gt;

&lt;h1&gt;
  
  
  Approaching Internationalization
&lt;/h1&gt;

&lt;p&gt;Websites often have an international audience. The process of serving translated content to the reader is often referred to as "Internationalization" (i18n) or "multi-language" support. In this blog post we want to showcase how we are approaching multi-language in our web projects using Wagtail.&lt;/p&gt;

&lt;p&gt;This blog post is targeted at developers, and especially developers interested in Wagtail, Python and Django. You probably need some basic Wagtail knowledge to make the most out of reading this article. We will showcase how you can create a Wagtail multi-language setup using &lt;strong&gt;separate page trees&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hZVcV4el--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wx5o6gg4k4aofaza2xq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hZVcV4el--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wx5o6gg4k4aofaza2xq4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
Our language-specific page trees, nicely separated.



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

&lt;p&gt;The solution outlined in this article has been heavily inspired by the &lt;a href="https://docs.wagtail.io/en/v2.6.2/advanced_topics/i18n/index.html"&gt;official documentation&lt;/a&gt; and this &lt;a href="https://www.apsl.net/blog/2016/12/28/wagtail-and-multilanguage/"&gt;blog post&lt;/a&gt;. It's used in production on multiple projects we have built, so it has been battle-tested and is a proven solution. The &lt;a href="https://www.austrianblog.com/"&gt;official blog&lt;/a&gt; of Austrian Airlines and codista.com (yes, &lt;a href="https://www.codista.com"&gt;our own website&lt;/a&gt;) are two examples using this solution.&lt;/p&gt;

&lt;p&gt;When approaching multi-language support you mainly have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Duplicating the fields on your model&lt;/li&gt;
&lt;li&gt;Duplicating your page tree&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As for option 1, the code snippet below shows an example of how you could duplicate the fields on your models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogPostPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title_de&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title_en&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;body_en&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamField&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
    &lt;span class="n"&gt;body_de&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamField&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;

    &lt;span class="c1"&gt;# Language-independent fields don't need to be duplicated
&lt;/span&gt;    &lt;span class="n"&gt;hero_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"wagtailimages.Image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"+"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We tried this approach and have not been very happy with it. Mainly because of three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You need some logic to access the correct field in the template based upon the requested language. More logic, means potentially more bugs.&lt;/li&gt;
&lt;li&gt;The page will be served under the same slug, independent from the language. Mostly you want the slug to reflect the language of the content, because it will give you some extra SEO bonus points.&lt;/li&gt;
&lt;li&gt;The page models become quite cumbersome to maintain. Having to duplicate each field for which you want to have a translation basically doubles the count of your page variables. More code, means potentially more bugs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two topics are solvable. Especially the first argument is easily solvable with some custom logic as &lt;a href="http://docs.wagtail.io/en/v2.6.2/advanced_topics/i18n/index.html#translating-content"&gt;defined in the docs&lt;/a&gt;. But the second argument is a really tough one to solve, which is only solvable if you dig deep into Wagtails way of serving pages.&lt;/p&gt;

&lt;p&gt;Therefore we decided to go the route of duplicating the page tree, which works really well for us. Duplicating the page tree has some very compelling advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You get nicely routed internationalized urls + slugs out of the (wagtail) box&lt;/li&gt;
&lt;li&gt;You keep your page models and your backend logic slim and tidy&lt;/li&gt;
&lt;li&gt;No need for custom logic to access the correct field translation, since you are just doing what you are always doing: rendering a page template with exactly the page fields you want to render&lt;/li&gt;
&lt;li&gt;Editing a page is easier for the content editors, since they have less fields in the admin view to fill and check.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The disadvantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have to duplicate the page tree.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Duplicating the page tree requires a bit more work, but from our point of view, it's worth it. Especially since that can be easily automated if your use case requires you to do so, or you do not want to put that burden on the content editors.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Root Page
&lt;/h1&gt;

&lt;p&gt;So let’s get to the point. What is needed to achieve this duplicate page tree goodness? Basically not very much. In order to implement basic multi-language support, the only thing you need is a root page that detects the requested language and redirects the user to the appropriate page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LanguageRedirectionPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""Redirects the user to the language specfic home page"""&lt;/span&gt;

    &lt;span class="n"&gt;parent_page_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# This will only return a language that is in the LANGUAGES Django setting
&lt;/span&gt;        &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_language_from_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;home_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home_page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that you have to set &lt;a href="https://docs.djangoproject.com/en/2.2/ref/settings/#languages"&gt;Django’s LANGUAGES setting&lt;/a&gt; so we don’t redirect non English/German users to pages that don’t exist.&lt;/p&gt;

&lt;p&gt;Additionally your &lt;code&gt;HomePage&lt;/code&gt; needs to know its own language. Here's an example for defining the &lt;code&gt;language&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"LANGUAGES"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"indicates the language this page serves as the main home page"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;hero_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hero_intro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hero Intro"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;parent_page_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"cms.LanguageRedirectionPage"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verbose_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home Page"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;verbose_name_plural&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home Pages"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is important to note that only the &lt;code&gt;HomePage&lt;/code&gt; needs to know its language. All pages below know it implicitly, because they are children of a language-specific &lt;code&gt;HomePage&lt;/code&gt;. That is basically everything to get started for having multi-language support in your next Wagtail project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Takeaways
&lt;/h1&gt;

&lt;p&gt;Duplicated page trees are your friend because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they solve a specific SEO question for you: providing unique slugs for unique content&lt;/li&gt;
&lt;li&gt;you get almost everything out of the box for free if you duplicate the page tree!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>wagtail</category>
      <category>multilanguage</category>
      <category>internationalization</category>
      <category>python</category>
    </item>
    <item>
      <title>Remote work: show current time tracking entry as your Slack status</title>
      <dc:creator>Codista</dc:creator>
      <pubDate>Mon, 06 Apr 2020 14:43:51 +0000</pubDate>
      <link>https://dev.to/codista_/remote-work-challenges-the-missing-context-4m53</link>
      <guid>https://dev.to/codista_/remote-work-challenges-the-missing-context-4m53</guid>
      <description>&lt;p&gt;Parts of our team at &lt;a href="https://www.codista.com/en/"&gt;Codista&lt;/a&gt;  have been working remotely since we founded our specialized agency. Having the option to work from home provides a lot of benefits - you save time commuting, it gives you the freedom to work from wherever you want, and often it is just more convenient (or even safer) to stay home. I personally like to work from home because I feel more focused and creative there.&lt;/p&gt;

&lt;p&gt;However, working in a &lt;strong&gt;distributed team&lt;/strong&gt; also comes with some challenges. One of these challenges is &lt;strong&gt;“missing shared context”&lt;/strong&gt;. If you are not in the same room as your colleagues, you just cannot see what they are doing. If you see your colleague walking into the meeting room, you know they are probably having a meeting or doing a video call. If you see your teammate staring very concentrated at their screen, you know that you probably should not disturb them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting an overview
&lt;/h1&gt;

&lt;p&gt;If you are in a position to manage a team and steer it into the right direction, it is even more important to have a &lt;strong&gt;high-level overview&lt;/strong&gt; of what everybody is doing. This &lt;strong&gt;“missing context”&lt;/strong&gt; can be really challenging for &lt;strong&gt;remote teams&lt;/strong&gt;, and can lead to a lot of misunderstandings or uncomfortable situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What the heck is Max doing the whole day?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Oh damn, I’m just in the middle of a very hard task - why does he want to chat with me just now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I really want to know what Luis is doing just now. But I do not want to ask him five times a day, because it distracts him and will prevent him from being productive.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This “missing context” situation is especially true for remote / &lt;strong&gt;work from home teams&lt;/strong&gt;. But not exclusively, missing context is also a topic for teams working in the same office. We experienced this “missing context” pain for some years, and last year we decided to do something about it, because it felt like a major &lt;a href="https://www.urbandictionary.com/define.php?term=pita"&gt;PITA&lt;/a&gt; to us and hindered us in our productivity.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solving the shared context problem
&lt;/h1&gt;

&lt;p&gt;In order to solve this “missing shared context” problem once and forever, we invented &lt;strong&gt;Tack&lt;/strong&gt;. Tack is a Slack app that takes your running &lt;a href="https://toggl.com/"&gt;Toggl&lt;/a&gt; time entry and puts it as your &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt; status, so your teammates always know where your head is at.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bLgyj0AE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/imnxvt4qmv5fo5qqyaq3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bLgyj0AE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/imnxvt4qmv5fo5qqyaq3.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, that's the elevator pitch... But let me explain in detail what Tack is doing and why it could help you and your team to be more productive:&lt;/p&gt;

&lt;p&gt;Toggl is a convenient time-tracking tool to log all your activities and assign them to specific clients/projects. Slack is a collaboration and communication tool, especially suitable for remote teams. Tack brings these two tools together by &lt;strong&gt;automatically setting the Slack status&lt;/strong&gt; to the currently running time entry in Toggl.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---7jIAgoX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7nv3f8emm4qrzsgmw1d9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---7jIAgoX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7nv3f8emm4qrzsgmw1d9.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tack solves the “missing context” pain for us, and after just a few days of using Tack we realized that it's a complete game changer for us, making us more productive and helping us work together more efficiently.&lt;/p&gt;

&lt;p&gt;Since we believe that a lot of remote teams out there feel the same pain, we decided to give Tack to the public, for free. So if my thoughts sound familiar to you, you might want to give it a try. By the way, over time Tack became a fully fledged Toggl client which allows you to start and stop your Toggl timer, without leaving Slack - your favorite collaboration tool.&lt;/p&gt;

&lt;p&gt;Tack can be added to your Slack Workspace via a single click. &lt;a href="https://www.codista.com/en/products/tack-syncing-toggl-time-tracking-with-slack/"&gt;Give it a spin&lt;/a&gt; and &lt;strong&gt;let us know what you think&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/FuLz5xe2uq0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>toggl</category>
      <category>slack</category>
      <category>timetracking</category>
      <category>remotework</category>
    </item>
  </channel>
</rss>
