<?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>Forem: Joe Attardi</title>
    <description>The latest articles on Forem by Joe Attardi (@joeattardi).</description>
    <link>https://forem.com/joeattardi</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%2F175791%2F52453f8f-e6aa-46c9-8f95-1d88a25218e2.jpg</url>
      <title>Forem: Joe Attardi</title>
      <link>https://forem.com/joeattardi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joeattardi"/>
    <language>en</language>
    <item>
      <title>Beyond breakpoints: Leveraging user preference media queries in CSS</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Fri, 25 Oct 2024 14:42:11 +0000</pubDate>
      <link>https://forem.com/joeattardi/beyond-breakpoints-leveraging-user-preference-media-queries-in-css-5el0</link>
      <guid>https://forem.com/joeattardi/beyond-breakpoints-leveraging-user-preference-media-queries-in-css-5el0</guid>
      <description>&lt;p&gt;If you build web sites or apps, chances are you work with media queries. These handy CSS tools allow you to adjust your layout depending on the size of the device. They are a key part of responsive design.&lt;/p&gt;

&lt;p&gt;You can query based on the width of the device, its aspect ratio, orientation, and more. These all allow you to fine-tune your layout for the device being used.&lt;/p&gt;

&lt;p&gt;You might not know that there are other media queries available that let you tailor your site or app based on the user's preferences rather than their device size and orientation.&lt;/p&gt;

&lt;h2&gt;
  
  
  User preference media queries
&lt;/h2&gt;

&lt;p&gt;There are several media queries that deal with user preferences. You can use these to fine tune your app's experience for the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic dark mode with &lt;code&gt;prefers-color-scheme&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Dark mode is everywhere these days. It seems like every site now has the sun/moon toggle button to switch between light mode and dark mode. You can take this one step further and use the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query to automatically determine if the user's device is using a light or dark color mode.&lt;/p&gt;

&lt;p&gt;Here's how it works: Define your CSS styles for light mode like you normally would. Then add the media query &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt;. In there, add CSS rules overriding your light mode colors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&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="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&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="p"&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="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&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="no"&gt;white&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;h3&gt;
  
  
  Tip: Create themes with CSS variables
&lt;/h3&gt;

&lt;p&gt;To make life a little easier, you can define all your colors in a theme, a set of CSS variables. Then inside your &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt; block, you only need to redefine the variables instead of re-adding your CSS selectors and rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;--main-background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;white&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--text-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;black&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--main-background&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&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="p"&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="py"&gt;--main-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&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;
  
  
  Tone down animations with &lt;code&gt;prefers-reduced-motion&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Animations can enhance your app's experience, but animations may be difficult for users with vision or vestibular problems. To assist these users, some devices and operating systems have an option in the user preferences to reduce motion.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; media query to detect when such an option is enabled for the user's device. You can then use alternate animations that are less intense (or turn them off altogether).&lt;/p&gt;

&lt;p&gt;Here's an example of using &lt;code&gt;prefers-reduced-motion&lt;/code&gt; to turn off an animation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.content&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;my-awesome-animation&lt;/span&gt; &lt;span class="m"&gt;250ms&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&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="p"&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="nc"&gt;.content&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="nb"&gt;none&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;One thing to note: If you turn off animations like this, make sure you don't have any code that relies on an &lt;code&gt;animationend&lt;/code&gt; event. When you set &lt;code&gt;animation&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt;, as shown above, this event will never be fired.&lt;/p&gt;

&lt;p&gt;Keep in mind that you don't have to turn off animations when &lt;code&gt;prefers-reduced-motion: reduce&lt;/code&gt; matches! Suppose you have a fancy reveal animation where the element bounces into view. When the user wants reduced motion, you could change this so it's a simple fade-in animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaptive contrast with &lt;code&gt;prefers-contrast&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Some users with vision difficulties may have increased contrast enabled on their device. You can handle such situations with the &lt;code&gt;prefers-contrast&lt;/code&gt; media query.&lt;/p&gt;

&lt;p&gt;If this media query matches, you can adjust your contrast to make things easier to see for these users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&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-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* Make the border stand out more against the background */&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;black&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;h2&gt;
  
  
  Checking media queries with JavaScript
&lt;/h2&gt;

&lt;p&gt;Suppose you're animating with JavaScript - for example, maybe you're using the Web Animations API. Since these animations aren't defined in the CSS, you can't turn them off with the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; media query.&lt;/p&gt;

&lt;p&gt;Or can you?&lt;/p&gt;

&lt;p&gt;Yes! You can use the &lt;code&gt;window.matchMedia&lt;/code&gt; method to evaluate a media query from JavaScript. The media query is passed as an argument, and the method returns something called a &lt;code&gt;MediaQueryList&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This object has a &lt;code&gt;matches&lt;/code&gt; property, which is a boolean indicating whether or not this media query currently matches. Based on the value, you can decide whether or not to animate.&lt;/p&gt;

&lt;p&gt;Here's a simple example showing how &lt;code&gt;window.matchMedia&lt;/code&gt; can be used to conditionally call &lt;code&gt;animate&lt;/code&gt; on a DOM element:&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;// Check to see if we should animate&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&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="s1"&gt;(prefers-reduced-motion: reduce)&lt;/span&gt;&lt;span class="dl"&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;query&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;someElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;animate&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;h3&gt;
  
  
  Reacting to changes
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;MediaQueryList&lt;/code&gt; even has a &lt;code&gt;change&lt;/code&gt; event which fires if conditions change and the media query no longer applies.&lt;/p&gt;

&lt;p&gt;Suppose you have some JavaScript code that you only want to run for large displays. If the user's screen size is small, you want to skip this code. You can do this by calling &lt;code&gt;window.matchMedia&lt;/code&gt; using a &lt;code&gt;max-width&lt;/code&gt; query or something similar.&lt;/p&gt;

&lt;p&gt;Suppose that later, the user resizes the window, and now you want to run the large screen specific JavaScript code. Just listen for the &lt;code&gt;MediaQueryList&lt;/code&gt;'s &lt;code&gt;change&lt;/code&gt; event, and you can check the event's &lt;code&gt;matches&lt;/code&gt; property to see what the new value is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;These media queries can help you to better personalize your user's experience within your website or app. You can automatically apply a dark color scheme, and even fine-tune your animations and contrast depending on user preferences. They also help with accessibility, which is always a win.&lt;/p&gt;

&lt;p&gt;It's still in an experimental stage, but you can also keep an eye out for the &lt;code&gt;prefers-reduced-data&lt;/code&gt; query which will indicate whether the user's device has a requirement that is uses less data. This can be used to provide alternate content that uses less data. For example, you could skip non-essential images (or use lower-resolution versions).&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using git bisect, the detective tool for debugging</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Fri, 04 Oct 2024 03:19:15 +0000</pubDate>
      <link>https://forem.com/joeattardi/using-git-bisect-the-detective-tool-for-debugging-21p8</link>
      <guid>https://forem.com/joeattardi/using-git-bisect-the-detective-tool-for-debugging-21p8</guid>
      <description>&lt;p&gt;Youre working on a bug fix and find some bad code that causes a bug. The &lt;code&gt;git blame&lt;/code&gt; command will tell you who the last developer was that modified that line, and when they modified it. But what if you dont know what code introduced the bug? For these situations, the &lt;code&gt;git bisect&lt;/code&gt; command can help.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;git bisect&lt;/code&gt;, youll need to find a commit where the bug exists (a bad commit), and youll also need to find an older commit where the bug doesnt exist (a good commit).&lt;/p&gt;

&lt;p&gt;Once you identify these two commits, &lt;code&gt;git bisect&lt;/code&gt; will guide you through a binary search of the commit history.&lt;/p&gt;

&lt;p&gt;This can save you considerable time if there are a lot of commits between the bad commit and the good commit. At each step of the binary search, you perform a test to see if the bug exists. If the bug exists at this commit, you tell &lt;code&gt;git bisect&lt;/code&gt; that this is a bad commit. If the bug doesnt exist, you indicate that its a good commit.&lt;/p&gt;

&lt;p&gt;Depending on whether the commit is bad or good, &lt;code&gt;git bisect&lt;/code&gt; picks another middle point between the commit you identified and the bad commit. The process continues until Git has identified the first bad commit which introduced the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example bisect
&lt;/h2&gt;

&lt;p&gt;Lets walk through a short &lt;code&gt;git bisect&lt;/code&gt; and see a visualization of each step.&lt;/p&gt;

&lt;p&gt;First, to start the process, run &lt;code&gt;git bisect start&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% git bisect start
status: waiting for both good and bad commits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you've checked out a commit or branch that exhibits the bug you're investigating, and run &lt;code&gt;git bisect bad&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% git bisect bad
status: waiting for good commit(s), bad commit known
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll need to check out a known good commit and run &lt;code&gt;git bisect good&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% git checkout &amp;lt;commit ID&amp;gt;
% git bisect good
Bisecting: X revisions left to test after this (roughly Y steps)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you've identified a good commit and a bad commit, and Git is pointing to a commit in the middle for you to check.&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%2Fjiepi4220kd8nrj42ckg.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%2Fjiepi4220kd8nrj42ckg.png" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run or test your code, and see if the bug exists here. If you can't reproduce the bug, mark this as a good commit by running &lt;code&gt;git bisect good&lt;/code&gt;. Now Git knows that the commits between the first good commit and this one are all good:&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%2F4ltx6eti1gqm71ylm4f2.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%2F4ltx6eti1gqm71ylm4f2.png" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, Git will continue the binary search. It will pick the commit in the middle between the good and bad commits, and check it out for you to check:&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%2Fmgqkgnpktutf8k8po289.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%2Fmgqkgnpktutf8k8po289.png" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suppose that here, you are able to reproduce the bug. Mark this as a bad commit by running &lt;code&gt;git bisect bad&lt;/code&gt;. Git will mark this commit as bad:&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%2Frmdzk080aae7y9mci4lu.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%2Frmdzk080aae7y9mci4lu.png" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will pick the next commit between the good and bad commits, and continue binary searching:&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%2Fq5hpethy6afafkk3w810.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%2Fq5hpethy6afafkk3w810.png" width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's assume that this is a good commit, and you can't reproduce the bug. Just as you did before, run &lt;code&gt;git bisect good&lt;/code&gt; to mark this as a good commit:&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%2Fz6rqheg7v8mg3x49ecsm.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%2Fz6rqheg7v8mg3x49ecsm.png" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, Git has enough information to tell you the first bad commit.&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%2F5p4srt1mardor00c5h02.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%2F5p4srt1mardor00c5h02.png" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can look at the changes from that commit and, hopefully, determine what change caused the bug.&lt;/p&gt;

&lt;p&gt;When you're done, run &lt;code&gt;git bisect reset&lt;/code&gt; to exit bisect mode and return to the commit you were at when you started bisecting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixing up good and bad commits
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; can only work if the good commit is an ancestor of a bad commit. If you mark a commit as good, then check out an earlier commit and mark it as bad, you'll get an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Some good revs are not ancestors of the bad rev.
git bisect cannot work properly in this case.
Maybe you mistook good and bad revs?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automating &lt;code&gt;git bisect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;It might be tedious to manually test your code at each step of a &lt;code&gt;git bisect&lt;/code&gt;, but you might be able to automate it. You can use &lt;code&gt;git bisect run&lt;/code&gt; and give it the name of a script or other command to run. Git will check the exit code of the command at each step.&lt;/p&gt;

&lt;p&gt;If the exit code is 0, it's treated as a good commit. If you want the commit to be treated as a bad commit, your command should have an exit code between 1 and 127. However, there is one exception to this. Git reserves the exit code 125 to have a special meaning. If, for some reason, this commit cant be tested, your command can return an exit code of 125 and it will skip that commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;git bisect&lt;/code&gt; command can help you quickly narrow down which commit introduced a bug or other undesirable behavior. It does this by performing a binary search on the commit history between a known good commit and a known bad commit.&lt;/p&gt;

&lt;p&gt;If you have automated tests, you can even use &lt;code&gt;git bisect run&lt;/code&gt; to automatically control the process of testing and identifying good and bad commits.&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Steps for installing a local AI copilot in Visual Studio Code</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Thu, 12 Sep 2024 20:12:07 +0000</pubDate>
      <link>https://forem.com/joeattardi/steps-for-installing-a-local-ai-copilot-in-visual-studio-code-bf0</link>
      <guid>https://forem.com/joeattardi/steps-for-installing-a-local-ai-copilot-in-visual-studio-code-bf0</guid>
      <description>&lt;p&gt;Does your company block ChatGPT or GitHub Copilot? Do you have security or trust concerns sending your code to a third party AI service? You might not know this, but you can run a large language model (LLM) locally on your computer, and even integrate it with Visual Studio Code.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; tool, you can download and run models locally. In this post, Ill guide you through the steps to run the Code Llama model using Ollama, and integrate it into Visual Studio Code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ollama.com/library/codellama" rel="noopener noreferrer"&gt;Code Llama&lt;/a&gt; is an LLM from Meta that is focused on generating and talking about code. Its based on their Llama 2 model, and supports many different languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Ollama and the Code Llama model
&lt;/h2&gt;

&lt;p&gt;Your first step is to install Ollama. Go over to &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;https://ollama.com&lt;/a&gt; where you can download and install it. Once Ollama is up and running, you should have a new terminal command, &lt;code&gt;ollama&lt;/code&gt;. To see if its installed correctly, open a terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should print the Ollama version. If you see this, then youre good to go! Next, download the Code Llama model by running this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama pull codellama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may take a while, depending on your Internet connection. The 7b version of Code Llama is 3.8 gigabytes. Go get a cup of coffee, tea, or your favorite beverage while Ollama downloads this model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up CodeGPT
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codegpt.co/" rel="noopener noreferrer"&gt;CodeGPT&lt;/a&gt; has a Visual Studio Code extension where you can interact with models directly in the editor. In VS Code, go to the Extensions tab and search for codegpt. Youll see several results, make sure to get the one with the blue check mark:&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%2Fxa96gbsvpsxl1242fywq.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%2Fxa96gbsvpsxl1242fywq.png" width="566" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once CodeGPT is installed, you should see a new CodeGPT icon in the editors sidebar. When you click on this, youll be taken to the CodeGPT interface. Click the dropdown menu at the top of this panel and select Ollama as the provider, and &lt;code&gt;codellama&lt;/code&gt; as the model:&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%2F1mr8v9y419c5udjaycde.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%2F1mr8v9y419c5udjaycde.png" width="563" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once youre up and running, you will see a text area at the bottom of this panel to start chatting. Try entering a prompt such as Generate the code for a simple React component.&lt;/p&gt;

&lt;p&gt;Code Llama will start processing your request. Keep in mind that running a model locally is not as powerful, or fast, as an online service like Meta AI or ChatGPT. After a few seconds, you should have a result in the chat window.&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%2Ftqk3zorl0usjgcn4vr0p.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%2Ftqk3zorl0usjgcn4vr0p.png" width="563" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up completion
&lt;/h2&gt;

&lt;p&gt;You can also use CodeGPT to suggest code completion, like GitHub Copilot and similar tools do. To set this up, in the CodeGPT Chat window, click the Menu button at the top left part of the screen. A menu will slide out with several options.&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%2Fnktpl5lru5baop6lfrzt.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%2Fnktpl5lru5baop6lfrzt.png" width="315" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Autocomplete to set up code completion.&lt;/p&gt;

&lt;p&gt;Code Llama has a &lt;code&gt;code&lt;/code&gt; variation that you can use for code completion. It is a separate model, so youll have to make another large download. Select the &lt;code&gt;codellama:code&lt;/code&gt; model from the AI Model dropdown:&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%2Flbkyzlewi6y1wcvp6kav.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%2Flbkyzlewi6y1wcvp6kav.png" width="403" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, make sure to click the toggle switch to enable completion:&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%2F525asbges8e8rucilk4j.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%2F525asbges8e8rucilk4j.png" width="383" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, as you type in your editor, Code Llama will make suggestions for you. For example, here it is filling in the &lt;code&gt;PropTypes&lt;/code&gt; for a &lt;code&gt;Greeter&lt;/code&gt; component:&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%2Fcq03n6w1hf7wq664xk06.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%2Fcq03n6w1hf7wq664xk06.png" width="416" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like a suggestion, you can press the Tab key to accept 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%2F6ogcqvtip1y7hglyobuq.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%2F6ogcqvtip1y7hglyobuq.png" width="369" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Have fun!
&lt;/h2&gt;

&lt;p&gt;Thats really all there is to it. You now have AI chat and code completion integrated in Visual Studio Code!&lt;/p&gt;

&lt;p&gt;]]&amp;gt;&lt;/p&gt;

</description>
      <category>visualstudiocode</category>
      <category>ai</category>
      <category>aitools</category>
    </item>
    <item>
      <title>New book: Web API Cookbook</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Wed, 03 Apr 2024 20:50:48 +0000</pubDate>
      <link>https://forem.com/joeattardi/new-book-web-api-cookbook-3nee</link>
      <guid>https://forem.com/joeattardi/new-book-web-api-cookbook-3nee</guid>
      <description>&lt;p&gt;Announcing my new book from O'Reilly Media, &lt;em&gt;Web API Cookbook&lt;/em&gt;! &lt;/p&gt;

&lt;p&gt;It's now available from &lt;a href="https://www.amazon.com/Web-API-Cookbook-JavaScript-Applications/dp/1098150694" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt; and &lt;a href="https://www.oreilly.com/library/view/web-api-cookbook/9781098150686/" rel="noopener noreferrer"&gt;O'Reilly&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The book contains examples or "recipes" demonstrating how to accomplish specific tasks using only the APIs built in to modern web browsers.&lt;/p&gt;

&lt;p&gt;Many things that once required third-party JavaScript libraries or browser plugins are now supported by the browser platform itself.&lt;/p&gt;

&lt;p&gt;The book covers topics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working with persisted data and local files&lt;/li&gt;
&lt;li&gt;Internationalization&lt;/li&gt;
&lt;li&gt;Speech synthesis and recognition&lt;/li&gt;
&lt;li&gt;Communciating with APIs and real-time data&lt;/li&gt;
&lt;li&gt;Reacting to changes in the DOM&lt;/li&gt;
&lt;li&gt;Custom elements&lt;/li&gt;
&lt;li&gt;Reading device information and sensors&lt;/li&gt;
&lt;li&gt;Performance statistics&lt;/li&gt;
&lt;li&gt;The Web Animations API&lt;/li&gt;
&lt;/ul&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%2Fily2crdb3zumpcq65vza.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%2Fily2crdb3zumpcq65vza.png" alt="Book cover" width="800" height="1049"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>books</category>
    </item>
    <item>
      <title>Understanding color contrast for accessibility</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Fri, 06 Oct 2023 14:43:31 +0000</pubDate>
      <link>https://forem.com/joeattardi/understanding-color-contrast-for-accessibility-4d6n</link>
      <guid>https://forem.com/joeattardi/understanding-color-contrast-for-accessibility-4d6n</guid>
      <description>&lt;p&gt;Accessibility is a popular topic these days - as it should be. There are many benefits of making your application accessible - it promotes inclusivity by making your app usable by everyone.&lt;/p&gt;

&lt;p&gt;There are many aspects of accessibility that you need to keep in mind, one of which is color contrast. Proper color contrast not only looks good, but it's critical for users with low vision.&lt;/p&gt;

&lt;p&gt;Specifically, you need to make sure that there is enough contrast between your background color and text color. If the contrast is too low, the text is not easily distinguishable from the background and it's hard to read. Even for users without disabilities, this is problematic as it can lead to eye strain.&lt;/p&gt;

&lt;p&gt;The Web Content Accessibility Guidelines (WCAG) provides some guidance on contrast ratios.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a contrast ratio?
&lt;/h2&gt;

&lt;p&gt;The term "contrast ratio" refers to the difference, or ratio, in brightness between the background and text colors.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.w3.org/TR/WCAG21/" rel="noopener noreferrer"&gt;WCAG guidelines&lt;/a&gt; formally define the &lt;a href="https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio" rel="noopener noreferrer"&gt;contrast ratio&lt;/a&gt; as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(L1 + 0.05) / (L2 + 0.05), where&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;L1 is the relative luminance of the lighter of the colors, and&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;L2 is the relative luminance of the darker of the colors.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How much contrast is enough?
&lt;/h2&gt;

&lt;p&gt;The contrast ratios required by WCAG differ slightly depending on the size of the text.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large text
&lt;/h3&gt;

&lt;p&gt;Large text size is defined as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For &lt;strong&gt;bold&lt;/strong&gt; text, 14 points or larger&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For non-bold text, 18 points or larger&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Large text has a more relaxed contrast requirement since larger text is easier to read. For large text, the minimum contrast ratio is 3:1. This minimum is known as Level AA.&lt;/p&gt;

&lt;p&gt;There is also a notion of enhanced contrast, known as Level AAA. For large text, AAA requires a minimum ratio of 4.5:1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Normal text
&lt;/h3&gt;

&lt;p&gt;Since smaller text is not as easy to read, it has stricter contrast requirements.&lt;/p&gt;

&lt;p&gt;For Level AA, text must have a minimum contrast ratio of 4.5:1. The enhanced Level AAA contrast is a minimum of 7:1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-text elements
&lt;/h3&gt;

&lt;p&gt;Non-text elements, such as icons, have a minimum contrast ratio of 3:1 for Level AA.&lt;/p&gt;

&lt;p&gt;Level AAA does not define a minimum contrast for non-text elements.&lt;/p&gt;

&lt;p&gt;The following table summarizes the different minimum contrast ratios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Large text&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Normal text&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Non-text&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3:1&lt;/td&gt;
&lt;td&gt;4.5:1&lt;/td&gt;
&lt;td&gt;3:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AAA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.5:1&lt;/td&gt;
&lt;td&gt;7:1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Checking contrast
&lt;/h2&gt;

&lt;p&gt;The easiest way to check your color contrast is to use a tool. WebAIM has an excellent [Contrast Checker](&lt;a href="https://webaim.org/resources/contrastchecker/" rel="noopener noreferrer"&gt;https://webaim.org/resources/contrastchecker/&lt;/a&gt;). You can enter your foreground and background colors, and it will show you if the contrast meets AA and AAA standards.&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%2Ff3i8u3sjexlgskh5xmof.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%2Ff3i8u3sjexlgskh5xmof.png" alt="The WebAIM contrast checker tool" width="721" height="793"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Chrome developer tools will also show the contrast ratio when inspecting an element:&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%2Ff0vvd757y2me7ayzavhm.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%2Ff0vvd757y2me7ayzavhm.png" alt="Chrome developer tools showing contrast" width="327" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualizing contrast
&lt;/h2&gt;

&lt;p&gt;Here are a few examples of different color contrast ratios. Screenshots are courtesy of the &lt;a href="https://webaim.org/resources/contrastchecker/" rel="noopener noreferrer"&gt;WebAIM Contrast Checker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;1.4:1 (does not meet minimum standard):&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%2Fpgrpca12gr74f15pw9qy.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%2Fpgrpca12gr74f15pw9qy.png" alt="1.4:1 contrast ratio" width="326" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4.51:1 (meets level AA standard):&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%2Fs1npoeq5in28rvnq5dxy.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%2Fs1npoeq5in28rvnq5dxy.png" alt="4.51:1 contrast ratio" width="326" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7:1 (meets level AAA standard):&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%2Fl145gzxv3g3clk7egaut.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%2Fl145gzxv3g3clk7egaut.png" alt="7:1 contrast ratio" width="328" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using sufficient color contrast is good for everyone, but critical for accessibility. Of course, color alone is not enough to make an accessible experience. For other resources to make your UI accessible, check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.w3.org/WAI/standards-guidelines/wcag/" rel="noopener noreferrer"&gt;WCAG 2 Overview&lt;/a&gt; [w3.org]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://webaim.org/" rel="noopener noreferrer"&gt;WebAIM&lt;/a&gt; [webaim.org]&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Understanding error handling in Promise chains</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Wed, 13 Sep 2023 00:10:02 +0000</pubDate>
      <link>https://forem.com/joeattardi/understanding-error-handling-in-promise-chains-22d9</link>
      <guid>https://forem.com/joeattardi/understanding-error-handling-in-promise-chains-22d9</guid>
      <description>&lt;h2&gt;
  
  
  Promise chains
&lt;/h2&gt;

&lt;p&gt;You can create a chain of &lt;code&gt;Promise&lt;/code&gt;s by returning new &lt;code&gt;Promise&lt;/code&gt;s from a &lt;code&gt;then&lt;/code&gt; handler. Here's a simple example that chains 3 promises together:&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Done!`&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 output of this chain will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Success: 1
Success: 2
Success: 3. Done!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To handle any errors that may occur in the chain, you can add a call to &lt;code&gt;catch&lt;/code&gt; at the end of the chain. If any of the &lt;code&gt;Promise&lt;/code&gt;s are rejected, this &lt;code&gt;catch&lt;/code&gt; handler will run, and the rest of the chain is skipped.&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Done!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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 output of this chain will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Success: 1
Error: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The weird parts
&lt;/h2&gt;

&lt;p&gt;If you add more &lt;code&gt;then&lt;/code&gt; calls after the &lt;code&gt;catch&lt;/code&gt;, they will run!&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Done!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Another then!&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get this output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Success: 1
Error: 2
Another then!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does the chain continue after the &lt;code&gt;catch&lt;/code&gt;? As it turns out, you can return another &lt;code&gt;Promise&lt;/code&gt; from a &lt;code&gt;catch&lt;/code&gt; handler. Here, the &lt;code&gt;catch&lt;/code&gt; handler just prints to the console. The handler function, then, returns &lt;code&gt;undefined&lt;/code&gt;. This actually returns a new &lt;code&gt;Promise&lt;/code&gt;, fulfilled with the value &lt;code&gt;undefined&lt;/code&gt;. You can verify this by adding the &lt;code&gt;id&lt;/code&gt; argument to the last &lt;code&gt;then&lt;/code&gt;:&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Done!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Success: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;And the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Success: 1
Error: 2
Success: undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Re-rejecting
&lt;/h3&gt;

&lt;p&gt;This is a contrived scenario, but consider a function that does some asynchronous work and returns a &lt;code&gt;Promise&lt;/code&gt;. Maybe it's a function that wraps the Fetch API, to return the JSON content. It has a &lt;code&gt;catch&lt;/code&gt; for centralized request error logging:&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;getJSON&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetch error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;What happens if there's an error with the Fetch call? Before reading this post, the result might surprise you!&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;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://invalid.fake&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success!&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logically you might expect that &lt;code&gt;Error!&lt;/code&gt; will be printed. But what actually happens is that &lt;code&gt;getJSON&lt;/code&gt; logs the Fetch error but returns a &lt;em&gt;fulfilled&lt;/em&gt; &lt;code&gt;Promise&lt;/code&gt;. Your &lt;code&gt;then&lt;/code&gt; handler will be executed and print:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Success! undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to get the result you want, the &lt;code&gt;catch&lt;/code&gt; handler inside &lt;code&gt;getJSON&lt;/code&gt; has to return a &lt;em&gt;rejected&lt;/em&gt; &lt;code&gt;Promise&lt;/code&gt;. You have to "re-reject" it:&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;getJSON&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetch error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;You could also &lt;code&gt;throw&lt;/code&gt; the error, which will implicitly return a rejected &lt;code&gt;Promise&lt;/code&gt; as well:&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;getJSON&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetch error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&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;Either way, now when you call &lt;code&gt;getJSON&lt;/code&gt; and an error occurs, it will correctly return a rejected &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Promise&lt;/code&gt; chains actually work a lot like &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; blocks in JavaScript. If an exception is thrown within a &lt;code&gt;try&lt;/code&gt; block, it jumps right to the &lt;code&gt;catch&lt;/code&gt; block and skips the rest of the &lt;code&gt;try&lt;/code&gt; - just like how &lt;code&gt;catch()&lt;/code&gt; skips the rest of the &lt;code&gt;Promise&lt;/code&gt; chain.&lt;/p&gt;

&lt;p&gt;Also, if you have a function that catches an exception, you'll need to re-throw it if you want it to propagate back up to the calling function.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>async</category>
      <category>promises</category>
      <category>programming</category>
    </item>
    <item>
      <title>Using the HTML `picture` element to show a WebP image</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Mon, 03 Aug 2020 11:09:38 +0000</pubDate>
      <link>https://forem.com/joeattardi/using-the-html-picture-element-to-show-a-webp-image-kee</link>
      <guid>https://forem.com/joeattardi/using-the-html-picture-element-to-show-a-webp-image-kee</guid>
      <description>&lt;p&gt;The WebP image format has been around since 2010 when the standard was first announced. One benefit of using WebP images is that they are smaller than both JPEGs and PNGs, sometimes significantly.&lt;/p&gt;

&lt;p&gt;WebP is supported in &lt;em&gt;almost&lt;/em&gt; all modern browsers, as can be seen &lt;a href="https://caniuse.com/#feat=webp" rel="noopener noreferrer"&gt;here&lt;/a&gt;. However, it currently is not supported in Safari and IE11. So we can't use a standard &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element, as the image will not display in these browsers.&lt;/p&gt;

&lt;p&gt;Luckily there is a solution with the HTML &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element. This is a very powerful and flexible element that lets you define multiple sources for an image.&lt;/p&gt;

&lt;p&gt;First, convert your image to WebP. There are several easy online converters that can do this. Next, use a &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element:&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;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="na"&gt;srcSet=&lt;/span&gt;&lt;span class="s"&gt;"/my-image.webp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/my-image.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last child of a &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element is a fallback &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element. This will be used if the browser does not support WebP. This also allows the image to be shown on browsers that do not support the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Speaking of browser support, it's pretty decent. All modern browsers (Edge, Firefox, Chrome, and Safari) support &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;. It's not supported in IE11, but the fallback &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element will be used instead.&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Vanilla JavaScript emoji picker! New and improved.</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Thu, 23 Jul 2020 16:55:02 +0000</pubDate>
      <link>https://forem.com/joeattardi/vanilla-javascript-emoji-picker-new-and-improved-4cln</link>
      <guid>https://forem.com/joeattardi/vanilla-javascript-emoji-picker-new-and-improved-4cln</guid>
      <description>&lt;p&gt;&lt;a href="https://emoji-button.js.org" rel="noopener noreferrer"&gt;Emoji Button&lt;/a&gt; is a vanilla JavaScript emoji picker component I have been working on over the past year or so. Because it's just plain JavaScript, it can be made to work with any framework, site, or app.&lt;/p&gt;

&lt;p&gt;Version 4 has just been released, with performance improvements and some new features, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom emojis&lt;/li&gt;
&lt;li&gt;UI plugins&lt;/li&gt;
&lt;li&gt;More UI customization&lt;/li&gt;
&lt;li&gt;Fuzzy search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free and open source, MIT licensed. Hope someone out there finds it useful!&lt;/p&gt;

&lt;p&gt;Details, documentation, and demos can be found at &lt;a href="https://emoji-button.js.org" rel="noopener noreferrer"&gt;https://emoji-button.js.org/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Code can be found at &lt;a href="https://github.com/joeattardi/emoji-button" rel="noopener noreferrer"&gt;https://github.com/joeattardi/emoji-button&lt;/a&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%2Fi%2F5rttrsi9yhixupbprpwv.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%2Fi%2F5rttrsi9yhixupbprpwv.png" alt="Screenshot" width="775" height="856"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>New book: Modern CSS</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Tue, 26 May 2020 13:28:21 +0000</pubDate>
      <link>https://forem.com/joeattardi/new-book-modern-css-7m9</link>
      <guid>https://forem.com/joeattardi/new-book-modern-css-7m9</guid>
      <description>&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%2Fi%2Flc9ajrg5949ltzpnaust.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%2Fi%2Flc9ajrg5949ltzpnaust.png" alt="Modern CSS cover" width="800" height="1036"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am excited to announce that my new book, &lt;a href="https://moderncss.info" rel="noopener noreferrer"&gt;Modern CSS&lt;/a&gt;, is now available!&lt;/p&gt;

&lt;p&gt;The book is targeted at beginners and experienced developers who might not be up to date on the latest CSS topics like flexbox and grid.&lt;/p&gt;

&lt;p&gt;The topics covered include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;box model&lt;/li&gt;
&lt;li&gt;specificity&lt;/li&gt;
&lt;li&gt;selectors&lt;/li&gt;
&lt;li&gt;units&lt;/li&gt;
&lt;li&gt;colors&lt;/li&gt;
&lt;li&gt;inline vs block elements&lt;/li&gt;
&lt;li&gt;backgrounds&lt;/li&gt;
&lt;li&gt;gradients&lt;/li&gt;
&lt;li&gt;shadows&lt;/li&gt;
&lt;li&gt;borders&lt;/li&gt;
&lt;li&gt;text and fonts&lt;/li&gt;
&lt;li&gt;layout&lt;/li&gt;
&lt;li&gt;positioning&lt;/li&gt;
&lt;li&gt;transforms&lt;/li&gt;
&lt;li&gt;transitions&lt;/li&gt;
&lt;li&gt;animations&lt;/li&gt;
&lt;li&gt;floats&lt;/li&gt;
&lt;li&gt;responsive design&lt;/li&gt;
&lt;li&gt;Flexbox&lt;/li&gt;
&lt;li&gt;CSS Grid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information on the book, including a full table of contents and sample chapter, you can go to &lt;a href="https://moderncss.info" rel="noopener noreferrer"&gt;https://moderncss.info&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for looking!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>Let's make a CSS cube</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Wed, 13 May 2020 17:06:59 +0000</pubDate>
      <link>https://forem.com/joeattardi/let-s-make-a-css-cube-1fed</link>
      <guid>https://forem.com/joeattardi/let-s-make-a-css-cube-1fed</guid>
      <description>&lt;p&gt;You can do a lot with CSS transforms! In this post we'll walk through making a 3D cube.&lt;/p&gt;

&lt;p&gt;First, let's create the markup:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cube"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"face top"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Top&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"face bottom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bottom&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"face left"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Left&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"face right"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Right&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"face front"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Front&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"face back"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Back&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each face of the cube is its own &lt;code&gt;div&lt;/code&gt; element. Let's create some initial styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.cube&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;skyblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&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.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&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've given some styling to the cube faces and absolutely positioned them so they are all sitting on top of one another. First, we will rotate each face so that it's facing the proper direction. The next step will be to move each face out from the center to the edges of the cube.&lt;/p&gt;

&lt;p&gt;Here is what we have so far:&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%2Fi%2Frdol2oop32el7ugdk1rm.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%2Fi%2Frdol2oop32el7ugdk1rm.png" alt="The initial positioned elements" width="442" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are currently looking at the cube head-on. It will be easier to visualize if we rotate our "camera" a bit. Let's add a &lt;code&gt;rotate3d&lt;/code&gt; transform to the cube:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cube&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate3d&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;45deg&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;Now the elements are rotated at an angle, so we can see the 3D structure better:&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%2Fi%2F9qyxm1y6zs77q49m6o0p.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%2Fi%2F9qyxm1y6zs77q49m6o0p.png" alt="Rotated in 3D space" width="440" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's tackle the front first. Our cube is 200px by 200px by 200px. Currently all the elements are in the dead center of the cube. The front is already facing the right direction, we just need to move it out by 100px so that it is at the front edge. Let's add a new rule for the front and move it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.front&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100px&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;Now the front face of the cube is in place:&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%2Fi%2F4bxvzhtodflqykpz0oy8.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%2Fi%2F4bxvzhtodflqykpz0oy8.png" alt="The front face in position" width="614" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is the back. It is already in the proper plane, but we need to rotate it 180 degrees around the y-axis so that the text is facing outwards. Like the front, we also need to move it out by 100px:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.back&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;180deg&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 back face is now in the proper position:&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%2Fi%2F9oeh9gh73lrdf3p2xnga.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%2Fi%2F9oeh9gh73lrdf3p2xnga.png" alt="Back face in position" width="678" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's do the left and right next. These need to be rotated 90 degrees around the y-axis so that they are both facing outwards on each side. The left side needs to be rotated by -90 degrees. &lt;/p&gt;

&lt;p&gt;Then they need to be moved along the x-axis to the edges of the cube:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.left&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-90deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.right&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;90deg&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;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%2Fi%2Fpydyt1pnc1zb1jn407ds.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%2Fi%2Fpydyt1pnc1zb1jn407ds.png" alt="Left and right in position" width="724" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we need to position the top and bottom. These need to be rotated 90 degrees around the x-axis so that they are facing up and down. Again, the top will need a rotation of 90 degrees and the bottom -90 degrees, so that the text direction is correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.top&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;90deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.bottom&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-90deg&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;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%2Fi%2F3pfy41q2zcyv2rf90ijc.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%2Fi%2F3pfy41q2zcyv2rf90ijc.png" alt="The completed cube" width="712" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like a cube!&lt;/p&gt;

&lt;p&gt;To show off our new cube, let's give it a turning animation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;turn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate3d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate3d&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;360deg&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="nc"&gt;.cube&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&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;turn&lt;/span&gt; &lt;span class="m"&gt;5s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="n"&gt;infinite&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;Lastly, we want to be mindful of accessibility. If the user has disabled animations in their operating system, let's not animate the cube but rather display it at the 45 degree angle we had before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&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="p"&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="nc"&gt;.cube&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="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate3d&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;45deg&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;Here is the finished product in a CodePen:&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Did you like this post? Learn more CSS in my upcoming book, &lt;a href="https://leanpub.com/modern-css" rel="noopener noreferrer"&gt;Modern CSS&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CSS fundamentals: Selectors</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Sun, 03 May 2020 18:06:33 +0000</pubDate>
      <link>https://forem.com/joeattardi/css-fundamentals-selectors-2pd4</link>
      <guid>https://forem.com/joeattardi/css-fundamentals-selectors-2pd4</guid>
      <description>&lt;p&gt;One of the core concepts in CSS is that of selectors. A selector determines which element(s) a CSS rule applies to. There are several ways an element can be targeted with a selector.&lt;/p&gt;

&lt;p&gt;CSS selectors can target multiple elements on the page. That is, a single CSS rule can apply to multiple elements. An element or class selector can select multiple different elements that have that class or element name.&lt;/p&gt;

&lt;p&gt;Similarly, a single HTML element can be affected by multiple CSS rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic selector types
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The universal selector
&lt;/h3&gt;

&lt;p&gt;The universal selector, specified simply as an asterisk (&lt;code&gt;*&lt;/code&gt;), matches all elements. This can be specified as a single selector, to select all elements in the document, or in combination with combinators (discussed below).&lt;/p&gt;

&lt;h3&gt;
  
  
  Element selectors
&lt;/h3&gt;

&lt;p&gt;An element selector targets an HTML element by its element or tag name. The syntax of the selector is simply the name of the element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25px&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;In this example, all p elements on the page will have a margin of 25 pixels applied to them.&lt;/p&gt;

&lt;h3&gt;
  
  
  ID selectors
&lt;/h3&gt;

&lt;p&gt;An HTML element can have an &lt;code&gt;id&lt;/code&gt; attribute. As a general rule, there should only be one element with a given &lt;code&gt;id&lt;/code&gt;. If there are multiple elements with the same &lt;code&gt;id&lt;/code&gt;, only one of them will be targeted with an ID selector.&lt;/p&gt;

&lt;p&gt;An ID selector is specified with the &lt;code&gt;#&lt;/code&gt; character followed by the &lt;code&gt;id&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25px&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 above selector will target the element with an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;header&lt;/code&gt; and give it a padding of 25 pixels.&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;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In general, the use of ID selectors should be avoided. Due to their tight coupling to a specific HTML element, they are not reusable. The CSSLint tool has a good explanation as to why you should avoid ID selectors, which can be read in full here: &lt;a href="https://github.com/CSSLint/csslint/wiki/Disallow-ids-in-selectors" rel="noopener noreferrer"&gt;https://github.com/CSSLint/csslint/wiki/Disallow-ids-in-selectors&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The explanation reads, in part:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For years, developers have treated IDs as the way to say "that thing!" However, IDs have a downside: they are completely unique and therefore cannot be reused. You could potentially style every element on your page with an ID selector but you would lose a lot of the benefit of CSS in the process.&lt;/p&gt;

&lt;p&gt;One of CSS's benefits is the ability to reuse style rules in multiple places. When you start out with an ID selector, you're automatically limiting that style to a single element.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Class selectors
&lt;/h3&gt;

&lt;p&gt;While only a single element can be targeted by an ID selector, any number of HTML elements can have the same &lt;code&gt;class&lt;/code&gt; attribute. A class selector will match every element in the document with the given &lt;code&gt;class&lt;/code&gt;. Class selectors are specified with a dot, followed by the name of the class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.nav-link&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="no"&gt;darkcyan&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 rule will match every element with a class of &lt;code&gt;nav-link&lt;/code&gt; and give it a color of &lt;code&gt;darkcyan&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attribute selectors
&lt;/h3&gt;

&lt;p&gt;HTML elements can also be selected by their attribute values, or by the presence of an attribute. The attribute is specified inside square brackets, and the attribute selector can take several forms.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[attributeName]&lt;/code&gt;&lt;br&gt;
Selects all elements that have the given attribute, regardless of its value.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[attributeName="value"]&lt;/code&gt;&lt;br&gt;
Selects all elements that have the given attribute, whose value is the string &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[attributeName*="value"]&lt;/code&gt;&lt;br&gt;
Selects all elements that have the given attribute, whose value contains the string &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[attributeName~="value"]&lt;/code&gt;&lt;br&gt;
Selects all elements that have the given attribute, whose value contains the string &lt;code&gt;value&lt;/code&gt; separated by whitespace.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[attributeName^="value"]&lt;/code&gt;&lt;br&gt;
Selects all elements that have the given attribute, whose value begins with &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[attributeName$="value"]&lt;/code&gt;&lt;br&gt;
Selects all elements that have the given attribute, whose value ends with &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Combining selectors
&lt;/h2&gt;

&lt;p&gt;Any of the above selectors (with the exception of the universal selector) can be used alone or in conjunction with other selectors to make the selector more specific. This is best illustrated with some examples:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;div.my-class&lt;/code&gt;&lt;br&gt;
Matches all &lt;code&gt;div&lt;/code&gt; elements with a &lt;code&gt;class&lt;/code&gt; of &lt;code&gt;my-class&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;span.class-one.class-two&lt;/code&gt;&lt;br&gt;
Matches all &lt;code&gt;span&lt;/code&gt; elements with a &lt;code&gt;class&lt;/code&gt; of &lt;em&gt;both&lt;/em&gt; &lt;code&gt;class-one&lt;/code&gt; and &lt;code&gt;class-two&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a.nav-link[href*="example.com"]&lt;/code&gt;&lt;br&gt;
Matches all &lt;code&gt;a&lt;/code&gt; elements with a &lt;code&gt;class&lt;/code&gt; of &lt;code&gt;nav-link&lt;/code&gt; that have an &lt;code&gt;href&lt;/code&gt; attribute that contains the string &lt;code&gt;example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A rule can also have multiple selectors separated by a comma:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.class-one, .class-two&lt;/code&gt;&lt;br&gt;
Matches all elements with a &lt;code&gt;class&lt;/code&gt; of &lt;code&gt;class-one&lt;/code&gt; &lt;em&gt;as well as&lt;/em&gt; all elements with a &lt;code&gt;class&lt;/code&gt; of &lt;code&gt;class-two&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Selector combinators
&lt;/h2&gt;

&lt;p&gt;There's even more you can do with selectors. Combinators are used to select more specific elements. Combinators are used in conjunction with the basic selectors discussed above. For a given rule, multiple basic selectors can be used, joined by a combinator.&lt;/p&gt;
&lt;h3&gt;
  
  
  Descendant combinator
&lt;/h3&gt;

&lt;p&gt;The descendant combinator matches an element that is a descendant of the element on the left hand side. Descendant means that the element exists somewhere within the child hierarchy - it does not have to be a direct child.&lt;/p&gt;

&lt;p&gt;The descendant combinator is specified with a space character.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.header div&lt;/code&gt;&lt;br&gt;
Matches all &lt;code&gt;div&lt;/code&gt; elements that are direct or indirect children of an element with a &lt;code&gt;class&lt;/code&gt; of &lt;code&gt;header&lt;/code&gt;. If any of these &lt;code&gt;div&lt;/code&gt;s have children that are also &lt;code&gt;div&lt;/code&gt;s, those &lt;code&gt;div&lt;/code&gt;s will also be matched by the selector.&lt;/p&gt;
&lt;h3&gt;
  
  
  Child combinator
&lt;/h3&gt;

&lt;p&gt;The child combinator matches an element that is a direct child of the element on the left hand side. It is specified with a &lt;code&gt;&amp;gt;&lt;/code&gt; character.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.header &amp;gt; div&lt;/code&gt;&lt;br&gt;
Matches all div elements that are direct children of an element with a class of header. If those divs have children that are also divs, those divs will not be matched by the selector.&lt;/p&gt;
&lt;h3&gt;
  
  
  General sibling combinator
&lt;/h3&gt;

&lt;p&gt;The general sibling combinator matches an element that is a sibling, but not necessarily an immediate sibling, of the element on the left hand side. It is specified with a &lt;code&gt;~&lt;/code&gt; character.&lt;/p&gt;

&lt;p&gt;Consider the following HTML:&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;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following selector would select both of the &lt;code&gt;div&lt;/code&gt;s, with the &lt;code&gt;class&lt;/code&gt; &lt;code&gt;body&lt;/code&gt; or &lt;code&gt;footer&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.header ~ div&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjacent sibling combinator
&lt;/h3&gt;

&lt;p&gt;The adjacent sibling combinator is similar to the general sibling combinator, except it only matches elements that are an immediate sibling. It is specified with a &lt;code&gt;+&lt;/code&gt; character.&lt;/p&gt;

&lt;p&gt;Looking again at the above HTML structure, the following selector would only select the &lt;code&gt;div&lt;/code&gt; with the &lt;code&gt;class&lt;/code&gt; &lt;code&gt;body&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.header + div&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining combinators
&lt;/h3&gt;

&lt;p&gt;These combinators can be combined to form even more specific selectors, for example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;div.header &amp;gt; div + button&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This selector matches a button element that is an immediate sibling of a div element, which in turn is an immediate child of a div with the class header.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pseudo-classes
&lt;/h2&gt;

&lt;p&gt;Another tool in the CSS selector toolbox is the pseudo-class. A pseudo-class allows you to select elements based on some special state of the element, in addition to all the selectors discussed above.&lt;/p&gt;

&lt;p&gt;Some pseudo-classes let you select elements based on UI state, while others let you select elements based on their position in the document (with more precision than the combinators).&lt;/p&gt;

&lt;p&gt;There are many pseudo-classes, but here are some of the more commonly used ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI state
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;:hover&lt;/code&gt;&lt;br&gt;
Matches an element that the mouse cursor is currently hovering over. This is typically used for buttons and links, but can be applied to any type of element.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:active&lt;/code&gt;&lt;br&gt;
Matches an element that is currently being activated. For buttons and links, this usually means the mouse button has been pressed, but not yet released.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:visited&lt;/code&gt;&lt;br&gt;
Matches a link whose URL has already been visited by the user.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:focus&lt;/code&gt;&lt;br&gt;
Matches an element that currently has the focus. This is typically used for buttons, links, and text fields.&lt;/p&gt;
&lt;h3&gt;
  
  
  Document structure
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;:first-child&lt;/code&gt;&lt;br&gt;
Matches an element that is the first child of its parent. Consider the following example HTML:&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;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Item one&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Item two&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selector &lt;code&gt;.my-list &amp;gt; li:first-child&lt;/code&gt; will match the first list item only.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:last-child&lt;/code&gt;&lt;br&gt;
Matches an element that is the last child of its parent. In the above example, the selector &lt;code&gt;.my-list &amp;gt; li:last-child&lt;/code&gt; will match the last list item only.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:nth-child()&lt;/code&gt;&lt;br&gt;
Matches an element that is the nth child of its parent. The value of &lt;code&gt;n&lt;/code&gt; is passed as a parameter to the pseudo-class. The index of the first child is &lt;code&gt;1&lt;/code&gt;. Going back to the example HTML above, we can also select "Item two" with the selector &lt;code&gt;.my-list &amp;gt; li:nth-child(2)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This pseudo-class can even select children at a certain interval. For example, in a longer list, we could select every other list item with the selector &lt;code&gt;.my-list &amp;gt; li:nth-child(2n)&lt;/code&gt;. Or, we could select every four items with the selector &lt;code&gt;.my-list &amp;gt; li:nth-child(4n)&lt;/code&gt;. We can even select all odd-numbered children with the selector &lt;code&gt;.my-list &amp;gt; li:nth-child(odd)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A complete list of pseudo-classes, including some that are still experimental, can be found at &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading, I hope you found this useful, whether you're a CSS beginner or just someone looking for a refresher!&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Shiny button hover effect with CSS</title>
      <dc:creator>Joe Attardi</dc:creator>
      <pubDate>Wed, 29 Apr 2020 17:00:17 +0000</pubDate>
      <link>https://forem.com/joeattardi/shiny-button-hover-effect-with-css-3b8e</link>
      <guid>https://forem.com/joeattardi/shiny-button-hover-effect-with-css-3b8e</guid>
      <description>&lt;p&gt;This one is based on a shiny button I added to the navigation on &lt;a href="https://joeattardi.codes" rel="noopener noreferrer"&gt;my portfolio site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's done by having an &lt;code&gt;::after&lt;/code&gt; pseudo element, which is a rotated rectangle, quickly zoom across over the button.&lt;/p&gt;

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

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
