<?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: Mats Bryntse</title>
    <description>The latest articles on DEV Community by Mats Bryntse (@matsbryntse).</description>
    <link>https://dev.to/matsbryntse</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%2F719764%2Ff6dc4dce-194b-4ed7-95c1-2c18ccbdaac1.jpg</url>
      <title>DEV Community: Mats Bryntse</title>
      <link>https://dev.to/matsbryntse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matsbryntse"/>
    <language>en</language>
    <item>
      <title>Making an interactive CSS avatar editor with zero lines of JS</title>
      <dc:creator>Mats Bryntse</dc:creator>
      <pubDate>Thu, 11 Aug 2022 09:56:53 +0000</pubDate>
      <link>https://dev.to/matsbryntse/making-an-interactive-css-avatar-editor-with-zero-lines-of-js-249o</link>
      <guid>https://dev.to/matsbryntse/making-an-interactive-css-avatar-editor-with-zero-lines-of-js-249o</guid>
      <description>&lt;p&gt;&lt;a href="https://www.bryntum.com/blog/making-an-interactive-avatar-editor-with-zero-lines-of-js/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gG4fzefW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mlposwajeygw.i.optimole.com/C3ZG6aY.IQWs%257E1e938/w:768/h:522/q:94/https://www.bryntum.com/wp-content/uploads/2022/08/featured.png" alt="" width="768" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post explores CSS interactivity by showing some techniques that can be used to create a simple avatar editor, without any JavaScript. It was amusing to implement and I hope it will be equally amusing to read &amp;amp; try. With that said, I would not recommend anyone actually using this in production. 😊&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Interactivity using CSS
&lt;/h2&gt;

&lt;p&gt;The CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:checked"&gt;:checked&lt;/a&gt; pseudo-class selector can be used to add a basic level of interactivity to a page without using JavaScript. It is a special selector in that it is toggled on and off as a user toggles a checkbox or radio button. For example, the following snippet makes a checked checkbox twice as large as an unchecked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:checked {
    transform : scale(2);
}

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

&lt;/div&gt;



&lt;p&gt;Which looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eDOlqQlh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3fvagb5ny7ogf6sslaa8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eDOlqQlh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3fvagb5ny7ogf6sslaa8.png" alt="Image description" width="226" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By linking a label to the checkbox or radio button (using &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;for&lt;/code&gt;), you can also click on the label to toggle the checkbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="checkbox" id="box" /&amp;gt;
&amp;lt;label for="box"&amp;gt;Click label to toggle&amp;lt;/label&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;By using &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators"&gt;CSS sibling combinators&lt;/a&gt; you can affect the label when the checkbox is toggled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:checked + label {
    background : orangered;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If you then hide the checkbox, it can still be toggled using the label. Which opens up for interesting possibilities, like in these CSS only examples found on CodePen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codepen.io/MPDoctor/pen/mpJdYe"&gt;Tabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/erikterwan/pen/EVzeRP"&gt;Hamburger menu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/finnhvman/pen/xJRMJp"&gt;Block stacking game&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building a CSS only range control
&lt;/h2&gt;

&lt;p&gt;The avatar editor shown at the top uses a set of range controls to affect the appearance of the avatar. To allow this without using JavaScript, we cannot use &lt;code&gt;&amp;lt;input type="range"&amp;gt;&lt;/code&gt;. Instead, we have to roll our own using the techniques described above.&lt;/p&gt;

&lt;p&gt;Each segment of the range will be represented by a radio button and a label. The label will be styled to look like a segment of a range control. Below is the HTML defining a range control with 4 segments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="range"&amp;gt;
    &amp;lt;input type="radio" id="range4" name="range"&amp;gt;
    &amp;lt;label for="range4"&amp;gt;&amp;lt;/label&amp;gt;
    &amp;lt;input type="radio" id="range3" name="range"&amp;gt;
    &amp;lt;label for="range3"&amp;gt;&amp;lt;/label&amp;gt;
    &amp;lt;input type="radio" id="range2" name="range" checked&amp;gt;
    &amp;lt;label for="range2"&amp;gt;&amp;lt;/label&amp;gt;
    &amp;lt;input type="radio" id="range1" name="range"&amp;gt;
    &amp;lt;label for="range1"&amp;gt;&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Without styles we’re not fooling anyone:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5KCt3YNp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdswl6p9zob1541yhlsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5KCt3YNp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdswl6p9zob1541yhlsf.png" alt="Image description" width="120" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By adding a little CSS, we can make it look like the track of a range control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.range {
    display : flex;
}

/* Hide the radio buttons */
.range input {
    display : none; 
}

/* Style labels to look like a solid range control track */
.range label {
    width : 16px;
    height : 6px;
    background : #efefef;
    border-block : 1px solid #b2b2b2;
    display : flex;
    cursor : pointer;
    align-items : center;
}

.range label:first-of-type {
    border-radius : 6px 0 0 6px;
    border-inline-start : 1px solid #b2b2b2;
}

.range label:last-of-type {
    border-radius : 0 6px 6px 0;
    border-inline-end : 1px solid #b2b2b2;
}

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

&lt;/div&gt;



&lt;p&gt;Resulting in this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9-pVRWv1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d51d4tfgl7ols77q4quh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9-pVRWv1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d51d4tfgl7ols77q4quh.png" alt="Image description" width="168" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To highlight the current value, we leverage &lt;code&gt;:checked&lt;/code&gt; and style a pseudo-element for the label to act as the thumb:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aL8W0k2q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9v1ejz5le5d447a3d05a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aL8W0k2q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9v1ejz5le5d447a3d05a.png" alt="Image description" width="152" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now on to the tricky part, we also want to highlight the part of the track before the thumb. Problem is, there is no CSS combinator (not yet anyway) to target previous siblings. By reversing the element order in DOM we can target what visually appears to be previous siblings with the &lt;code&gt;~&lt;/code&gt; combinator 😵‍💫&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.range {
    /* Reverse element order */
    flex-direction : row-reverse;
    /* Move them back to the left */
    justify-content : flex-end;
}

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

&lt;/div&gt;



&lt;p&gt;A mess:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yNzSUSdp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ue3w22m1pxcbxidwrrkv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yNzSUSdp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ue3w22m1pxcbxidwrrkv.png" alt="Image description" width="148" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First segment is now last and vice versa, you also have to switch places of the &lt;code&gt;:first-of-type&lt;/code&gt; and &lt;code&gt;:last-of-type&lt;/code&gt; selectors for appearance to be correct again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ztMiWvz---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsjaoy9skts8gg0hk40x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ztMiWvz---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsjaoy9skts8gg0hk40x.png" alt="Image description" width="152" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And lastly we add the track highlight (made possible with the reversal above):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.range :checked ~ label {
    background : #0075ff;
    border-color : #4b76bb;
}

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

&lt;/div&gt;



&lt;p&gt;Looks like a range control!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Using the range control for interactivity
&lt;/h2&gt;

&lt;p&gt;While the range control above looks nice and is interactive, we should use it to manipulate something else 🤔. We can (currently) only affect sibling elements, by once again using the &lt;code&gt;~&lt;/code&gt; combinator. Therefor, we have to put another element into the outer &lt;code&gt;.range&lt;/code&gt; element&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="range"&amp;gt;
    &amp;lt;!-- Existing range markup --&amp;gt;

    &amp;lt;div class="avatar"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;And use some more CSS magic, here to scale the avatar depending on range value (using CSS variables):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Represent the leftmost value, since we reversed the elements */
:nth-of-type(4):checked ~ .avatar {
    --avatar-size : 0.5;
}

:nth-of-type(3):checked ~ .avatar {
    --avatar-size : 0.75;
}

:nth-of-type(2):checked ~ .avatar {
    --avatar-size : 1;
}

/* Rightmost value */
:nth-of-type(1):checked ~ .avatar {
    --avatar-size : 1.25;
}

.avatar {
    /* Lots of CSS here to make the avatar look like a dog */

    transform : scale(var(--avatar-size));
}

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

&lt;/div&gt;



&lt;p&gt;Try it out:&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;That’s it!&lt;/strong&gt; 🎉 By using the techniques in the post it is possible to create the full avatar editor shown at top, be sure to check it out over at &lt;a href="https://codepen.io/johan-bryntum/pen/BarYdmq"&gt;CodePen&lt;/a&gt;. If you’re already using similar tricks in your application, we would love to hear about it.&lt;/p&gt;

&lt;p&gt;PS. In the not too distant future, we will be able to use the &lt;code&gt;:has()&lt;/code&gt; selector to affect an element that i️s not a direct sibling (we could also avoid reversing elements using it). Stay tuned for a future update using it ⭐&lt;/p&gt;

</description>
      <category>css</category>
      <category>development</category>
      <category>tipsntricks</category>
    </item>
  </channel>
</rss>
