<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>James Steinbach</title>
  <subtitle>Front-end architect. Speaker &amp; writer. React &amp; Vue, Sass &amp; CSS, performance &amp; accessibility.</subtitle>
  <link href="https://jdsteinbach.com/feed.xml" rel="self"/>
  <link href="https://jdsteinbach.com"/>
  <updated>2020-10-27T00:00:00+00:00</updated>
  <id>https://jdsteinbach.com</id>
  <author>
    <name>James Steinbach</name>
    <email>jdsteinbach@gmail.com</email>
  </author>
  <entry>
    <title>An Introduction to CSS Logical Properties</title>
    <link href="https://jdsteinbach.com/css/logical-properties/"/>
    <updated>2020-10-27T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/logical-properties/</id>
    <content type="html"><p>For years, we’ve identified CSS locations with <em>physical</em> keywords: <code class="inline">top</code>, <code class="inline">right</code>, <code class="inline">bottom</code>, and <code class="inline">left</code>. These words are tied to the physical dimensions of the browser viewport itself. Any property containing the word <code class="inline">left</code> is connected to the left edge of the browser window. Many properties use this physical location syntax (<code class="inline">margin</code>, <code class="inline">padding</code>, <code class="inline">border</code>, and the <code class="inline">position</code>-related properties). </p>
<p>We’ve been writing CSS like this for so many years, it’s hard to imagine changing the way we think about identifying locations. However, there’s a new evolution in the way we identify locations and directions in CSS, and it allows us to create much more robust layouts with less code! Let’s take a look at <em>logical properties</em>.</p>
<h2>What Logical Properties Describe</h2>
<p>Logical properties respond automatically to text direction and writing mode. This means that locations formerly identified by <code class="inline">left</code> and <code class="inline">right</code> will automatically reverse for RTL layouts, and horizontal and vertical properties will automatically rotate for vertical writing mode.</p>
<p>If you’re not familiar with “LTR” (left-to-right) and “RTL” (right-to-left) or vertical writing mode, I recommend reading Jen Simmons’ <a href="https://24ways.org/2016/css-writing-modes/">post on writing modes</a> or watching her <a href="https://talks.jensimmons.com/A2frEN">writing modes video</a>.</p>
<h3>Block &amp; Inline</h3>
<p>The new keywords <code class="inline">block</code> and <code class="inline">inline</code> describe vertical and horizontal axes. To remember how these properties work, I like to picture a long paragraph of text.</p>
<p>The <code class="inline">block</code> axis runs from the start to the end of the <strong>block</strong> of text. For languages with horizontal text direction (including both LTR and RTL), that’s the axis that runs from the top to the bottom of the paragraph. For languages with vertical writing mode, the <code class="inline">block</code> axis still runs from the start to the end of each <strong>block</strong> of text. Because these languages present text in vertical columns, however, the <code class="inline">block</code> axis automatically adapts and runs horizontally.</p>
<p>The <code class="inline">inline</code> axis runs from the start to the end of a <strong>line</strong> of text <strong>in</strong> the paragraph. For LTR and RTL languages, that’s horizontal. Just like before, when you use <code class="inline">inline</code> properties in a vertical writing mode, the inline axis adapts and runs vertically.</p>
<p>Another way to describe these new axes is:</p>
<ul>
<li><code class="inline">block</code> = perpendicular to a line of text
</li>
<li><code class="inline">inline</code> = parallel to a line of text
</li>
</ul>
<h3>Start &amp; End</h3>
<p>Now that we can use <code class="inline">block</code> and <code class="inline">inline</code> to identify vertical and horizontal dimensions in a way that adapts to writing mode, we can add  <code class="inline">start</code> and <code class="inline">end</code> to identify which end of the axis we need to target.</p>
<h3>Comparison to Physical Properties</h3>
<p>Let’s get our bearings by looking at the old familiar physical properties</p>
<p><img src="/images/physical-properties.png" alt="Physical Properties"></p>
<p>Now, how do logical properties compare to current physical properties in LTR writing mode?</p>
<ul>
<li><code class="inline">block-start</code> matches <code class="inline">top</code>
</li>
<li><code class="inline">inline-start</code> matches <code class="inline">left</code>
</li>
<li><code class="inline">block-end</code> matches <code class="inline">bottom</code>
</li>
<li><code class="inline">inline-end</code> matches <code class="inline">right</code>
</li>
</ul>
<p><img src="/images/logical-properties-ltr.png" alt="Logical Properties in LTR Writing Mode"></p>
<p>Now, watch how these properties respond to a change in localization. If our site is translated into Arabic or Hebrew, the <code class="inline">dir="rtl"</code> attribute will cause this change in the <code class="inline">inline</code> locations:</p>
<ul>
<li><code class="inline">block-start</code> matches <code class="inline">top</code>
</li>
<li><code class="inline">inline-start</code> matches <code class="inline">right</code>
</li>
<li><code class="inline">block-end</code> matches <code class="inline">bottom</code>
</li>
<li><code class="inline">inline-end</code> matches <code class="inline">left</code>
</li>
</ul>
<p><img src="/images/logical-properties-rtl.png" alt="Logical Properties in RTL Writing Mode"></p>
<p>RTL is cool, but let’s check out the vertical text. Han-based languages (sometimes identified as CJK: Chinese, Japanese, Korean) can run vertically from right to left. If we were building a web app that would be localized for users reading these languages, <a href="https://24ways.org/2016/css-writing-modes/">we could support that writing mode by adding <code class="inline">writing-mode: vertical-rl</code></a> to the root element. Then our logical properties would adapt to match the rotated <code class="inline">block</code> and <code class="inline">inline</code> axes:</p>
<ul>
<li><code class="inline">block-start</code> matches <code class="inline">right</code>
</li>
<li><code class="inline">inline-start</code> matches <code class="inline">top</code>
</li>
<li><code class="inline">block-end</code> matches <code class="inline">left</code>
</li>
<li><code class="inline">inline-end</code> matches <code class="inline">bottom</code>
</li>
</ul>
<p><img src="/images/logical-properties-vertical-rl.png" alt="Logical Properties in Vertical RL Writing Mode"></p>
<p>All the physical properties I mentioned above can be replaced with logical properties: <code class="inline">margin</code>, <code class="inline">padding</code>, and <code class="inline">border</code> properties can be replaced with logical suffixes. Positioning properties like <code class="inline">top</code> are replaced by <code class="inline">inset-block-start</code>. You can even use <code class="inline">float</code> and <code class="inline">clear</code> with logical values: <code class="inline">inline-start</code> and <code class="inline">inline-end</code>.</p>
<h2>Using Logical Properties</h2>
<p>Let’s take a look at some examples of logical properties in real life!</p>
<h3>Available Properties &amp; Values</h3>
<p>Here are a few lists of logical properties and values:</p>
<h4>Properties &amp; Values that You Might Not Have Realized Are Logical</h4>
<p>In Flexbox and Grid, <code class="inline">justify-*</code> and <code class="inline">align-*</code> use logical values.</p>
<ul>
<li><code class="inline">start</code>
</li>
<li><code class="inline">end</code>
</li>
<li><code class="inline">flex-start</code>
</li>
<li><code class="inline">flex-end</code>
</li>
</ul>
<h4>Logical Properties with Solid Modern Browser Support</h4>
<ul>
<li><code class="inline">margin-block-start</code> and similar
</li>
<li><code class="inline">padding-inline-end</code> and similar
</li>
<li><code class="inline">border-block-end</code> and similar
</li>
<li><code class="inline">text-align</code> with <code class="inline">start</code> / <code class="inline">end</code> values
</li>
</ul>
<h4>Logical Properties &amp; Values with only Firefox Support</h4>
<ul>
<li><code class="inline">inset-block-start</code> and similar
</li>
<li><code class="inline">float</code> with logical values
</li>
<li><code class="inline">clear</code> with logical values
</li>
</ul>
<p>Breaking these properties down helps us decide which to use.</p>
<ul>
<li>Seeing Flexbox and Grid as logical property systems help us get our minds around the concepts.
</li>
<li>The properties that are widely supported are worth exploring now (if you don’t support IE11).
</li>
<li>The properties and values that are Firefox-only are good to know about, but not ready for production use, in my opinion.
</li>
</ul>
<h3>Text Alignment in a Grid</h3>
<p>To begin, here’s an example of logical properties at work in some code that may feel familiar to many front-end devs.</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;job&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;job__title&quot;</span>&gt;</span>Office Manager<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;job__location&quot;</span>&gt;</span>Remote / Chicago<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;job__department&quot;</span>&gt;</span>Employee Experience<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;job__hours&quot;</span>&gt;</span>Full-Time<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="hljs language-css"><span class="hljs-selector-class">.job</span> {
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-number">1</span>fr auto;
  <span class="hljs-attribute">column-gap</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: start;
}

<span class="hljs-selector-class">.job__location</span>,
<span class="hljs-selector-class">.job__hours</span> {
  <span class="hljs-attribute">justify-self</span>: end;
}
</code></pre>
<p>We’ve created a simple job posting card layout with CSS Grid. The column containing the title and department will take up as much room as is available, and the column with the location and hours will be only as wide as it needs to be. We’ll need to align both the location and hours to the end of their column.</p>
<p>Let’s find the logical properties here. First, in <code class="inline">justify-content</code> - the grid columns will all be shifted to the <code class="inline">start</code> of the inline axis. In LTR, they’ll be at the left; in RTL, they’ll be at the right. Second, we’re overriding that <code class="inline">start</code> value for the location and hours. We always want that text to be aligned to the end of the grid, so we justify it with <code class="inline">end</code>. In the past, you might’ve just put <code class="inline">text-align: right</code> on that element (and then needed more CSS to override that in RTL mode). However, using Grid with <code class="inline">justify-self: end</code>, we get the same visual effect <em>and</em> it automatically switches for non-LTR writing modes.</p>
<p>And yes, <code class="inline">text-align: end</code> would produce the same effect as <code class="inline">justify-self: end</code> in this context.</p>
<p><iframe src="https://codepen.io/jdsteinbach/pen/LYKxXKz?editors=1100#0" width="100%" height="500"></iframe></p>
<h3>Absolutely-Positioned Buttons</h3>
<p>I recently worked on a project with horizontally-scrollable rows. Each row had scroll-left / scroll-right buttons absolutely positioned at the left and right edges. The row used Grid layout, so as soon as we went RTL, it flipped automatically, but the buttons stayed put. Let’s use logical properties to solve that problem.</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;scroll-row&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;scroll-row__contents&quot;</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- lots of items --&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;scroll-row__button scroll-row__button--back&quot;</span>&gt;</span>Back<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;scroll-row__button scroll-row__button--forward&quot;</span>&gt;</span>Forward<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="hljs language-css"><span class="hljs-selector-class">.scroll-row</span> {
  <span class="hljs-attribute">position</span>: relative;
}

<span class="hljs-selector-class">.scroll-row__button</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-class">.scroll-row__button--back</span> {
  <span class="hljs-comment">/* left: -50px; */</span>
  <span class="hljs-attribute">inset-inline-start</span>: -<span class="hljs-number">50px</span>;
}

<span class="hljs-selector-class">.scroll-row__button--forward</span> {
  <span class="hljs-comment">/* right: -50px; */</span>
  <span class="hljs-attribute">inset-inline-end</span>: -<span class="hljs-number">50px</span>;
}
</code></pre>
<p>Using <code class="inline">left</code> and <code class="inline">right</code> would have caused RTL problems, but using <code class="inline">inset-inline-*</code> creates positioning that responds correctly to changes in writing mode.</p>
<p><iframe src="https://codepen.io/jdsteinbach/pen/XWLpyvN?editors=1100#0" width="100%" height="500"></iframe></p>
<h3>Floated Images</h3>
<p>You can use logical properties to make sure floated images are positioned correctly as well. With the existing physical properties, it was common to float images like this:</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">img</span><span class="hljs-selector-class">.left</span> {
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">float</span>: left;
  <span class="hljs-attribute">clear</span>: left;
}

<span class="hljs-selector-tag">img</span><span class="hljs-selector-class">.right</span> {
  <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">float</span>: right;
  <span class="hljs-attribute">clear</span>: right;
}
</code></pre>
<p>But now, with logical properties, we can make that more resilient:</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">img</span><span class="hljs-selector-class">.start</span> {
  <span class="hljs-attribute">margin-inline-end</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin-block-end</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">float</span>: inline-start;
  <span class="hljs-attribute">clear</span>: inline-start;
}

<span class="hljs-selector-tag">img</span><span class="hljs-selector-class">.end</span> {
  <span class="hljs-attribute">margin-inline-start</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">float</span>: inline-end;
  <span class="hljs-attribute">clear</span>: inline-end;
}
</code></pre>
<p><iframe src="https://codepen.io/jdsteinbach/pen/vYqgQor?editors=1100#0" width="100%" height="500"></iframe></p>
<h2>Conclusion</h2>
<h3>Browser Support</h3>
<p>Logical properties appear to have remarkably good browser support. According to caniuse, <a href="https://caniuse.com/#search=logical%20properties">logical property support</a> is as good as Grid support: all major desktop browsers, Mobile Safari, and modern Android browsers.</p>
<p><strong>However, that’s not the end of the story.</strong> I wish it were. It’d be great if logical properties worked as well as caniuse indicates!</p>
<p>First, a number of the properties I showed above are only supported by Firefox at the time of writing:</p>
<ul>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_inset-block-start"><code class="inline">inset-block-start</code></a>
</li>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_inset-block-end"><code class="inline">inset-block-end</code></a>
</li>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_inset-inline-start"><code class="inline">inset-inline-start</code></a>
</li>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_inset-inline-end"><code class="inline">inset-inline-end</code></a>
</li>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_clear_flow_relative_values"><code class="inline">clear: inline-*</code></a>
</li>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_float_flow_relative_values"><code class="inline">float: inline-*</code></a>
</li>
</ul>
<p>This means “non-supporting browsers” is a pretty large category. You still support some browsers that are at least partially non-supporting, even if you’ve dropped IE11. In this case, you could start writing logical properties now and use <a href="https://github.com/csstools/postcss-logical">a PostCSS plugin called postcss-logical-properties</a> to back-fill the old physical values. However, it’s worth noting that that plugin only supports LTR/RTL, not any vertical writing modes.</p>
<p>Without using that PostCSS plugin (which I haven’t tried out on any significant projects, so your mileage may vary), fallback CSS is a bit tricky. The fallback CSS requires an additional attribute selector (<code class="inline">[dir="rtl"]</code>) and that raises your specificity. That higher specificity now overrides your logical property code, even in modern browsers. In the code example below, the 2nd block overrides the 3rd even in supporting browsers, making the logical property effectively worthless. (<a href="https://css-tricks.com/logic-in-media-queries/#article-header-id-5"><code class="inline">@supports</code> and <code class="inline">@media</code> queries don’t increase specificity.</a>)</p>
<pre><code class="hljs language-css"><span class="hljs-selector-class">.element</span> {
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-selector-attr">[dir=<span class="hljs-string">&quot;rtl&quot;</span>]</span> <span class="hljs-selector-class">.element</span> {
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-keyword">@supports</span> (<span class="hljs-attribute">margin-inline-end</span>: <span class="hljs-number">0</span>) {
  <span class="hljs-selector-class">.element</span> {
    <span class="hljs-attribute">margin-inline-end</span>: <span class="hljs-number">2rem</span>;
  }
}
</code></pre>
<p>If all your supported browsers support <code class="inline">@supports</code>, you could do use both positive and negative <code class="inline">@supports</code> queries. <em>Note: IE11 doesn’t support <code class="inline">@supports</code> so this won’t work there.</em></p>
<pre><code class="hljs language-css"><span class="hljs-keyword">@supports</span> <span class="hljs-keyword">not</span> (<span class="hljs-attribute">margin-inline-end</span>: <span class="hljs-number">0</span>) {
  <span class="hljs-selector-class">.element</span> {
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">2rem</span>;
  }
  <span class="hljs-selector-attr">[dir=<span class="hljs-string">&quot;rtl&quot;</span>]</span> <span class="hljs-selector-class">.element</span> {
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">2rem</span>;
  }
}
<span class="hljs-keyword">@supports</span> (<span class="hljs-attribute">margin-inline-end</span>: <span class="hljs-number">0</span>) {
  <span class="hljs-selector-class">.element</span> {
    <span class="hljs-attribute">margin-inline-end</span>: <span class="hljs-number">2rem</span>;
  }
}
</code></pre>
<p>However, that’s a lot of code. In fact, you could delete almost half (6 of those 13 lines) and nothing would change. <span aria-label="sad cat emoji"><span aria-hidden="true">😿</span></span></p>
<p>Unlike CSS Grid and some other areas where <em>new CSS allows entirely new behavior</em>, there’s no visible difference for users if you use logical properties with fallbacks. There are extra lines of code, I’m afraid.</p>
<p>As a rule, I don’t subscribe to the idea that “we shouldn’t use new CSS if we still need to write fallback styles.” However, in this case, writing logical properties with physical property fallbacks has significant specificity complications (which aren’t an issue with Grid). Additionally, the presence of merely partial support in Chrome and Safari makes fallbacks entirely necessary. Here’s a very <a href="https://medium.com/@elad/why-css-logical-properties-arent-ready-for-use-c102925a5cba">detailed explanation of current shortcomings with logical properties</a>.</p>
<h3>My Recommendations</h3>
<p>At this point, my recommendation for implementing logical properties is cautious:</p>
<ul>
<li>Only use properties and values that work in all your supported browsers.
</li>
<li>If you need to include fallback CSS in the same stylesheet, just use the fallback. Save logical properties for later.
</li>
<li>If you need to support older browsers but you’re itching to use logical properties, use logical properties with a PostCSS plugin that converts them to physical properties.
</li>
<li>If your project uses code-splitting in a way that allows different CSS delivery methods that match browser support for logical properties, use logical properties in the CSS that’s delivered to supporting browsers and use physical properties in the CSS that’s sent to non-supporting browsers.
</li>
</ul>
<p>Even if your current projects and requirements don’t allow you to use logical properties in all the ways mentioned here, don’t lose heart! Support will continue to grow and browser support baselines will improve. Knowing this syntax will set up well to write better CSS in the near future.</p>
<h3>Resources</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties</a>/
</li>
<li><a href="https://www.smashingmagazine.com/2018/03/understanding-logical-properties-values">https://www.smashingmagazine.com/2018/03/understanding-logical-properties-values</a>/
</li>
<li><a href="https://css-tricks.com/css-logical-properties">https://css-tricks.com/css-logical-properties</a>/
</li>
</ul>
</content>
  </entry>
  <entry>
    <title>Accessible Loading Indicators – with No Extra Elements!</title>
    <link href="https://jdsteinbach.com/css/accessible-loading-indicators/"/>
    <updated>2020-03-02T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/accessible-loading-indicators/</id>
    <content type="html"><p>Read my article “<a href="https://dockyard.com/blog/2020/03/02/accessible-loading-indicatorswith-no-extra-elements" title="Accessible Loading Indicators – with No Extra Elements!" target="_blank">Accessible Loading Indicators – with No Extra Elements!</a>” at <a href="http://DockYard.com">DockYard.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>Safe Fallback for CSS Lazy Loading</title>
    <link href="https://jdsteinbach.com/css/async-css-noscript/"/>
    <updated>2019-09-12T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/async-css-noscript/</id>
    <content type="html"><p>Recently, Scott Jehl published a short post about <a href="https://www.filamentgroup.com/lab/load-css-simpler/">“The Simplest Way to Load CSS Asynchronously.”</a> In it, he recommends adding <code>media=&quot;print&quot;</code> to your <code>link</code> tag, then adding an inline JS attribute as well: <code>onload=&quot;this.media='all'&quot;</code>. Put together, here’s the recommended code:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;styles.css&quot;</span> <span class="hljs-attr">media</span>=<span class="hljs-string">&quot;print&quot;</span> <span class="hljs-attr">onload</span>=<span class="hljs-string">&quot;this.media=&#x27;all&#x27;&quot;</span> /&gt;</span>
</code></pre>
<p>A bunch of other publishers &amp; newsletters picked up on this too (CSS-Tricks, Smashing Magazine, CSS Weekly, and Calibre, to name a few). It’s a pretty handy trick! No extra JS libraries needed for this async load.</p>
<p>However, there’s a small resiliency issue. <strong>If JS is disabled, the CSS won’t load.</strong></p>
<p>I know, I know, the number of people who intentionally disable JS is very small. But counterpoint: the solution I’m recommending here is also super easy to implement! In most cases, it’d take less than 30 seconds to add. To make sure your CSS loads even with JS disabled, add another <code>link</code> to your stylesheet inside a <code>&lt;noscript&gt;</code> tag.</p>
<p>Here’s the improved code snippet:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;styles.css&quot;</span> <span class="hljs-attr">media</span>=<span class="hljs-string">&quot;print&quot;</span> <span class="hljs-attr">onload</span>=<span class="hljs-string">&quot;this.media=&#x27;all&#x27;&quot;</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">noscript</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;styles.css&quot;</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">noscript</span>&gt;</span>
</code></pre>
<p>There you go! Maybe no one will actually need that <code>noscript</code> copy, but it’s very easy to add and it makes sure that the inline JS solution Scott recommended is truly a progressive enhancement, no side effects.</p>
</content>
  </entry>
  <entry>
    <title>Just Details; No Devil</title>
    <link href="https://jdsteinbach.com/css/just-details-no-devil/"/>
    <updated>2019-05-03T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/just-details-no-devil/</id>
    <content type="html"><p>Read my article “<a href="https://dockyard.com/blog/2019/05/03/just-details-no-devil" title=" Just Details; No Devil" target="_blank"> Just Details; No Devil </a>” at <a href="http://DockYard.com">DockYard.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>CSS Selectors for the Entire Last Row of a Dynamic Grid</title>
    <link href="https://jdsteinbach.com/css/select-last-row-dynamic-grid/"/>
    <updated>2019-04-19T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/select-last-row-dynamic-grid/</id>
    <content type="html"><p>Read my article “<a href="https://dockyard.com/blog/2019/04/16/css-selectors-for-the-entire-last-row-of-a-dynamic-grid" title=" CSS Selectors for the Entire Last Row of a Dynamic Grid" target="_blank"> CSS Selectors for the Entire Last Row of a Dynamic Grid </a>” at <a href="http://DockYard.com">DockYard.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>Preventing Content Reflow From Lazy-Loaded Images</title>
    <link href="https://jdsteinbach.com/css/preventing-content-reflow-lazy-loaded-images/"/>
    <updated>2018-11-29T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/preventing-content-reflow-lazy-loaded-images/</id>
    <content type="html"><p>Read my article “<a href="https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/" title=" Preventing Content Reflow From Lazy-Loaded Images" target="_blank"> Preventing Content Reflow From Lazy-Loaded Images </a>” at <a href="http://CSS-Tricks.com">CSS-Tricks.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>“Work-Life Balance” at DockYard</title>
    <link href="https://jdsteinbach.com/misc/work-life-balance-dockyard/"/>
    <updated>2018-06-21T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/misc/work-life-balance-dockyard/</id>
    <content type="html"><p>Read my article “<a href="https://dockyard.com/blog/2018/06/21/work-life-balance-at-dockyard" title="Work-Life Balance at DockYard" target="_blank">‘Work-Life Balance’ at DockYard</a>” at <a href="http://DockYard.com">DockYard.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>Writing Your First PostCSS Plugin</title>
    <link href="https://jdsteinbach.com/css/postcss-plugin/"/>
    <updated>2018-02-01T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/postcss-plugin/</id>
    <content type="html"><p>Read my article “<a href="https://dockyard.com/blog/2018/02/01/writing-your-first-postcss-plugin" title="Writing Your First PostCSS Plugin" target="_blank">Writing Your First PostCSS Plugin</a>” at <a href="http://DockYard.com">DockYard.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>Animating Background Gradients to Make Your PWA More Native</title>
    <link href="https://jdsteinbach.com/css/animating-gradients/"/>
    <updated>2017-10-16T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/css/animating-gradients/</id>
    <content type="html"><p>Read my article “<a href="https://dockyard.com/blog/2017/10/17/animating-background-gradients-pwa" title="Animating Background Gradients to Make Your PWA More Native" target="_blank">Animating Background Gradients to Make Your PWA More Native</a>” at <a href="http://DockYard.com">DockYard.com</a>.</p>
</content>
  </entry>
  <entry>
    <title>Distributed Mentoring</title>
    <link href="https://jdsteinbach.com/misc/distributed-mentoring/"/>
    <updated>2017-06-24T00:00:00+00:00</updated>
    <id>https://jdsteinbach.com/misc/distributed-mentoring/</id>
    <content type="html"><p>When most people think about mentoring, they picture someone more experienced investing time in a close relationship with someone less experienced. Maybe you thought about someone who’d done that for you - a co-worker, parent, or teacher. In my web development career, I’ve had a different kind of mentoring experience than that standard one. Instead of having an experienced developer invest lots of time in me and my career, I’ve benefited from little bits of help from lots of people over the years. <strong>I like to call this “distributed” mentorship: remote learning from people around the world.</strong></p>
<h2 id="getting-started" tabindex="-1">Getting Started <a class="anchor-link" href="#getting-started">#</a></h2>
<p>About 10 years ago, I got started in web development as a hobby. I was in grad school (studying an entirely unrelated field) and managed to have “free time” one semester. My dad had built a few sites back in the 90s and I liked computers so I thought it would be fun to learn how to build a site. My roommate was studying computer science and I asked him how to build a site. He was working on software development, so he just laughed and showed me w3schools. (Yeah, I know NOW not to go there, but hey, I learned a few things from it back then!) I got more direction from a friend David (who was a sysadmin). He wasn’t an “expert” at front-end development, but he was more familiar with it. He gave me a piece of fantastic advice: “most websites are built with tables, but CSS is the new thing that cutting-edge sites are using, so start there and forget tables.”</p>
<p>I remember the new things back then: learning HTML, figuring out that PHP let me have variables and logic, installing IIS &amp; then Apache on my XP laptop, exploring CSS, being mystified that Chrome and Firefox had different default margins on text elements. Most of those ups &amp; downs were part of the standard learning curve for front-end devs back then. Through this time, I didn’t really have a “mentor” like we usually think of them. David and I had plenty more conversations - he ended up being my hosting service for years and I learned a lot from him. I remember sitting across the table from him at a bagel place while he drew DNS / hosting diagrams on a napkin. I still really enjoy digging into server admin stuff so I’m grateful for what I learned from him.</p>
<p>Within a year or so of getting experienced with front-end dev, I realized people would probably pay me money for this “hobby.” So I started telling friends what I could do and built a few small paid sites on the side.</p>
<h2 id="the-weird-lull" tabindex="-1">The Weird Lull <a class="anchor-link" href="#the-weird-lull">#</a></h2>
<p>After I graduated I got a job maintaining a website for a small non-profit (and running activities for their events every month and a host of other things: it was very much an “everyone helps with everything” kind of organization). I was given a privately developed CMS with no documentation and little to no positive help from the guy who’d built it. I learned a lot that year, but it was more visual design from the organization’s graphic design team - I was pretty isolated as the only “web person.” A year later, I moved on having learned very little: a bit of JavaScript and “never try to edit an undocumented code base.”</p>
<h2 id="a-real-job%2C-almost" tabindex="-1">A Real Job, Almost <a class="anchor-link" href="#a-real-job%2C-almost">#</a></h2>
<p>After that, I moved back to my college town for more grad school (nope, still not studying dev/tech <code>¯\_(ツ)_/¯</code>) and got a job as a graduate assistant running a ZenCart site. I’d been getting more familiar with PHP by using WordPress so that much of the code wasn’t too difficult. What was more challenging was the dozen or so custom MySQL queries I used to handle reporting every quarter. The site was a small MP3 store, so we reported sales to the original artists and paid their licensing fees to the license holders on any tracks they covered from another artist. The worst queries took up more than a page in Word (yep, that’s where everything was stored when I arrived). Since then, I’ve forgotten most of that, but that was also a great year for me - I sold my first few Wordpress sites to some clients and settled solidly on WP as a starting point for client work. There were some fun customizations: mp3 storage &amp; playback (before MediaElement.JS was part of WP) and PayPal integration (price &amp; SKU custom fields, no plugins), but nothing that I’d consider “advanced” today.</p>
<p>During this time, I didn’t have a “mentor” <em>per se</em>. I learned a lot from the developer who handed off that ZenCart installation to me - he was kind enough to stay close for support questions for a few months. By now, though, I was relying on a growing network of <strong>distributed mentors</strong>: devs I followed on Twitter and trustworthy publisher. I’d become an avid follower of <a href="https://css-tricks.com/">CSS-Tricks</a>, <a href="https://alistapart.com/">A List Apart</a>, and <a href="http://www.smashingmagazine.com/">Smashing Mag</a>. I didn’t personally meet people like Chris Coyier, Eric Meyer, Jeffrey Zeldman, Andrew Clarke, or Vitaly Freidman, but I read their posts &amp; books and “got mentored” by them from a distance.</p>
<p>During this time I started to realize that not every mentorship is an in-person relationship over a long period of time. There are times I’ve looked back and wondered, “who was my mentor during this phase?” and come up blank - until I stop to remember how much I owe to the developers who write and publish and share their knowledge across the web.</p>
<h2 id="a-real-job%2C-for-real" tabindex="-1">A Real Job, For Real <a class="anchor-link" href="#a-real-job%2C-for-real">#</a></h2>
<p>A few more years of freelance work went by while I finished up that degree. Then after graduating again, I got hired at my first <em>real</em> developer job. Not “a small part of my workload” development or “part-time for a college,” but real-live, full-time, development-only work.</p>
<p>By this time, Ethan Marcotte and his magical media queries had changed my CSS world, and shortly into this job, I swallowed my pride (“but my CSS is good enough already!”) and learned Sass. I got pretty proficient at jQuery and was picking up bits of the plain JS that lived beneath it. I was cranking out custom WordPress themes every month or so and loving it. I was the only developer there (it was a <em>small</em> agency), so there wasn’t much “in-person” mentoring. However that year I got to attend CSSDevConf (my first dev conference!) and came away amazed. I got to meet some of the devs who had been my “publishing mentors” to that point (hey, Chris &amp; Estelle!) and I met several more devs whose work I started following &amp; learning from immediately (lookin’ at you, Rachel, Micah, &amp; Winston). I discovered CSS animation that weekend and immediately started adding pizzazz to the sites I was working on.</p>
<p>Conference mentors are another useful kind of distributed mentor too. I didn’t know them at all before then, but Micah Godbolt and Winston Hearn took time to answer my questions about their talks. I got to sit by Chris Coyier, Tab Atkins, and Estelle Weyl one meal, and chat with Jonathan Snook over appetizers one evening. These brief interactions may not seem like very much on their own, but for me, they were really powerful. To realize that these experts were normal people who provide helpful advice and have friendly conversations with a newbie developer was tremendously encouraging.</p>
<h2 id="a-big-job" tabindex="-1">A Big Job <a class="anchor-link" href="#a-big-job">#</a></h2>
<p>I’ve talked to a lot of developers whose careers have started similarly: learn/freelance, take a real job, then learn so much in your first year that your next job is a massive jump up. That’s what happened. I got an offer to join a team of a dozen developers at a larger agency. This was my first experience working with a significant in-person team. It was exciting to see how our individual strengths and weaknesses quickly moved us into collaborative mentorships. I inherited several bloated CSS-framework-based sites and helped the team move forward into lighter, more customizable Sass workflows. I learned the basics of Angular and Ember from devs with more JS experience than I had.</p>
<h2 id="staying-home" tabindex="-1">Staying Home <a class="anchor-link" href="#staying-home">#</a></h2>
<p>A year later, I took a job with a remote company. The challenges of learning remote work were balanced by a lot of positives: the team had an established workflow, people were already communicating well in Slack &amp; the PM app, and the dev team was internal (so we worked with our own marketing and design departments, instead of reporting to external clients). The same collaborative pattern was at work there too: we had a dev who was our PHP expert, one who was the JS expert, one who knew the company history and business model well, and a CSS/Sass expert (me). We learned from each other well. Again, it didn’t feel like “traditional” mentorship. Depending on the topic and context, each of us played the role of mentor or mentee at one time or another. And again, since the whole team was remote, this was another form of distributed mentorship. It was a fantastic team to be part of for 2 years.</p>
<h2 id="flying-solo-(for-now)" tabindex="-1">Flying Solo (for now) <a class="anchor-link" href="#flying-solo-(for-now)">#</a></h2>
<p>This spring, I shifted positions again: it took a few months for things to settle, but I’m a contractor/consultant right now. I’ve been working as a team lead with a company that manages a flexible crew of contractors for web/software development and that’s been exciting. Having great onboarding/training conversations, constructive code reviews, and teaching moments with junior and intermediate developers has been enjoyable and fulfilling. I’m currently working with a friend to build a company that incorporates both top-notch web app development and training / apprenticeship opportunities for new devs.</p>
<h2 id="take-aways" tabindex="-1">Take-Aways <a class="anchor-link" href="#take-aways">#</a></h2>
<p>I’ve benefited a lot from “distributed mentoring.” Twitter people, online publishers, conference speakers &amp; friends, co-workers and peers - they’ve all played important roles in my career, even though it hasn’t looked like a stereotypical mentorship.</p>
<p>If you’re starting your journey as a developer, start building a good network of people whose tweets/posts/blogs/work you can follow online and learn from remotely. Also, it can be a tough investment to swing, but attending conferences is a huge help, especially in your first couple years of “full-time” development work.</p>
<p>If you’ve gotten enough experience to start specializing in a particular language, stack, or framework, use that expertise to grow collaboratively with others. Don’t get stuck behind fear that you’re “not expert enough yet” - if you know just a couple things that a friend or peer doesn’t, you can help them along. And of course, stay friends with people who know at least a little more than you about some topics so they can help you along.</p>
</content>
  </entry>
</feed>
