<?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: Alec Lomas</title>
    <description>The latest articles on Forem by Alec Lomas (@lowmess).</description>
    <link>https://forem.com/lowmess</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%2F160424%2F0d42fc72-211f-408f-8a9d-ed1b521efe8f.png</url>
      <title>Forem: Alec Lomas</title>
      <link>https://forem.com/lowmess</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lowmess"/>
    <language>en</language>
    <item>
      <title>Styling Links for Print</title>
      <dc:creator>Alec Lomas</dc:creator>
      <pubDate>Fri, 03 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/lowmess/styling-links-for-print-49fi</link>
      <guid>https://forem.com/lowmess/styling-links-for-print-49fi</guid>
      <description>&lt;p&gt;A few weeks ago, amid the ongoing COVID-19 pandemic, my apartment building left a note in my mailbox. Among the helpful information and polite requests to wear masks in shared areas, I noticed a piece of text that had a very familiar style. It was blue, and underlined, and seemed to hint at a missing piece of content. In short, it was a link. Of course, this being a printed piece of mail, the link wasn't much help to me. And, while this note was certainly a Google Docs or Word document, it made me think about the way links are styled on the web.&lt;/p&gt;

&lt;p&gt;While people aren't printing out websites at the same volume they used to, print styles are still very important. Modern browsers use these styles when saving a page as a PDF, for example. The problem is that links in this context are not always clickable. But with a little CSS we can ensure the user is always shown the URL the link points to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="err"&gt;^=&lt;/span&gt;&lt;span class="s2"&gt;'http'&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;' ('&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&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="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:link&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s2"&gt;'/'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;' (https://mywebsite.com'&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&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="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:link&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s2"&gt;'#'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-decoration&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="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This CSS takes advantage of the &lt;code&gt;print&lt;/code&gt; media query to set special styles on links in that context. Specifically, we are creating &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements"&gt;a psuedo-element&lt;/a&gt; on links that begin with &lt;code&gt;http&lt;/code&gt; (absolute links) and &lt;code&gt;/&lt;/code&gt; (root-relative links). The &lt;code&gt;content&lt;/code&gt; of those elements is set to the link element's &lt;code&gt;href&lt;/code&gt; attribute using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/attr"&gt;the &lt;code&gt;attr&lt;/code&gt; function&lt;/a&gt;. As a bonus, we also remove the link-styling for anchor links, since those have no practical purpose in print contexts.&lt;/p&gt;

&lt;p&gt;Of course this isn't an advised solution to &lt;em&gt;every&lt;/em&gt; link, but it is particularly useful for blog posts, technical documentation, and any content that a user might want to save to refer to later. Printing websites not be a common occurrence anymore, but setting print styles absolutely should be.&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>No Red Squigglies For Semicolons</title>
      <dc:creator>Alec Lomas</dc:creator>
      <pubDate>Wed, 04 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/lowmess/no-red-squigglies-for-semicolons-3751</link>
      <guid>https://forem.com/lowmess/no-red-squigglies-for-semicolons-3751</guid>
      <description>&lt;p&gt;Us developers are an opinionated bunch. We share our opinions wide and loud through blog posts, tweets, talks at meetups, in meetings. The tenacity with which we hold these opinions seems to have &lt;a href="https://en.wikipedia.org/wiki/Law_of_triviality"&gt;an inverse correlation with their impact&lt;/a&gt;, and nowhere is this strange dichotomy more present than in stylistic choices. Should we use tabs or spaces for whitespace? Should we use semicolons or not? Single quotes or double quotes?&lt;/p&gt;

&lt;p&gt;Collectively, these stylistic choices are known as bikeshedding. Thousands of words have been written about how we should be painting our bikesheds. We have many tools to help us paint the bikeshed. But, truth be told, I don’t care much about what color the bikeshed is. What I do care about is my code editor’s linting extension putting a red squiggly line under a function declaration because there’s a space between the name of the function and its parameter list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linters Are For Code Quality, Formatters Are For Style
&lt;/h2&gt;

&lt;p&gt;Linters are incredible code quality tools. They enforce best patterns and can help catch bugs well before our code makes it to a browser. But they are ill-equipped to handle issues of style. Attempting to use an undefined variable is a bug, and your linter should point this out to you. Using single quotes instead of double quotes, on the other hand, will not impact your code’s logic or ability to run. As such, your linter should not care about this issue. At the very least it should not interrupt your work to tell you about it.&lt;/p&gt;

&lt;p&gt;Formatters are a different kind of tool altogether. They care only about enforcing a consistent style and rarely (if ever) give real-time feedback. Instead, they wait until your work is at a safe stopping point to run: when you save a file, when you commit, or when you explicitly tell the formatter to run. Because of this, they are much less likely to introduce friction into your workflow.&lt;/p&gt;

&lt;p&gt;In the JavaScript world the most used linter is &lt;a href="https://eslint.org"&gt;ESLint&lt;/a&gt;, and the most used formatter is &lt;a href="https://prettier.io"&gt;Prettier&lt;/a&gt;. &lt;a href="https://www.lowmess.com/colophon/"&gt;They’re what I use&lt;/a&gt; across my projects. Unfortunately, many popular ESLint configs include strict enforcement of stylistic rules. Recognizing that conflicts between the two tools would arise, the Prettier team created &lt;a href="https://github.com/prettier/eslint-config-prettier"&gt;&lt;code&gt;eslint-config-prettier&lt;/code&gt;&lt;/a&gt;. The aptly-named ESLint config turns off any rules that conflict with Prettier, giving it full dominion over all issues of style. Using it allows us to keep the code quality benefits of our linter, only without those pesky bikeshedding errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Have to Remember To Do It, You’ll Forget To Do It
&lt;/h2&gt;

&lt;p&gt;The workflow friction does not stop with squiggly lines, of course. If your setup relies on you to remember to run a linter or formatter (or test suite) against your code, you will forget to do so. Probably not every time, maybe even not most of the time, but &lt;em&gt;definitely&lt;/em&gt; some of the time. This is especially true when working in a shared codebase: we can’t be sure all members of our team have the same linting &amp;amp; formatting extensions installed, that those extenstions are configured the same way, or be sure our teammates will remember to run those tools manually. Instead, I find it better to set these tools up to run with Git hooks.&lt;/p&gt;

&lt;p&gt;Git hooks allow us to run arbitrary commands when performing a Git function; they allow us to run scripts before or after committing or pushing or pulling. While possible to &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks"&gt;set these hooks up manually&lt;/a&gt;, tools like &lt;a href="https://github.com/typicode/husky"&gt;husky&lt;/a&gt; make setting them up much easier. I also often use a tool called &lt;a href="https://github.com/okonet/lint-staged"&gt;lint-staged&lt;/a&gt; to lint &amp;amp; format only those files staged for commit. This makes the process much faster, and ensures that no extraneous files are included in a commit.&lt;/p&gt;

&lt;p&gt;Combined with removing bikeshedding rules from your linter, introducing a formatter and setting up Git hooks will remove a fair amount of friction from your code writing process. Setting these tools up — ESLint, Prettier, husky, &amp;amp; lint-staged — are among the first things I do in any new JavaScript project. I highly recommend giving it a shot.&lt;/p&gt;

</description>
      <category>bikeshedding</category>
      <category>tooling</category>
      <category>eslint</category>
      <category>prettier</category>
    </item>
    <item>
      <title>On Declarative Styling</title>
      <dc:creator>Alec Lomas</dc:creator>
      <pubDate>Fri, 19 Jul 2019 08:09:07 +0000</pubDate>
      <link>https://forem.com/lowmess/on-declarative-styling-3pno</link>
      <guid>https://forem.com/lowmess/on-declarative-styling-3pno</guid>
      <description>&lt;p&gt;There is a problem at the heart of CSS. It's not the cascade, or specificity, or inconsistencies between rendering engines -- though these things can be annoying. No, it's much simpler than that: the problem is that we can write too much of it.&lt;/p&gt;

&lt;p&gt;I am not talking about &lt;a href="https://css-tricks.com/oh-no-stylesheet-grows-grows-grows-append-stylesheet-problem/"&gt;append-only stylesheets&lt;/a&gt; (though these too cause their issues). Even if we're extremely disciplined about refactoring our CSS, and we only add new rules when absolutely needed, something is still wrong. The problem is the flexibility of the language itself. There are nearly unlimited valid values that a &lt;code&gt;padding&lt;/code&gt; declaration can take, and while extremely freeing this also introduces surface area for inconsistencies in our designs. And consistency is key to good design! It reduces the end user's cognitive load, it (generally) looks better, and it minimizes the workload for designers &amp;amp; developers to boot.&lt;/p&gt;

&lt;p&gt;Artificially limiting the number of values that we can use in declarations is key to avoiding these inconsistencies. We want a declaration like &lt;code&gt;padding&lt;/code&gt; to act a little more like &lt;code&gt;float&lt;/code&gt;; we should only be able to set a value that we've defined in our governing system. There are many techniques and technologies that can help us accomplish this (or at least get us close). I call the overarching concept that these tools encompass "declarative styling".&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining "Declarative"
&lt;/h2&gt;

&lt;p&gt;This term -- declarative styling -- is derived from the computer science concept &lt;a href="https://en.wikipedia.org/wiki/Declarative_programming"&gt;declarative programming&lt;/a&gt;. It means we want to tell the computer the rules for drawing our interface, and let it follow those rules for us. We no longer want to write &lt;code&gt;padding: 1rem&lt;/code&gt;, we want to write something like &lt;code&gt;padding: 3&lt;/code&gt; and have the computer replace the &lt;code&gt;3&lt;/code&gt; with the 3rd value in our spacing scale.&lt;/p&gt;

&lt;p&gt;This accomplishes several things for us. It ensures consistency across our design by allowing us to use a reference rather than a specific value. It also reduces the cognitive load for stakeholders by providing a common language to communicate in. These factors (among others) can make designing and iterating faster, and all but eliminate the inherent friction in designer-developer handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Tokens
&lt;/h2&gt;

&lt;p&gt;Those familiar with the concept of &lt;a href="https://css-tricks.com/what-are-design-tokens/"&gt;design tokens&lt;/a&gt; may find a lot of conceptual overlap here. Design tokens are an essential part of declarative styling: they are how we define our custom subset of styling options. If a rule in our stylesheet declares a &lt;code&gt;background-color&lt;/code&gt;, that swatch should be found in our tokens.&lt;/p&gt;

&lt;p&gt;There are many techniques for storing and parsing design tokens. I'm partial to the JSON-based &lt;a href="https://system-ui.com/theme"&gt;System UI theme specification&lt;/a&gt;, which organizes our tokens into a variety of scales. Several of the tools discussed below rely on this or a similar technique, but the concept remains the same: there should be a source of truth for these values, and it should not be the CSS rule itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Techniques
&lt;/h2&gt;

&lt;p&gt;Much like there are a multitude of ways to store our tokens, there are many, many ways to apply them to our styles.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Variables
&lt;/h3&gt;

&lt;p&gt;The most commonly-used solution, and one you're likely familiar with, is to use variables whenever possible. Although Sass and LESS have had variables since their inception, CSS now has native variable support &lt;a href="http://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;with the custom properties specification&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;padding&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;--spacing-3&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;Unlike the variables of preprocessors like Sass and LESS, custom properties can take full advantage of the cascade. This means we can create fully themeable component styles natively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* First we define our variables in the base component style */&lt;/span&gt;
  &lt;span class="py"&gt;--button-padding-y&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;--spacing-2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--button-padding-x&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;--spacing-3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c"&gt;/* Then we apply our variables to our declarations */&lt;/span&gt;
  &lt;span class="nl"&gt;padding&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;--button-padding-y&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;--button-padding-x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* By updating our variables, the styles will change automatically */&lt;/span&gt;
&lt;span class="nc"&gt;.btn--large&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--button-padding-y&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;--spacing-3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--button-padding-x&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;--spacing-4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To take full advantage of this system, we need to convert our tokens to custom properties in the &lt;code&gt;:root&lt;/code&gt; selector. The easiest way to do this is to copy and paste the values by hand, though there are &lt;a href="https://github.com/salesforce-ux/theo"&gt;tools to automate the process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, no method is without a downside. In this case, the biggest flaw is the same as its biggest draw: the low barrier to entry. There are no guardrails to stop you from writing &lt;code&gt;padding: 24px&lt;/code&gt; instead of using your variable. It takes a lot of discipline to not deviate from the system, and any time you're writing new CSS is an opportunity to create a discrepancy. But combined with a strict code review process, this can be a powerful way of enforcing consistency while creating a themeable system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomic CSS
&lt;/h3&gt;

&lt;p&gt;Atomic CSS (aka Functional CSS, aka Utility-first CSS) libraries like BassCSS, Tachyons, and Tailwind CSS are declarative by definition. Classes like &lt;code&gt;p3&lt;/code&gt; automatically follow our &lt;code&gt;padding&lt;/code&gt; rule from above: we're telling the engine to apply equal padding to all sides (the &lt;code&gt;p&lt;/code&gt;) using the third step from our spacing scale (the &lt;code&gt;3&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"p3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hi&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;Customizing BassCSS and Tachyons can be tough, &lt;a href="https://www.lowmess.com/blog/customizing-tachyons/"&gt;but it is possible&lt;/a&gt;. Tailwind, on the other hand, is &lt;a href="https://tailwindcss.com/docs/configuration"&gt;fully customizable by default&lt;/a&gt;. The Tailwind configuration system is a plain JS object exported from a Node script. A major benefit to this approach is that we can read our tokens from a JSON or YAML file, and apply them to our custom config with a few lines of code.&lt;/p&gt;

&lt;p&gt;I'm on the record as being a big, big fan of atomic CSS. But I'm not blind to the disadvantages. The learning curve can be quite steep; not only do we need to internalize the naming scheme, but we also need to rethink how we apply our CSS. Because we also need to apply a fair amount of classnames to our components, I also tend to recommend this approach only for very simple projects or for projects that have a powerful templating system. Applying atomic CSS classes to a React component or Pug mixin makes applying the classnames to our elements much more palatable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styled System
&lt;/h3&gt;

&lt;p&gt;The advent of CSS-in-JS has presented us with a unique opportunity: by taking advantage of an expressive, fully-featured programming language, we can create abstractions on top of CSS that would otherwise be impossible. For example, by taking the lessons learned from atomic CSS and applying them to a JSX-based CSS-in-JS system, &lt;a href="https://jxnblk.com/"&gt;Brent Jackson&lt;/a&gt; has created several purpose-built declarative styling libraries.&lt;/p&gt;

&lt;p&gt;The first of these libraries, the foundation upon which the others are built, is called &lt;a href="https://styled-system.com/"&gt;Styled System&lt;/a&gt;. The library provides a consistent interface to refer to your design tokens when defining or consuming a component. The true genius of Styled System is &lt;a href="https://styled-system.com/responsive-styles"&gt;how it handles responsive styles&lt;/a&gt;. By default, Styled System props accept a string, which the library parses and converts to a value represented by a design token. If the token is not found, the literal value is passed to the underlying CSS-in-JS library. However, by passing a prop an array, it will apply the 0-index value to the component by default, the 1-index value to the component at the first breakpoint, and so on and so forth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="s2"&gt;`
  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;space&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="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyStyledSystemComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Hi
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;&amp;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 approach allows us to not only create components that are not only consistent with our system, but are extremely portable as well. A rule of thumb I try to follow when styling a component is to only define surrounding vertical margin when the component is actually consumed in a view. By spreading our props to a root component defined with Styled System, it becomes trivial to follow this rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyView&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyStyledSystemComponent&lt;/span&gt; &lt;span class="na"&gt;my&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Because Styled System (when used in React) reads from the theme put into context from your CSS-in-JS library of choice's &lt;code&gt;ThemeProvider&lt;/code&gt;, creating themeable components is a breeze. However, attaching &lt;code&gt;space&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;, and other token references to all your components can be quite annoying. Luckily, Mr. Jackson has also created a library of primitive components built on top of Styled System called &lt;a href="https://rebassjs.org/"&gt;Rebass&lt;/a&gt;. These components operate like the &lt;code&gt;Box&lt;/code&gt; component we utilized inside of &lt;code&gt;MyStyledSystemComponent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most recently, Brent has released &lt;a href="https://theme-ui.com/"&gt;Theme UI&lt;/a&gt;, a further iteration of this idea. Theme UI exposes an &lt;code&gt;sx&lt;/code&gt; prop on JSX components that allows us to reference our tokens, provides primitive components to use as a foundation, handles styling of markdown content with MDX, and much, much more. It's an exciting evolution of the approach, and one I intend to explore more fully in the future.&lt;/p&gt;

&lt;p&gt;The Styled System approach, much like the others, comes with its fair share of downsides. For starters, it shares the same learning curve problem as atomic CSS. It can be somewhat cumbersome to set up, particularly when not using Rebass or Theme UI's primitive components. And while it can technically work with any framework that supports JSX, React is the only true first-class Styled System citizen. But, when given a choice, it is still my preferred method for defining and consuming styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Vision of the Future
&lt;/h2&gt;

&lt;p&gt;The web has always been an extremely flexible and expressive platform. This has lead to some amazing creative applications of technology, new media experiences, and beautiful interactions. It has also created plenty of &lt;a href="https://en.wiktionary.org/wiki/footgun"&gt;footgun&lt;/a&gt; opportunities. By placing self-imposed constraints on the very fabric of the web, we get to remove the more self-sabotaging aspects of designing for the platform. Rather than limiting our creativity, these constraints provide guardrails for us to explore the limits of our designs while providing a consistent and visually harmonious interface to our users.&lt;/p&gt;

&lt;p&gt;The declarative styling methodology will continue to evolve over time. As design tokens become more prevalent, design software will add first-class support for them. More libraries and methods will evolve for applying them to our products, both on the web and beyond. As we solve the problem at the heart of CSS, the language we use to communicate between ideation and implementation will meld into one. Perhaps our tools will as well.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post originally appeared &lt;a href="https://www.lowmess.com/blog/on-declarative-styling/"&gt;on my personal blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>css</category>
      <category>react</category>
      <category>ui</category>
    </item>
    <item>
      <title>Request Timeouts With the Fetch API</title>
      <dc:creator>Alec Lomas</dc:creator>
      <pubDate>Sat, 30 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/lowmess/request-timeouts-with-the-fetch-api-3bc3</link>
      <guid>https://forem.com/lowmess/request-timeouts-with-the-fetch-api-3bc3</guid>
      <description>&lt;p&gt;I’m a big fan of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;Fetch API&lt;/a&gt;. I use it regularly in all sorts of projects, including my site and the API that powers the stats on the &lt;a href="https://www.lowmess.com/about/"&gt;about page&lt;/a&gt;. However it isn’t always as clear how to do things like error handling and request timeouts as it is in libraries like &lt;a href="https://github.com/axios/axios"&gt;Axios&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re not familiar with &lt;code&gt;fetch&lt;/code&gt;, it’s a native API that massively simplifies making AJAX requests compared to the older &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHTTPRequest"&gt;XHR&lt;/a&gt; method, and &lt;a href="https://caniuse.com/#feat=fetch"&gt;it’s supported in all modern browsers&lt;/a&gt;. When it initially landed, however, there was no easy way to handle request timeouts. You could fake it with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race"&gt;&lt;code&gt;Promise.race&lt;/code&gt;&lt;/a&gt; or by &lt;a href="https://github.com/github/fetch/issues/175#issuecomment-216791333"&gt;wrapping your &lt;code&gt;fetch&lt;/code&gt; in another Promise&lt;/a&gt;, but these solutions don’t actually cancel the request. This is where &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController"&gt;&lt;code&gt;AbortController&lt;/code&gt;&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AbortController&lt;/code&gt; is an API that, much like its name and my previous sentence suggests, allows us to abort (cancel) requests. Though &lt;a href="https://caniuse.com/#feat=abortcontroller"&gt;browser support isn’t &lt;em&gt;wonderful&lt;/em&gt; at time of writing&lt;/a&gt;, it can be used in most modern browsers and &lt;a href="https://github.com/mo/abortcontroller-polyfill"&gt;polyfills are available&lt;/a&gt;. The API itself has a very small surface area: a &lt;code&gt;signal&lt;/code&gt; property to attach to request objects, and an &lt;code&gt;abort&lt;/code&gt; method to actually cancel the request. Because the API is so simple, it’s very flexible — Jake Archibald has &lt;a href="https://developers.google.com/web/updates/2017/09/abortable-fetch"&gt;a fairly in-depth article on the Google Developers blog&lt;/a&gt; going over various cancellation scenarios, as well as the history behind the API, and I highly recommend giving it a read.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;AbortController&lt;/code&gt;, it becomes trivial to cancel a request if it doesn’t resolve before a given period of time: if the &lt;code&gt;abort&lt;/code&gt; method is called before the request resolves (or before the response &lt;code&gt;Body&lt;/code&gt; is consumed), the request is cancelled; if it’s called after, the browser just ignores the call. To put it all together, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an instance of &lt;code&gt;AbortController&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;setTimeout&lt;/code&gt; function that calls the controller’s &lt;code&gt;abort&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;Pass the controller’s &lt;code&gt;signal&lt;/code&gt; to &lt;code&gt;fetch&lt;/code&gt;’s options object&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;First, because we’re basically writing a shim around &lt;code&gt;fetch&lt;/code&gt;, I’m going to add an extra little perk. If the response doesn’t return in the &lt;code&gt;200&lt;/code&gt; range (that is, if &lt;code&gt;response.ok&lt;/code&gt; evaluates to &lt;code&gt;false&lt;/code&gt;), we’re going to throw an error. We absolutely do not need to do this — we could just catch our timeout and the function would work the same (we actually don’t even &lt;em&gt;need&lt;/em&gt; to do that). However, I always perform this check anyways, so this removes a lot of boilerplate code for me.&lt;/p&gt;

&lt;p&gt;Anyways, here is my generic &lt;code&gt;fetchWithTimeout&lt;/code&gt; function. It should work in any environment that supports &lt;code&gt;fetch&lt;/code&gt; and &lt;code&gt;AbortController&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchWithTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&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="c1"&gt;// Lets set up our `AbortController`, and create a request options object&lt;/span&gt;
  &lt;span class="c1"&gt;// that includes the controller's `signal` to pass to `fetch`.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Set a timeout limit for the request using `setTimeout`. If the body of this&lt;/span&gt;
  &lt;span class="c1"&gt;// timeout is reached before the request is completed, it will be cancelled.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&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;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Because _any_ response is considered a success to `fetch`,&lt;/span&gt;
      &lt;span class="c1"&gt;// we need to manually check that the response is in the 200 range.&lt;/span&gt;
      &lt;span class="c1"&gt;// This is typically how I handle that.&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&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="c1"&gt;// When we abort our `fetch`, the controller conveniently throws a named&lt;/span&gt;
      &lt;span class="c1"&gt;// error, allowing us to handle them separately from other errors.&lt;/span&gt;
      &lt;span class="k"&gt;if&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="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Response timed out&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using the function is fairly straightforward. Because we return &lt;code&gt;fetch&lt;/code&gt; directly, we can use it in much the same way; the only change should be the addition of a third parameter (our &lt;code&gt;time&lt;/code&gt; argument) and the extra error handling we discussed above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This example _always_ logs the error, because I'm telling httpstat.us to wait&lt;/span&gt;
&lt;span class="c1"&gt;// at least 1s before responding, but setting the timeout threshold to 500ms.&lt;/span&gt;
&lt;span class="c1"&gt;// Also, this could definitely be written in async/await if you preferred.&lt;/span&gt;
&lt;span class="nx"&gt;fetchWithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://httpstat.us/200?sleep=1000&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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="mi"&gt;500&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`This will never log out: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;json&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="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="nx"&gt;error&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;That’s it. That’s the whole post. Though the snippet is ultimately pretty simple (it’s 20 lines without whitespace and comments) writing this provided me with three major benefits: it forced me to abstract the function to the most reusable version I could, it gave me an opportunity to research &lt;code&gt;AbortController&lt;/code&gt; to make sure I knew exactly how it behaved, and it provided a place where I can come find this snippet in the future instead of rooting through old projects.&lt;/p&gt;

</description>
      <category>fetch</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Functional-First Accordions</title>
      <dc:creator>Alec Lomas</dc:creator>
      <pubDate>Sun, 30 Dec 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/lowmess/functional-first-accordions-4opn</link>
      <guid>https://forem.com/lowmess/functional-first-accordions-4opn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update (December 2019):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the year since I've wrote this, it's become increasingly clear that this is not a good method for building accordions. Beyond all the work it takes to make the thing functional (you know, the point of this post), &lt;a href="https://daverupert.com/2019/12/why-details-is-not-an-accordion/"&gt;Dave Rupert recently wrote a short post detailing the accessibility shortcomings of this approach&lt;/a&gt;. I share Dave's opinion that the web, as a platform, needs to bring some of these common controls to the table to ensure a consistent, accessible experience for all of its users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are few UI components encountered as frequently as the accordion. Even if you’re unfamiliar with the term, you’re familiar with the pattern: a string of text, sometimes accompanied by a button or icon, that reveals more content underneath when clicked. Thousands of developers and hundreds of UI frameworks have created accordions, often with their own rigid markup structures and (potentially bloated) JavaScript. But did you know that a perfectly functional accordion component ships with &lt;a href="https://caniuse.com/#feat=details"&gt;most modern browsers&lt;/a&gt;? Check this out:&lt;/p&gt;

&lt;p&gt;This is my cool accordion&lt;/p&gt;


&lt;p&gt;100% HTML, baby. #usetheplatform&lt;/p&gt;

&lt;p&gt;That’s all HTML! The code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;details&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;This is my cool accordion&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;100% HTML, baby. #usetheplatform&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The element that helps us achieve this marvel of modern engineering is called &lt;a href="http://developer.mozilla.org/en-US/docs/Web/HTML/Element/details"&gt;&lt;code&gt;details&lt;/code&gt;&lt;/a&gt; (along with &lt;code&gt;summary&lt;/code&gt; to set custom titles). It allows us to create content that is not visible until the element is clicked on. Sounds like an accordion to me!&lt;/p&gt;

&lt;p&gt;Of course, there are some downsides to the &lt;code&gt;details&lt;/code&gt; element. For starters, it isn’t exactly what I’d call attractive. There’s no way to animate content in and out, and no way to adjust the “twistie” (the triangle icon that indicates the component state). In addition, working in &lt;em&gt;most&lt;/em&gt; modern browsers means that it doesn’t work in &lt;em&gt;all&lt;/em&gt; modern browsers, not to mention legacy browsers. But, much like our UI framework-developing forebears, we can use some CSS &amp;amp; JavaScript to solve those issues, with the added benefit of an accordion that works when JavaScript (and even CSS!) is disabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Beauty Is in the Details
&lt;/h2&gt;

&lt;p&gt;The first and easiest changes we should make just involve making the element display consistently in all browsers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;details&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="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;summary&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="nb"&gt;list-item&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;Similarly easy, though a little more opinionated, is changing the cursor to highlight that the element is interactive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&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 let’s get into the fun stuff. First up is changing the default twistie to be something closer to convention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Hide the default twistie */&lt;/span&gt;
&lt;span class="c"&gt;/* Spec-compliant: */&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style-type&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="c"&gt;/* Non-standard: */&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-details-marker&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="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="c"&gt;/* Display a more common one */&lt;/span&gt;
&lt;span class="nt"&gt;summary&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;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&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="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;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;right&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;details&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&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;&lt;a href="https://codepen.io/lowmess/pen/zMjagE"&gt;Here’s the accordion&lt;/a&gt; as it looks at this point:&lt;/p&gt;

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

&lt;p&gt;Not bad for a few lines of CSS! There’s a few more changes I would make before calling this anything approaching good-looking, though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;details&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&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="m"&gt;#eee&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;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="nd"&gt;:first-of-type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&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="m"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&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="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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://codepen.io/lowmess/pen/eQrjBG"&gt;Now we’ve got a relatively attractive accordion component&lt;/a&gt;. We could stop now, and we would have a working component that, with a few tweaks, could be dropped onto almost any site and Just Work™️.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Enchanting Progressive Enhancement
&lt;/h2&gt;

&lt;p&gt;We don’t want to be outdone by those UI frameworks of yore, though. So we’ll need to do a little more work to animate the content in and out. The base markup does have to be changed to be a little less clean, and we’ll need some new CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;details&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;An updated details accordion&lt;span class="nt"&gt;&amp;lt;/summary&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;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;I'm some content!&lt;span class="nt"&gt;&amp;lt;/p&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;/details&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="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;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.4s&lt;/span&gt; &lt;span class="n"&gt;ease&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.is-closed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-height&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="nl"&gt;margin-top&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="nl"&gt;margin-bottom&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="nl"&gt;padding-top&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="nl"&gt;padding-bottom&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="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="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 may have noticed that we didn’t apply the &lt;code&gt;is-closed&lt;/code&gt; class to the content in the markup. Since we’re using JS to trigger that class, and it hides the content, we only want to apply the class if JS is allowed to run on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;details .content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-closed&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;There’s one last piece we need to set into place before we can trigger the animation. When the element is closed, it has an applied &lt;code&gt;max-height&lt;/code&gt; of &lt;code&gt;0&lt;/code&gt;. Since the &lt;code&gt;auto&lt;/code&gt; declaration is not animatable, we’ll need to apply a defined &lt;code&gt;max-height&lt;/code&gt; to the content when it’s open. For the smoothest possible animation, that &lt;code&gt;max-height&lt;/code&gt; should be the same size as the content height. We can write a simple function to calculate this height and store it as an attribute on the content element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getContentHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Force node to display properly&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Calculate height and store it&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-height&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Reset node to initial state&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-closed&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;Now that we’ve accounted for that wrinkle, we can handle interaction on the element. In a bit of premature optimization, we’ll &lt;a href="https://javascript.info/event-delegation"&gt;delegate the event&lt;/a&gt; to the document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&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;All we’ve told the browser at this point is “if the user has clicked a &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; element, don’t do anything”. The default behavior associated with clicking a &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; element is to set/remove the &lt;code&gt;open&lt;/code&gt; attribute on its parent &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element (the browser then knows to hide/show the content inside of &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; based on this attribute). The browser won’t wait for the content to animate out before hiding it, so we need to do that manually inside our handler. The code to do this is fairly straightforward, and boils down to toggling attributes and classes in a specific order at a specified time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside of our event handler&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accordion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;details&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accordion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Handle closing&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accordion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Animate content out&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;max-height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Wait for animation to finish, then remove the `open` attribute&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&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;accordion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&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="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Exit handler&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Handle opening&lt;/span&gt;
&lt;span class="c1"&gt;// Set the `open` attribute so the content will display&lt;/span&gt;
&lt;span class="nx"&gt;accordion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// If our content does not have a calculated height, calculate it&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;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getContentHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Wait a beat for the height to calculate, then animate content in&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&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;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-closed&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="mi"&gt;0&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://codepen.io/lowmess/pen/yQjRXy"&gt;Putting it all together&lt;/a&gt; gives us a fully functional, animated accordion component. And because we started with an HTML element that gives us our core functionality by default, a user doesn’t need to have JavaScript running to access the hidden content.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Extra Credit
&lt;/h2&gt;

&lt;p&gt;Although the accordion we’ve created is great, we’ve only covered the baseline functionality. There are a few ways we can change or improve the functionality further, including but not limited to:&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve Accessibility
&lt;/h3&gt;

&lt;p&gt;Because the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element is baked-in to the platform, it should be accessible by default. However, this only applies to browsers that actually support the element. If you have users from browsers that don’t support the element, considerations for accessibility should be taken (adding &lt;code&gt;tabindex&lt;/code&gt; and &lt;code&gt;aria-&lt;/code&gt; attributes, for example).&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle Window Resizing
&lt;/h3&gt;

&lt;p&gt;The vertical expansion animation we’re using to transition accordion content in and out of view is smooth and natural, but there is one issue: it isn’t responsive. Because we only calculate the height of the element once, if the element width changes to the degree that the content flows to a new line, the animation will break. An ideal solution would account for this, and there are a variety of ways we could do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Increase Interaction Target Size
&lt;/h3&gt;

&lt;p&gt;We set container padding on the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element to give the contents of the accordion room to breath. However, this means to open the accordion you have to click or tap on the &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; content. Setting the padding on the &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; and content container independently should make the accordion a little easier to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Animate the Twistie
&lt;/h3&gt;

&lt;p&gt;Animating the twistie to compliment the content animation would bring another level of polish to our accordion. Extra extra credit: make the animation happen on interaction (instead of relying on the &lt;code&gt;[open]&lt;/code&gt; attribute).&lt;/p&gt;

&lt;h3&gt;
  
  
  Force Content to Show In Non-Screen Environments
&lt;/h3&gt;

&lt;p&gt;Accordions can bring a lot to the table in screen-based environments, however they can also make content inaccessible in non-screen environments (such as when printing or using a screenreader). The content should be accessible in all environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Only Open One Item At a Time
&lt;/h3&gt;

&lt;p&gt;Just kidding. If &lt;a href="https://www.nngroup.com/articles/accordions-complex-content/"&gt;you need an accordion at all&lt;/a&gt;, you probably &lt;a href="https://www.smashingmagazine.com/2017/06/designing-perfect-accordion-checklist/#designing-interaction-for-the-accordion"&gt;should not do this&lt;/a&gt;. As it is an action not directly requested by the user, it can cause frustration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Package the Accordion Into a Reusable Component
&lt;/h3&gt;

&lt;p&gt;We should probably abstract away all of this markup to ensure consistency and improve DX. Frameworks like React and Vue provide a simple way to do this, but it should also be possible in most templating languages.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>ui</category>
      <category>css</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
