<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://keithjgrant.com/">
  <title>Keith J. Grant</title>
  <link href="https://keithjgrant.com/posts/index.xml" rel="self"/>
  <link href="https://keithjgrant.com/"/>
  <updated>2025-07-15T19:46:14Z</updated>
  <id>https://keithjgrant.com/</id>
  <author>
    <name>Keith J. Grant</name>
    <email>barkeep@keithjgrant.com</email>
    <uri>https://keithjgrant.com/</uri>
  </author>
  <icon>http://keithjgrant.com/favicon.ico</icon>
  <logo>http://keithjgrant.com/images/favicon-96.png</logo>
  <entry>
    <title>An AI Skeptic Uses AI for a Week</title>
    <link href="https://keithjgrant.com/posts/2025/07/an-ai-skeptic-uses-ai-for-a-week/"/>
    <published>2025-07-15T19:46:14Z</published>
    <updated>2025-07-15T19:46:14Z</updated>
    <id>https://keithjgrant.com/posts/2025/07/an-ai-skeptic-uses-ai-for-a-week/</id>
    <content xml:lang="en" type="html">&lt;p&gt;If you’re an AI skeptic, I get it.
I &lt;a href=&quot;https://keithjgrant.com/posts/2024/05/i-do-not-like-these-llms/&quot;&gt;was one too&lt;/a&gt;, and still am in some ways.
I don’t see any benefit to stuffing a chatbot into most of the apps and websites I use.
I don’t care about a summary of an email I received or a meeting I attended.&lt;/p&gt;
&lt;p&gt;In that regard, there’s stock-price-driven hype train that I’m ready to get past.
However, that’s not all there is to it.&lt;/p&gt;
&lt;h2 id=&quot;it-helped-me-make-bbq&quot; tabindex=&quot;-1&quot;&gt;It helped me make BBQ &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/07/an-ai-skeptic-uses-ai-for-a-week/#it-helped-me-make-bbq&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have made a point the last week or two to really dive in and give AI a fair shake,
and it has opened my eyes.
AI doesn’t belong everywhere, but it does have its place,
both for daily activities and to assist with software development.&lt;/p&gt;
&lt;p&gt;The first, simpler thing I did was use &lt;a href=&quot;https://gemini.google.com/app&quot;&gt;Google’s Gemini&lt;/a&gt;
to help make a purchasing decision.
I was looking for a wireless meat thermometer that I can use when smoking and grilling.
There are lot of options on the market, but most of them only work in conjunction
with a smartphone app.
I like those apps, but I wanted one that also has its own standalone base so I can
see check the temps at a glance in the kitchen.&lt;/p&gt;
&lt;p&gt;So I told Gemini in a few sentences what I was looking for, and how many probes I wanted.
It crunched for a while,
then came back with a list of models that met my criteria along with a summary of reviews.
This included two I was already aware of from earlier research
(The &lt;a href=&quot;https://www.typhur.com/products/sync-wireless-thermometer&quot;&gt;Typhur Sync&lt;/a&gt;
and &lt;a href=&quot;https://www.thermomaven.com/products/p2&quot;&gt;Thermomaven P2&lt;/a&gt;), plus a third
I had not heard of (The &lt;a href=&quot;https://inkbird.com/collections/wireless-thermometers/products/wifi-bluetooth-meat-thermometer-int-12-bw&quot;&gt;Inkbird INT-12-BW&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Gemini showed its whole “thought process”, including links it scanned,
reasons why it eliminated other models from the list, and a fairly reasonable
synopsis of what differentiated these three.
All three models met my criteria exactly.&lt;/p&gt;
&lt;p&gt;It was far, far faster than reading dozens of reviews and watching multiple
YouTube videos.
The synopsis wasn’t perfect — for one model, it listed a probe thickness in mm,
while the other two were “thin” and “very thin” — but it was enough of a start
for me to dig in and do further research if I wanted.
Of course I checked a few reviews myself afterwards to make sure everything checked
out with Gemini’s response, and it did.
(In the end I opted for the Thermomaven; the median priced of the three.)&lt;/p&gt;
&lt;p&gt;Would this be silly for simpler web searches? Of course.
But for something like this, where I know I would be clicking through dozens of links,
I think it really helped narrow my search.&lt;/p&gt;
&lt;h2 id=&quot;it-is-impressive-at-software-development&quot; tabindex=&quot;-1&quot;&gt;It is impressive at software development &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/07/an-ai-skeptic-uses-ai-for-a-week/#it-is-impressive-at-software-development&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Of course the real hot topic isn’t web search,
it’s what this means for the world of software development.
What I found here blew me away.&lt;/p&gt;
&lt;p&gt;I installed &lt;a href=&quot;https://www.warp.dev/&quot;&gt;Warp&lt;/a&gt; and used it as part of all my
development work for a week or two.
This let me choose from multiple agents, though I just left it on the default,
Claude 4 Sonnet.&lt;/p&gt;
&lt;p&gt;I have used Copilot in the past.
Critics often refer to LLMs as “glorified auto-complete”,
and that’s very much what Copilot felt like to me.
It was useful for rapidly spitting out large blocks of repetative code,
and it could write small, common utility functions for me.&lt;/p&gt;
&lt;p&gt;Claude is different.
It can index my entire codebase,
make sense of how the application is structured as a whole,
and determine where and how to make changes.
It shows me a diff of every change it wants to make before making it,
which I can accept, reject, or further refine with additional requests.&lt;/p&gt;
&lt;p&gt;At first, I gave it simple tasks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On the Organizations pages, change the text on all ‘Add users’ buttons to ‘Assign users’.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It did so without any trouble, and without me ever telling it what the
organization pages are or what React components render on them.
What immediately stood out to me is it found the pertinent files &lt;em&gt;far&lt;/em&gt; faster
than I would have.
Our codebase has nearly 3,000 files of TypeScript, and sometimes just tracking
down the right components for a change can take a little work if it’s in a
corner of the application I’m not familiar with.&lt;/p&gt;
&lt;p&gt;I had another simple change to make:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On the Organization Users page,
change the ‘Manage roles’ button icon from a gear to a pencil and update the tooltip
to say ‘Manage organization roles’.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It figured out it needed to import &lt;code&gt;PencilAltIcon&lt;/code&gt;
(matching a pattern we use elsewhere in the application,
notably not using the &lt;code&gt;PencilIcon&lt;/code&gt;)
and replaced the &lt;code&gt;CogIcon&lt;/code&gt; in the component.
It didn’t delete the old import of &lt;code&gt;CogIcon&lt;/code&gt;, but a follow-up request could
have done it (IIRC, I did it myself).
Then a bit more of a challenge: tests.
We’re in the process of migrating tests off of Cypress, so we now have a
hodgepodge of Cypress, Playwright, and Vitest tests in our repository.
This component was still tested with Cypress component tests, so I asked
Claude to create a vitest file, expecting it to write an empty scaffold
where I could place tests.
Instead, it wrote a half dozen tests for the component:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;render the user list with data&lt;/li&gt;
&lt;li&gt;show the empty state when no users are assigned&lt;/li&gt;
&lt;li&gt;display an error message when loading fails&lt;/li&gt;
&lt;li&gt;show the assign users button when the user has permissions&lt;/li&gt;
&lt;li&gt;disable the assign users button when the user lacks permissions&lt;/li&gt;
&lt;li&gt;show the empty state with a permissions error when the user lacks permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They were all good tests, though I told it to rephrase test names to start
with “should”.
Next I asked it to compare these tests with the existing Cypress tests and
determine which Cypress tests were no longer needed.
It correctly assessed them, and, after I prompted, deleted the obsolete
Cypress tests (leaving one other more complex test in place that I wasn’t ready to convert yet).&lt;/p&gt;
&lt;p&gt;Some of these tasks required a little refinement and/or follow-up requests
(“Move the mock data to files with names ending &lt;code&gt;.fixture.json&lt;/code&gt;”).
It’s not a matter of asking for magic and then mentally checking out,
but it is still impressive.
At this point, I was determined to push this thing as far as I could and see what happened.&lt;/p&gt;
&lt;h3 id=&quot;giving-it-a-more-challenging-task&quot; tabindex=&quot;-1&quot;&gt;Giving it a more challenging task &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/07/an-ai-skeptic-uses-ai-for-a-week/#giving-it-a-more-challenging-task&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;That “Assign users” button opens up a multi-step wizard.
The second step in that wizard is further sub-divided into two sub-steps,
but we’re in the process of consolidating some endpoints and they can be merged.&lt;/p&gt;
&lt;p&gt;So, in a new session and a new git branch, here’s the prompt I gave Claude:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The wizard to assign users to an organization has two screens for assigning roles:
one for Automation Execution (awx) and another for Automation Decisions (eda).
I need to consolidate these two screens into a single screen that uses
gatewayAPI urls instead of awxAPI and edaAPI endpoints.
Role definitions can be retreived from &lt;code&gt;/role_definitions/&lt;/code&gt;.
At the end of the wizard, these assignments need to be POSTed to &lt;code&gt;/organizations/:id/users/associate/&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It created the new consolidated wizard step and replaced the two sub-steps
with the new one.
It figured out how the wizard component worked, how this particular instance
of the wizard component pieced together its steps into a configuration array
containing multiple other components, and it determined how to work with that
without any more specific guidance.
This involves dozens of React components and almost as many hooks working in conjunction.
I was genuinely floored.&lt;/p&gt;
&lt;p&gt;I did notice it was repeating some work we already have elsewhere in the codebase,
so I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;there is a generic SelectRolesStep component. Can we use that in this implementation?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It did so.&lt;/p&gt;
&lt;p&gt;After each of these prompts, it showed its thinking,
usually a screen or so of text.
It listed each of the files it read at each stage to see how they work together
(sometimes a significant list).
Then in presented the proposed code changes.&lt;/p&gt;
&lt;p&gt;If something was not quite right, I had the option to ask for refinement
immediately, or to accept the change and make a follow-up request afterwards.
At the end of the process, it presented a full page summary of the changes we made,
including a five bullet point list of the “Benefits of the consolidation”.
That’s more than I need, really, but it gives a sense that this understands
what the changes are for at a higher level than my prompt provided.&lt;/p&gt;
&lt;p&gt;At one point, I noticed a proposed change included a repeated set of five or
six lines of code, syntactically incorrect.
I thought I would just accept the change and delete the duplication myself,
but I didn’t need to.
After applying the change, Claude immediately suggested a second edit,
fixing the problem.&lt;/p&gt;
&lt;p&gt;This particular piece of work took maybe seven or eight prompts,
including updates to tests.
A few of the test updates needed a little more hand-refinement afterwards,
but it took care of the bulk of the work.&lt;/p&gt;
&lt;p&gt;The whole process felt very much like pair programming with a junior developer,
but one who thinks and makes code changes incredibly quickly.
I had to point out a couple places where our codebase already provided utilities
so it wasn’t duplicating work.
In other cases, it found things that would have taken some digging on my part.
If something wasn’t quite right when I tested the UI,
I told it — sometimes it found the problem and fixed it; other times it struggled.
The place it seemed to have the hardest time was dealing with TypeScript errors
(relatable), and for those I ended up fixing the issues myself.&lt;/p&gt;
&lt;p&gt;I managed staging and committing all changes myself so I could see everything
it was doing to the codebase — though you can make Claude do this as well if
you’re confident enough in its abilities.&lt;/p&gt;
&lt;h2 id=&quot;it%E2%80%99s-a-tool&quot; tabindex=&quot;-1&quot;&gt;It’s a tool &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/07/an-ai-skeptic-uses-ai-for-a-week/#it%E2%80%99s-a-tool&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I saw this post recently on Mastodon:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m witnessing professionals spending hours to engineer prompts for Copilot to
do basic things like find a line in a file that has a string in it,
or figure out who committed most frequently to a sub-path in a repository,
or generate boiler plate code for classes… None of these things require #AI.
&lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;git log..&lt;/code&gt;, and editor snippets have existed for a long time.
They are quick. They are EXACT. They are FREE. They do not boil the oceans.
They do not displace the workforce. They are more efficient and productive.&lt;/p&gt;
&lt;p&gt;Learn the tools of your trade.
If all you’re doing is using AI, that means AI can and will replace you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— &lt;a href=&quot;https://hachyderm.io/@reyjrar/114825106144746426&quot;&gt;@reyjrar@hachyderm.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I understand what he’s getting at here, and it’s an absolutely fair point.
I don’t want to turn a blind eye to the resources used in making or using AI,
its impact on society, and all the ethical questions surrounding it.
But I think at the same time he misses a key fact:
AI is now one of the tools of the trade.&lt;/p&gt;
&lt;p&gt;Just like any tool, the trick is going to be learning when to use it and when
not to.
It’s certainly going to take some experience to get a feel for what it can
and cannot do, and when it is and is not more efficient than other approaches.&lt;/p&gt;
&lt;p&gt;At least one &lt;a href=&quot;https://bsky.app/profile/axsaucedo.bsky.social/post/3ltzf55yf7j2h&quot;&gt;early study&lt;/a&gt;
indicates AI doesn’t provide the productivity boost many think it does.
This is important to keep an eye on.
At the same time, I don’t think productivity is the only gain;
in a couple instances, it found bugs in my code that I had missed.&lt;/p&gt;
&lt;p&gt;With this Warp prompt in front of me, there is certainly a temptation to
disengage my thinking and make it do all the work.
For simple use-cases, I can see where that sort “vibe coding” can work,
but at this junction, I don’t see that being my approach.
I value clean, maintanable code too much to blindly accept all changes
AI produces.&lt;/p&gt;
&lt;p&gt;Just like working with a junior developer,
I need to keep my mind engaged to track what it’s doing.
If something looks funny, I’ll ask it to do it differently.
As I get more familiar with it, I’ll turn to it more when it makes sense to
and less because it’s a fun novelty.&lt;/p&gt;
&lt;p&gt;My gut says this is really efficient at finding what needs to change
and scaffolding in a rough approach,
but generally less efficient in the polishing stages of development.&lt;/p&gt;
&lt;p&gt;In all, I think I’ve come to find a more balanced approach is needed when it
comes to AI.
It’s no magic bullet, but neither is it a complete waste of time.
In coding, the answer has always been “it depends,” and that still hold true today.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Simplify Nested Code</title>
    <link href="https://keithjgrant.com/posts/2025/02/simplify-nested-code/"/>
    <published>2025-02-11T16:02:37Z</published>
    <updated>2025-02-11T16:02:37Z</updated>
    <id>https://keithjgrant.com/posts/2025/02/simplify-nested-code/</id>
    <content xml:lang="en" type="html">&lt;p&gt;One of the most common pieces of feedback I leave in code reviews,
at least in the realm of code readability, is to reduce nesting.
In general, the more deeply-nested code is,
the more complex it is to track mentally.
This is especially the case with &lt;code&gt;if&lt;/code&gt; statements.&lt;/p&gt;
&lt;p&gt;A have placed a code snippet below;
which you can imagine might appear as part of a larger function.
This code is doing something relatively simple:
setting a status message based on a few criteria.
Unfortunately, while the message itself is simple,
getting your head around all the different execution paths is not.
Understanding this code means you have to think about error state,
loading state, an &lt;code&gt;isActive&lt;/code&gt; flag, an &lt;code&gt;isDefault&lt;/code&gt; flag, and
whether other items in the array are active.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDefault &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inactive&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error loading item&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Loading...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;No item selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve all seen code similar to this.
We’ve probably all seen code even worse.&lt;/p&gt;
&lt;p&gt;Often, code like this evolves over time.
It may have been very simple initially, but as needs arose or edge cases emerged,
developers addressed them by adding new conditionals.&lt;/p&gt;
&lt;p&gt;I’m not going to say this code is wrong, per se,
because it does work and it’s not so obtuse as to be indecipherable.
In fact, this is a perfectly reasonable first draft when writing code.
But it has some weaknesses that could be improved.&lt;/p&gt;
&lt;h2 id=&quot;return-early&quot; tabindex=&quot;-1&quot;&gt;Return early &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/02/simplify-nested-code/#return-early&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All the complexity in this code lies in the conditional logic.
One of the conditionals is made more complicated by a combination of several
values plus an inline function.
The most difficult part of understanding
this code lies in following all the branching ways execution may flow.&lt;/p&gt;
&lt;p&gt;If you were to pick up this code to make edits,
you would have to carefully read through these conditionals
and try to keep all the possible paths in mind.
It’s a lot of little things you need to keep in your working memory
while you make changes.&lt;/p&gt;
&lt;p&gt;To minimize this mental load, it’s best to reduce the number of conditionals,
and to remove &lt;code&gt;else&lt;/code&gt; clauses entirely if possible.&lt;/p&gt;
&lt;p&gt;The best technique to do this is to &lt;em&gt;return early&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If the block of code is part of a longer function,
enclose it in a new function of its own.
Return out of that function as various cases are handled.
Doing this results in code more like the following:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getMessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDefault &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inactive&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error loading item&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Loading...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;No item selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, there are no &lt;code&gt;else&lt;/code&gt; statements required.
Each time a case is handled,
the message value is returned,
and that condition can be disregarded for the rest of the function.
The nesting has been simplified, making the code a lot more linear.&lt;/p&gt;
&lt;div class=&quot;alert&quot;&gt;
Note: I defined this function inline,
where the pertinent values are all in scope.
If this were part of a much longer function, however,
I would probably move the &lt;code&gt;getMessage()&lt;/code&gt; function definition below it,
passing the necessary values as parameters.
This would keep the flow of the larger function more straightforward,
and setting the &lt;code&gt;message&lt;/code&gt; variable would only require a one-line call
to the new function.
&lt;/div&gt;
&lt;h2 id=&quot;handle-edge-cases-first&quot; tabindex=&quot;-1&quot;&gt;Handle edge cases first &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/02/simplify-nested-code/#handle-edge-cases-first&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the beginning of the new function,
checks are made to see if we’re on the “happy path”,
and if so the function returns the expected value (&lt;code&gt;&#39;Active&#39;&lt;/code&gt; or &lt;code&gt;&#39;Inactive&#39;&lt;/code&gt;).
After that, all the edge cases are handled.&lt;/p&gt;
&lt;p&gt;This approach requires two nested conditionals at the beginning of the function.
Instead, it’s often simpler to reverse the order and handle the edge cases first.
The errors and loading states are a quick check in this case,
so make those checks as the very first thing in the function,
moving the core logic of the logic below them:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error loading resource&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Loading...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;No item selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDefault &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inactive&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when working on the meat of the function,
you can know that edge cases are dealt with and dismiss them from your mind.
This frees up your mental energy to focus only on the core logic
you’re concerned with.
This is especially useful when the logic is more complicated.&lt;/p&gt;
&lt;p&gt;This code is the exact same number of lines as the original,
but the logical flow is much simpler.&lt;/p&gt;
&lt;h2 id=&quot;simplify-boolean-logic&quot; tabindex=&quot;-1&quot;&gt;Simplify boolean logic &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2025/02/simplify-nested-code/#simplify-boolean-logic&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There’s one final way you can simplify this code further.&lt;/p&gt;
&lt;p&gt;Boolean logic is not intuitive.
The computer is excellent at it, but it does not come naturally to us.
Think about a simple example:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When does the &lt;code&gt;else&lt;/code&gt; case execute?&lt;/p&gt;
&lt;p&gt;It executes when the conditional is false,
or &lt;code&gt;if (!a || !b)&lt;/code&gt;.
Reversing the logic mentally means negating each value and changing
the &lt;em&gt;and&lt;/em&gt; to an &lt;em&gt;or&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It gets even more complicated with three or more values.
When does the statement &lt;code&gt;if (a &amp;amp;&amp;amp; b || !c)&lt;/code&gt; evaluate to false?
Can you figure it out instantly by looking at it,
or do you need to trace through each piece of it carefully to be sure?&lt;/p&gt;
&lt;p&gt;If this sort of logic is part of two or three nested conditionals,
and you find a new edge case in your code,
how confident will you be that you’re placing the new code in the right
part of the conditional?
How confident will you be adding a new variable &lt;code&gt;d&lt;/code&gt; to the conditionals
and understanding how it impacts the existing logic?&lt;/p&gt;
&lt;p&gt;This kind of change to code is extremely prone to bugs.&lt;/p&gt;
&lt;p&gt;When I encounter a complex conditional,
I always look to see if there’s a way to simplify it down and assign
it to a variable with an easy to understand name.&lt;/p&gt;
&lt;p&gt;In the code above,
this certainly means moving the inline function out of the &lt;code&gt;if&lt;/code&gt; statement:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; noActiveItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’d also consider setting the entire conditional to a single variable,
called &lt;code&gt;isActive&lt;/code&gt;.
This gets me a final version of the code that looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getMessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error loading resource&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Loading...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;No item selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; noActiveItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDefault &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; noActiveItems&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inactive&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when the next developer comes along,
this should be much faster to skim and understand.&lt;/p&gt;
&lt;p&gt;Edge cases are dealt with, and can be disregarded before the core logic
is considered.
The final &lt;code&gt;if (isActive)&lt;/code&gt; is easy to understand without even reading
the two lines above it.
If the developer needs to look closely at how &lt;code&gt;isActive&lt;/code&gt; is determined,
they can, but if that’s not a part of the logic they’re looking to change,
they can almost completely ignore the lines that assign values to
&lt;code&gt;noActiveItems&lt;/code&gt; and &lt;code&gt;isActive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This code flows linearly,
has no deep nesting,
and requires much less focused effort to parse.
Life is just a bit easier for developers maintaining it,
and any new changes will be less prone to bugs as a result.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Theme Machine 2.0</title>
    <link href="https://keithjgrant.com/posts/2024/12/theme-machine-20/"/>
    <published>2024-12-30T19:15:24Z</published>
    <updated>2024-12-30T19:15:24Z</updated>
    <id>https://keithjgrant.com/posts/2024/12/theme-machine-20/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I’ve just released a huge update to the &lt;a href=&quot;https://tools.keithjgrant.com/theme-machine/&quot;&gt;Theme Machine&lt;/a&gt;.
This is a tool a first put online &lt;a href=&quot;https://keithjgrant.com/posts/2024/06/theme-machine-a-css-color-palette-tool&quot;&gt;last summer&lt;/a&gt;
for building color palettes and exporting them as CSS custom properties.
If you haven’t played with it yet, go a head and give it a try.&lt;/p&gt;
&lt;p&gt;Note: I made the tool responsive, so you can play around with it on mobile and
see what it does, but it’s really designed to be used on desktop as part of
a web development workflow.&lt;/p&gt;
&lt;h2 id=&quot;new-changes&quot; tabindex=&quot;-1&quot;&gt;New changes &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#new-changes&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There’s a lot in this update!
Here are all the biggest changes:&lt;/p&gt;
&lt;h3 id=&quot;more-vivid-colors&quot; tabindex=&quot;-1&quot;&gt;More vivid colors &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#more-vivid-colors&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Originally, I thought it made sense to have your fully saturated accent colors,
as chosen in the color wheel,
and then slightly desaturated shades of that
to use for UI elements like backgrounds and borders.
However, the Theme Machine already generates one or two neutral colors
based on your chosen colors,
so this seems unnecessary.
If you want more desaturated colors, you can add them,
but by default now the generated shades have enough saturation/chroma to
match your exact selected colors.&lt;/p&gt;
&lt;h3 id=&quot;shade-generation-settings&quot; tabindex=&quot;-1&quot;&gt;Shade generation settings &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#shade-generation-settings&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2024/shade-generation-settings.png&quot; alt=&quot;Shade generation settings menu, including three options for lightness
calculation and two options for chroma calculation&quot; /&gt;&lt;/p&gt;
&lt;p&gt;With the chroma changes above, I had some decisions to make regarding how to
treat generation of the shades of each color.
Online polling led to split opinions, so I decided to expose the option to
the user and let you decide what you need.&lt;/p&gt;
&lt;p&gt;There are three options for how shade lightness is calculated:
“Uniform lightness” generates shades of hand-selected lightness values
(derived from &lt;a href=&quot;https://open-props.style/&quot;&gt;Open Props&lt;/a&gt; 2.0 beta).
It’s unlikely your selected colors precisely align with one of these lightness
values, however, so this option will slightly round your selected color to
the nearest shade.
With this option, each palette will have uniform lightness across all shades
(e.g. &lt;code&gt;--blue-4&lt;/code&gt; will have the exact same lightness as &lt;code&gt;--green-4&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I think these hand-selected values produce the best looking palette, so I’ve
left it as the default,
but if you have precise brand colors you want in your palettes,
the “prioritize selected colors” option will preserve them exactly.
The shades above and below your color are spaced evenly in the OKLCH color space.&lt;/p&gt;
&lt;p&gt;The final option, “center on selected colors” places your selected colors
at the center of the palette, and evenly distributes lighter and darker shades
in each direction.&lt;/p&gt;
&lt;p&gt;For the chroma options, the default value, “centered” places the most vivid
colors near the middle of each palette
(This is also based on Open Props approach).
If your selected colors are particularly light or dark, however, this can
produce overly saturated colors near the middle.
The second option, therefore, ensures your selected colors are the most saturated
ones in each palette, and the lighter and darker shades have less chroma.&lt;/p&gt;
&lt;h3 id=&quot;color-wheel-options&quot; tabindex=&quot;-1&quot;&gt;Color wheel options &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#color-wheel-options&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Color Wheel allows you to select harmonious colors of equal lightness
and chroma, using the &lt;a href=&quot;https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/&quot;&gt;OKLCH color space&lt;/a&gt;.
This can be problematic with some hues, especially yellows,
which get muddy and brown at lower lightness levels.&lt;/p&gt;
&lt;p&gt;I’ve added an option to “brighten muddy yellows” to help with this.
You can also fine tune your colors further in the Shade Generator after
selecting them in the Color Wheel.&lt;/p&gt;
&lt;p&gt;I’ve also changed the color wheel behavior, so the colors you find in the
wheel are not automatically all added to your palette.
You can add them all yourself, or add them individually if you wish.
Then, you can continue to use the wheel to find more colors and add those
to your palette before moving on to the Shade Generator page.&lt;/p&gt;
&lt;h3 id=&quot;semantic-color-variables&quot; tabindex=&quot;-1&quot;&gt;Semantic color variables &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#semantic-color-variables&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2024/tm-semantic-values.png&quot; alt=&quot;Color swatches of the semantic color variables&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are new &lt;a href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#semantic-properties&quot;&gt;semantic custom properties&lt;/a&gt;
you can edit to build out your general website theme.
There are three background colors, two text colors, and a primary brand color.&lt;/p&gt;
&lt;p&gt;Swatches of each are displayed near the bottom of the screen.
Click on one of these to edit it, by selecting from the shade palettes above.&lt;/p&gt;
&lt;h3 id=&quot;wcag-contrast-checking&quot; tabindex=&quot;-1&quot;&gt;WCAG contrast checking &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#wcag-contrast-checking&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2024/wcag-contrast-check.png&quot; alt=&quot;WCAG contrast levels for text, headings, and graphics&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Examples of both text colors on each of the the background colors appear on
the right side of the screen.
A green checkmark circle icon appears if the combination meets the AA standard
for text color. This is stacked in front of a second circle if AAA is met.
If the contrast is low, a red caution icon appears.&lt;/p&gt;
&lt;p&gt;Click on the icon to see the exact contrast measurement and which WCAG
criteria this value meets for text, headings, and graphical elements such as
icons and buttons.&lt;/p&gt;
&lt;h3 id=&quot;full-preview-page&quot; tabindex=&quot;-1&quot;&gt;Full preview page &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#full-preview-page&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve found that a palette in the Theme Machine doesn’t always feel the same
after exporting it and using it to build a full interface.
The Theme Machine has a dark background and colors of its own that can influence
how your theme colors appear.&lt;/p&gt;
&lt;p&gt;A new Preview page shows only your colors,
building the page entirely out of your selected semantic colors and
shades.
This should give a clearer idea of how your theme will appear in a real-world
website.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2024/theme-machine-preview.png&quot; alt=&quot;Preview page view, with a tooltip showing the named variables used
for a button background and text colors&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A number of mock UI widgets appear on the page in a range of your colors.
Hovering the mouse over this will display exactly which of your colors were
used to generate the element.&lt;/p&gt;
&lt;h3 id=&quot;public-issue-tracker&quot; tabindex=&quot;-1&quot;&gt;Public issue tracker &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/12/theme-machine-20/#public-issue-tracker&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve started a &lt;a href=&quot;https://github.com/keithjgrant/theme-machine/issues&quot;&gt;public issue tracker&lt;/a&gt;
on GitHub where you can leave feedback, report bugs, and request features.
Let me know what you want to see!&lt;/p&gt;
&lt;p&gt;I have a handful of further improvements I’d like to make, but until then,
&lt;a href=&quot;https://tools.keithjgrant.com/theme-machine/&quot;&gt;happy theming!!&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>CSS in Depth Addendum</title>
    <link href="https://keithjgrant.com/posts/2024/10/css-in-depth-addendum/"/>
    <published>2024-10-17T19:37:07Z</published>
    <updated>2024-10-17T19:37:07Z</updated>
    <id>https://keithjgrant.com/posts/2024/10/css-in-depth-addendum/</id>
    <content xml:lang="en" type="html">&lt;p&gt;As I wrote the both editions of
&lt;a href=&quot;https://www.manning.com/books/css-in-depth-second-edition?a_aid=kjg&amp;amp;a_bid=a7bc24da&amp;amp;chan=mm_website&quot;&gt;CSS in Depth&lt;/a&gt;,
I worked hard to stay on top of emerging features in CSS and where browsers
were at in regards to adding support.
The book writing process is lengthy, so this sometimes involved making my best
estimates about what features would have browser support in at least one major
browser by the time the book went to print.&lt;/p&gt;
&lt;p&gt;This approach enabled me to be one of the first books to print covering
significant CSS features like grid (in the first edition), &lt;code&gt;@scope&lt;/code&gt;, nesting,
and color blending (in the second), among many other smaller features.
Overall, I’m happy with the way I handled this, and I’m proud to have such
a cutting-edge book available now.&lt;/p&gt;
&lt;p&gt;However, there are a few smaller features I left out of the second edition
that I now wish I hadn’t.
I was aware of them while writing, but I simply underestimated how rapidly
browsers were going to implement them.
Some of these had not even been proposed when I started on the second edition,
and yet they already have at least some browser support, only 18 months later.&lt;/p&gt;
&lt;p&gt;If you’ve read, or plan to read, CSS in Depth, here are three brand new
features that didn’t make it into the book,
but I think are worth knowing (and the chapter numbers in the Second
Edition where I would put them).&lt;/p&gt;
&lt;h2 id=&quot;new-text-wrap-options&quot; tabindex=&quot;-1&quot;&gt;New text-wrap options &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/10/css-in-depth-addendum/#new-text-wrap-options&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;text-wrap&lt;/code&gt; property now supports two new values, &lt;code&gt;balance&lt;/code&gt; and &lt;code&gt;pretty&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Applying &lt;code&gt;text-wrap: balance&lt;/code&gt; to an element will adjust line wrapping of text
so each line is roughly the same length, rather than putting as much text
as possible on each line but the last.
This is most useful for headings or other short pieces of text.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;text-wrap: pretty&lt;/code&gt; will mostly behave like &lt;code&gt;text-wrap: wrap&lt;/code&gt;,
except it will prevent an “orphan” (a lone word on its own line) from the last
line of each paragraph.&lt;/p&gt;
&lt;p&gt;For a deeper look at these, check out
&lt;a href=&quot;https://blog.stephaniestimac.com/posts/2023/10/css-text-wrap/&quot;&gt;this article&lt;/a&gt;
from Stephanie Stimac.
Browser support is still pretty new, but progressive enhancement is easy.
You can pretty safely add this sort of code to your stylesheet,
and it will improve the typography in browsers that support it:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;h1, h2, h3, h4, h5, h6&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-wrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; balance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-wrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pretty&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Where it belongs: Chapter 12, Typography and Spacing&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;easier-dark-mode-with-light-dark()&quot; tabindex=&quot;-1&quot;&gt;Easier dark mode with light-dark() &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/10/css-in-depth-addendum/#easier-dark-mode-with-light-dark()&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We have a lot of power in CSS now to support both light and dark mode based
on user preferences.
I cover aspects of this in several places throughout my book,
but some of the techniques used are a little verbose.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;light-dark()&lt;/code&gt; function allows you to provide a color for both light mode
and dark mode in one single line:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.widget&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;light-dark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;#eee&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #222&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;light-dark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;#222&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #eee&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sets near-black text (&lt;code&gt;#222&lt;/code&gt;) on a light gray background (&lt;code&gt;#eee&lt;/code&gt;) in
light mode, but reverses the colors in dark mode.
The browser decides which of the two values to use based on the current
value of the &lt;code&gt;color-scheme&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://nerdy.dev/page-and-component-light-dark-strategies&quot;&gt;Adam Argyle’s article&lt;/a&gt;
for several examples and more details on this function.
This is in &lt;a href=&quot;https://caniuse.com/mdn-css_types_color_light-dark&quot;&gt;the latest version of all major browsers now&lt;/a&gt;,
but it’s probably a bit too soon to rely on it quite yet, at least without
some sort of fallback plan.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Where it belongs: Chapter 11, Color and Contrast&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;anchor-positioning&quot; tabindex=&quot;-1&quot;&gt;Anchor positioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/10/css-in-depth-addendum/#anchor-positioning&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Anchor positioning is a brand new method that allows you to place an
absolutely positioned element relative to another element.
Where older methods required placement to be relative to some ancestor
element, this allows much more flexible positioning.&lt;/p&gt;
&lt;p&gt;This feature is a bit more complicated than those above,
and require a deeper dive to fully understand.
Here is a &lt;a href=&quot;https://css-tricks.com/css-anchor-positioning-guide/&quot;&gt;CSS anchor positioning guide&lt;/a&gt;
I recommend.&lt;/p&gt;
&lt;p&gt;This feature emerged rapidly.
It was first proposed in May of 2023, and arrived in Chrome only a year later.
We’re still waiting on other browsers to &lt;a href=&quot;https://caniuse.com/css-anchor-positioning&quot;&gt;add support&lt;/a&gt;,
but this is a feature worth keeping an eye on.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Where it belongs: Chapter 6, Positioning and Stacking Contexts&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Structured Approach to Custom Properties</title>
    <link href="https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/"/>
    <published>2024-06-28T19:40:06Z</published>
    <updated>2024-06-28T19:40:06Z</updated>
    <id>https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Yesterday, I &lt;a href=&quot;https://keithjgrant.com/posts/2024/06/theme-machine-a-css-color-palette-tool/&quot;&gt;announced the release&lt;/a&gt;
of the Theme Machine, a tool for generating custom palettes as CSS custom properties.
I hope this tool proves useful for many developers,
but it’s really only the first step in managing color in CSS.
I think it’s worth stepping back and looking at the bigger picture of
custom property management in general.&lt;/p&gt;
&lt;p&gt;If you’re anything like me, you’ve probably tried a handful of different
approaches to organizing your custom properties.
Most approaches I’ve used have been adequate,
but I rarely feel completely satisfied with them.
I spent far too long flailing about trying to find what worked.
I should have looked sooner to the realm of design tokens.
I think some common standards when building a design system are useful
when applied directly to CSS custom properties.&lt;/p&gt;
&lt;p&gt;I’d like to lay out my current approach.
In many ways, these thoughts are still a work in progress,
but I feel good enough about them to teach them,
and at the very least invite feedback.
You’ll notice my thoughts here start concrete,
but things will start to get a little fuzzier toward the end of this post.&lt;/p&gt;
&lt;h2 id=&quot;three-tiers&quot; tabindex=&quot;-1&quot;&gt;Three tiers &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#three-tiers&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every custom property you define should fall in one of three categories,
and you should always be aware when defining or using a custom property
which category it belongs to.
I’m going to refer to these three types of property as
&lt;em&gt;primitive&lt;/em&gt;, &lt;em&gt;semantic&lt;/em&gt;, and &lt;em&gt;module&lt;/em&gt; properties.
If this sounds familiar from the world of design tokens,
that’s because it’s nearly the exact same concept.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Primitive&lt;/strong&gt; properties hold raw, generic values.
A design begins with initial decisions about what colors and fonts and
other elements “fit” the design.
These values should be stored in primitive properties.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic&lt;/strong&gt; properties supply context and meaning.
The names of these properties imply where they should be used throughout
the stylesheet. These are often built out of primitive properties,
but supply additional direction as to their use.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Module&lt;/strong&gt; properties are used only within the confines of a particular
module or component on the page. If a dropdown menu has its own drop shadow
in a custom property, for instance,
it may be defined within the scope of that module rather than globally
on the page as a whole.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ll explain each of these in more depth and provide some practical examples
to show what they typically look like.&lt;/p&gt;
&lt;h2 id=&quot;primitive-properties&quot; tabindex=&quot;-1&quot;&gt;Primitive properties &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#primitive-properties&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Primitive custom properties are &lt;em&gt;global&lt;/em&gt; and &lt;em&gt;static&lt;/em&gt;.
Chief among these are your color palettes.
They will typically also include font stacks.
There may be a place for a few other items in this tier,
but most things beyond these will typically belong in other tiers.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;98% 0.008 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;93% 0.028 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;80% 0.045 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;66% 0.056 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;58% 0.059 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;49% 0.053 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;35% 0.042 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;20% 0.025 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--blue-9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;10% 0.014 250deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* repeat this pattern as needed for gray, green, etc. */&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;--font-serif&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Georgia Pro&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Georgia&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--font-sans-serif&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Source Sans 3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--font-monospace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Fira Mono&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Consolas&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; monospace&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s the key rule for this first tier:
you should not be using them throughout your stylesheet.
You should instead be using them to build custom properties on the next tier,
the semantic properties.
Let’s look at the other types of properties,
then I’ll come back to this rule and why I try to hold myself to it.&lt;/p&gt;
&lt;h2 id=&quot;semantic-properties&quot; tabindex=&quot;-1&quot;&gt;Semantic properties &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#semantic-properties&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Semantic properties are the real building blocks of your design.
While the primitive properties define all the values you’ll need
within the scope of your design,
each semantic property maps one of those primitive values to a practical intent.&lt;/p&gt;
&lt;p&gt;In this tier, you can define a set of background colors, text colors,
common margins and border radius sizes.
Many of these properties will be derived from primitive properties,
looking something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--text-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--text-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;--font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--font-serif&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--font-body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--font-sans-serif&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--font-code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--font-monospace&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some properties on this tier might be defined with explicit values.
You could potentially define primitives for common lengths, like
&lt;code&gt;--length-sm: 2px&lt;/code&gt; and &lt;code&gt;--length-md: 4px&lt;/code&gt; then use these to define
semantic border radius properties,
but in many cases this is likely overkill, and it’s more practical to
simply apply these values directly to the semantic properties like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--radius-sm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--radius-md&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--radius-lg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 8px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other useful semantic properties include anything and everything you want
to keep consistent throughout your design.
This often includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;font sizes&lt;/li&gt;
&lt;li&gt;font weights (especially if you have a variable font and want to specify
uncommon weights like 300 or 900)&lt;/li&gt;
&lt;li&gt;line/border widths&lt;/li&gt;
&lt;li&gt;spacer sizes for padding and margin&lt;/li&gt;
&lt;li&gt;border radius&lt;/li&gt;
&lt;li&gt;drop shadows&lt;/li&gt;
&lt;li&gt;easing functions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Theoretically, a robust set of semantic property names could be reused
over and over again across projects with few modifications.
Only the values assigned to those properties would change across
different designs.&lt;/p&gt;
&lt;h3 id=&quot;the-potential-to-be-dynamic&quot; tabindex=&quot;-1&quot;&gt;The potential to be dynamic &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#the-potential-to-be-dynamic&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Like primitive properties, semantic properties are global to the page.
However, some of them might be dynamic.
This is especially common if you want to allow the user to switch
themes or you want to support both light and dark modes.
The primitive &lt;code&gt;--gray-2&lt;/code&gt; always means the same gray,
but it’s only a background color in light mode.
In dark mode, it might be a text color.
You can make this distinction when you define your semantic properties.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--text-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--text-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--background-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--background-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--background-3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-7&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--text-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--text-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this is even simpler with the &lt;code&gt;light-dark()&lt;/code&gt;,
though browser support for this function is still pretty fresh:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;light-dark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--background-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;light-dark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--gray-8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* etc */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;alert&quot;&gt;
&lt;p&gt;If you use &lt;a href=&quot;https://lightningcss.dev/&quot;&gt;Lightning CSS&lt;/a&gt;,
you can use &lt;code&gt;light-dark()&lt;/code&gt; now, as they found
&lt;a href=&quot;https://lightningcss.dev/transpilation.html#light-dark()-color-function&quot;&gt;a creative way&lt;/a&gt;
to transpile it to more broadly supported syntax —
though be sure to test this well if you allow the user to dynamically
switch themes.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;reduced-cognitive-load&quot; tabindex=&quot;-1&quot;&gt;Reduced cognitive load &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#reduced-cognitive-load&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As well as enabling theming support,
semantic properties streamline the mental task of everyday coding.
You might have eight or ten or 16 different grays and just as many blues,
but in your semantic properties,
you only have a small handful of background colors.&lt;/p&gt;
&lt;p&gt;When you need to set the background color of a module,
and you use primitives directly, you’ll find yourself thinking,
“Did I use &lt;code&gt;--gray-4&lt;/code&gt; or &lt;code&gt;--gray-3&lt;/code&gt; for the background of that
other similar module?”
But with a much smaller subset of semantic background properties to choose from,
you’re more likely to recall that &lt;code&gt;--background-2&lt;/code&gt; is the one you’ve
been reaching for repeatedly.&lt;/p&gt;
&lt;p&gt;When you rely on semantic properties instead of your primitives,
you don’t have to keep making the same decisions over and over,
and you don’t have to continually refer back to other code to remember
the decisions you’ve already made.&lt;/p&gt;
&lt;h2 id=&quot;module-properties&quot; tabindex=&quot;-1&quot;&gt;Module properties &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#module-properties&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last tier is for properties that are not global,
but are instead scoped locally to a single module/component on the page.&lt;/p&gt;
&lt;p&gt;If you need to calculate multiple properties based on a single value,
or you need to change the size of part of a component responsively,
this is a way to do it.&lt;/p&gt;
&lt;p&gt;For example, in the current design on this site,
I use this sort of property to manage the circle clip path
of the banner image at the top of every post.
It looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.banner-image&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;35vw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 750px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--offset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10vw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--radius&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; at &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--radius&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--circle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;shape-outside&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--circle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--radius&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;shape-margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These variables are all defined and used locally only within this particular
module.
Because they’re defined right here, I don’t really need to worry about
variable name collision from other modules higher in the DOM.
(Though you do need to avoid overriding semantic properties that might
be referenced by any nested child modules.)&lt;/p&gt;
&lt;p&gt;This also makes aspects of responsive design much simpler.
Use a media query or container query to adjust a module custom property:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 720px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.logo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 70px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just so much less cognitive load than re-defining a bunch of
properties directly.
Responsive design often involves a lot of looking up and down the
stylesheet to see which properties are specified at which breakpoints
and trying to keep mental track of which override in various circumstances.
It’s not always practical to relegate this logic purely to a custom property,
but it’s absolutely worth doing when you can.&lt;/p&gt;
&lt;h2 id=&quot;trying-not-to-use-primitive-properties-directly&quot; tabindex=&quot;-1&quot;&gt;Trying not to use primitive properties directly &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#trying-not-to-use-primitive-properties-directly&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I mentioned early the general rule that I avoid assigning primitive
properties directly in my styles.
Rather, I use them only to build out other custom properties.&lt;/p&gt;
&lt;p&gt;It’s often very tempting to break this rule.
Maybe you’ll really want a component on the page to have
a border that’s colored &lt;code&gt;--blue-6&lt;/code&gt;.
If I’m totally honest, I break it sometimes, too,
but it always gives me pause.
It will often make some trouble down the road, and I weigh whether I
want to deal with that.
I ask myself a few questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is this really a one-off use of this value?&lt;/li&gt;
&lt;li&gt;Why don’t any of my existing semantic properties meet this need?&lt;/li&gt;
&lt;li&gt;Is there a general semantic meaning I’m reaching for, and should I codify
it in a semantic property if one doesn’t already exist?&lt;/li&gt;
&lt;li&gt;Will this complicate any theme switching on my site?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ideally, there will be a semantic meaning I can create instead.
I’ll add a new semantic variable to the root of the page and
rely on that.&lt;/p&gt;
&lt;p&gt;At the same time, I’m also cautious about populating the page with hundreds
of semantic properties that are only ever used once in the stylesheet.&lt;/p&gt;
&lt;p&gt;My hope is that I will, over time, develop a robust system of common
semantic properties that will greatly reduce the need to ever reach
for these one-offs.
I have further thoughts on this already, and hopefully I’ll be able
to gather them into another post in the future.&lt;/p&gt;
&lt;h2 id=&quot;thoughts-on-naming-conventions&quot; tabindex=&quot;-1&quot;&gt;Thoughts on naming conventions &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/a-structured-approach-to-custom-properties/#thoughts-on-naming-conventions&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ideally, I’d like to propose a clean naming convention that always
makes clear which property is of which type,
but I’ve never really seen or come up with something I totally love.&lt;/p&gt;
&lt;p&gt;It’s unfortunate that custom property names are severely limited.
You can use alphanumerics, hyphens, and underscores.
The only other characters available &lt;em&gt;non-ASCII&lt;/em&gt; unicode.
As fun as the property &lt;code&gt;--🎩-color&lt;/code&gt; is,
it’s totally impractical for real use.
Characters that are easy to type like dollar signs, asterisks, periods,
and parenthesis only work if you escape them with a backslash,
and I just find that ugly.&lt;/p&gt;
&lt;p&gt;Module properties might be the easiest to identify.
I think a leading underscore is a reasonable convention,
akin to private variables in many programming languages.
You can see at a glance that &lt;code&gt;color: var(--_color);&lt;/code&gt; references a
local, module-specific property.
I’ve used this convention occasionally, but not always consistently.&lt;/p&gt;
&lt;p&gt;As far as differentiating primitive vs. semantic properties,
nearly every idea leads down the realm of BEM-like syntax,
with a clutter of double hyphens or a mix of hyphens and underscores,
but I don’t think anyone was ever really excited about that in the past
and I would really love to avoid going down that sort of path again.&lt;/p&gt;
&lt;p&gt;One thing that might be practical is the use of uppercase characters.
Perhaps an all-lowercase &lt;code&gt;--size-sm&lt;/code&gt; could indicate a primitive property,
while a capitalized &lt;code&gt;--Spacer-md&lt;/code&gt; or even &lt;code&gt;--SpacerMd&lt;/code&gt; indicates
a semantic one.
Or maybe every primitive could be prefixed with &lt;code&gt;p-&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I don’t know. I’m not sure I love camel-casing my custom properties,
or the distinction needs to be so explicitly described in the syntax.
I guess I have a thing for code that doesn’t look ugly to my eyes.&lt;/p&gt;
&lt;p&gt;I’d love to hear your thoughts on all this.&lt;/p&gt;
&lt;p&gt;Hopefully it’s helpful to you to draw these distinctions between
different types of custom property.
When I survey existing pattern libraries, I see a lot of this kind of pattern,
but I haven’t seen it really being explicitly discussed anywhere publicly.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Theme Machine: A CSS Color Palette Tool</title>
    <link href="https://keithjgrant.com/posts/2024/06/theme-machine-a-css-color-palette-tool/"/>
    <published>2024-06-27T17:27:49Z</published>
    <updated>2024-06-27T17:27:49Z</updated>
    <id>https://keithjgrant.com/posts/2024/06/theme-machine-a-css-color-palette-tool/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Sometimes, I just want to start building a project.&lt;/p&gt;
&lt;p&gt;I’ll create a new stylesheet, define my base styles and a handful of custom
properties, and start coding.
Before I know it, I realize I need a couple more colors.
I’ll either just define an ad-hoc color right there in the stylesheet, or,
if I’m being disciplined, I’ll define a new custom property for it and use
it accordingly.&lt;/p&gt;
&lt;p&gt;But I always find I need more colors.
I need a whole spectrum to choose from.
I know I can, and maybe should, start by dropping in a bunch of colors
from &lt;a href=&quot;https://yeun.github.io/open-color/&quot;&gt;Open Color&lt;/a&gt; or
&lt;a href=&quot;https://open-props.style/&quot;&gt;Open Props&lt;/a&gt;, but what I really want are &lt;em&gt;my&lt;/em&gt; colors.
I want to choose my own tint of blue, or green, or red.&lt;/p&gt;
&lt;p&gt;So I finally set aside the project that needed those colors,
and built a tool to help with that.
Don’t ask me where this lands on that old
&lt;a href=&quot;https://xkcd.com/1205/&quot;&gt;XKCD efficiency chart&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ve called this the &lt;a href=&quot;https://tools.keithjgrant.com/theme-machine/&quot;&gt;Theme Machine&lt;/a&gt;.
It’s really two tools in one.&lt;/p&gt;
&lt;h2 id=&quot;color-picker&quot; tabindex=&quot;-1&quot;&gt;Color picker &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/theme-machine-a-css-color-palette-tool/#color-picker&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First is the &lt;a href=&quot;https://tools.keithjgrant.com/theme-machine/wheel/&quot;&gt;Color Picker&lt;/a&gt;.
This is a color wheel, with draggable handles for selecting colors.
It can automate choosing complimentary colors, or analagous colors adjacent
to one another, or two sets of complimentary colors.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2024/theme-machine/tm-wheel.png&quot; alt=&quot;A color wheel with small circles indicating four selected colors&quot; width=&quot;450&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you already have a primary brand color, you can paste that in the input
field in any valid CSS format.
Or, just have fun playing with the wheel until you find a color that speaks to you.
Toggle the mode between complimentary, analagous, split complimentary, or even
freestyle color schemes to find other colors that go with your selection.&lt;/p&gt;
&lt;p&gt;Adjust the lightness or vividness (chroma) sliders, and all the colors will
adjust together to remain harmonious.
You’re basically working with
&lt;a href=&quot;https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/&quot;&gt;OKLCH color&lt;/a&gt; directly.&lt;/p&gt;
&lt;p&gt;It will warn you if your colors are out gamut for most displays.
Fix this if you can, but don’t sweat it too much unless several of
your colors show the warning;
you’ll be able to fine tune each color further in the next step.&lt;/p&gt;
&lt;h2 id=&quot;palette-generator&quot; tabindex=&quot;-1&quot;&gt;Palette generator &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/theme-machine-a-css-color-palette-tool/#palette-generator&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you like your colors, click the button at the bottom to export them
to the second tool, the
&lt;a href=&quot;https://tools.keithjgrant.com/theme-machine/palette/&quot;&gt;Palette Generator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2024/theme-machine/tm-palettes.png&quot; alt=&quot;A color wheel with small circles indicating four selected colors&quot; width=&quot;650&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For each of the colors you selected,
the Theme Machine will generate an entire spectrum of shades from white to black.
It will also provide an “accent” color that’s the fully vivid version of the
color you chose.
It will make an educated guess at good names for those colors which will be
used to produce the custom properties.&lt;/p&gt;
&lt;p&gt;Depending on the colors you chose, it will also generate one or two neutral
tones and a spectrum of shades for those as well.&lt;/p&gt;
&lt;p&gt;Each palette can be renamed or otherwise adjusted to your liking.
You can also add additional colors to your palette.&lt;/p&gt;
&lt;p&gt;The Theme Machine creates CSS custom properties for all of your palettes.
When you’re happy with your selections,
copy the CSS on the right and drop it into your project.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tools.keithjgrant.com/theme-machine/&quot;&gt;Give it a go&lt;/a&gt;!
Let me know if you have any problems, questions, or feature requests.
Happy styling!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Assessing Web Component Libraries</title>
    <link href="https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/"/>
    <published>2024-06-11T15:30:22Z</published>
    <updated>2024-06-11T15:30:22Z</updated>
    <id>https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I’ve started on a project recently and realized it’s a perfect use case
for web components.
It’s an &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; site that’s primarily markdown,
so dropping custom elements into the markdown is just too easy.
It requires basically no new plumbing beyond the bare bones esbuild setup I’ve
already got in place.&lt;/p&gt;
&lt;p&gt;This is my first time in web components doing anything more involved than a
quick dabble,
so I ended up spending the last week exploring some of the various
web-component-based framework option out there,
and boy do I have &lt;em&gt;opinions&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;general-thoughts-on-wc-and-frameworks&quot; tabindex=&quot;-1&quot;&gt;General thoughts on WC and frameworks &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/#general-thoughts-on-wc-and-frameworks&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before I dive into the various frameworks I assessed,
I want to share some general thoughts on web components that color the
impressions I had.&lt;/p&gt;
&lt;p&gt;First of all, I don’t want Shadow DOM except in a few very specific circumstances.
So many in the WC community make a huge deal about “scoped styles” provided
by Shadow DOM.
Let me put it plainly: this is a flat out lie, and probably the biggest
thing that put me off from web components for a long time.&lt;/p&gt;
&lt;p&gt;Shadow DOM does not provide scoped styles; it provides &lt;em&gt;isolated&lt;/em&gt; styles.
That’s a very different thing for a very different purpose than every day
application development.
Thankfully, others in the community are
&lt;a href=&quot;https://www.hjorthhansen.dev/you-might-not-need-shadow-dom/&quot;&gt;starting to come around on this&lt;/a&gt;.
The thinking seems to be shifting here,
For me, the realization that I can use custom elements all in the light DOM
has finally made web components a practical option.&lt;/p&gt;
&lt;p&gt;The second thing about vanilla web components is that they, by themselves,
&lt;a href=&quot;https://keithjgrant.com/posts/2023/07/web-components-arent-components&quot;&gt;aren’t a substitute&lt;/a&gt; for
traditional JS web frameworks.
They are just too low level, and are missing way too many essential quality
of life improvements found in those frameworks.&lt;/p&gt;
&lt;p&gt;Thankfully, there are some very lightweight frameworks built on native web
components that aim to ease development.
I’m still a bit unsure whether these are suitable replacements
for more broadly used frameworks like React when it comes to full-featured,
complex apps that are on the far end of the spectrum away from static
web pages — i.e. true applications —
but these certainly can have a place near the middle of that spectrum.&lt;/p&gt;
&lt;h2 id=&quot;lit&quot; tabindex=&quot;-1&quot;&gt;Lit &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/#lit&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; is the most well known of these frameworks.
It’s got the longest history — it’s a successor to Polymer, which was
the first (very polyfill-dependent) big WC framework.&lt;/p&gt;
&lt;p&gt;My first impressions were good here, though I also had the name “Lit HTML”
in my head and it took a while to piece together that Lit is the framework
and lit-html is the library it uses internally,
which parses HTML template strings in order to build a DOM representation.&lt;/p&gt;
&lt;p&gt;If you really want, you can build your own framework on top of lit-html.
I entertained that idea for a bit, but eventually decided that was signing
up for a whole lot of work when I really just wanted to get going on my
actual project.&lt;/p&gt;
&lt;p&gt;Defining a custom element comes down to creating a class that extends
&lt;code&gt;LitElement&lt;/code&gt;, which itself extends the native &lt;code&gt;HTMLElement&lt;/code&gt;.
I like that this means if I need to get to the bare metal and do something
that’s not explicitly supported by Lit, I can.
The inner markup is defined via a template literal in a render function,
which looks something like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LitElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; html&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;div&gt;Component content here&amp;lt;/div&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one thing I really don’t like about Lit is it defaults to putting every
component in Shadow DOM.
Opting out requires overriding the &lt;code&gt;createRenderRoot&lt;/code&gt; function in each and
every one of your components.
I really wish this was doable with a simple flag to the constructor or
something instead, or even better, a global config option that made it so
Shadow DOM was opt-in rather than opt-out in each component.&lt;/p&gt;
&lt;p&gt;My big stumbling block here came when I needed to build a component that used
&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s but I wanted to stay in the Light DOM.
This lead me to the next framework.&lt;/p&gt;
&lt;h2 id=&quot;enhance&quot; tabindex=&quot;-1&quot;&gt;Enhance &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/#enhance&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://enhance.dev/&quot;&gt;Enhance&lt;/a&gt; aims to be a full-featured web framework.
It’s got its own build pipeline and has out-of-the-box
&lt;abbr title=&quot;Server Side Rendering&quot;&gt;SSR&lt;/abbr&gt;,
which is really impressive.
However, you don’t have to do all that and can instead just use their
&lt;code&gt;CustomElement&lt;/code&gt; class to to build custom elements in the site you’ve already got,
which suits my needs more right now.
I like that flexibility.&lt;/p&gt;
&lt;p&gt;It also supports &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements in Light DOM components, which is also
very nice.&lt;/p&gt;
&lt;p&gt;Similar to Lit, you create a custom element by extending their &lt;code&gt;CustomElement&lt;/code&gt;
class, which is extends &lt;code&gt;HTMLElement&lt;/code&gt;.
It also uses a template literal to render component contents.&lt;/p&gt;
&lt;p&gt;As I experimented with Enhance a bit, though, some of its opinions started
to rub me the wrong way.
The main &lt;code&gt;CustomElement&lt;/code&gt; class doesn’t do anything to wire up event handlers
for you;
you have to manually bind them to the class,
manually invoke &lt;code&gt;addEventListener&lt;/code&gt;,
and manually evoke &lt;code&gt;removeEventListener&lt;/code&gt; for each one.
There is &lt;a href=&quot;https://enhance.dev/cookbook/use-event-listeners#reducing-boilerplate&quot;&gt;an odd way around this&lt;/a&gt;,
where, instead of extending the base class,
you define your entire component in an object literal then pass that to
an &lt;code&gt;enhance()&lt;/code&gt; function which builds the class for you.
I can’t entirely put my finger on why, but this just feels wrong to me.
I shouldn’t have to be an extra layer removed from the abstraction for what I
consider a bare bones essential feature of a modern framework.&lt;/p&gt;
&lt;p&gt;The other thing I didn’t like was how strictly prescriptive the framework
is regarding file structure.
This doesn’t matter if you’re only using their &lt;code&gt;CustomElement&lt;/code&gt; class in your
own build pipeline,
but as I read through the docs, this left a bad taste in my mouth.
Enhance really pitches the idea that it’s a full-fledged application framework,
but this convention feels much more in line with a static site generator.
It gives me the impression it’s trying a little too hard to be both,
and I’m cautious about what compromises they’re going to have to make to go
down both paths at the same time.&lt;/p&gt;
&lt;p&gt;This is probably a fantastic framework for some folks, but eventually I
decided there was just too much I wasn’t a fan of.&lt;/p&gt;
&lt;h2 id=&quot;webc&quot; tabindex=&quot;-1&quot;&gt;WebC &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/#webc&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I saw this listed alongside Lit and Enhance around the web, so I looked it up.
This was created by &lt;a href=&quot;https://www.zachleat.com/&quot;&gt;Zach Leatherman&lt;/a&gt; as what I guess
I’d describe as a sub-project of Eleventy.
The &lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/&quot;&gt;WebC docs&lt;/a&gt; seem to live on the
Eleventy website.&lt;/p&gt;
&lt;p&gt;WebC is not written in JavaScript.
It’s written in HTML, with a series of custom directive-like attributes,
which look something like:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;webc:&lt;/span&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(myItem, myIndex) of [1, 2, 3]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@text&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;myIndex&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;syntax-highlight&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;webc:&lt;/span&gt;import&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;npm:@11ty/eleventy-plugin-syntaxhighlight&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  function myFunction() { return true; }
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;syntax-highlight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This to me is an immediate non-starter.
Sure, this is great for some small, light-touch stuff,
but I need to build my components in a Turing-complete language.
I don’t want to deal with the headache that inevitably emerges the moment I
need to do something that doesn’t have a corresponding, already-supported
directive.
I’ve been down that road with too many languages in the past.&lt;/p&gt;
&lt;p&gt;It didn’t take long for me to toss this one aside.
Sorry Zach.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot; tabindex=&quot;-1&quot;&gt;Final thoughts &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/06/assessing-web-component-libraries/#final-thoughts&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Evaluating these were hard.
I’ve been developing primarily in React for over a decade,
so I have a lot of habits and opinions that have been influenced by that
framework.&lt;/p&gt;
&lt;p&gt;I know web components are a whole different thing,
so I tried my best to come at them with an open mind.
It needs to be okay for a new framework to not look like the old one,
so I often had to stop and sort whether I disliked something on merit versus
disliking it because it’s not what I’m used to.&lt;/p&gt;
&lt;p&gt;I eventually landed back on Lit for my current project.
I even &lt;a href=&quot;https://gist.github.com/keithjgrant/bfb4c13806ec6cba643a9f2e86b2718c&quot;&gt;devised a way&lt;/a&gt;
to use &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s in a Light DOM custom element.&lt;/p&gt;
&lt;p&gt;I think I’m happy with it.
We’ll see once I’m a lot further down the road.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>CSS in Depth, Second Edition</title>
    <link href="https://keithjgrant.com/posts/2024/05/css-in-depth-second-edition/"/>
    <published>2024-05-30T21:18:50Z</published>
    <updated>2024-05-30T21:18:50Z</updated>
    <id>https://keithjgrant.com/posts/2024/05/css-in-depth-second-edition/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I’m so excited to say I’m just finishing up reviews of the final copyedits for
the second edition of my book,
&lt;a href=&quot;https://www.manning.com/books/css-in-depth-second-edition?a_aid=kjg&amp;amp;a_bid=a7bc24da&amp;amp;chan=mm_website&quot;&gt;CSS in Depth&lt;/a&gt;!
If you’ve ordered a print edition of the book, it’ll be on its way soon.
If you haven’t ordered a copy, you can order now on &lt;a href=&quot;https://www.manning.com/books/css-in-depth-second-edition?a_aid=kjg&amp;amp;a_bid=a7bc24da&amp;amp;chan=mm_website&quot;&gt;manning.com&lt;/a&gt; —
both the print edition and eBook will get you immediate access to the
early access (“MEAP”) eBook while the publisher finishes finalizing page layout.&lt;/p&gt;
&lt;h2 id=&quot;big-changes&quot; tabindex=&quot;-1&quot;&gt;Big changes &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/05/css-in-depth-second-edition/#big-changes&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This edition features significant updates since the first edition.
If you’re looking to really understand CSS, this is the book for you.
Or if you’re already a seasoned developer looking to catch up on all the
rapid changes of the past few years, this is for you, too.&lt;/p&gt;
&lt;p&gt;Every chapter has been updated;
most chapters have at least one new section, and some are heavily revised throughout.
Plus I’ve written three entirely new chapters.&lt;/p&gt;
&lt;p&gt;The first edition, published in 2018, bridged the gap from the old way
of laying out webpages (floats) to the new (flexbox and grid).
In this edition, I’ve tossed out all the stuff that is now fully obsolete,
and have focused on bridging the transition into the next phase of CSS development.
That means logical properties, subgrid, layers, scope, container queries,
new color spaces, color mixing, and relative color functions, and more.
I’ve also thrown in a new chapter covering filters, masks, shapes, and clip paths,
because I always regretted not including those the first time around
(and browser support is much better now).&lt;/p&gt;
&lt;p&gt;As an extra bonus, a handful of key pages will be printed &lt;em&gt;in color&lt;/em&gt;.
I can’t do proper justice to new color spaces and gradient features without that,
right?&lt;/p&gt;
&lt;h2 id=&quot;css-for-2024-and-beyond&quot; tabindex=&quot;-1&quot;&gt;CSS for 2024 and beyond &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/05/css-in-depth-second-edition/#css-for-2024-and-beyond&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a forward-looking book.
It will give you a mastery of the language and prepare you for all the new
CSS features that are emerging today.
&lt;a href=&quot;https://www.manning.com/books/css-in-depth-second-edition?a_aid=kjg&amp;amp;a_bid=a7bc24da&amp;amp;chan=mm_website&quot;&gt;Get yourself a copy&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;If you’ve already purchased it, thank you!
Please help spread the word.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>I Do Not Like These LLMs</title>
    <link href="https://keithjgrant.com/posts/2024/05/i-do-not-like-these-llms/"/>
    <published>2024-05-28T15:40:20Z</published>
    <updated>2024-05-28T15:40:20Z</updated>
    <id>https://keithjgrant.com/posts/2024/05/i-do-not-like-these-llms/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Altman Sam&lt;br /&gt;
Sam Altman&lt;/p&gt;
&lt;p&gt;That Sam Altman&lt;br /&gt;
That Sam Altman!&lt;br /&gt;
I do not like&lt;br /&gt;
that Sam Altman.&lt;/p&gt;
&lt;p&gt;Do you like&lt;br /&gt;
these LLMs?&lt;/p&gt;
&lt;p&gt;I do not like them,&lt;br /&gt;
AI Man.&lt;br /&gt;
I do not like&lt;br /&gt;
these LLMs.&lt;/p&gt;
&lt;p&gt;Would you like them&lt;br /&gt;
Here and there?&lt;/p&gt;
&lt;p&gt;I would not like them&lt;br /&gt;
here or there.&lt;br /&gt;
I would not like them anywhere.&lt;br /&gt;
I do not like these LLMs.&lt;br /&gt;
I do not like them, Sam Altman.&lt;/p&gt;
&lt;p&gt;Would you like them&lt;br /&gt;
on your phone?&lt;br /&gt;
Would you like them&lt;br /&gt;
to run your home?&lt;/p&gt;
&lt;p&gt;I do not like them&lt;br /&gt;
on my phone.&lt;br /&gt;
I do not want them&lt;br /&gt;
to run my home.&lt;br /&gt;
I do not like them&lt;br /&gt;
here or there.&lt;br /&gt;
I do not like them anywhere.&lt;br /&gt;
I do not like these LLMs.&lt;br /&gt;
I do not like them, Sam Altman.&lt;/p&gt;
&lt;p&gt;Would you use them&lt;br /&gt;
in VS Code?&lt;br /&gt;
Would you use them&lt;br /&gt;
in Stack Overflow?&lt;/p&gt;
&lt;p&gt;Not in VS Code.&lt;br /&gt;
Not Stack Overflow.&lt;br /&gt;
Not on my phone.&lt;br /&gt;
Not in my home.&lt;br /&gt;
I would not use them here or there.&lt;br /&gt;
I would not use them anywhere.&lt;br /&gt;
I would not use these LLMs.&lt;br /&gt;
I do not like them, Sam Altman.&lt;/p&gt;
&lt;p&gt;Would you? Could you?&lt;br /&gt;
in your bank?&lt;br /&gt;
Use them! Use them!&lt;br /&gt;
I’ll take your thanks.&lt;/p&gt;
&lt;p&gt;I would not,&lt;br /&gt;
could not,&lt;br /&gt;
in my bank!&lt;/p&gt;
&lt;p&gt;You may like them.&lt;br /&gt;
I do affirm.&lt;br /&gt;
You may like them&lt;br /&gt;
in iTerm?&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I would not, could not in iTerm.&lt;br /&gt;
Not in my bank! That makes me squirm.&lt;br /&gt;
I do not like them in VS Code.&lt;br /&gt;
I do not like them with Stack Overflow.&lt;br /&gt;
I do not like them on my phone.&lt;br /&gt;
I do not like them in my home.&lt;br /&gt;
I do not like them here or there.&lt;br /&gt;
I do not like them anywhere.&lt;br /&gt;
I do not like these LLMs.&lt;br /&gt;
I do not like them, Sam Altman.&lt;/p&gt;
&lt;p&gt;A car! A car!&lt;br /&gt;
A car! A car!&lt;br /&gt;
Could you, would you&lt;br /&gt;
ride an AI car?&lt;/p&gt;
&lt;p&gt;Not in my car! Not in iTerm!&lt;br /&gt;
Not in my bank! Sam! I stand firm!&lt;br /&gt;
I would not, could not, in VS Code.&lt;br /&gt;
I could not, would not, in Stack Overflow!&lt;br /&gt;
I will not use these in my home.&lt;br /&gt;
I will not use them on my phone.&lt;br /&gt;
I will not use them here or there.&lt;br /&gt;
I will not use them anywhere.&lt;br /&gt;
I do not like them, Sam Altman.&lt;/p&gt;
&lt;p&gt;Say!&lt;br /&gt;
In your search?&lt;br /&gt;
Here in your search!&lt;br /&gt;
Would you, could you, in your search?&lt;/p&gt;
&lt;p&gt;I would not, could not,&lt;br /&gt;
in my search.&lt;/p&gt;
&lt;p&gt;Would you, could you,&lt;br /&gt;
to book a plane?&lt;/p&gt;
&lt;p&gt;I would not, could not, to book a plane.&lt;br /&gt;
Not in my search. Not in my car,&lt;br /&gt;
Not in my bank, Not in iTerm.&lt;br /&gt;
I do not like them; I am firm.&lt;br /&gt;
Not on my phone. Not VS Code.&lt;br /&gt;
Not in my home. Not Stack Overflow.&lt;br /&gt;
I will not use them here or there.&lt;br /&gt;
I do not like them anywhere!&lt;/p&gt;
&lt;p&gt;You do not like&lt;br /&gt;
these LLMs?&lt;/p&gt;
&lt;p&gt;I do not&lt;br /&gt;
like them,&lt;br /&gt;
Sam Altman.&lt;/p&gt;
&lt;p&gt;Could you, would you,&lt;br /&gt;
to choose your food?&lt;/p&gt;
&lt;p&gt;I would not,&lt;br /&gt;
could not,&lt;br /&gt;
to choose my food!&lt;/p&gt;
&lt;p&gt;Would you, could you,&lt;br /&gt;
eat pizza glue?&lt;/p&gt;
&lt;p&gt;I could not, would not, eat pizza glue.&lt;br /&gt;
I will not, will not, in my food.&lt;br /&gt;
I will not use it to book a plane.&lt;br /&gt;
I will not use it in my car.&lt;br /&gt;
Not in a search! Not in iTerm!&lt;br /&gt;
Not in my bank! I’m very firm!&lt;br /&gt;
I do not like them in VS Code.&lt;br /&gt;
I do not like them in Stack Overflow.&lt;br /&gt;
I will not use them on my phone.&lt;br /&gt;
I do not like them in my home.&lt;br /&gt;
I do not like them here or there.&lt;br /&gt;
I do not like them ANYWHERE!&lt;/p&gt;
&lt;p&gt;I do not like&lt;br /&gt;
these LLMs!&lt;br /&gt;
I do not like them,&lt;br /&gt;
Sam Altman.&lt;/p&gt;
&lt;p&gt;You do not like them.&lt;br /&gt;
So you say.&lt;br /&gt;
Try them! Try them!&lt;br /&gt;
And you may.&lt;br /&gt;
Try them and you may I say.&lt;/p&gt;
&lt;p&gt;Sam!&lt;br /&gt;
If you will let me be,&lt;br /&gt;
I will try them.&lt;br /&gt;
You will see.&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;They told me to eat rocks.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Weighing in on CSS Masonry</title>
    <link href="https://keithjgrant.com/posts/2024/05/weighing-in-on-css-masonry/"/>
    <published>2024-05-17T18:45:48Z</published>
    <updated>2024-05-17T18:45:48Z</updated>
    <id>https://keithjgrant.com/posts/2024/05/weighing-in-on-css-masonry/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I love to see the discussion moving on CSS “masonry”
(or “waterfall” or whatever the name may eventually be).
The key question at this point seems to be whether this specification should
continue to live as a part of grid, or if it should be its own thing.&lt;/p&gt;
&lt;p&gt;Kicking off the discussion are this
&lt;a href=&quot;https://webkit.org/blog/15269/help-us-invent-masonry-layouts-for-css-grid-level-3/&quot;&gt;Webkit blog post&lt;/a&gt;
and this &lt;a href=&quot;https://developer.chrome.com/blog/masonry&quot;&gt;Chrome blog post&lt;/a&gt;.
I want to give my take.&lt;/p&gt;
&lt;p&gt;Should masonry be its own &lt;code&gt;display&lt;/code&gt; method outside grid? Absolutely.&lt;/p&gt;
&lt;p&gt;I can think of a number of reasons why it should be separate
— many of which Rachel Andrew lists in the Chrome blog post above
— but one key argument jumped out at me.
She only touched on it briefly, but for me it sealed the argument:
&lt;code&gt;grid-template-areas&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-developers-learn-and-use-grid&quot; tabindex=&quot;-1&quot;&gt;How developers learn and use grid &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/05/weighing-in-on-css-masonry/#how-developers-learn-and-use-grid&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Grid is an immensely versatile layout system.
You can rely on grid to implicitly place the grid items;
you can use grid line numbers to position items in the grid;
you can name grid lines and place items using those names.
And you can use &lt;code&gt;grid-template-areas&lt;/code&gt; to draw an ASCII-art style layout
with named areas, then place items into these areas by name.&lt;/p&gt;
&lt;p&gt;This last approach allows for an approachable, high-level introduction to grid:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.my-page&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;logo   header header&#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;nav    nav    nav&#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;main   main   sidebar1&#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;main   main   sidebar2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr 2fr 1fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Personally, I don’t use &lt;code&gt;grid-template-areas&lt;/code&gt; very often.
I’m happy with a slightly lower-level approach using numbered or named lines,
but I completely understand the appeal of named template areas.
From what I’ve seen in discussions and blog posts online,
naming template areas has a huge broad appeal, especially among newer CSS developers.
It’s approachable and serves as a gentle introduction to the monstrously large
set of features available in grid.&lt;/p&gt;
&lt;p&gt;For many developers, it appears to me this is the primary
— and possibly &lt;em&gt;only&lt;/em&gt;
— way they know how to use grid.&lt;/p&gt;
&lt;p&gt;The problem is, this feature is almost meaningless in masonry.&lt;/p&gt;
&lt;p&gt;To educate people about masonry layout, we can’t simply say,
“just define a grid and set grid rows to &lt;code&gt;masonry&lt;/code&gt;.
If that developer’s understanding of grid is limited to named template areas,
they will happily type &lt;code&gt;display: grid&lt;/code&gt; and, as is their habit,
&lt;code&gt;grid-template-areas:&lt;/code&gt; and will run into a problem, right out of the gate.
What should this property be set to?
The way forward for them will not be immediately clear.&lt;/p&gt;
&lt;p&gt;This developer has in mind the goal of learning how masonry works,
but the way to achieving that goal lies in a deeper understanding of grid,
apart from masonry.&lt;/p&gt;
&lt;h2 id=&quot;a-clearer-educational-path&quot; tabindex=&quot;-1&quot;&gt;A clearer educational path &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/05/weighing-in-on-css-masonry/#a-clearer-educational-path&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If masonry is a part of grid, the way to teach masonry becomes:
“go learn grid at a deeper level.”
Then, upon learning more about grid, the next step becomes:
“half of what you just learned about grid does not apply to masonry.”&lt;/p&gt;
&lt;p&gt;That sounds like a pretty terrible learning experience.&lt;/p&gt;
&lt;p&gt;Masonry has a number of things in common with grid,
and a lot of the knowledge of one can be brought to the other,
but they also diverge in a lot of ways.&lt;/p&gt;
&lt;p&gt;I think it’s far more approachable to teach &lt;code&gt;display: masonry&lt;/code&gt;
and all the new properties that go with it.
Then, at the end of that lesson, you can say,
“and the good news is, most of what you just learned applies to grid as well!”&lt;/p&gt;
&lt;p&gt;Now, instead of putting in work and being confronted with the news that you
didn’t need it all, you get the opposite: after putting in the work,
you learn it does double-duty!
As a student, I’d prefer that learning experience any day.
As a teacher, I’d definitely prefer to give that learning experience.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>My CSS resets</title>
    <link href="https://keithjgrant.com/posts/2024/01/my-css-resets/"/>
    <published>2024-01-04T17:49:36Z</published>
    <updated>2024-01-04T17:49:36Z</updated>
    <id>https://keithjgrant.com/posts/2024/01/my-css-resets/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Every now and then, I see someone post their latest and greatest set of CSS resets.
Here’s mine:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@layer&lt;/span&gt; reset&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;*,
  *::before,
  *::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; border-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unset&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;button,
  input,
  textarea,
  select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;img,
  picture,
  svg,
  canvas&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;max-inline-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;block-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reduce&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;*,
    *::before,
    *::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.01ms &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;animation-iteration-count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;transition-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.01ms &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;scroll-behavior&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some popular resets are a bit more opinionated than this,
but this one strikes the right balance for me.
My goal is that I can copy this whole thing into any project the
moment I start it and be on good footing when I start building out the
global styles.&lt;/p&gt;
&lt;p&gt;I’ll break it down bit by bit:&lt;/p&gt;
&lt;h2 id=&quot;box-sizing-reset&quot; tabindex=&quot;-1&quot;&gt;Box sizing reset &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/01/my-css-resets/#box-sizing-reset&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;*,
*::before,
*::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; border-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make everything use the more predictable border box sizing.&lt;/p&gt;
&lt;p&gt;My previous technique involved setting this to &lt;code&gt;inherit&lt;/code&gt; and then applying
&lt;code&gt;border-box&lt;/code&gt; sizing once at the root.
The benefit of that was to make it easier to revert a chunk of the page to
&lt;code&gt;content-box&lt;/code&gt; sizing if needed, specifically when bringing in a 3rd party widget.&lt;/p&gt;
&lt;p&gt;However, I found I haven’t had to do that in ages.
Any plugins or 3rd party components I use these days are modern enough that
they too use border box sizing.&lt;/p&gt;
&lt;p&gt;In practicality, that was a small piece of complexity I never really need,
so I’ve simplified to this current approach.&lt;/p&gt;
&lt;h2 id=&quot;remove-body-margin&quot; tabindex=&quot;-1&quot;&gt;Remove body margin &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/01/my-css-resets/#remove-body-margin&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unset&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Does anybody ever want that 10px body margin?
It’s a holdover from a lost age of the web, so I nuke it.&lt;/p&gt;
&lt;p&gt;I like using &lt;code&gt;unset&lt;/code&gt; rather than &lt;code&gt;0&lt;/code&gt; here because it’s just a bit more
clear what my intention is.
I’m not &lt;em&gt;setting&lt;/em&gt; a &lt;code&gt;0px&lt;/code&gt; margin, I’m &lt;em&gt;removing&lt;/em&gt; a setting the browser has made.&lt;/p&gt;
&lt;h2 id=&quot;form-input-fonts&quot; tabindex=&quot;-1&quot;&gt;Form input fonts &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/01/my-css-resets/#form-input-fonts&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;button,
input,
textarea,
select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default form fonts are weird, especially for buttons.
I’m going to set a font and font size I like on the page.
This makes sure my selection carries over to buttons and inputs.&lt;/p&gt;
&lt;p&gt;I generally caution against the use of the &lt;code&gt;font&lt;/code&gt; shorthand property,
because it overrides so many different &lt;code&gt;font-*&lt;/code&gt; properties all at once,
but in this case, that’s exactly what I want.
Inherit &lt;em&gt;everything&lt;/em&gt;.&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2024/gary-oldman-everyone.webp&quot; alt=&quot;Gary Oldman shouting &#39;Everyone!&#39;&quot; /&gt;
&lt;p&gt;I’ll almost certainly be adding more opinionated styles to these elements
in my global styles layer,
but this puts them in a reasonable state even before I get to that.&lt;/p&gt;
&lt;h2 id=&quot;images-and-similar-media&quot; tabindex=&quot;-1&quot;&gt;Images and similar media &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/01/my-css-resets/#images-and-similar-media&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img,
picture,
svg,
canvas&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-inline-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;block-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can’t recall the last time I used an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; as an inline element.
They’re almost always part of the page structure, so I make them block level.
This ensures they don’t overflow horizontally out of their container
and generally behave more predictably.&lt;/p&gt;
&lt;p&gt;Harry Roberts has a &lt;a href=&quot;https://twitter.com/csswizardry/status/1717841334462005661&quot;&gt;rather interesting&lt;/a&gt;,
highly opinionated take that goes a lot further than this.
I like his ideas there, but in practicality, they’re just a bit too much for me.
I figure I can layer in his tricks when I really need them,
but honestly that doesn’t happen very often.&lt;/p&gt;
&lt;h2 id=&quot;reduced-motion&quot; tabindex=&quot;-1&quot;&gt;Reduced motion &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2024/01/my-css-resets/#reduced-motion&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reduce&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;*,
  *::before,
  *::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.01ms &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;animation-iteration-count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transition-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.01ms &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;scroll-behavior&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove decorative motion for users who don’t want them.
This is pretty standard stuff.
The one thing of note here is that these don’t set transitions
and animations to &lt;code&gt;none&lt;/code&gt;,
rather they shorten the durations a super short interval.
This way, they’re impossible to perceive, but they still run;
browser events like &lt;code&gt;transitionend&lt;/code&gt; will fire as normal and any code
depending on those events won’t break.&lt;/p&gt;
&lt;p&gt;Interestingly, I’m toying with the idea of reworking this bit of code on this site.
Some of my posts include tutorials and demos that describe building transitions,
and the demos don’t work because I’ve got this sort of code in my stylesheet.
At some point, I’ll rejigger what occurs on what layer, but that’s a special
case for this particular blog, and not any other projects.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Be Excited for New Features</title>
    <link href="https://keithjgrant.com/posts/2023/11/be-excited-for-new-features/"/>
    <published>2023-11-16T17:49:26Z</published>
    <updated>2023-11-16T17:49:26Z</updated>
    <id>https://keithjgrant.com/posts/2023/11/be-excited-for-new-features/</id>
    <content xml:lang="en" type="html">&lt;blockquote&gt;
“Boy, that’s a cool new browser feature.
It’s a shame I won’t be able to use it for ten years.”
&lt;/blockquote&gt;
&lt;p&gt;I’ve been seeing a sentiment a lot lately,
a sort of despair that some new browser feature won’t be reasonably
available for production use for years.
And I get it; it was this way for a long time.
We web developers still have a lingering bit of emotional trauma from
the early days of flexbox, web fonts, and the first CSS3 features.&lt;/p&gt;
&lt;p&gt;The old adage comes to mind:
&lt;em&gt;Fool me once, shame on you.
Fool my twice, shame on me&lt;/em&gt;.
And boy, have we been fooled a few times in the past.&lt;/p&gt;
&lt;p&gt;However, it’s not 2013 anymore.
This resignation regarding new browser features—especially in the realm of
CSS—is outdated.
These days, most brand spanking new features are ready for production use within
months, not years.&lt;/p&gt;
&lt;h2 id=&quot;be-an-optimist&quot; tabindex=&quot;-1&quot;&gt;Be an optimist &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/11/be-excited-for-new-features/#be-an-optimist&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last couple years have been wild for CSS.
Dozens of highly anticipated new features have begun rolling out.
Let’s look at some examples.&lt;/p&gt;
&lt;p&gt;Cascade layers &lt;a href=&quot;https://caniuse.com/css-cascade-layers&quot;&gt;first shipped&lt;/a&gt;
in Firefox on February 8, 2022.
This was followed almost immediately by Chrome, Safari, and Edge.
The last major browser to ship support was Opera, on April 19, only two
months after Firefox.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color&quot;&gt;OKLCH&lt;/a&gt; color
&lt;a href=&quot;https://caniuse.com/mdn-css_types_color_oklch&quot;&gt;first arrived&lt;/a&gt; in
Chrome on March 6, 2023.
Firefox was the last major browser to ship support,
two months later on May 8.
Some of the more obscure mobile browsers like Samsung Internet were only another
couple months after that.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;color-mix()&lt;/code&gt; function
&lt;a href=&quot;https://caniuse.com/mdn-css_types_color_color-mix&quot;&gt;arrived in Safari&lt;/a&gt;
on December 12, 2022.
The Blink browsers (Chrome, Edge, Opera) shipped in March.
Firefox was the last to add support on May 8, 2023.
That’s full browser support within a five month window.&lt;/p&gt;
&lt;p&gt;This is the norm now.
Yes, there are exceptions, so it’s important to keep an eye on things,
but we don’t need to resign ourselves to defeat as a default anymore.&lt;/p&gt;
&lt;h2 id=&quot;caveats-and-exceptions&quot; tabindex=&quot;-1&quot;&gt;Caveats and exceptions &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/11/be-excited-for-new-features/#caveats-and-exceptions&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s important to be aware that there’s generally a bit of a delay after release
before all users upgrade their browsers.
This can generally take anywhere from three to nine months to hit critical
mass—depending in large part what you consider “critical mass” to be given
your user base and the ease of defining fallback behavior for the specific
feature in question.
Perhaps you target a part of the market that happens to be particularly
slow at upgrades; I totally acknowledge that is an unfortunate reality
for some of us.&lt;/p&gt;
&lt;p&gt;So, depending on a few factors like these,
you might be able to use a new features anywhere from the moment it ships
to a year or (in absolutely horrible circumstances) two years after that.&lt;/p&gt;
&lt;p&gt;Some features are more complex, admittedly.
Chrome has eagerly shipped &lt;a href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back&quot;&gt;@scope&lt;/a&gt;,
while Firefox has yet to formally take a stance on the specification.
(Safari has indicated support.)&lt;/p&gt;
&lt;p&gt;But again, this is the exception, not the rule.
So learn those exciting new features, keeping an eye on the overall landscape,
and get ready to use them confidently.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Workarounds for Buggy Gradients</title>
    <link href="https://keithjgrant.com/posts/2023/11/problematic-color-gradients-and-workarounds/"/>
    <published>2023-11-03T16:17:02Z</published>
    <updated>2023-11-03T16:17:02Z</updated>
    <id>https://keithjgrant.com/posts/2023/11/problematic-color-gradients-and-workarounds/</id>
    <content xml:lang="en" type="html">&lt;style&gt; ._gradient-swatch { margin-block: 1px; padding: 12px; border-radius: 0.25em; color: white; font-weight: bold; } 
.iBhsYr { background-image: linear-gradient(to right , blue, yellow); }
.ieThtX { background-image: linear-gradient(to right in oklch, blue, yellow); }
.fVfqNY { background-image: linear-gradient(to right in oklch, blue, white); }
.dQwfGe { background-image: linear-gradient(to right in oklch, blue, gray); }
.gYSgPk { background-image: linear-gradient(to right in oklch, blue, black); }
.hgozdZ { background-image: linear-gradient(to right in oklch, blue, transparent); }
.hLjSyZ { background-image: linear-gradient(to right in oklch, red, white); }
.jpvvTL { background-image: linear-gradient(to right in oklch, red, gray); }
.fOrInV { background-image: linear-gradient(to right in oklch, red, black); }
.dCUapM { background-image: linear-gradient(to right in oklch, red, transparent); }
.iAYvHl { background-image: linear-gradient(to right in oklch, blue, oklch(100% 0.001 90deg)); }
.htALWM { background-image: linear-gradient(to right in oklch, blue, oklch(100% 0.001 264deg)); }
.ihrpg { background-image: linear-gradient(to right in oklch, blue, oklch(50% 0.001 264deg)); }
.cYEVEV { background-image: linear-gradient(to right in oklch, blue, oklch(0% 0.001 264deg)); }
.cMZYBX { background-image: linear-gradient(to right in oklch, blue, oklch(0 0.001 264deg / 0)); }
.byXDqW { background-image: linear-gradient(to right in oklch, red, oklch(100% 0.001 29deg)); }
.kSCyme { background-image: linear-gradient(to right in oklch, red, oklch(50% 0.001 29deg)); }
.oYACr { background-image: linear-gradient(to right in oklch, red, oklch(0% 0.001 29deg)); }
.guvTLh { background-image: linear-gradient(to right in oklch, red, oklch(0 0.001 29deg / 0)); }
.jzWYYI { background-image: linear-gradient(to right in hsl, blue, white); }
.jMhTvm { background-image: linear-gradient(to right in hsl, blue, gray); }
.fvXcs { background-image: linear-gradient(to right in hsl, blue, black); }
.eNDaKB { background-image: linear-gradient(to right in hsl, blue, transparent); }
.daZrRp { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 1% 10%)); }
.ezbDFy { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 1% 9.9%)); }
.fLhOTr { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 1% 9.8%)); }
.hviSCk { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 1% 9.7%)); }
.jIeDiD { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 100% 99.9%)); }
.fygEpj { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 100% 0.1%)); }
.chQeoK { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 2% 50%)); }
.jtZzyK { background-image: linear-gradient(to right in hsl, blue, hsl(240deg 100% 1% / 0)); }&lt;/style&gt;
  &lt;div id=&quot;mdx-root&quot;&gt;
&lt;p&gt;CSS has a great new feature where you can specify the color space to use by
adding &lt;code&gt;in &amp;lt;colorspace&amp;gt;&lt;/code&gt; to the gradient (not yet supported in Firefox).
This is especially useful for avoiding the
&lt;a href=&quot;https://css-tricks.com/the-gray-dead-zone-of-gradients/&quot;&gt;gray dead zone&lt;/a&gt;
that comes from rectangular color spaces like the default sRGB.
Just look at the difference between a blue-to-yellow gradient in srgb compared
to oklch with the same beginning and end:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;linear-gradient(to right, blue, yellow)&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;iBhsYr _gradient-swatch&quot;&gt;srgb — blue to yellow&lt;/div&gt;
&lt;p&gt;&lt;code&gt;linear-gradient(to right in oklch, blue, yellow)&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;ieThtX _gradient-swatch&quot;&gt;oklch — blue to yellow&lt;/div&gt;
&lt;p&gt;I like that second one a lot better, though there certainly may be specific
use cases where the first could make sense, too.&lt;/p&gt;
&lt;p&gt;Unfortunately, if you’ve experimented with this much, you may have run into
some weird behavior when building any gradients to white, black, gray, or transparent.
This is especally bad when using blue, but it does affect other hues as well.
Look at the teal and purple that shows up in these gradients, when they really
should consist only of shades of blue:&lt;/p&gt;
&lt;div class=&quot;fVfqNY _gradient-swatch&quot;&gt;oklch — blue to white&lt;/div&gt;
&lt;div class=&quot;dQwfGe _gradient-swatch&quot;&gt;oklch — blue to gray&lt;/div&gt;
&lt;div class=&quot;gYSgPk _gradient-swatch&quot;&gt;oklch — blue to black&lt;/div&gt;
&lt;div class=&quot;hgozdZ _gradient-swatch&quot;&gt;oklch — blue to transparent&lt;/div&gt;
&lt;p&gt;Here are red gradients, some of which show an orange or burgandy center.&lt;/p&gt;
&lt;div class=&quot;hLjSyZ _gradient-swatch&quot;&gt;oklch — red to white&lt;/div&gt;
&lt;div class=&quot;jpvvTL _gradient-swatch&quot;&gt;oklch — red to gray&lt;/div&gt;
&lt;div class=&quot;fOrInV _gradient-swatch&quot;&gt;oklch — red to black&lt;/div&gt;
&lt;div class=&quot;dCUapM _gradient-swatch&quot;&gt;oklch — red to transparent&lt;/div&gt;
&lt;p&gt;And to make matters worse, this behavior isn’t the same across browsers.
The center of that second gradient (blue to gray) is teal in Chrome, but
purple in Safari.
The red to gray gradient differs between the browsers as well.
Thankfully, there’s a reasonable workaround once you understand the problem.&lt;/p&gt;
&lt;p&gt;The most reasonable solution is to do the gradient in sRGB or another
rectangular color space.
When you aren’t defining a gradient between different hues, the benefit of
using a polar color space doesn’t really apply.
However, out of academic curiousity, I dug into the cause of these bugs
and happened to find some reasonable workaround within the OKLCH and HSL
color spaces.&lt;/p&gt;
&lt;h2 id=&quot;workarounds-in-oklch&quot;&gt;Workarounds in OKLCH&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/11/problematic-color-gradients-and-workarounds/#workarounds-in-oklch&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As far as I understand it, this is a browser bug, possibly relating to fact that the colors
white, black, transparent, and any true neutral gray can be represented multiple
ways all the polar color spaces.
&lt;code&gt;oklch(100% 0 0deg)&lt;/code&gt; is white, but so is &lt;code&gt;oklch(100% 0 240deg)&lt;/code&gt;.
The first is a “red” white with no chroma, and the second is a “blue” white
with no chroma.&lt;/p&gt;
&lt;p&gt;For some reason, the color &lt;code&gt;white&lt;/code&gt; is interpreted as having just the tiniest bit
of chroma, but in a yellow-green hue.
Below is an incorrectly interpreted blue to white gradient, followed by a correctly
interpreted gradient to &lt;code&gt;oklch(100% 0.001 90deg)&lt;/code&gt;.
The two appear exactly the same, and I think for some reason this shows how
the browsers are converting &lt;code&gt;white&lt;/code&gt; to OKLCH.
The result occurs if you specify white in a non-oklch color syntax, including
hex, RGB, and HSL.&lt;/p&gt;
&lt;div class=&quot;fVfqNY _gradient-swatch&quot;&gt;oklch — blue to white&lt;/div&gt;
&lt;div class=&quot;iAYvHl _gradient-swatch&quot;&gt;oklch — blue to oklch(100% 0.001 90deg)&lt;/div&gt;
&lt;p&gt;Thankfully, there’s a workaround: specify the neutral color in the same color
space as the gradient, and match the hue: &lt;code&gt;oklch(100% 0 264deg)&lt;/code&gt;.
This seems to fix the issues in Safari and about half of the cases in Chrome.
To fix those last remaining bugs in Chrome, add back just a smidge of chroma:
&lt;code&gt;oklch(100% 0.001 264deg)&lt;/code&gt;.
This trick works for black, gray, and transparent.
Here are all the same gradients as above, but corrected:&lt;/p&gt;
&lt;div class=&quot;htALWM _gradient-swatch&quot;&gt;oklch — blue to oklch(100% 0.001 264deg)&lt;/div&gt;
&lt;div class=&quot;ihrpg _gradient-swatch&quot;&gt;oklch — blue to oklch(50% 0.001 264deg)&lt;/div&gt;
&lt;div class=&quot;cYEVEV _gradient-swatch&quot;&gt;oklch — blue to oklch(0% 0.001 264deg)&lt;/div&gt;
&lt;div class=&quot;cMZYBX _gradient-swatch&quot;&gt;oklch — blue to oklch(0 0.001 264deg / 0)&lt;/div&gt;
&lt;p&gt;And corrected red gradients:&lt;/p&gt;
&lt;div class=&quot;byXDqW _gradient-swatch&quot;&gt;oklch — red to oklch(100% 0.001 29deg)&lt;/div&gt;
&lt;div class=&quot;kSCyme _gradient-swatch&quot;&gt;oklch — red to oklch(50% 0.001 29deg)&lt;/div&gt;
&lt;div class=&quot;oYACr _gradient-swatch&quot;&gt;oklch — red to oklch(0% 0.001 29deg)&lt;/div&gt;
&lt;div class=&quot;guvTLh _gradient-swatch&quot;&gt;oklch — red to oklch(0 0.001 29deg / 0)&lt;/div&gt;
&lt;h2 id=&quot;workarounds-in-hsl&quot;&gt;Workarounds in HSL&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/11/problematic-color-gradients-and-workarounds/#workarounds-in-hsl&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some HSL gradients suffer from the same issues.
Red gradients seem to behave as expected, but these blue gradients all have
some vibrant purple in the middle:&lt;/p&gt;
&lt;div class=&quot;jzWYYI _gradient-swatch&quot;&gt;hsl — blue to white&lt;/div&gt;
&lt;div class=&quot;jMhTvm _gradient-swatch&quot;&gt;hsl — blue to gray&lt;/div&gt;
&lt;div class=&quot;fvXcs _gradient-swatch&quot;&gt;hsl — blue to black&lt;/div&gt;
&lt;div class=&quot;eNDaKB _gradient-swatch&quot;&gt;hsl — blue to transparent&lt;/div&gt;
&lt;p&gt;A similar approach can fix this, but it’s a little more finnicky.
For white, black, and transparent, you need to set the saturation to 100%.
Saturation as low as 1% &lt;em&gt;can&lt;/em&gt; work, but I&amp;#x27;ve found that in Safari, it’s very
particular based on the precise luminescent value.
Using 100% seems to be the most reliable fix, and this is fine because
at these lightness levels, saturation is irrelevant anyway.&lt;/p&gt;
&lt;p&gt;Dark grays can be problematic.
Chrome seems to handle this fine, but in Safari, variations
of a tenth of a percent can have wild effects on the result.
In the following gradients, lightness of 10% and 9.7% work fine,
but values between that inexplicably result in a purple gradient:&lt;/p&gt;
&lt;div class=&quot;daZrRp _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 1% 10%)&lt;/div&gt;
&lt;div class=&quot;ezbDFy _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 1% 9.9%)&lt;/div&gt;
&lt;div class=&quot;fLhOTr _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 1% 9.8%)&lt;/div&gt;
&lt;div class=&quot;hviSCk _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 1% 9.7%)&lt;/div&gt;
&lt;p&gt;In these cases, a slighly higher saturation (at least 2%) seems to
fix the issues.
Here are fixed gradients in HSL:&lt;/p&gt;
&lt;div class=&quot;jIeDiD _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 100% 99.9%)&lt;/div&gt;
&lt;div class=&quot;fygEpj _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 100% 0.1%)&lt;/div&gt;
&lt;div class=&quot;chQeoK _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 2% 50%)&lt;/div&gt;
&lt;div class=&quot;jtZzyK _gradient-swatch&quot;&gt;hsl — blue to hsl(240deg 100% 1% / 0)&lt;/div&gt;
&lt;h2 id=&quot;issues-in-lab-and-lch-color&quot;&gt;Issues in LAB and LCH color&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/11/problematic-color-gradients-and-workarounds/#issues-in-lab-and-lch-color&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As a final note, the LAB and LCH color spaces also display unexpected hues
in some gradients, most notably a purple showing up when the gradient should
be blue. The bug in these cases lies in the definitions of the color spaces
themselves, and not in the browser’s interpretation.&lt;/p&gt;
&lt;p&gt;You should avoid these color spaces and use the superceding ones instead:
OKLAB and &lt;a href=&quot;https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/&quot;&gt;OKLCH&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;script&gt;(()=&gt;{var X=Object.create;var L=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var z=Object.getPrototypeOf,W=Object.prototype.hasOwnProperty;var T=(n,e)=&gt;()=&gt;(e||n((e={exports:{}}).exports,e),e.exports);var P=(n,e,r,l)=&gt;{if(e&amp;&amp;typeof e==&quot;object&quot;||typeof e==&quot;function&quot;)for(let o of q(e))!W.call(n,o)&amp;&amp;o!==r&amp;&amp;L(n,o,{get:()=&gt;e[o],enumerable:!(l=F(e,o))||l.enumerable});return n};var c=(n,e,r)=&gt;(r=n!=null?X(z(n)):{},P(e||!n||!n.__esModule?L(r,&quot;default&quot;,{value:n,enumerable:!0}):r,n));var i=T((te,A)=&gt;{A.exports=window.React});var j=T($=&gt;{var k=c(i());$.useMDXComponents=function(){return{h2:function(r){let{children:l,...o}=r,h=R(l);return k.default.createElement(&quot;h2&quot;,{...o,id:h},l,k.default.createElement(&quot;a&quot;,{className:&quot;header-anchor&quot;,href:`#${h}`},&quot;&#92;xB6&quot;))}}};function R(n){return n.toLowerCase().split(&quot; &quot;).slice(0,6).join(&quot;-&quot;).replace(/[^A-Za-z0-9-]/g,&quot;&quot;)}});var x=c(j()),t=c(i());var u=c(i());var N=new Set([&quot;a&quot;,&quot;abbr&quot;,&quot;address&quot;,&quot;area&quot;,&quot;article&quot;,&quot;aside&quot;,&quot;audio&quot;,&quot;b&quot;,&quot;base&quot;,&quot;bdi&quot;,&quot;bdo&quot;,&quot;big&quot;,&quot;blockquote&quot;,&quot;body&quot;,&quot;br&quot;,&quot;button&quot;,&quot;canvas&quot;,&quot;caption&quot;,&quot;cite&quot;,&quot;code&quot;,&quot;col&quot;,&quot;colgroup&quot;,&quot;data&quot;,&quot;datalist&quot;,&quot;dd&quot;,&quot;del&quot;,&quot;details&quot;,&quot;dfn&quot;,&quot;dialog&quot;,&quot;div&quot;,&quot;dl&quot;,&quot;dt&quot;,&quot;em&quot;,&quot;embed&quot;,&quot;fieldset&quot;,&quot;figcaption&quot;,&quot;figure&quot;,&quot;footer&quot;,&quot;form&quot;,&quot;h1&quot;,&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;,&quot;head&quot;,&quot;header&quot;,&quot;hgroup&quot;,&quot;hr&quot;,&quot;html&quot;,&quot;i&quot;,&quot;iframe&quot;,&quot;img&quot;,&quot;input&quot;,&quot;ins&quot;,&quot;kbd&quot;,&quot;keygen&quot;,&quot;label&quot;,&quot;legend&quot;,&quot;li&quot;,&quot;link&quot;,&quot;main&quot;,&quot;map&quot;,&quot;mark&quot;,&quot;menu&quot;,&quot;menuitem&quot;,&quot;meta&quot;,&quot;meter&quot;,&quot;nav&quot;,&quot;noscript&quot;,&quot;object&quot;,&quot;ol&quot;,&quot;optgroup&quot;,&quot;option&quot;,&quot;output&quot;,&quot;p&quot;,&quot;param&quot;,&quot;picture&quot;,&quot;pre&quot;,&quot;progress&quot;,&quot;q&quot;,&quot;rp&quot;,&quot;rt&quot;,&quot;ruby&quot;,&quot;s&quot;,&quot;samp&quot;,&quot;script&quot;,&quot;section&quot;,&quot;select&quot;,&quot;small&quot;,&quot;source&quot;,&quot;span&quot;,&quot;strong&quot;,&quot;style&quot;,&quot;sub&quot;,&quot;summary&quot;,&quot;sup&quot;,&quot;table&quot;,&quot;tbody&quot;,&quot;td&quot;,&quot;textarea&quot;,&quot;tfoot&quot;,&quot;th&quot;,&quot;thead&quot;,&quot;time&quot;,&quot;title&quot;,&quot;tr&quot;,&quot;track&quot;,&quot;u&quot;,&quot;ul&quot;,&quot;use&quot;,&quot;var&quot;,&quot;video&quot;,&quot;wbr&quot;,&quot;circle&quot;,&quot;clipPath&quot;,&quot;defs&quot;,&quot;ellipse&quot;,&quot;foreignObject&quot;,&quot;g&quot;,&quot;image&quot;,&quot;line&quot;,&quot;linearGradient&quot;,&quot;marker&quot;,&quot;mask&quot;,&quot;path&quot;,&quot;pattern&quot;,&quot;polygon&quot;,&quot;polyline&quot;,&quot;radialGradient&quot;,&quot;rect&quot;,&quot;stop&quot;,&quot;svg&quot;,&quot;text&quot;,&quot;tspan&quot;]);var s=c(i());function d(n){let e=J(n)&gt;&gt;&gt;0;return Z(e)}var U=8362;function J(n){let e=U,r=n.length;for(;r;)e=e*33^n.charCodeAt(--r);return e}var Y=/(a)(d)/gi,g=52,O=n=&gt;String.fromCharCode(n+(n&gt;25?39:97));function Z(n){let e=&quot;&quot;,r;for(r=Math.abs(n);r&gt;g;r=r/g|0)e=O(r%g)+e;return(O(r%g)+e).replace(Y,&quot;$1-$2&quot;)}var D={};function E(n,e){D[n]||(D[n]=e)}function f(n,e,r,l){let o=Q(e,r,l);return(0,s.useMemo)(()=&gt;{function H(b){let{children:_,css:p,...K}=b,y=[o],w;return p&amp;&amp;(w=d(p),y.push(w)),b.className&amp;&amp;y.push(b.className),s.default.createElement(s.default.Fragment,null,p?s.default.createElement(&quot;style&quot;,null,`.${w} {${p}}`):null,s.default.createElement(n,{...K,className:y.join(&quot; &quot;)},_))}return H.className=o,H},[o])}function I(n,e){let r=M(n,e),l=d(r);E(l,r)}function Q(n,e,r){let l=M(n,e),o=r||d(l),h=`.${o} {${l}}`;return E(o,h),o}function M(n,e){return n.reduce((r,l,o)=&gt;`${r}${l}${e[o]||&quot;&quot;}`,&quot;&quot;).replace(/&#92;s+/g,&quot; &quot;)}function S(n,...e){return f(&quot;div&quot;,n,e)}N.forEach(n=&gt;{S[n]=(e,...r)=&gt;f(n,e,r)});S.raw=(n,...e)=&gt;I(n,e);var C=S;function a({colorSpace:n,start:e,end:r,annotate:l}){C.raw`
    ._gradient-swatch {
      margin-block: 1px;
      padding: 12px;
      border-radius: 0.25em;
      color: white;
      font-weight: bold;
    }
  `;let o=C`
    background-image: linear-gradient(to right ${n?`in ${n}`:&quot;&quot;}, ${e}, ${r});
  `;return u.default.createElement(u.default.Fragment,null,l?u.default.createElement(&quot;code&quot;,null,&quot;linear-gradient(to right&quot;,n?` in ${n}`:&quot;&quot;,&quot;,&quot;,&quot; &quot;,e,&quot;, &quot;,r,&quot;)&quot;):null,u.default.createElement(o,{className:&quot;_gradient-swatch&quot;},n||&quot;srgb&quot;,&quot; &#92;u2014 &quot;,e,&quot; to &quot;,r))}var m=c(i());function v({query:n,fallback:e,children:r}){return typeof window&gt;&quot;u&quot;?null:window.CSS.supports(n)?m.default.createElement(m.default.Fragment,null,r):m.default.createElement(&quot;p&quot;,null,m.default.createElement(&quot;i&quot;,null,e))}function B(n){let e=Object.assign({p:&quot;p&quot;,code:&quot;code&quot;,a:&quot;a&quot;,h2:&quot;h2&quot;,em:&quot;em&quot;},(0,x.useMDXComponents)(),n.components);return t.default.createElement(t.default.Fragment,null,t.default.createElement(v,{query:&quot;background: linear-gradient(in lch, black, white)&quot;,fallback:&quot;This post features live demos that your browser doesn&#92;u2019t yet support. Try visiting in a recent version of Chrome, Edge, or Safari.&quot;}),`
`,t.default.createElement(e.p,null,`CSS has a great new feature where you can specify the color space to use by
adding `,t.default.createElement(e.code,null,&quot;in &lt;colorspace&gt;&quot;),` to the gradient (not yet supported in Firefox).
This is especially useful for avoiding the
`,t.default.createElement(e.a,{href:&quot;https://css-tricks.com/the-gray-dead-zone-of-gradients/&quot;},&quot;gray dead zone&quot;),`
that comes from rectangular color spaces like the default sRGB.
Just look at the difference between a blue-to-yellow gradient in srgb compared
to oklch with the same beginning and end:`),`
`,t.default.createElement(e.p,null,t.default.createElement(e.code,null,&quot;linear-gradient(to right, blue, yellow)&quot;)),`
`,t.default.createElement(a,{start:&quot;blue&quot;,end:&quot;yellow&quot;}),`
`,t.default.createElement(e.p,null,t.default.createElement(e.code,null,&quot;linear-gradient(to right in oklch, blue, yellow)&quot;)),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;yellow&quot;}),`
`,t.default.createElement(e.p,null,`I like that second one a lot better, though there certainly may be specific
use cases where the first could make sense, too.`),`
`,t.default.createElement(e.p,null,`Unfortunately, if you&#92;u2019ve experimented with this much, you may have run into
some weird behavior when building any gradients to white, black, gray, or transparent.
This is especally bad when using blue, but it does affect other hues as well.
Look at the teal and purple that shows up in these gradients, when they really
should consist only of shades of blue:`),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;white&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;gray&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;black&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;transparent&quot;}),`
`,t.default.createElement(e.p,null,&quot;Here are red gradients, some of which show an orange or burgandy center.&quot;),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;white&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;gray&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;black&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;transparent&quot;}),`
`,t.default.createElement(e.p,null,`And to make matters worse, this behavior isn&#92;u2019t the same across browsers.
The center of that second gradient (blue to gray) is teal in Chrome, but
purple in Safari.
The red to gray gradient differs between the browsers as well.
Thankfully, there&#92;u2019s a reasonable workaround once you understand the problem.`),`
`,t.default.createElement(e.p,null,`The most reasonable solution is to do the gradient in sRGB or another
rectangular color space.
When you aren&#92;u2019t defining a gradient between different hues, the benefit of
using a polar color space doesn&#92;u2019t really apply.
However, out of academic curiousity, I dug into the cause of these bugs
and happened to find some reasonable workaround within the OKLCH and HSL
color spaces.`),`
`,t.default.createElement(e.h2,null,&quot;Workarounds in OKLCH&quot;),`
`,t.default.createElement(e.p,null,`As far as I understand it, this is a browser bug, possibly relating to fact that the colors
white, black, transparent, and any true neutral gray can be represented multiple
ways all the polar color spaces.
`,t.default.createElement(e.code,null,&quot;oklch(100% 0 0deg)&quot;),&quot; is white, but so is &quot;,t.default.createElement(e.code,null,&quot;oklch(100% 0 240deg)&quot;),`.
The first is a &#92;u201Cred&#92;u201D white with no chroma, and the second is a &#92;u201Cblue&#92;u201D white
with no chroma.`),`
`,t.default.createElement(e.p,null,&quot;For some reason, the color &quot;,t.default.createElement(e.code,null,&quot;white&quot;),` is interpreted as having just the tiniest bit
of chroma, but in a yellow-green hue.
Below is an incorrectly interpreted blue to white gradient, followed by a correctly
interpreted gradient to `,t.default.createElement(e.code,null,&quot;oklch(100% 0.001 90deg)&quot;),`.
The two appear exactly the same, and I think for some reason this shows how
the browsers are converting `,t.default.createElement(e.code,null,&quot;white&quot;),` to OKLCH.
The result occurs if you specify white in a non-oklch color syntax, including
hex, RGB, and HSL.`),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;white&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;oklch(100% 0.001 90deg)&quot;}),`
`,t.default.createElement(e.p,null,`Thankfully, there&#92;u2019s a workaround: specify the neutral color in the same color
space as the gradient, and match the hue: `,t.default.createElement(e.code,null,&quot;oklch(100% 0 264deg)&quot;),`.
This seems to fix the issues in Safari and about half of the cases in Chrome.
To fix those last remaining bugs in Chrome, add back just a smidge of chroma:
`,t.default.createElement(e.code,null,&quot;oklch(100% 0.001 264deg)&quot;),`.
This trick works for black, gray, and transparent.
Here are all the same gradients as above, but corrected:`),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;oklch(100% 0.001 264deg)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;oklch(50% 0.001 264deg)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;oklch(0% 0.001 264deg)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;blue&quot;,end:&quot;oklch(0 0.001 264deg / 0)&quot;}),`
`,t.default.createElement(e.p,null,&quot;And corrected red gradients:&quot;),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;oklch(100% 0.001 29deg)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;oklch(50% 0.001 29deg)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;oklch(0% 0.001 29deg)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;oklch&quot;,start:&quot;red&quot;,end:&quot;oklch(0 0.001 29deg / 0)&quot;}),`
`,t.default.createElement(e.h2,null,&quot;Workarounds in HSL&quot;),`
`,t.default.createElement(e.p,null,`Some HSL gradients suffer from the same issues.
Red gradients seem to behave as expected, but these blue gradients all have
some vibrant purple in the middle:`),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;white&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;gray&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;black&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;transparent&quot;}),`
`,t.default.createElement(e.p,null,`A similar approach can fix this, but it&#92;u2019s a little more finnicky.
For white, black, and transparent, you need to set the saturation to 100%.
Saturation as low as 1% `,t.default.createElement(e.em,null,&quot;can&quot;),` work, but I&#39;ve found that in Safari, it&#92;u2019s very
particular based on the precise luminescent value.
Using 100% seems to be the most reliable fix, and this is fine because
at these lightness levels, saturation is irrelevant anyway.`),`
`,t.default.createElement(e.p,null,`Dark grays can be problematic.
Chrome seems to handle this fine, but in Safari, variations
of a tenth of a percent can have wild effects on the result.
In the following gradients, lightness of 10% and 9.7% work fine,
but values between that inexplicably result in a purple gradient:`),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 1% 10%)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 1% 9.9%)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 1% 9.8%)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 1% 9.7%)&quot;}),`
`,t.default.createElement(e.p,null,`In these cases, a slighly higher saturation (at least 2%) seems to
fix the issues.
Here are fixed gradients in HSL:`),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 100% 99.9%)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 100% 0.1%)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 2% 50%)&quot;}),`
`,t.default.createElement(a,{colorSpace:&quot;hsl&quot;,start:&quot;blue&quot;,end:&quot;hsl(240deg 100% 1% / 0)&quot;}),`
`,t.default.createElement(e.h2,null,&quot;Issues in LAB and LCH color&quot;),`
`,t.default.createElement(e.p,null,`As a final note, the LAB and LCH color spaces also display unexpected hues
in some gradients, most notably a purple showing up when the gradient should
be blue. The bug in these cases lies in the definitions of the color spaces
themselves, and not in the browser&#92;u2019s interpretation.`),`
`,t.default.createElement(e.p,null,`You should avoid these color spaces and use the superceding ones instead:
OKLAB and `,t.default.createElement(e.a,{href:&quot;/posts/2023/04/its-time-to-learn-oklch-color/&quot;},&quot;OKLCH&quot;),&quot;.&quot;))}function G(n={}){let{wrapper:e}=Object.assign({},(0,x.useMDXComponents)(),n.components);return e?t.default.createElement(e,n,t.default.createElement(B,n)):B(n)}var be=G,V=ReactDOM.createRoot(document.getElementById(&quot;mdx-root&quot;));V.render(t.default.createElement(G,null));})();
&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>Scope vs Shadow DOM</title>
    <link href="https://keithjgrant.com/posts/2023/08/scope-vs-shadow-dom/"/>
    <published>2023-08-25T16:43:03Z</published>
    <updated>2023-08-25T16:43:03Z</updated>
    <id>https://keithjgrant.com/posts/2023/08/scope-vs-shadow-dom/</id>
    <content xml:lang="en" type="html">&lt;style&gt;&lt;/style&gt;
  &lt;div id=&quot;mdx-root&quot;&gt;&lt;p&gt;When CSS grid first arrived, there were a lot of people saying,
“Why do we need this? We already have flexbox!”
I’m getting very much the same vibe now with &lt;a href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back/&quot;&gt;@scope&lt;/a&gt;,
especially from enthusiasts of web components and shadow DOM.&lt;/p&gt;
&lt;p&gt;But here’s the thing: CSS @scope and shadow DOM are not competing standards.
They’re complementary.
They meet different needs entirely —
but with enough healthy overlap in functionality so there isn’t a gap.&lt;/p&gt;
&lt;p&gt;So should we make shadow DOM more ergonomic?
Yes please!
I’m all for continuing to improve the specification here.
By promoting @scope, I’m not belittling shadow DOM.
Please don’t take this following statement as a criticism:&lt;/p&gt;
&lt;h2 id=&quot;shadow-dom-does-not-provide-what&quot;&gt;Shadow DOM does not provide what @scope does&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/08/scope-vs-shadow-dom/#shadow-dom-does-not-provide-what&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Language is hard, especially when it comes to the terms we use in the industry
to convey various ideas.
The term &lt;a href=&quot;https://keithjgrant.com/posts/2023/07/web-components-arent-components&quot;&gt;component&lt;/a&gt;, for instance,
can mean multiple different things to different developers.&lt;/p&gt;
&lt;p&gt;Similarly, something different comes to mind for all of us when we hear about
the concept of style scoping.
This is only natural, because we all work in different corners of the industry.
We’ve each had to tackle problems and use cases that others haven’t.&lt;/p&gt;
&lt;p&gt;It’s so easy to hear that @scope is coming to CSS and think,
“wait, don’t we already have scoping in the shadow DOM?” —
because we do have scoping in the shadow DOM.
But it’s a very different definition of scoping!&lt;/p&gt;
&lt;p&gt;Shadow DOM and web components are perfect for some use cases.
If you need to encapsulate something so others can put it on their sites,
this is exactly what you need.
Red Hat does this: the navigation bar at the top of the page on many Red Hat
sites is a web component.
This works well because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;these sites are all branded the same, and&lt;/li&gt;
&lt;li&gt;they each need the same user experience for navigation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Something like an embedded social media post is probably another perfect use-case.&lt;/p&gt;
&lt;p&gt;This is not what @scope is for.&lt;/p&gt;
&lt;p&gt;Shadow DOM is strongly isolated from the rest of the page.
Essentially, shadow DOM throws away the entire CSS cascade.
It cuts a hole out of the page, and allows you to put something else
in that hole.&lt;/p&gt;
&lt;p&gt;But this is not everybody’s use case.
If you are working on a single web application, with unique branding
and a consistent user experience, the last thing you want is to isolate
all the parts of the page from the cascade.
You want everything to look and feel the same!
You want the cascade to apply, but with the ability to explicitly control
how it applies.
Components need to be able to override parts of the cascade — but not be isolated
from it entirely.&lt;/p&gt;
&lt;p&gt;@scope is about embracing the cascade, and controlling it.
In many ways, this is the exact opposite of what shadow DOM does.&lt;/p&gt;
&lt;p&gt;Yes, shadow DOM should continue to grow and be improved, but at a fundamental
level, it does not address the same problem @scope does.
Yes, both scope styles, but they are two radically different definitions
of scope. &lt;sup&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2023/08/scope-vs-shadow-dom/#footnote-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-greatest-shortcoming-in-our-industry&quot;&gt;The greatest shortcoming in our industry&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/08/scope-vs-shadow-dom/#the-greatest-shortcoming-in-our-industry&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have never personally needed shadow DOM.
Every time I dig in to assess it, I come away frustrated.&lt;/p&gt;
&lt;p&gt;At first, I thought this was a failing of the spec — how could they be
so out of touch with the real needs of developers?
But since then I’ve realized that’s not it at all.&lt;/p&gt;
&lt;p&gt;Instead, I have slowly realized that the needs across our industry vary wildly.
Some developers love shadow DOM, because it does exactly what they need.
But when they look at @scope, they feel
the same way I do when I look at shadow DOM — it’s not at all the right tool
for their job.&lt;/p&gt;
&lt;p&gt;This is something our industry is terrible at:
we are really bad at understanding and appreciating the varying needs
of other developers who work on different problems than us.
Why would someone want to colocate their CSS inside their JavaScript files?
Why would someone be so vehement about avoiding a JavaScript framework?
Why would someone want to entirely encapsulate their web components from
the light DOM?
And why would someone want this @scope thing when we already have shadow DOM?&lt;/p&gt;
&lt;p&gt;We each solve different problems and often have wildly different needs
from our tech tools.
That’s okay.
That’s healthy.&lt;/p&gt;
&lt;p&gt;For me, and my problem space, shadow DOM is a backwards paradigm to the one I
need. I long for @scope.&lt;/p&gt;
&lt;p&gt;Maybe you don’t. And that’s fine.&lt;/p&gt;
&lt;p&gt;But please don’t try to shoot down the one feature that’s been desperately
missing from CSS because you don’t have a personal need for it.&lt;/p&gt;
&lt;aside class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;footnote-1&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;If I were the king of terminology, I would say that shadow DOM provides
style &lt;i&gt;encapsulation&lt;/i&gt;, not scoping. But that ship has kinda sailed.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/aside&gt;&lt;/div&gt;&lt;script&gt;(()=&gt;{var M=Object.create;var m=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var u=(n,e)=&gt;()=&gt;(e||n((e={exports:{}}).exports,e),e.exports);var S=(n,e,o,l)=&gt;{if(e&amp;&amp;typeof e==&quot;object&quot;||typeof e==&quot;function&quot;)for(let s of I(e))!k.call(n,s)&amp;&amp;s!==o&amp;&amp;m(n,s,{get:()=&gt;e[s],enumerable:!(l=D(e,s))||l.enumerable});return n};var r=(n,e,o)=&gt;(o=n!=null?M(O(n)):{},S(e||!n||!n.__esModule?m(o,&quot;default&quot;,{value:n,enumerable:!0}):o,n));var i=u((B,f)=&gt;{f.exports=window.React});var y=u(w=&gt;{var h=r(i());w.useMDXComponents=function(){return{h2:function(o){let{children:l,...s}=o,p=C(l);return h.default.createElement(&quot;h2&quot;,{...s,id:p},l,h.default.createElement(&quot;a&quot;,{className:&quot;header-anchor&quot;,href:`#${p}`},&quot;&#92;xB6&quot;))}}};function C(n){return n.toLowerCase().split(&quot; &quot;).slice(0,6).join(&quot;-&quot;).replace(/[^A-Za-z0-9-]/g,&quot;&quot;)}});var d=r(y()),t=r(i());var a=r(i());function g({children:n}){return a.default.createElement(&quot;aside&quot;,{className:&quot;footnotes&quot;},a.default.createElement(&quot;ol&quot;,null,a.Children.toArray(n).map((e,o)=&gt;a.default.createElement(c,{...e.props,key:o,id:o+1}))))}function c({id:n,children:e}){return a.default.createElement(&quot;li&quot;,{id:`footnote-${n}`},e)}function b({id:n}){return a.default.createElement(&quot;sup&quot;,null,a.default.createElement(&quot;a&quot;,{href:`#footnote-${n}`},n))}function v(n){let e=Object.assign({p:&quot;p&quot;,a:&quot;a&quot;,h2:&quot;h2&quot;,ol:&quot;ol&quot;,li:&quot;li&quot;},(0,d.useMDXComponents)(),n.components);return t.default.createElement(t.default.Fragment,null,t.default.createElement(e.p,null,`When CSS grid first arrived, there were a lot of people saying,
&#92;u201CWhy do we need this? We already have flexbox!&#92;u201D
I&#92;u2019m getting very much the same vibe now with `,t.default.createElement(e.a,{href:&quot;/posts/2023/04/scoped-css-is-back/&quot;},&quot;@scope&quot;),`,
especially from enthusiasts of web components and shadow DOM.`),`
`,t.default.createElement(e.p,null,`But here&#92;u2019s the thing: CSS @scope and shadow DOM are not competing standards.
They&#92;u2019re complementary.
They meet different needs entirely &#92;u2014
but with enough healthy overlap in functionality so there isn&#92;u2019t a gap.`),`
`,t.default.createElement(e.p,null,`So should we make shadow DOM more ergonomic?
Yes please!
I&#92;u2019m all for continuing to improve the specification here.
By promoting @scope, I&#92;u2019m not belittling shadow DOM.
Please don&#92;u2019t take this following statement as a criticism:`),`
`,t.default.createElement(e.h2,null,&quot;Shadow DOM does not provide what @scope does&quot;),`
`,t.default.createElement(e.p,null,`Language is hard, especially when it comes to the terms we use in the industry
to convey various ideas.
The term `,t.default.createElement(e.a,{href:&quot;/posts/2023/07/web-components-arent-components&quot;},&quot;component&quot;),`, for instance,
can mean multiple different things to different developers.`),`
`,t.default.createElement(e.p,null,`Similarly, something different comes to mind for all of us when we hear about
the concept of style scoping.
This is only natural, because we all work in different corners of the industry.
We&#92;u2019ve each had to tackle problems and use cases that others haven&#92;u2019t.`),`
`,t.default.createElement(e.p,null,`It&#92;u2019s so easy to hear that @scope is coming to CSS and think,
&#92;u201Cwait, don&#92;u2019t we already have scoping in the shadow DOM?&#92;u201D &#92;u2014
because we do have scoping in the shadow DOM.
But it&#92;u2019s a very different definition of scoping!`),`
`,t.default.createElement(e.p,null,`Shadow DOM and web components are perfect for some use cases.
If you need to encapsulate something so others can put it on their sites,
this is exactly what you need.
Red Hat does this: the navigation bar at the top of the page on many Red Hat
sites is a web component.
This works well because:`),`
`,t.default.createElement(e.ol,null,`
`,t.default.createElement(e.li,null,&quot;these sites are all branded the same, and&quot;),`
`,t.default.createElement(e.li,null,&quot;they each need the same user experience for navigation.&quot;),`
`),`
`,t.default.createElement(e.p,null,&quot;Something like an embedded social media post is probably another perfect use-case.&quot;),`
`,t.default.createElement(e.p,null,&quot;This is not what @scope is for.&quot;),`
`,t.default.createElement(e.p,null,`Shadow DOM is strongly isolated from the rest of the page.
Essentially, shadow DOM throws away the entire CSS cascade.
It cuts a hole out of the page, and allows you to put something else
in that hole.`),`
`,t.default.createElement(e.p,null,`But this is not everybody&#92;u2019s use case.
If you are working on a single web application, with unique branding
and a consistent user experience, the last thing you want is to isolate
all the parts of the page from the cascade.
You want everything to look and feel the same!
You want the cascade to apply, but with the ability to explicitly control
how it applies.
Components need to be able to override parts of the cascade &#92;u2014 but not be isolated
from it entirely.`),`
`,t.default.createElement(e.p,null,`@scope is about embracing the cascade, and controlling it.
In many ways, this is the exact opposite of what shadow DOM does.`),`
`,t.default.createElement(e.p,null,`Yes, shadow DOM should continue to grow and be improved, but at a fundamental
level, it does not address the same problem @scope does.
Yes, both scope styles, but they are two radically different definitions
of scope. `,t.default.createElement(b,{id:&quot;1&quot;})),`
`,t.default.createElement(e.h2,null,&quot;The greatest shortcoming in our industry&quot;),`
`,t.default.createElement(e.p,null,`I have never personally needed shadow DOM.
Every time I dig in to assess it, I come away frustrated.`),`
`,t.default.createElement(e.p,null,`At first, I thought this was a failing of the spec &#92;u2014 how could they be
so out of touch with the real needs of developers?
But since then I&#92;u2019ve realized that&#92;u2019s not it at all.`),`
`,t.default.createElement(e.p,null,`Instead, I have slowly realized that the needs across our industry vary wildly.
Some developers love shadow DOM, because it does exactly what they need.
But when they look at @scope, they feel
the same way I do when I look at shadow DOM &#92;u2014 it&#92;u2019s not at all the right tool
for their job.`),`
`,t.default.createElement(e.p,null,`This is something our industry is terrible at:
we are really bad at understanding and appreciating the varying needs
of other developers who work on different problems than us.
Why would someone want to colocate their CSS inside their JavaScript files?
Why would someone be so vehement about avoiding a JavaScript framework?
Why would someone want to entirely encapsulate their web components from
the light DOM?
And why would someone want this @scope thing when we already have shadow DOM?`),`
`,t.default.createElement(e.p,null,`We each solve different problems and often have wildly different needs
from our tech tools.
That&#92;u2019s okay.
That&#92;u2019s healthy.`),`
`,t.default.createElement(e.p,null,`For me, and my problem space, shadow DOM is a backwards paradigm to the one I
need. I long for @scope.`),`
`,t.default.createElement(e.p,null,&quot;Maybe you don&#92;u2019t. And that&#92;u2019s fine.&quot;),`
`,t.default.createElement(e.p,null,`But please don&#92;u2019t try to shoot down the one feature that&#92;u2019s been desperately
missing from CSS because you don&#92;u2019t have a personal need for it.`),`
`,t.default.createElement(g,null,t.default.createElement(c,null,t.default.createElement(&quot;p&quot;,null,t.default.createElement(e.p,null,`If I were the king of terminology, I would say that shadow DOM provides
style `,t.default.createElement(&quot;i&quot;,null,&quot;encapsulation&quot;),&quot;, not scoping. But that ship has kinda sailed.&quot;)))))}function E(n={}){let{wrapper:e}=Object.assign({},(0,d.useMDXComponents)(),n.components);return e?t.default.createElement(e,n,t.default.createElement(v,n)):v(n)}var L=E,x=ReactDOM.createRoot(document.getElementById(&quot;mdx-root&quot;));x.render(t.default.createElement(E,null));})();
&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>Web Components Aren’t Components</title>
    <link href="https://keithjgrant.com/posts/2023/07/web-components-arent-components/"/>
    <published>2023-07-26T23:08:00Z</published>
    <updated>2023-07-26T23:08:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/07/web-components-arent-components/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Modern web frameworks are built entirely on the concept of components.
In the realm of these frameworks, a component is a reusable piece of application
code.
All logic is encapsulated in one place, so the component can be dropped into
the application anywhere it’s needed.
Components are nested within one another to compose the structure of the page.&lt;/p&gt;
&lt;p&gt;Vitally, it must be in control of its own styling.
Yes, it should be visually cohesive with the application at large,
but when it comes to a reusable component, you can’t afford for unexpected
styles to suddenly occur when you add the component to a new context.
It’s styles need to be higher priority than the styles outside the
component—including its parent components.
By corollary, a component’s styles should not be overly powerful at impacting
the styles of the component’s child components.&lt;/p&gt;
&lt;h2 id=&quot;enter-%E2%80%9Cweb-components%E2%80%9D&quot; tabindex=&quot;-1&quot;&gt;Enter “web components” &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/07/web-components-arent-components/#enter-%E2%80%9Cweb-components%E2%80%9D&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We also have “web components”.
These are built directly into the platform, comprising something called custom elements.
Custom elements are a web primitive, a way of extending HTML itself.
They are ideal for broad distribution and re-use in various frameworks/sites/apps.
But to be broadly usable, they must allow for the application to have priority assigning styles.&lt;/p&gt;
&lt;p&gt;The idea of a universally re-usable Typahead Lookup component is valiant.
Nobody likes building that over and over again every time they switch JS frameworks
or move to a new project.
But in order for this to work, it must be completely re-stylable.
It needs to be capable of visually matching whatever application it’s used in,
with consistent colors, fonts, spacing, and icon styles.
This means, while it should have some default styling, it must surrender full
control to the outer application.&lt;/p&gt;
&lt;p&gt;This is not a component.&lt;/p&gt;
&lt;p&gt;At least, it’s not the same definition for component that modern frameworks use.&lt;/p&gt;
&lt;p&gt;If I find a &lt;code&gt;&amp;lt;typeahead-lookup&amp;gt;&lt;/code&gt; custom element that meets my application’s
needs functionally, that’s great!
I won’t have to roll my own autocomplete functionality!
But I will still need to wrap it in a component of my own.
I need to apply my application’s styling, and encapsulate the lookahead
in a way that protects it from receiving unwanted styles when it’s used across
my application.
I probably also need to wire it into my application’s data and possibly ensure
certain options are turned on or off so I don’t have to remember them every time
I use the custom element.&lt;/p&gt;
&lt;p&gt;Maybe that’s as simple as wrapping it in a div and applying a class name.
But depending on the architecture of my application at large, it’s highly
likely it will require wrapping it in a full component (React, Vue, or otherwise).&lt;/p&gt;
&lt;p&gt;Now, I get that it is possible to skip this step.
You can build a custom element in such a way that it’s internal parts are
locked away in the shadow DOM and cannot be styled from outside.
In which case, you will have to internalize your application’s styles inside
the shadow DOM.
But that’s tedious to do, not to mention it defeats the whole promise of
“framework agnostic” web components that can be broadly distributed and re-used
in various applications.&lt;/p&gt;
&lt;h2 id=&quot;why-web-components-are-slow-to-catch-on&quot; tabindex=&quot;-1&quot;&gt;Why web components are slow to catch on &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/07/web-components-arent-components/#why-web-components-are-slow-to-catch-on&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think the difficulty with web components is not that they’re bad in and of themselves.
If they came along before the advent of modern JS frameworks, they would
probably be the way things are done at large.&lt;/p&gt;
&lt;p&gt;However, in the current landscape, web components live in an awkward place:
They provide a lot of the same functionality as framework components, but
they are not a one-for-one replacement.
The two provide a lot of the same benefits to the developer, but not &lt;em&gt;all&lt;/em&gt;
the same benefits.&lt;/p&gt;
&lt;p&gt;If you happen to work in a problem space where you need widespread distribution
of custom elements, this may be enticing enough to adopt web components.
But that’s not most developers.&lt;/p&gt;
&lt;p&gt;For a developer familiar with modern web frameworks, web components don’t offer
enough compelling benefits to switch — you still need the framework to
componentize everything and manage the application at a high level.
And if you have a framework, there’s really very little web components can
offer on top of that.&lt;/p&gt;
&lt;h2 id=&quot;web-development-is-a-wide-world&quot; tabindex=&quot;-1&quot;&gt;Web development is a wide world &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/07/web-components-arent-components/#web-development-is-a-wide-world&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I love the web.
I love the promise of open standards.
And I think web components serve a useful role.&lt;/p&gt;
&lt;p&gt;My gut tells me they’re more useful toward the website end of the spectrum
than they are on the web application end — but I have little data or
broad enough experience to back this up.&lt;/p&gt;
&lt;p&gt;I think we also need to be okay accepting that they aren’t the right
solution for everyone.
And we need to acknowledge that we’ve probably done some undue damage by
trying to say they are.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Re-evaluating px vs em in Media Queries</title>
    <link href="https://keithjgrant.com/posts/2023/05/px-vs-em-in-media-queries/"/>
    <published>2023-05-31T18:37:00Z</published>
    <updated>2023-05-31T18:37:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/05/px-vs-em-in-media-queries/</id>
    <content xml:lang="en" type="html">&lt;style&gt; ._mq-table { border-spacing: 0.3rem; } ._mq-table th { font-weight: inherit; vertical-align: baseline; text-align: end; } ._mq-table th[rowspan] { font-weight: bold; } ._mq-table thead th { text-align: center; } ._mq-small { font-size: 0.8rem; } 
.kKooHg { background-color: var(--accent-color-1); width: 35px; height: 35px; }
.kUHaAq { background-color: var(--accent-color-3); width: 35px; height: 35px; }&lt;/style&gt;
  &lt;div id=&quot;mdx-root&quot;&gt;&lt;p&gt;It’s been a while since I considered what unit I should use in media queries.
In the past, I (and many others) have recommended using ems rather than px,
because “it’s the only unit that performs consistently in all major browsers.”
In the past, Safari has been a bit quirky here,
but I know they’ve made some major improvements recently,
so I figure this is worth revisiting.&lt;/p&gt;
&lt;p&gt;Unfortunately, what I found is that there is still no simple answer —
but it is a little different than it used to be, so it’s worth taking note.
You can &lt;a href=&quot;https://keithjgrant.com/posts/2023/05/px-vs-em-in-media-queries/#the-takeaway&quot;&gt;skip to the takeaway&lt;/a&gt; if you want.&lt;/p&gt;
&lt;h2 id=&quot;zoom-default-font-size-etc&quot;&gt;Zoom, default font size, etc.&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/05/px-vs-em-in-media-queries/#zoom-default-font-size-etc&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s consider all the ways users can change the font size in the browser.
Some of these options are harder to find in various browsers,
but here are the options available.&lt;/p&gt;
&lt;p&gt;The first option is zoom, which can be changed using Ctrl+plus and Ctrl+minus
in all major browsers (or by selecting options in the View menu)
This scales everything on the page up and down.
Effectively, this scales a px up and down on the page,
and in turn scales ems and rems up and down.&lt;/p&gt;
&lt;p&gt;The second option is changing the default font size.
This generally changes the meaning of the &lt;code&gt;medium&lt;/code&gt; keyword for font size,
which in turn scales ems and rems up and down, but not px.
This may or may not change the meaning of ems/rems inside a media query,
however — more on that in a minute.
In Safari, this setting is rather difficult to find; you have to open the View
menu, then hold the Option key and select “Make text bigger/smaller”.&lt;/p&gt;
&lt;p&gt;The third option — which I don’t recall seeing when I last checked several
years ago — is setting a minimum font size.
This option doesn’t scale the whole page, but it does guarantee no
fonts ever render at tiny sizes when the user specifies.&lt;/p&gt;
&lt;p&gt;Firefox also has a fourth option, “zoom text size only” which behaves
similar to zoom, but only increases/decreases text size.
As far as I can tell, this behave essentially the same as changing
the default font size.&lt;/p&gt;
&lt;h2 id=&quot;how-i-tested&quot;&gt;How I tested&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/05/px-vs-em-in-media-queries/#how-i-tested&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I created &lt;a href=&quot;https://codepen.io/keithjgrant/pen/ExdBXbZ&quot;&gt;this Codepen&lt;/a&gt;.
It’s got three divs, each with a blue background color.
Then I use different media queries to change each div to a green background
above a certain width.
The queries are set to a min-width of 800px, 50em, and 50rem.
Depending on browser settings and viewport size, it ends up looking something
like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/mq-units.png&quot; alt=&quot;Three boxes with lorem ipsum inside. The first is green and the second two are blue.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here’s the CSS; I used 50em and 50rem since those are equal to the 800px
media query with default font settings:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;211&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;deg&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;px&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.px&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;179&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;deg&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.em&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;179&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;deg&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.rem&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;179&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;deg&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I opened up Chrome, Firefox and Safari and started playing with the settings.
Here’s what I found:&lt;/p&gt;
&lt;table class=&quot;_mq-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th colSpan=&quot;2&quot;&gt;&lt;/th&gt;&lt;th&gt;px&lt;/th&gt;&lt;th&gt;em&lt;/th&gt;&lt;th&gt;rem&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th rowspan=&quot;3&quot;&gt;Zoom&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Firefox&lt;/th&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th rowspan=&quot;3&quot;&gt;Font size&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Firefox&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th rowspan=&quot;3&quot;&gt;Min font size&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Firefox&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th rowspan=&quot;1&quot;&gt;Zoom text only&lt;/th&gt;&lt;th&gt;Firefox&lt;/th&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class=&quot;_mq-small&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;kKooHg&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;scales&lt;/div&gt;&lt;/td&gt;&lt;td&gt;Media query scales with font size&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;kUHaAq&quot;&gt;&lt;div class=&quot;sr-only&quot;&gt;static&lt;/div&gt;&lt;/td&gt;&lt;td&gt;Media query remains static&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I was disappointed to see Safari is still an outlier,
where ems and rems don’t scale in media queries based on the user’s default
font size.&lt;/p&gt;
&lt;p&gt;In the past, rems in media queries have been kind of unpredictable,
but based on these findings, they now behave the same as ems in nearly
every circumstance — with the exception of how they respond to a minimum
font size in Safari.&lt;/p&gt;
&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/05/px-vs-em-in-media-queries/#the-takeaway&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There is no clear-cut winner I can point to after looking at this.
It probably depends on what you want to prioritize.&lt;/p&gt;
&lt;p&gt;If you want consistency across all browsers, you should now use px in your
media queries.&lt;/p&gt;
&lt;p&gt;However, this may not get you exactly the behavior you want for users who
change their default font sizes.
If you want your breakpoints to scale along with the user’s font size,
you should favor ems instead — but keep in mind you won’t get this
benefit Safari unless the user has their minimum font size setting configured.&lt;/p&gt;
&lt;p&gt;I feel like maybe this means it doesn’t matter quite so much as it has in the
past, but there is still no one perfect answer.
At this point, I am comfortable using px again, but I’ll probably evaluate it
on a case-by-case basis.&lt;/p&gt;&lt;/div&gt;&lt;script&gt;(()=&gt;{var L=Object.create;var I=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var z=(a,e)=&gt;()=&gt;(e||a((e={exports:{}}).exports,e),e.exports);var Q=(a,e,s,m)=&gt;{if(e&amp;&amp;typeof e==&quot;object&quot;||typeof e==&quot;function&quot;)for(let o of B(e))!P.call(a,o)&amp;&amp;o!==s&amp;&amp;I(a,o,{get:()=&gt;e[o],enumerable:!(m=Z(e,o))||m.enumerable});return a};var u=(a,e,s)=&gt;(s=a!=null?L(G(a)):{},Q(e||!a||!a.__esModule?I(s,&quot;default&quot;,{value:a,enumerable:!0}):s,a));var p=z((te,q)=&gt;{q.exports=window.React});var M=z(T=&gt;{var y=u(p());T.useMDXComponents=function(){return{h2:function(s){let{children:m,...o}=s,c=V(m);return y.default.createElement(&quot;h2&quot;,{...o,id:c},m,y.default.createElement(&quot;a&quot;,{className:&quot;header-anchor&quot;,href:`#${c}`},&quot;&#92;xB6&quot;))}}};function V(a){return a.toLowerCase().split(&quot; &quot;).slice(0,6).join(&quot;-&quot;).replace(/[^A-Za-z0-9-]/g,&quot;&quot;)}});var S=u(M()),t=u(p());var n=u(p());var j=new Set([&quot;a&quot;,&quot;abbr&quot;,&quot;address&quot;,&quot;area&quot;,&quot;article&quot;,&quot;aside&quot;,&quot;audio&quot;,&quot;b&quot;,&quot;base&quot;,&quot;bdi&quot;,&quot;bdo&quot;,&quot;big&quot;,&quot;blockquote&quot;,&quot;body&quot;,&quot;br&quot;,&quot;button&quot;,&quot;canvas&quot;,&quot;caption&quot;,&quot;cite&quot;,&quot;code&quot;,&quot;col&quot;,&quot;colgroup&quot;,&quot;data&quot;,&quot;datalist&quot;,&quot;dd&quot;,&quot;del&quot;,&quot;details&quot;,&quot;dfn&quot;,&quot;dialog&quot;,&quot;div&quot;,&quot;dl&quot;,&quot;dt&quot;,&quot;em&quot;,&quot;embed&quot;,&quot;fieldset&quot;,&quot;figcaption&quot;,&quot;figure&quot;,&quot;footer&quot;,&quot;form&quot;,&quot;h1&quot;,&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;,&quot;head&quot;,&quot;header&quot;,&quot;hgroup&quot;,&quot;hr&quot;,&quot;html&quot;,&quot;i&quot;,&quot;iframe&quot;,&quot;img&quot;,&quot;input&quot;,&quot;ins&quot;,&quot;kbd&quot;,&quot;keygen&quot;,&quot;label&quot;,&quot;legend&quot;,&quot;li&quot;,&quot;link&quot;,&quot;main&quot;,&quot;map&quot;,&quot;mark&quot;,&quot;menu&quot;,&quot;menuitem&quot;,&quot;meta&quot;,&quot;meter&quot;,&quot;nav&quot;,&quot;noscript&quot;,&quot;object&quot;,&quot;ol&quot;,&quot;optgroup&quot;,&quot;option&quot;,&quot;output&quot;,&quot;p&quot;,&quot;param&quot;,&quot;picture&quot;,&quot;pre&quot;,&quot;progress&quot;,&quot;q&quot;,&quot;rp&quot;,&quot;rt&quot;,&quot;ruby&quot;,&quot;s&quot;,&quot;samp&quot;,&quot;script&quot;,&quot;section&quot;,&quot;select&quot;,&quot;small&quot;,&quot;source&quot;,&quot;span&quot;,&quot;strong&quot;,&quot;style&quot;,&quot;sub&quot;,&quot;summary&quot;,&quot;sup&quot;,&quot;table&quot;,&quot;tbody&quot;,&quot;td&quot;,&quot;textarea&quot;,&quot;tfoot&quot;,&quot;th&quot;,&quot;thead&quot;,&quot;time&quot;,&quot;title&quot;,&quot;tr&quot;,&quot;track&quot;,&quot;u&quot;,&quot;ul&quot;,&quot;use&quot;,&quot;var&quot;,&quot;video&quot;,&quot;wbr&quot;,&quot;circle&quot;,&quot;clipPath&quot;,&quot;defs&quot;,&quot;ellipse&quot;,&quot;foreignObject&quot;,&quot;g&quot;,&quot;image&quot;,&quot;line&quot;,&quot;linearGradient&quot;,&quot;marker&quot;,&quot;mask&quot;,&quot;path&quot;,&quot;pattern&quot;,&quot;polygon&quot;,&quot;polyline&quot;,&quot;radialGradient&quot;,&quot;rect&quot;,&quot;stop&quot;,&quot;svg&quot;,&quot;text&quot;,&quot;tspan&quot;]);var i=u(p());function h(a){let e=Y(a)&gt;&gt;&gt;0;return K(e)}var U=8362;function Y(a){let e=U,s=a.length;for(;s;)e=e*33^a.charCodeAt(--s);return e}var J=/(a)(d)/gi,E=52,_=a=&gt;String.fromCharCode(a+(a&gt;25?39:97));function K(a){let e=&quot;&quot;,s;for(s=Math.abs(a);s&gt;E;s=s/E|0)e=_(s%E)+e;return(_(s%E)+e).replace(J,&quot;$1-$2&quot;)}var $={};function w(a,e){$[a]||($[a]=e)}function f(a,e,s,m){let o=W(e,s,m);return(0,i.useMemo)(()=&gt;{function C(b){let{children:O,css:d,...X}=b,g=[o],N;return d&amp;&amp;(N=h(d),g.push(N)),b.className&amp;&amp;g.push(b.className),i.default.createElement(i.default.Fragment,null,d?i.default.createElement(&quot;style&quot;,null,`.${N} {${d}}`):null,i.default.createElement(a,{...X,className:g.join(&quot; &quot;)},O))}return C.className=o,C},[o])}function A(a,e){let s=D(a,e),m=h(s);w(m,s)}function W(a,e,s){let m=D(a,e),o=s||h(m),c=`.${o} {${m}}`;return w(o,c),o}function D(a,e){return a.reduce((s,m,o)=&gt;`${s}${m}${e[o]||&quot;&quot;}`,&quot;&quot;).replace(/&#92;s+/g,&quot; &quot;)}function x(a,...e){return f(&quot;div&quot;,a,e)}j.forEach(a=&gt;{x[a]=(e,...s)=&gt;f(a,e,s)});x.raw=(a,...e)=&gt;A(a,e);var k=x;function v(){return k.raw`
    ._mq-table {
      border-spacing: 0.3rem;
    }

    ._mq-table th {
      font-weight: inherit;
      vertical-align: baseline;
      text-align: end;
    }

    ._mq-table th[rowspan] {
      font-weight: bold;
    }

    ._mq-table thead th {
      text-align: center;
    }

    ._mq-small {
      font-size: 0.8rem;
    }
  `,n.default.createElement(n.default.Fragment,null,n.default.createElement(&quot;table&quot;,{className:&quot;_mq-table&quot;},n.default.createElement(&quot;thead&quot;,null,n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,{colSpan:&quot;2&quot;}),n.default.createElement(&quot;th&quot;,null,&quot;px&quot;),n.default.createElement(&quot;th&quot;,null,&quot;em&quot;),n.default.createElement(&quot;th&quot;,null,&quot;rem&quot;))),n.default.createElement(&quot;tbody&quot;,null,n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,{rowSpan:&quot;3&quot;},&quot;Zoom&quot;),n.default.createElement(&quot;th&quot;,null,&quot;Chrome&quot;),n.default.createElement(r,null),n.default.createElement(r,null),n.default.createElement(r,null)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,null,&quot;Firefox&quot;),n.default.createElement(r,null),n.default.createElement(r,null),n.default.createElement(r,null)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,null,&quot;Safari&quot;),n.default.createElement(r,null),n.default.createElement(r,null),n.default.createElement(r,null))),n.default.createElement(&quot;tbody&quot;,null,n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,{rowSpan:&quot;3&quot;},&quot;Font size&quot;),n.default.createElement(&quot;th&quot;,null,&quot;Chrome&quot;),n.default.createElement(l,null),n.default.createElement(r,null),n.default.createElement(r,null)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,null,&quot;Firefox&quot;),n.default.createElement(l,null),n.default.createElement(r,null),n.default.createElement(r,null)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,null,&quot;Safari&quot;),n.default.createElement(l,null),n.default.createElement(l,null),n.default.createElement(l,null))),n.default.createElement(&quot;tbody&quot;,null,n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,{rowSpan:&quot;3&quot;},&quot;Min font size&quot;),n.default.createElement(&quot;th&quot;,null,&quot;Chrome&quot;),n.default.createElement(l,null),n.default.createElement(l,null),n.default.createElement(l,null)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,null,&quot;Firefox&quot;),n.default.createElement(l,null),n.default.createElement(l,null),n.default.createElement(l,null)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,null,&quot;Safari&quot;),n.default.createElement(l,null),n.default.createElement(r,null),n.default.createElement(l,null))),n.default.createElement(&quot;tbody&quot;,null,n.default.createElement(&quot;tr&quot;,null,n.default.createElement(&quot;th&quot;,{rowSpan:&quot;1&quot;},&quot;Zoom text only&quot;),n.default.createElement(&quot;th&quot;,null,&quot;Firefox&quot;),n.default.createElement(l,null),n.default.createElement(r,null),n.default.createElement(r,null)))),n.default.createElement(&quot;table&quot;,{className:&quot;_mq-small&quot;},n.default.createElement(&quot;tbody&quot;,null,n.default.createElement(&quot;tr&quot;,null,n.default.createElement(r,null),n.default.createElement(&quot;td&quot;,null,&quot;Media query scales with font size&quot;)),n.default.createElement(&quot;tr&quot;,null,n.default.createElement(l,null),n.default.createElement(&quot;td&quot;,null,&quot;Media query remains static&quot;)))))}function r(){let a=k.td`
    background-color: var(--accent-color-1);
    width: 35px;
    height: 35px;
  `;return n.default.createElement(a,null,n.default.createElement(&quot;div&quot;,{className:&quot;sr-only&quot;},&quot;scales&quot;))}function l(){let a=k.td`
    background-color: var(--accent-color-3);
    width: 35px;
    height: 35px;
  `;return n.default.createElement(a,null,n.default.createElement(&quot;div&quot;,{className:&quot;sr-only&quot;},&quot;static&quot;))}function F(a){let e=Object.assign({p:&quot;p&quot;,a:&quot;a&quot;,h2:&quot;h2&quot;,code:&quot;code&quot;,img:&quot;img&quot;,pre:&quot;pre&quot;,span:&quot;span&quot;},(0,S.useMDXComponents)(),a.components);return t.default.createElement(t.default.Fragment,null,t.default.createElement(e.p,null,`It&#92;u2019s been a while since I considered what unit I should use in media queries.
In the past, I (and many others) have recommended using ems rather than px,
because &#92;u201Cit&#92;u2019s the only unit that performs consistently in all major browsers.&#92;u201D
In the past, Safari has been a bit quirky here,
but I know they&#92;u2019ve made some major improvements recently,
so I figure this is worth revisiting.`),`
`,t.default.createElement(e.p,null,`Unfortunately, what I found is that there is still no simple answer &#92;u2014
but it is a little different than it used to be, so it&#92;u2019s worth taking note.
You can `,t.default.createElement(e.a,{href:&quot;#the-takeaway&quot;},&quot;skip to the takeaway&quot;),&quot; if you want.&quot;),`
`,t.default.createElement(e.h2,null,&quot;Zoom, default font size, etc.&quot;),`
`,t.default.createElement(e.p,null,`Let&#92;u2019s consider all the ways users can change the font size in the browser.
Some of these options are harder to find in various browsers,
but here are the options available.`),`
`,t.default.createElement(e.p,null,`The first option is zoom, which can be changed using Ctrl+plus and Ctrl+minus
in all major browsers (or by selecting options in the View menu)
This scales everything on the page up and down.
Effectively, this scales a px up and down on the page,
and in turn scales ems and rems up and down.`),`
`,t.default.createElement(e.p,null,`The second option is changing the default font size.
This generally changes the meaning of the `,t.default.createElement(e.code,null,&quot;medium&quot;),` keyword for font size,
which in turn scales ems and rems up and down, but not px.
This may or may not change the meaning of ems/rems inside a media query,
however &#92;u2014 more on that in a minute.
In Safari, this setting is rather difficult to find; you have to open the View
menu, then hold the Option key and select &#92;u201CMake text bigger/smaller&#92;u201D.`),`
`,t.default.createElement(e.p,null,`The third option &#92;u2014 which I don&#92;u2019t recall seeing when I last checked several
years ago &#92;u2014 is setting a minimum font size.
This option doesn&#92;u2019t scale the whole page, but it does guarantee no
fonts ever render at tiny sizes when the user specifies.`),`
`,t.default.createElement(e.p,null,`Firefox also has a fourth option, &#92;u201Czoom text size only&#92;u201D which behaves
similar to zoom, but only increases/decreases text size.
As far as I can tell, this behave essentially the same as changing
the default font size.`),`
`,t.default.createElement(e.h2,null,&quot;How I tested&quot;),`
`,t.default.createElement(e.p,null,&quot;I created &quot;,t.default.createElement(e.a,{href:&quot;https://codepen.io/keithjgrant/pen/ExdBXbZ&quot;},&quot;this Codepen&quot;),`.
It&#92;u2019s got three divs, each with a blue background color.
Then I use different media queries to change each div to a green background
above a certain width.
The queries are set to a min-width of 800px, 50em, and 50rem.
Depending on browser settings and viewport size, it ends up looking something
like this:`),`
`,t.default.createElement(e.p,null,t.default.createElement(e.img,{src:&quot;/images/2023/mq-units.png&quot;,alt:&quot;Three boxes with lorem ipsum inside. The first is green and the second two are blue.&quot;})),`
`,t.default.createElement(e.p,null,`Here&#92;u2019s the CSS; I used 50em and 50rem since those are equal to the 800px
media query with default font settings:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token selector&quot;},&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;padding&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;2&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;margin-block&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;hsl&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;211&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;deg&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;40&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;80&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@media&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;min-width&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;800&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;px&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.px&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;hsl&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;179&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;deg&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;40&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;80&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@media&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;min-width&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;50&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;em&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.em&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;hsl&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;179&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;deg&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;40&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;80&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@media&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;min-width&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;50&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.rem&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;hsl&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;179&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;deg&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;40&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;80&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`Then I opened up Chrome, Firefox and Safari and started playing with the settings.
Here&#92;u2019s what I found:`),`
`,t.default.createElement(v),`
`,t.default.createElement(e.p,null,`I was disappointed to see Safari is still an outlier,
where ems and rems don&#92;u2019t scale in media queries based on the user&#92;u2019s default
font size.`),`
`,t.default.createElement(e.p,null,`In the past, rems in media queries have been kind of unpredictable,
but based on these findings, they now behave the same as ems in nearly
every circumstance &#92;u2014 with the exception of how they respond to a minimum
font size in Safari.`),`
`,t.default.createElement(e.h2,null,&quot;The takeaway&quot;),`
`,t.default.createElement(e.p,null,`There is no clear-cut winner I can point to after looking at this.
It probably depends on what you want to prioritize.`),`
`,t.default.createElement(e.p,null,`If you want consistency across all browsers, you should now use px in your
media queries.`),`
`,t.default.createElement(e.p,null,`However, this may not get you exactly the behavior you want for users who
change their default font sizes.
If you want your breakpoints to scale along with the user&#92;u2019s font size,
you should favor ems instead &#92;u2014 but keep in mind you won&#92;u2019t get this
benefit Safari unless the user has their minimum font size setting configured.`),`
`,t.default.createElement(e.p,null,`I feel like maybe this means it doesn&#92;u2019t matter quite so much as it has in the
past, but there is still no one perfect answer.
At this point, I am comfortable using px again, but I&#92;u2019ll probably evaluate it
on a case-by-case basis.`))}function H(a={}){let{wrapper:e}=Object.assign({},(0,S.useMDXComponents)(),a.components);return e?t.default.createElement(e,a,t.default.createElement(F,a)):F(a)}var Ee=H,R=ReactDOM.createRoot(document.getElementById(&quot;mdx-root&quot;));R.render(t.default.createElement(H,null));})();
&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>The Webdev Social Schism</title>
    <link href="https://keithjgrant.com/posts/2023/05/the-webdev-social-schism/"/>
    <published>2023-05-02T18:16:00Z</published>
    <updated>2023-05-02T18:16:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/05/the-webdev-social-schism/</id>
    <content xml:lang="en" type="html">&lt;p&gt;A while back, &lt;a href=&quot;https://front-end.social/@zachleat@zachleat.com/109989741752470227&quot;&gt;Zach Leatherman commented&lt;/a&gt;
that Eleventy will need to maintain a presence on Twitter to stay in touch
with portions of the webdev community.
In particular, the “JavaScript folks” that have “dug in there,”
and this really struck a cord with something I’ve been feeling in my gut for
a while now.&lt;/p&gt;
&lt;p&gt;I’m afraid the web development community has fractured in two.&lt;/p&gt;
&lt;p&gt;On one side, we have the “open web” idealists,
who were eager to move their online social presence to the Fediverse.
They love that it’s decentralized.
They value that it’s not beholden to any capitalistic influence.
They either turn a blind eye to
&lt;a href=&quot;https://erinkissane.com/blue-skies-over-mastodon&quot;&gt;the flaws of Mastodon&lt;/a&gt;,
or believe they are far outweighed by the benefits.&lt;/p&gt;
&lt;p&gt;On the other side, we have the Twitter holdouts.
They tend to poke fun at the idealism of Mastodon and the first group’s
doe-eyed embracing of it.
Sure, these folks enjoy laughing at Elon Musk’s fumbling of Twitter,
but they aren’t about to leave unless it collapses entirely.
And, hey wouldn’t you know it, Jack Dorsey has an alternative in the works
that this group is eager to get on board with.&lt;/p&gt;
&lt;p&gt;Obviously, these are generalizations.
There are plenty of people who are maintaining a presence on both networks,
or are mostly on one because that’s where more of their social circle happens
to be now.&lt;/p&gt;
&lt;p&gt;I suspect this is highly correlated with Chris Coyier‘s
&lt;a href=&quot;https://css-tricks.com/the-great-divide/&quot;&gt;“Great Divide”&lt;/a&gt; —
a sort of next step in the evolution of those differences.&lt;/p&gt;
&lt;h2 id=&quot;the-political-factor&quot; tabindex=&quot;-1&quot;&gt;The political factor &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/05/the-webdev-social-schism/#the-political-factor&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Twitter was a fantastic thing for the webdev community.
In many ways, it made the community what it is today.
And plenty of words have been said mourning the apparent pending loss
of that era.&lt;/p&gt;
&lt;p&gt;But what really concerns me now,
as things have been shaking out for half a year now,
is how much this divide feels like a political one.&lt;/p&gt;
&lt;p&gt;By and large, it feels to me like the Mastodon contingent tends to be mostly
liberal — skeptical of the capitalist idealogy of a corporate-run social network.
And the Twitter holdouts tends to be more conservative — derisive of the
hippie-dippy decentralized network.&lt;/p&gt;
&lt;h2 id=&quot;this-feels-ugly&quot; tabindex=&quot;-1&quot;&gt;This feels ugly &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/05/the-webdev-social-schism/#this-feels-ugly&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I don’t want to be on only one side of this.
I don’t want &lt;em&gt;anybody&lt;/em&gt; to be on only one side of this.
While there’s certainly something comfortable about huddling up with
folks I agree with, I want to participate with the entirety of our
industry.
And I certainly don’t want to see us split long-term along a largely
political divide. That’s gross.&lt;/p&gt;
&lt;p&gt;I don’t know what the solution is.
I’m wondering whether I need to pick my Twitter account back up,
or get active on Blue Sky, if I can ever get in there.
I have major ideological problems with both of them,
but maybe I need to choose more important priorities than those ideals.
Maybe it’s worth a compromise there in order to not
feed the divide further.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Scoped CSS is Back</title>
    <link href="https://keithjgrant.com/posts/2023/04/scoped-css-is-back/"/>
    <published>2023-04-24T15:54:00Z</published>
    <updated>2023-04-24T15:54:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/04/scoped-css-is-back/</id>
    <content xml:lang="en" type="html">&lt;style&gt; @scope (._green) { p { color: green; } } @scope (._blue) { p { color: blue; } } &lt;/style&gt;
  &lt;div id=&quot;mdx-root&quot;&gt;&lt;p&gt;Several years ago, I made a plea to &lt;a href=&quot;https://keithjgrant.com/posts/2016/06/save-scoped-css/&quot;&gt;save scoped CSS&lt;/a&gt;.
One of the top features on my CSS wishlist was on the chopping block,
and despite a &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/3547&quot;&gt;pretty big push&lt;/a&gt;
from the community, it died.&lt;/p&gt;
&lt;p&gt;Well, great news — it’s back.
And it’s so much better than the previous version.&lt;/p&gt;
&lt;p&gt;Even better, the W3C spec is mostly stable,
and there’s a working prototype in Chrome now.
We just need a little interest from the community to entice other
browsers to build their implementations and kick this over the finish line.&lt;/p&gt;
&lt;h2 id=&quot;whats-the-idea&quot;&gt;What’s the idea&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back/#whats-the-idea&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are two key things scope brings to CSS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;More control over which selectors target which elements (i.e. better manipulation
of the cascade)&lt;/li&gt;
&lt;li&gt;The ability for one set of styles to override another based on &lt;em&gt;proximity&lt;/em&gt;
in the DOM&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Scoped styles allow you to contain a set of styles within a single component
on the page.
You can use a &lt;code&gt;.title&lt;/code&gt; selector that only works within a Card component,
and a separate &lt;code&gt;.title&lt;/code&gt; selector that only works in an Accordion.
You can stop selectors from one component from targeting elements
in a child component — or you can allow them reach in, if that’s what you need.&lt;/p&gt;
&lt;p&gt;You will not need BEM-style classnames anymore.&lt;/p&gt;
&lt;p&gt;Furthermore, proximity becomes a first-class citizen in the cascade.
If two components target the same element (with the same specificity),
the inner component’s styles will override those of the outer component.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back/#how-it-works&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It all starts with the &lt;code&gt;@scope&lt;/code&gt; rule and a selector, like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* Scope the following styles to inside `.card` */&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token pseudo-class&quot;&gt;:scope&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token color&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.title&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Georgia&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These styles are all scoped to &lt;code&gt;.card&lt;/code&gt; elements.
&lt;code&gt;:scope&lt;/code&gt; is a special pseudo-class that targets the &lt;code&gt;.card&lt;/code&gt; element itself,
and &lt;code&gt;.title&lt;/code&gt; targets titles inside cards.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;@scope&lt;/code&gt; rule itself adds no specificity to these selectors, so they’re
both (0, 1, 0).
Yes, specificity still matters, but that’s a Good Thing™️. More on that shortly.&lt;/p&gt;
&lt;p&gt;At this point, this is nothing you can’t already do with regular descendant
selectors.
But new, previously impossible options start opening up when you apply an
inner bound to the scope or overlap multiple scopes on the page.
Let’s see what those do…&lt;/p&gt;
&lt;h3&gt;Inner scope bound&lt;/h3&gt;
&lt;p&gt;Let’s say you anticipate putting other components inside your Cards,
so you don’t want that &lt;code&gt;.title&lt;/code&gt; selector to target anything other than
the one title that belongs to the Card.
To do that, you put an inner-bound on the scope like so:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; to &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.slot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* Scoped styles target only inside `.card` but not inside `.slot` */&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token pseudo-class&quot;&gt;:scope&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token color&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.title&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Georgia&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Think of the “to” keyword here like “until”:
this scope is defined from &lt;code&gt;.card&lt;/code&gt; to &lt;code&gt;.slot&lt;/code&gt;.
Now, none of the scoped selectors will target anything inside the Card’s
&lt;code&gt;.slot&lt;/code&gt; element.
So you can build your card like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Moon lander&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;slot&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- scoped styles won’t target anything here! --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reach of the scope is restricted, keeping it from targeting anything inside
&lt;code&gt;.slot&lt;/code&gt;.
This way, you can nest two scopes, and each one can make use of the same
generic &lt;code&gt;title&lt;/code&gt; class name without conflicting.
In fact, you might not even need the class name anymore at all:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; to &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.slot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;h3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Georgia&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.accordion&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; to &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.slot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;h3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;text-transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; uppercase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;letter-spacing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.01&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can put an Accordion inside a Card — or a Card inside an Accordion — and they will
each style their own &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;s without conflicting.&lt;/p&gt;
&lt;p&gt;This has been colloquially named “donut scoping” since the scope has a hole in it.
(It can also have multiple holes in it, if the inner bound selector targets
multiple elements.)
&lt;a href=&quot;https://www.miriamsuzanne.com/&quot;&gt;Miriam Suzanne&lt;/a&gt; suggest a possible way to use
this is to consistently use &lt;code&gt;data-*&lt;/code&gt; attributes and attribute selectors for
your scope:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;[data-scope=&lt;span class=&quot;token string&quot;&gt;&amp;#x27;media&amp;#x27;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; to &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;scope [data-scope]&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* scoped styles go here */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…though I find I prefer the simple selectors that come from a
class-based approach.
But maybe that’s just my old BEM habits showing.&lt;/p&gt;
&lt;h3&gt;Proximity precedence&lt;/h3&gt;
&lt;p&gt;The other aspect to scoping is the concept of proximity:
styles from an inner scope will override those from an outer scope.
Imagine you have two scopes like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.&lt;span class=&quot;token color&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token color&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.&lt;span class=&quot;token color&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token color&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply these to the following HTML.
These have no inner scope bound, so both &lt;code&gt;p&lt;/code&gt; selectors target
the inner paragraphs here.
In this case, the inner scope always takes precedence:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;green&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;I’m green&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;blue&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;I’m blue&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;blue&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;I’m blue&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;green&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;But I’m green&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here’s a working demo — Note this currently only works in Chrome with the
Experimental Web Platform Features flag turned on in
&lt;code&gt;chrome://flags&lt;/code&gt;. &lt;sup&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back/#footnote-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class=&quot;demo-container&quot;&gt;&lt;div&gt;&lt;div class=&quot;_green&quot;&gt;&lt;p&gt;I’m green&lt;/p&gt;&lt;div class=&quot;_blue&quot;&gt;&lt;p&gt;I’m blue&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;_blue&quot;&gt;&lt;p&gt;I’m blue&lt;/p&gt;&lt;div class=&quot;_green&quot;&gt;&lt;p&gt;But I’m green&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can inspect this in DevTools and see each scope overriding the other,
based on which one has the closest proximity:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/scope/scope-devtools-1.png&quot; alt=&quot;DevTools showing the green scope winning over the blue&quot; /&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/scope/scope-devtools-2.png&quot; alt=&quot;DevTools showing the blue scope winning over the green&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The catch here is that
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity&quot;&gt;selector specificity&lt;/a&gt;
still takes precedence, so if the outer scope targets an element with higher
specificity than the inner one, the outer scope’s styles will apply.
This was a &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/6790&quot;&gt;debated issue&lt;/a&gt;
during the development of the specification,
but — to the surprise of my past self — I think it’s the right call.&lt;/p&gt;
&lt;p&gt;This way, when two scopes target the same element,
you maintain control over which takes precedence.
Instead of the inner scope always winning,
you can tweak selector specificities so the higher-specificity
selector takes precedence, regardless which scope it belongs to.&lt;/p&gt;
&lt;p&gt;And when you don’t want this behavior, you have a few ways to prevent it.
You make use of
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers&quot;&gt;cascade layers&lt;/a&gt;
to give one component — or just parts of one component — precedence over another.
Or, you can apply an inner scope bound to the outer scope to keep it from happening.
After experimenting with scope for a bit, this feels to me like the right balance.
It gives you the most control, rather than leaving you beholden
to a rigid set of rules of the cascade.&lt;/p&gt;
&lt;h2 id=&quot;this-is-a-game-changer&quot;&gt;This is a game changer&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back/#this-is-a-game-changer&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you’ve developed large-scale apps and had to rely on CSS-in-JS libraries to
prevent class name collisions, I hope you can see the benefit this offers.
If you’ve rolled out complex BEM class name systems and fought to keep
all your selector specificities equal, think of the freedom
this can bring.
If you’ve ever used the shadow DOM to isolate styles but it was too heavy-handed,
this is a better way
(though there are still use-cases for Shadow DOM, of course).&lt;/p&gt;
&lt;p&gt;I can’t even imagine all the new ways we’ll be able to structure our code.
I think this will be as big as custom properties or even flexbox and grid.
Here are just a few ideas that I’ll certainly be experimenting with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define parts of a component with an inner bound and parts of it without, so
the scope of its ”chrome” styles (i.e. wrapper, toggle buttons, etc) don’t
affect its child contents, but it can influence the appearance of text within.&lt;/li&gt;
&lt;li&gt;Define portions of a component on different cascade layers so it can influence
its contained scopes but remain simple to override on a higher layer.&lt;/li&gt;
&lt;li&gt;Nested color themes.&lt;/li&gt;
&lt;li&gt;Easier ways to prevent style collisions in embedded demos in my blog posts.&lt;/li&gt;
&lt;li&gt;Container queries—What can we come up with by mixing and matching with those?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;we-need-more-browsers-on-board&quot;&gt;We need more browsers on board&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/scoped-css-is-back/#we-need-more-browsers-on-board&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point, Chrome seems to be on board—they’ve had the first working
prototype for several months now. It might be slightly behind the latest changes
to the spec, so keep an eye out for a few minor changes coming if you play
around with it.&lt;/p&gt;
&lt;p&gt;Firefox seems more reluctant—possibly because they got burned as the only
implementors of the first version of scope several years ago?
I’m not sure what the best way is to nudge them along.
Perhaps we just need more people blogging about this feature and creating buzz.
There is &lt;a href=&quot;https://github.com/mozilla/standards-positions/issues/472&quot;&gt;this open issue&lt;/a&gt;
requesting their position on the feature.
Maybe a little activity there would help.&lt;/p&gt;
&lt;p&gt;It sounds like Safari has expressed interest,
but it never hurts to let them know if the community would like to see it.
Maybe the next time &lt;a href=&quot;https://front-end.social/@jensimmons&quot;&gt;Jen Simmons&lt;/a&gt; takes
a poll on most requested features, let her know you want to see &lt;code&gt;@scope&lt;/code&gt; happen.&lt;/p&gt;
&lt;p&gt;Until then, have fun experimenting in Chrome!&lt;/p&gt;
&lt;aside class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;footnote-1&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;I tried to feature detect and hide this demo for browsers that don’t
support it, but I can&amp;#x27;t find quick a way to feature detect for &lt;code&gt;@scope&lt;/code&gt;
neatly, since &lt;code&gt;:scope&lt;/code&gt; is in fact an existing selector that is supported
in other browsers. Hopefully browsers will start supporting &lt;code&gt;@supports       at-rule(…)&lt;/code&gt; soon.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/aside&gt;&lt;/div&gt;&lt;script&gt;(()=&gt;{var G=Object.create;var I=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty;var M=(n,e)=&gt;()=&gt;(e||n((e={exports:{}}).exports,e),e.exports);var K=(n,e,a,l)=&gt;{if(e&amp;&amp;typeof e==&quot;object&quot;||typeof e==&quot;function&quot;)for(let s of U(e))!Z.call(n,s)&amp;&amp;s!==a&amp;&amp;I(n,s,{get:()=&gt;e[s],enumerable:!(l=X(e,s))||l.enumerable});return n};var r=(n,e,a)=&gt;(a=n!=null?G(J(n)):{},K(e||!n||!n.__esModule?I(a,&quot;default&quot;,{value:n,enumerable:!0}):a,n));var m=M((le,T)=&gt;{T.exports=window.React});var D=M(A=&gt;{var f=r(m());A.useMDXComponents=function(){return{h2:function(a){let{children:l,...s}=a,i=Q(l);return f.default.createElement(&quot;h2&quot;,{...s,id:i},l,f.default.createElement(&quot;a&quot;,{className:&quot;header-anchor&quot;,href:`#${i}`},&quot;&#92;xB6&quot;))}}};function Q(n){return n.toLowerCase().split(&quot; &quot;).slice(0,6).join(&quot;-&quot;).replace(/[^A-Za-z0-9-]/g,&quot;&quot;)}});var C=r(D()),t=r(m());var q=r(m());function y({children:n}){return q.default.createElement(&quot;div&quot;,{className:&quot;demo-container&quot;},n)}var o=r(m());var j=new Set([&quot;a&quot;,&quot;abbr&quot;,&quot;address&quot;,&quot;area&quot;,&quot;article&quot;,&quot;aside&quot;,&quot;audio&quot;,&quot;b&quot;,&quot;base&quot;,&quot;bdi&quot;,&quot;bdo&quot;,&quot;big&quot;,&quot;blockquote&quot;,&quot;body&quot;,&quot;br&quot;,&quot;button&quot;,&quot;canvas&quot;,&quot;caption&quot;,&quot;cite&quot;,&quot;code&quot;,&quot;col&quot;,&quot;colgroup&quot;,&quot;data&quot;,&quot;datalist&quot;,&quot;dd&quot;,&quot;del&quot;,&quot;details&quot;,&quot;dfn&quot;,&quot;dialog&quot;,&quot;div&quot;,&quot;dl&quot;,&quot;dt&quot;,&quot;em&quot;,&quot;embed&quot;,&quot;fieldset&quot;,&quot;figcaption&quot;,&quot;figure&quot;,&quot;footer&quot;,&quot;form&quot;,&quot;h1&quot;,&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;,&quot;head&quot;,&quot;header&quot;,&quot;hgroup&quot;,&quot;hr&quot;,&quot;html&quot;,&quot;i&quot;,&quot;iframe&quot;,&quot;img&quot;,&quot;input&quot;,&quot;ins&quot;,&quot;kbd&quot;,&quot;keygen&quot;,&quot;label&quot;,&quot;legend&quot;,&quot;li&quot;,&quot;link&quot;,&quot;main&quot;,&quot;map&quot;,&quot;mark&quot;,&quot;menu&quot;,&quot;menuitem&quot;,&quot;meta&quot;,&quot;meter&quot;,&quot;nav&quot;,&quot;noscript&quot;,&quot;object&quot;,&quot;ol&quot;,&quot;optgroup&quot;,&quot;option&quot;,&quot;output&quot;,&quot;p&quot;,&quot;param&quot;,&quot;picture&quot;,&quot;pre&quot;,&quot;progress&quot;,&quot;q&quot;,&quot;rp&quot;,&quot;rt&quot;,&quot;ruby&quot;,&quot;s&quot;,&quot;samp&quot;,&quot;script&quot;,&quot;section&quot;,&quot;select&quot;,&quot;small&quot;,&quot;source&quot;,&quot;span&quot;,&quot;strong&quot;,&quot;style&quot;,&quot;sub&quot;,&quot;summary&quot;,&quot;sup&quot;,&quot;table&quot;,&quot;tbody&quot;,&quot;td&quot;,&quot;textarea&quot;,&quot;tfoot&quot;,&quot;th&quot;,&quot;thead&quot;,&quot;time&quot;,&quot;title&quot;,&quot;tr&quot;,&quot;track&quot;,&quot;u&quot;,&quot;ul&quot;,&quot;use&quot;,&quot;var&quot;,&quot;video&quot;,&quot;wbr&quot;,&quot;circle&quot;,&quot;clipPath&quot;,&quot;defs&quot;,&quot;ellipse&quot;,&quot;foreignObject&quot;,&quot;g&quot;,&quot;image&quot;,&quot;line&quot;,&quot;linearGradient&quot;,&quot;marker&quot;,&quot;mask&quot;,&quot;path&quot;,&quot;pattern&quot;,&quot;polygon&quot;,&quot;polyline&quot;,&quot;radialGradient&quot;,&quot;rect&quot;,&quot;stop&quot;,&quot;svg&quot;,&quot;text&quot;,&quot;tspan&quot;]);var p=r(m());function u(n){let e=R(n)&gt;&gt;&gt;0;return te(e)}var V=8362;function R(n){let e=V,a=n.length;for(;a;)e=e*33^n.charCodeAt(--a);return e}var ee=/(a)(d)/gi,h=52,_=n=&gt;String.fromCharCode(n+(n&gt;25?39:97));function te(n){let e=&quot;&quot;,a;for(a=Math.abs(n);a&gt;h;a=a/h|0)e=_(a%h)+e;return(_(a%h)+e).replace(ee,&quot;$1-$2&quot;)}var z={};function b(n,e){z[n]||(z[n]=e)}function k(n,e,a,l){let s=ne(e,a,l);return(0,p.useMemo)(()=&gt;{function x(d){let{children:P,css:E,...Y}=d,N=[s],g;return E&amp;&amp;(g=u(E),N.push(g)),d.className&amp;&amp;N.push(d.className),p.default.createElement(p.default.Fragment,null,E?p.default.createElement(&quot;style&quot;,null,`.${g} {${E}}`):null,p.default.createElement(n,{...Y,className:N.join(&quot; &quot;)},P))}return x.className=s,x},[s])}function $(n,e){let a=F(n,e),l=u(a);b(l,a)}function ne(n,e,a){let l=F(n,e),s=a||u(l),i=`.${s} {${l}}`;return b(s,i),s}function F(n,e){return n.reduce((a,l,s)=&gt;`${a}${l}${e[s]||&quot;&quot;}`,&quot;&quot;).replace(/&#92;s+/g,&quot; &quot;)}function w(n,...e){return k(&quot;div&quot;,n,e)}j.forEach(n=&gt;{w[n]=(e,...a)=&gt;k(n,e,a)});w.raw=(n,...e)=&gt;$(n,e);var L=w;function v(){return L.raw`
    @scope (._green) {
      p {
        color: green;
      }
    }

    @scope (._blue) {
      p {
        color: blue;
      }
    }
  `,o.default.createElement(&quot;div&quot;,null,o.default.createElement(&quot;div&quot;,{className:&quot;_green&quot;},o.default.createElement(&quot;p&quot;,null,&quot;I&#92;u2019m green&quot;),o.default.createElement(&quot;div&quot;,{className:&quot;_blue&quot;},o.default.createElement(&quot;p&quot;,null,&quot;I&#92;u2019m blue&quot;))),o.default.createElement(&quot;div&quot;,{className:&quot;_blue&quot;},o.default.createElement(&quot;p&quot;,null,&quot;I&#92;u2019m blue&quot;),o.default.createElement(&quot;div&quot;,{className:&quot;_green&quot;},o.default.createElement(&quot;p&quot;,null,&quot;But I&#92;u2019m green&quot;))))}var c=r(m());function O({children:n}){return c.default.createElement(&quot;aside&quot;,{className:&quot;footnotes&quot;},c.default.createElement(&quot;ol&quot;,null,c.Children.toArray(n).map((e,a)=&gt;c.default.createElement(S,{...e.props,key:a,id:a+1}))))}function S({id:n,children:e}){return c.default.createElement(&quot;li&quot;,{id:`footnote-${n}`},e)}function B({id:n}){return c.default.createElement(&quot;sup&quot;,null,c.default.createElement(&quot;a&quot;,{href:`#footnote-${n}`},n))}function H(n){let e=Object.assign({p:&quot;p&quot;,a:&quot;a&quot;,h2:&quot;h2&quot;,ol:&quot;ol&quot;,li:&quot;li&quot;,em:&quot;em&quot;,code:&quot;code&quot;,pre:&quot;pre&quot;,span:&quot;span&quot;,h3:&quot;h3&quot;,img:&quot;img&quot;,ul:&quot;ul&quot;},(0,C.useMDXComponents)(),n.components);return t.default.createElement(t.default.Fragment,null,t.default.createElement(e.p,null,&quot;Several years ago, I made a plea to &quot;,t.default.createElement(e.a,{href:&quot;/posts/2016/06/save-scoped-css/&quot;},&quot;save scoped CSS&quot;),`.
One of the top features on my CSS wishlist was on the chopping block,
and despite a `,t.default.createElement(e.a,{href:&quot;https://github.com/w3c/csswg-drafts/issues/3547&quot;},&quot;pretty big push&quot;),`
from the community, it died.`),`
`,t.default.createElement(e.p,null,`Well, great news &#92;u2014 it&#92;u2019s back.
And it&#92;u2019s so much better than the previous version.`),`
`,t.default.createElement(e.p,null,`Even better, the W3C spec is mostly stable,
and there&#92;u2019s a working prototype in Chrome now.
We just need a little interest from the community to entice other
browsers to build their implementations and kick this over the finish line.`),`
`,t.default.createElement(e.h2,null,&quot;What&#92;u2019s the idea&quot;),`
`,t.default.createElement(e.p,null,&quot;There are two key things scope brings to CSS:&quot;),`
`,t.default.createElement(e.ol,null,`
`,t.default.createElement(e.li,null,`More control over which selectors target which elements (i.e. better manipulation
of the cascade)`),`
`,t.default.createElement(e.li,null,&quot;The ability for one set of styles to override another based on &quot;,t.default.createElement(e.em,null,&quot;proximity&quot;),`
in the DOM`),`
`),`
`,t.default.createElement(e.p,null,`Scoped styles allow you to contain a set of styles within a single component
on the page.
You can use a `,t.default.createElement(e.code,null,&quot;.title&quot;),` selector that only works within a Card component,
and a separate `,t.default.createElement(e.code,null,&quot;.title&quot;),` selector that only works in an Accordion.
You can stop selectors from one component from targeting elements
in a child component &#92;u2014 or you can allow them reach in, if that&#92;u2019s what you need.`),`
`,t.default.createElement(e.p,null,&quot;You will not need BEM-style classnames anymore.&quot;),`
`,t.default.createElement(e.p,null,`Furthermore, proximity becomes a first-class citizen in the cascade.
If two components target the same element (with the same specificity),
the inner component&#92;u2019s styles will override those of the outer component.`),`
`,t.default.createElement(e.h2,null,&quot;How it works&quot;),`
`,t.default.createElement(e.p,null,&quot;It all starts with the &quot;,t.default.createElement(e.code,null,&quot;@scope&quot;),&quot; rule and a selector, like this:&quot;),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.card&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token comment&quot;},&quot;/* Scope the following styles to inside `.card` */&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token pseudo-class&quot;},&quot;:scope&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;padding&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token color&quot;},&quot;white&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.title&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-size&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1.2&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-family&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; Georgia&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;,&quot;),&quot; serif&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,&quot;These styles are all scoped to &quot;,t.default.createElement(e.code,null,&quot;.card&quot;),` elements.
`,t.default.createElement(e.code,null,&quot;:scope&quot;),&quot; is a special pseudo-class that targets the &quot;,t.default.createElement(e.code,null,&quot;.card&quot;),` element itself,
and `,t.default.createElement(e.code,null,&quot;.title&quot;),&quot; targets titles inside cards.&quot;),`
`,t.default.createElement(e.p,null,&quot;The &quot;,t.default.createElement(e.code,null,&quot;@scope&quot;),` rule itself adds no specificity to these selectors, so they&#92;u2019re
both (0, 1, 0).
Yes, specificity still matters, but that&#92;u2019s a Good Thing&#92;u2122&#92;uFE0F. More on that shortly.`),`
`,t.default.createElement(e.p,null,`At this point, this is nothing you can&#92;u2019t already do with regular descendant
selectors.
But new, previously impossible options start opening up when you apply an
inner bound to the scope or overlap multiple scopes on the page.
Let&#92;u2019s see what those do&#92;u2026`),`
`,t.default.createElement(e.h3,null,&quot;Inner scope bound&quot;),`
`,t.default.createElement(e.p,null,`Let&#92;u2019s say you anticipate putting other components inside your Cards,
so you don&#92;u2019t want that `,t.default.createElement(e.code,null,&quot;.title&quot;),` selector to target anything other than
the one title that belongs to the Card.
To do that, you put an inner-bound on the scope like so:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.card&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),&quot; to &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.slot&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token comment&quot;},&quot;/* Scoped styles target only inside `.card` but not inside `.slot` */&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token pseudo-class&quot;},&quot;:scope&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;padding&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token color&quot;},&quot;white&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.title&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-size&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1.2&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-family&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; Georgia&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;,&quot;),&quot; serif&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`Think of the &#92;u201Cto&#92;u201D keyword here like &#92;u201Cuntil&#92;u201D:
this scope is defined from `,t.default.createElement(e.code,null,&quot;.card&quot;),&quot; to &quot;,t.default.createElement(e.code,null,&quot;.slot&quot;),`.
Now, none of the scoped selectors will target anything inside the Card&#92;u2019s
`,t.default.createElement(e.code,null,&quot;.slot&quot;),` element.
So you can build your card like this:`),`
`,t.default.createElement(e.pre,{className:&quot;language-html&quot;},t.default.createElement(e.code,{className:&quot;language-html&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;card&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;h3&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;title&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;Moon lander&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;h3&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;slot&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
    `,t.default.createElement(e.span,{className:&quot;token comment&quot;},&quot;&lt;!-- scoped styles won&#92;u2019t target anything here! --&gt;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`)),`
`,t.default.createElement(e.p,null,`The reach of the scope is restricted, keeping it from targeting anything inside
`,t.default.createElement(e.code,null,&quot;.slot&quot;),`.
This way, you can nest two scopes, and each one can make use of the same
generic `,t.default.createElement(e.code,null,&quot;title&quot;),` class name without conflicting.
In fact, you might not even need the class name anymore at all:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.card&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),&quot; to &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.slot&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},&quot;h3&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-size&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1.2&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;rem&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-family&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; Georgia&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;,&quot;),&quot; serif&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.accordion&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),&quot; to &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.slot&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},&quot;h3&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;font-family&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; Helvetica&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;,&quot;),&quot; sans-serif&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;text-transform&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; uppercase&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;letter-spacing&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.01&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;em&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`You can put an Accordion inside a Card &#92;u2014 or a Card inside an Accordion &#92;u2014 and they will
each style their own `,t.default.createElement(e.code,null,&quot;&lt;h3&gt;&quot;),&quot;s without conflicting.&quot;),`
`,t.default.createElement(e.p,null,`This has been colloquially named &#92;u201Cdonut scoping&#92;u201D since the scope has a hole in it.
(It can also have multiple holes in it, if the inner bound selector targets
multiple elements.)
`,t.default.createElement(e.a,{href:&quot;https://www.miriamsuzanne.com/&quot;},&quot;Miriam Suzanne&quot;),` suggest a possible way to use
this is to consistently use `,t.default.createElement(e.code,null,&quot;data-*&quot;),` attributes and attribute selectors for
your scope:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;[data-scope=&quot;,t.default.createElement(e.span,{className:&quot;token string&quot;},&quot;&#39;media&#39;&quot;),&quot;]&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),&quot; to &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot;scope [data-scope]&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token comment&quot;},&quot;/* scoped styles go here */&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`&#92;u2026though I find I prefer the simple selectors that come from a
class-based approach.
But maybe that&#92;u2019s just my old BEM habits showing.`),`
`,t.default.createElement(e.h3,null,&quot;Proximity precedence&quot;),`
`,t.default.createElement(e.p,null,`The other aspect to scoping is the concept of proximity:
styles from an inner scope will override those from an outer scope.
Imagine you have two scopes like this:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.&quot;,t.default.createElement(e.span,{className:&quot;token color&quot;},&quot;green&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},&quot;p&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token color&quot;},&quot;green&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@scope&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),&quot;.&quot;,t.default.createElement(e.span,{className:&quot;token color&quot;},&quot;blue&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},&quot;p&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token color&quot;},&quot;blue&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`Apply these to the following HTML.
These have no inner scope bound, so both `,t.default.createElement(e.code,null,&quot;p&quot;),` selectors target
the inner paragraphs here.
In this case, the inner scope always takes precedence:`),`
`,t.default.createElement(e.pre,{className:&quot;language-html&quot;},t.default.createElement(e.code,{className:&quot;language-html&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;green&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;I&#92;u2019m green&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;blue&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
    `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;I&#92;u2019m blue&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`

`,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;blue&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;I&#92;u2019m blue&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;green&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
    `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;But I&#92;u2019m green&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;p&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`)),`
`,t.default.createElement(e.p,null,`And here&#92;u2019s a working demo &#92;u2014 Note this currently only works in Chrome with the
Experimental Web Platform Features flag turned on in
`,t.default.createElement(e.code,null,&quot;chrome://flags&quot;),&quot;. &quot;,t.default.createElement(B,{id:&quot;1&quot;})),`
`,t.default.createElement(y,null,t.default.createElement(v)),`
`,t.default.createElement(e.p,null,`You can inspect this in DevTools and see each scope overriding the other,
based on which one has the closest proximity:`),`
`,t.default.createElement(e.p,null,t.default.createElement(e.img,{src:&quot;/images/2023/scope/scope-devtools-1.png&quot;,alt:&quot;DevTools showing the green scope winning over the blue&quot;})),`
`,t.default.createElement(e.p,null,&quot;and&quot;),`
`,t.default.createElement(e.p,null,t.default.createElement(e.img,{src:&quot;/images/2023/scope/scope-devtools-2.png&quot;,alt:&quot;DevTools showing the blue scope winning over the green&quot;})),`
`,t.default.createElement(e.p,null,`The catch here is that
`,t.default.createElement(e.a,{href:&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity&quot;},&quot;selector specificity&quot;),`
still takes precedence, so if the outer scope targets an element with higher
specificity than the inner one, the outer scope&#92;u2019s styles will apply.
This was a `,t.default.createElement(e.a,{href:&quot;https://github.com/w3c/csswg-drafts/issues/6790&quot;},&quot;debated issue&quot;),`
during the development of the specification,
but &#92;u2014 to the surprise of my past self &#92;u2014 I think it&#92;u2019s the right call.`),`
`,t.default.createElement(e.p,null,`This way, when two scopes target the same element,
you maintain control over which takes precedence.
Instead of the inner scope always winning,
you can tweak selector specificities so the higher-specificity
selector takes precedence, regardless which scope it belongs to.`),`
`,t.default.createElement(e.p,null,`And when you don&#92;u2019t want this behavior, you have a few ways to prevent it.
You make use of
`,t.default.createElement(e.a,{href:&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers&quot;},&quot;cascade layers&quot;),`
to give one component &#92;u2014 or just parts of one component &#92;u2014 precedence over another.
Or, you can apply an inner scope bound to the outer scope to keep it from happening.
After experimenting with scope for a bit, this feels to me like the right balance.
It gives you the most control, rather than leaving you beholden
to a rigid set of rules of the cascade.`),`
`,t.default.createElement(e.h2,null,&quot;This is a game changer&quot;),`
`,t.default.createElement(e.p,null,`If you&#92;u2019ve developed large-scale apps and had to rely on CSS-in-JS libraries to
prevent class name collisions, I hope you can see the benefit this offers.
If you&#92;u2019ve rolled out complex BEM class name systems and fought to keep
all your selector specificities equal, think of the freedom
this can bring.
If you&#92;u2019ve ever used the shadow DOM to isolate styles but it was too heavy-handed,
this is a better way
(though there are still use-cases for Shadow DOM, of course).`),`
`,t.default.createElement(e.p,null,`I can&#92;u2019t even imagine all the new ways we&#92;u2019ll be able to structure our code.
I think this will be as big as custom properties or even flexbox and grid.
Here are just a few ideas that I&#92;u2019ll certainly be experimenting with:`),`
`,t.default.createElement(e.ul,null,`
`,t.default.createElement(e.li,null,`Define parts of a component with an inner bound and parts of it without, so
the scope of its &#92;u201Dchrome&#92;u201D styles (i.e. wrapper, toggle buttons, etc) don&#92;u2019t
affect its child contents, but it can influence the appearance of text within.`),`
`,t.default.createElement(e.li,null,`Define portions of a component on different cascade layers so it can influence
its contained scopes but remain simple to override on a higher layer.`),`
`,t.default.createElement(e.li,null,&quot;Nested color themes.&quot;),`
`,t.default.createElement(e.li,null,&quot;Easier ways to prevent style collisions in embedded demos in my blog posts.&quot;),`
`,t.default.createElement(e.li,null,&quot;Container queries&#92;u2014What can we come up with by mixing and matching with those?&quot;),`
`),`
`,t.default.createElement(e.h2,null,&quot;We need more browsers on board&quot;),`
`,t.default.createElement(e.p,null,`At this point, Chrome seems to be on board&#92;u2014they&#92;u2019ve had the first working
prototype for several months now. It might be slightly behind the latest changes
to the spec, so keep an eye out for a few minor changes coming if you play
around with it.`),`
`,t.default.createElement(e.p,null,`Firefox seems more reluctant&#92;u2014possibly because they got burned as the only
implementors of the first version of scope several years ago?
I&#92;u2019m not sure what the best way is to nudge them along.
Perhaps we just need more people blogging about this feature and creating buzz.
There is `,t.default.createElement(e.a,{href:&quot;https://github.com/mozilla/standards-positions/issues/472&quot;},&quot;this open issue&quot;),`
requesting their position on the feature.
Maybe a little activity there would help.`),`
`,t.default.createElement(e.p,null,`It sounds like Safari has expressed interest,
but it never hurts to let them know if the community would like to see it.
Maybe the next time `,t.default.createElement(e.a,{href:&quot;https://front-end.social/@jensimmons&quot;},&quot;Jen Simmons&quot;),` takes
a poll on most requested features, let her know you want to see `,t.default.createElement(e.code,null,&quot;@scope&quot;),&quot; happen.&quot;),`
`,t.default.createElement(e.p,null,&quot;Until then, have fun experimenting in Chrome!&quot;),`
`,t.default.createElement(O,null,t.default.createElement(S,null,t.default.createElement(&quot;p&quot;,null,t.default.createElement(e.p,null,`I tried to feature detect and hide this demo for browsers that don&#92;u2019t
support it, but I can&#39;t find quick a way to feature detect for `,t.default.createElement(e.code,null,&quot;@scope&quot;),`
neatly, since `,t.default.createElement(e.code,null,&quot;:scope&quot;),` is in fact an existing selector that is supported
in other browsers. Hopefully browsers will start supporting `,t.default.createElement(e.code,null,&quot;@supports       at-rule(&#92;u2026)&quot;),&quot; soon.&quot;)))))}function W(n={}){let{wrapper:e}=Object.assign({},(0,C.useMDXComponents)(),n.components);return e?t.default.createElement(e,n,t.default.createElement(H,n)):H(n)}var ve=W,ae=ReactDOM.createRoot(document.getElementById(&quot;mdx-root&quot;));ae.render(t.default.createElement(W,null));})();
&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>Transitioning to Height Auto (Two Ways)</title>
    <link href="https://keithjgrant.com/posts/2023/04/transitioning-to-height-auto/"/>
    <published>2023-04-21T17:17:00Z</published>
    <updated>2023-04-21T17:17:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/04/transitioning-to-height-auto/</id>
    <content xml:lang="en" type="html">&lt;style&gt;.ePYPdp { display: none; }
 @media (prefers-reduced-motion: reduce) { .ePYPdp { display: block; } } 
 .flex-collapse__wrapper { display: flex; } .flex-collapse__inner { max-height: 0; overflow: hidden; transition: max-height 0.5s ease-out; } .flex-collapse__wrapper.is-open .flex-collapse__inner { max-height: 100%; } 
.cKLKYl { padding: 0.4em 1em; border: var(--accent-bg-4) 2px solid; border-radius: 0; background: transparent; font-size: 0.875rem; text-transform: uppercase; cursor: pointer; &amp;:hover { background-color: var(--accent-bg-3); } &amp;:active { color: var(--accent-fg-3); } }
 .grid-collapse__wrapper { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.5s ease-out; } .grid-collapse__inner { overflow: hidden; } .grid-collapse__wrapper.is-open { grid-template-rows: 1fr; } &lt;/style&gt;
  &lt;div id=&quot;mdx-root&quot;&gt;&lt;p&gt;One of my longstanding annoyances with CSS has been the
&lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/626&quot;&gt;inability to transition to height auto&lt;/a&gt;.
It’s an incredibly common use-case that’s needed for dropdowns and accordions
and all sorts of other UI patterns.
Unfortunately, this just doesn’t work:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Doesn&amp;#x27;t work */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.dropdown&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; height &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;s&lt;/span&gt; ease-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.dropdown&lt;/span&gt;&lt;span class=&quot;token class&quot;&gt;.is-open&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’ll open and close just fine, but it won’t animate.
The normal course of action here has been to pull out JavaScript and set the
height explicitly based on the element’s &lt;code&gt;scrollHeight&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, I have come across a two different ways to solve the problem without
the help of JavaScript.
One approach transitions an element inside a flex item;
the other approach transitions an element as a grid item.
Each requires adding one or two extra elements to the page to pull it off,
but in many cases, that’s still better than the alternative.&lt;/p&gt;
&lt;h2 id=&quot;with-flexbox&quot;&gt;With flexbox&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/transitioning-to-height-auto/#with-flexbox&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I found this solution from Joakim Chiem in &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/626#issuecomment-676230792&quot;&gt;a comment&lt;/a&gt;
buried in the W3C GitHub issue.
(The comment is two and a half years old.
I am so sorry for not writing about it before now!)&lt;/p&gt;
&lt;p&gt;For this approach, wrap the element you want to transition in two divs: the
outer will be a flexbox and the inner one will be a flex item. With these in
place, you can transition &lt;code&gt;max-height&lt;/code&gt; from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;100%&lt;/code&gt;.
This leverages a quirk of how height/max-height work inside flexbox.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.wrapper&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.inner&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; max-height &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;s&lt;/span&gt; ease-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.wrapper&lt;/span&gt;&lt;span class=&quot;token class&quot;&gt;.is-open&lt;/span&gt; &lt;span class=&quot;token class&quot;&gt;.inner&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just be sure there&amp;#x27;s an extra div between the &lt;code&gt;wrapper&lt;/code&gt; and the &lt;code&gt;inner&lt;/code&gt; element:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;wrapper&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;inner&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Expandable content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;#x27;s what it looks like in action:&lt;/p&gt;
&lt;div class=&quot;demo-container&quot;&gt;&lt;div class=&quot;ePYPdp&quot;&gt;&lt;p&gt;&lt;em&gt;This demo won’t show the transition because you have reduced motion
settings enabled.&lt;/em&gt;&lt;/p&gt;&lt;/div&gt;&lt;style&gt;.iTjOIs {min-width: 11ch}&lt;/style&gt;&lt;button class=&quot;cKLKYl iTjOIs&quot;&gt;Open&lt;/button&gt;&lt;div class=&quot;flex-collapse__wrapper&quot;&gt;&lt;div&gt;&lt;div class=&quot;flex-collapse__inner&quot;&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent mi enim, venenatis non facilisis sed, finibus in enim. Sed auctor enim nisl, sit amet feugiat risus blandit vitae.&lt;/p&gt;&lt;p&gt;Integer dapibus tortor lorem, quis interdum erat maximus eu. Nam varius, nisl ac pharetra convallis, neque ex blandit quam, sit amet condimentum lectus elit vel ligula. Donec commodo porta elit quis facilisis. Sed non purus a massa convallis ultricies. Donec malesuada sollicitudin dui, vel lobortis arcu rutrum ut. Curabitur lobortis ac tellus non tempor. Donec nisl risus, cursus vel odio quis, convallis euismod ante.&lt;/p&gt;&lt;p&gt;Nulla lectus diam, sagittis id urna in, tincidunt facilisis sapien. Sed ante turpis, porttitor a diam at, auctor lobortis sapien. Cras ornare dolor sed arcu laoreet volutpat ut tristique ipsum.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The downside to this approach is that, while the inner element animates smoothly,
its container does not; that just snaps into place instantly.
But that might be prefereable in some situations, since the browser doesn&amp;#x27;t
need to constantly reflow all the contents of the page beneath it.&lt;/p&gt;
&lt;h2 id=&quot;with-grid&quot;&gt;With grid&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/transitioning-to-height-auto/#with-grid&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I first saw mention of this technique from Chris Coyier, but he kinda
&lt;a href=&quot;https://chriscoyier.net/2022/12/21/things-css-could-still-use-heading-into-2023/#animate-to-auto&quot;&gt;buried the lede&lt;/a&gt;
amid a longer wish-list of CSS features.&lt;/p&gt;
&lt;p&gt;This one is a bit simpler, and probably a little more understandable.
Make a CSS grid with a single grid item.
All you have to do is transition &lt;code&gt;grid-template-rows&lt;/code&gt; from &lt;code&gt;0fr&lt;/code&gt; to &lt;code&gt;1fr&lt;/code&gt;,
so the grid item transitions to its &lt;code&gt;auto&lt;/code&gt; height:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.wrapper&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;fr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid-template-rows &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;s&lt;/span&gt; ease-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.wrapper&lt;/span&gt;&lt;span class=&quot;token class&quot;&gt;.is-open&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;fr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token class&quot;&gt;.inner&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does not require the extra div.
Just a grid container and its one grid item:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;wrapper&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;inner&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Expandable content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Surprisingly, it’s that simple:&lt;/p&gt;
&lt;div class=&quot;demo-container&quot;&gt;&lt;div class=&quot;ePYPdp&quot;&gt;&lt;p&gt;&lt;em&gt;This demo won’t show the transition because you have reduced motion
settings enabled.&lt;/em&gt;&lt;/p&gt;&lt;/div&gt;&lt;style&gt;.iTjOIs {min-width: 11ch}&lt;/style&gt;&lt;button class=&quot;cKLKYl iTjOIs&quot;&gt;Open&lt;/button&gt;&lt;div class=&quot;grid-collapse__wrapper&quot;&gt;&lt;div class=&quot;grid-collapse__inner&quot;&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent mi enim, venenatis non facilisis sed, finibus in enim. Sed auctor enim nisl, sit amet feugiat risus blandit vitae.&lt;/p&gt;&lt;p&gt;Integer dapibus tortor lorem, quis interdum erat maximus eu. Nam varius, nisl ac pharetra convallis, neque ex blandit quam, sit amet condimentum lectus elit vel ligula. Donec commodo porta elit quis facilisis. Sed non purus a massa convallis ultricies. Donec malesuada sollicitudin dui, vel lobortis arcu rutrum ut. Curabitur lobortis ac tellus non tempor. Donec nisl risus, cursus vel odio quis, convallis euismod ante.&lt;/p&gt;&lt;p&gt;Nulla lectus diam, sagittis id urna in, tincidunt facilisis sapien. Sed ante turpis, porttitor a diam at, auctor lobortis sapien. Cras ornare dolor sed arcu laoreet volutpat ut tristique ipsum.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This approach feels a lot cleaner.
Only two DOM nodes needed, and all the page content beneath moves fluidly
like you probably expect.&lt;/p&gt;
&lt;p&gt;The one caveat to both of these is you can’t add any padding to the inner
element.
If you need padding, you have to add one more extra element inside the
collapsed element and put the padding on that.&lt;/p&gt;&lt;/div&gt;&lt;script&gt;(()=&gt;{var Q=Object.create;var O=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,V=Object.prototype.hasOwnProperty;var $=(n,e)=&gt;()=&gt;(e||n((e={exports:{}}).exports,e),e.exports);var Y=(n,e,a,l)=&gt;{if(e&amp;&amp;typeof e==&quot;object&quot;||typeof e==&quot;function&quot;)for(let s of Z(e))!V.call(n,s)&amp;&amp;s!==a&amp;&amp;O(n,s,{get:()=&gt;e[s],enumerable:!(l=U(e,s))||l.enumerable});return n};var c=(n,e,a)=&gt;(a=n!=null?Q(K(n)):{},Y(e||!n||!n.__esModule?O(a,&quot;default&quot;,{value:n,enumerable:!0}):a,n));var m=$((re,I)=&gt;{I.exports=window.React});var j=$(M=&gt;{var w=c(m());M.useMDXComponents=function(){return{h2:function(a){let{children:l,...s}=a,u=R(l);return w.default.createElement(&quot;h2&quot;,{...s,id:u},l,w.default.createElement(&quot;a&quot;,{className:&quot;header-anchor&quot;,href:`#${u}`},&quot;&#92;xB6&quot;))}}};function R(n){return n.toLowerCase().split(&quot; &quot;).slice(0,6).join(&quot;-&quot;).replace(/[^A-Za-z0-9-]/g,&quot;&quot;)}});var q=c(j()),t=c(m());var W=c(m());var T=new Set([&quot;a&quot;,&quot;abbr&quot;,&quot;address&quot;,&quot;area&quot;,&quot;article&quot;,&quot;aside&quot;,&quot;audio&quot;,&quot;b&quot;,&quot;base&quot;,&quot;bdi&quot;,&quot;bdo&quot;,&quot;big&quot;,&quot;blockquote&quot;,&quot;body&quot;,&quot;br&quot;,&quot;button&quot;,&quot;canvas&quot;,&quot;caption&quot;,&quot;cite&quot;,&quot;code&quot;,&quot;col&quot;,&quot;colgroup&quot;,&quot;data&quot;,&quot;datalist&quot;,&quot;dd&quot;,&quot;del&quot;,&quot;details&quot;,&quot;dfn&quot;,&quot;dialog&quot;,&quot;div&quot;,&quot;dl&quot;,&quot;dt&quot;,&quot;em&quot;,&quot;embed&quot;,&quot;fieldset&quot;,&quot;figcaption&quot;,&quot;figure&quot;,&quot;footer&quot;,&quot;form&quot;,&quot;h1&quot;,&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;,&quot;head&quot;,&quot;header&quot;,&quot;hgroup&quot;,&quot;hr&quot;,&quot;html&quot;,&quot;i&quot;,&quot;iframe&quot;,&quot;img&quot;,&quot;input&quot;,&quot;ins&quot;,&quot;kbd&quot;,&quot;keygen&quot;,&quot;label&quot;,&quot;legend&quot;,&quot;li&quot;,&quot;link&quot;,&quot;main&quot;,&quot;map&quot;,&quot;mark&quot;,&quot;menu&quot;,&quot;menuitem&quot;,&quot;meta&quot;,&quot;meter&quot;,&quot;nav&quot;,&quot;noscript&quot;,&quot;object&quot;,&quot;ol&quot;,&quot;optgroup&quot;,&quot;option&quot;,&quot;output&quot;,&quot;p&quot;,&quot;param&quot;,&quot;picture&quot;,&quot;pre&quot;,&quot;progress&quot;,&quot;q&quot;,&quot;rp&quot;,&quot;rt&quot;,&quot;ruby&quot;,&quot;s&quot;,&quot;samp&quot;,&quot;script&quot;,&quot;section&quot;,&quot;select&quot;,&quot;small&quot;,&quot;source&quot;,&quot;span&quot;,&quot;strong&quot;,&quot;style&quot;,&quot;sub&quot;,&quot;summary&quot;,&quot;sup&quot;,&quot;table&quot;,&quot;tbody&quot;,&quot;td&quot;,&quot;textarea&quot;,&quot;tfoot&quot;,&quot;th&quot;,&quot;thead&quot;,&quot;time&quot;,&quot;title&quot;,&quot;tr&quot;,&quot;track&quot;,&quot;u&quot;,&quot;ul&quot;,&quot;use&quot;,&quot;var&quot;,&quot;video&quot;,&quot;wbr&quot;,&quot;circle&quot;,&quot;clipPath&quot;,&quot;defs&quot;,&quot;ellipse&quot;,&quot;foreignObject&quot;,&quot;g&quot;,&quot;image&quot;,&quot;line&quot;,&quot;linearGradient&quot;,&quot;marker&quot;,&quot;mask&quot;,&quot;path&quot;,&quot;pattern&quot;,&quot;polygon&quot;,&quot;polyline&quot;,&quot;radialGradient&quot;,&quot;rect&quot;,&quot;stop&quot;,&quot;svg&quot;,&quot;text&quot;,&quot;tspan&quot;]);var p=c(m());function d(n){let e=te(n)&gt;&gt;&gt;0;return ae(e)}var ee=8362;function te(n){let e=ee,a=n.length;for(;a;)e=e*33^n.charCodeAt(--a);return e}var ne=/(a)(d)/gi,N=52,A=n=&gt;String.fromCharCode(n+(n&gt;25?39:97));function ae(n){let e=&quot;&quot;,a;for(a=Math.abs(n);a&gt;N;a=a/N|0)e=A(a%N)+e;return(A(a%N)+e).replace(ne,&quot;$1-$2&quot;)}var B={};function x(n,e){B[n]||(B[n]=e)}function k(n,e,a,l){let s=se(e,a,l);return(0,p.useMemo)(()=&gt;{function D(b){let{children:P,css:h,...z}=b,y=[s],v;return h&amp;&amp;(v=d(h),y.push(v)),b.className&amp;&amp;y.push(b.className),p.default.createElement(p.default.Fragment,null,h?p.default.createElement(&quot;style&quot;,null,`.${v} {${h}}`):null,p.default.createElement(n,{...z,className:y.join(&quot; &quot;)},P))}return D.className=s,D},[s])}function H(n,e){let a=L(n,e),l=d(a);x(l,a)}function se(n,e,a){let l=L(n,e),s=a||d(l),u=`.${s} {${l}}`;return x(s,u),s}function L(n,e){return n.reduce((a,l,s)=&gt;`${a}${l}${e[s]||&quot;&quot;}`,&quot;&quot;).replace(/&#92;s+/g,&quot; &quot;)}function C(n,...e){return k(&quot;div&quot;,n,e)}T.forEach(n=&gt;{C[n]=(e,...a)=&gt;k(n,e,a)});C.raw=(n,...e)=&gt;H(n,e);var i=C;function g({query:n,children:e}){let a=i.div`
    display: none;
  `;return i.raw`
    @media (${n}) {
      .${a.className} {
        display: block;
      }
    }
  `,W.default.createElement(a,null,e)}var G=c(m());function f({children:n}){return G.default.createElement(&quot;div&quot;,{className:&quot;demo-container&quot;},n)}var o=c(m());var J=c(m());function E({children:n,...e}){let a=i.button`
    padding: 0.4em 1em;
    border: var(--accent-bg-4) 2px solid;
    border-radius: 0;
    background: transparent;
    font-size: 0.875rem;
    text-transform: uppercase;
    cursor: pointer;

    &amp;:hover {
      background-color: var(--accent-bg-3);
    }

    &amp;:active {
      color: var(--accent-fg-3);
    }
  `;return J.default.createElement(a,{...e},n)}function S(){let[n,e]=(0,o.useState)(!1);return i.raw`
    .flex-collapse__wrapper {
      display: flex;
    }

    .flex-collapse__inner {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.5s ease-out;
    }

    .flex-collapse__wrapper.is-open .flex-collapse__inner {
      max-height: 100%;
    }
  `,o.default.createElement(o.default.Fragment,null,o.default.createElement(E,{onClick:()=&gt;e(!n),css:&quot;min-width: 11ch&quot;},n?&quot;Close&quot;:&quot;Open&quot;),o.default.createElement(&quot;div&quot;,{className:`flex-collapse__wrapper${n?&quot; is-open&quot;:&quot;&quot;}`},o.default.createElement(&quot;div&quot;,null,o.default.createElement(&quot;div&quot;,{className:&quot;flex-collapse__inner&quot;},o.default.createElement(&quot;p&quot;,null,&quot;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent mi enim, venenatis non facilisis sed, finibus in enim. Sed auctor enim nisl, sit amet feugiat risus blandit vitae.&quot;),o.default.createElement(&quot;p&quot;,null,&quot;Integer dapibus tortor lorem, quis interdum erat maximus eu. Nam varius, nisl ac pharetra convallis, neque ex blandit quam, sit amet condimentum lectus elit vel ligula. Donec commodo porta elit quis facilisis. Sed non purus a massa convallis ultricies. Donec malesuada sollicitudin dui, vel lobortis arcu rutrum ut. Curabitur lobortis ac tellus non tempor. Donec nisl risus, cursus vel odio quis, convallis euismod ante.&quot;),o.default.createElement(&quot;p&quot;,null,&quot;Nulla lectus diam, sagittis id urna in, tincidunt facilisis sapien. Sed ante turpis, porttitor a diam at, auctor lobortis sapien. Cras ornare dolor sed arcu laoreet volutpat ut tristique ipsum.&quot;)))))}var r=c(m());function _(){let[n,e]=(0,r.useState)(!1);return i.raw`
    .grid-collapse__wrapper {
      display: grid;
      grid-template-rows: 0fr;
      transition: grid-template-rows 0.5s ease-out;
    }

    .grid-collapse__inner {
      overflow: hidden;
    }

    .grid-collapse__wrapper.is-open {
      grid-template-rows: 1fr;
    }
  `,r.default.createElement(r.default.Fragment,null,r.default.createElement(E,{onClick:()=&gt;e(!n),css:&quot;min-width: 11ch&quot;},n?&quot;Close&quot;:&quot;Open&quot;),r.default.createElement(&quot;div&quot;,{className:`grid-collapse__wrapper${n?&quot; is-open&quot;:&quot;&quot;}`},r.default.createElement(&quot;div&quot;,{className:&quot;grid-collapse__inner&quot;},r.default.createElement(&quot;p&quot;,null,&quot;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent mi enim, venenatis non facilisis sed, finibus in enim. Sed auctor enim nisl, sit amet feugiat risus blandit vitae.&quot;),r.default.createElement(&quot;p&quot;,null,&quot;Integer dapibus tortor lorem, quis interdum erat maximus eu. Nam varius, nisl ac pharetra convallis, neque ex blandit quam, sit amet condimentum lectus elit vel ligula. Donec commodo porta elit quis facilisis. Sed non purus a massa convallis ultricies. Donec malesuada sollicitudin dui, vel lobortis arcu rutrum ut. Curabitur lobortis ac tellus non tempor. Donec nisl risus, cursus vel odio quis, convallis euismod ante.&quot;),r.default.createElement(&quot;p&quot;,null,&quot;Nulla lectus diam, sagittis id urna in, tincidunt facilisis sapien. Sed ante turpis, porttitor a diam at, auctor lobortis sapien. Cras ornare dolor sed arcu laoreet volutpat ut tristique ipsum.&quot;))))}function X(n){let e=Object.assign({p:&quot;p&quot;,a:&quot;a&quot;,pre:&quot;pre&quot;,code:&quot;code&quot;,span:&quot;span&quot;,h2:&quot;h2&quot;,em:&quot;em&quot;},(0,q.useMDXComponents)(),n.components);return t.default.createElement(t.default.Fragment,null,t.default.createElement(e.p,null,`One of my longstanding annoyances with CSS has been the
`,t.default.createElement(e.a,{href:&quot;https://github.com/w3c/csswg-drafts/issues/626&quot;},&quot;inability to transition to height auto&quot;),`.
It&#92;u2019s an incredibly common use-case that&#92;u2019s needed for dropdowns and accordions
and all sorts of other UI patterns.
Unfortunately, this just doesn&#92;u2019t work:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token comment&quot;},&quot;/* Doesn&#39;t work */&quot;),`
`,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.dropdown&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;height&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;transition&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; height &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.5&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;s&quot;),&quot; ease-out&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.dropdown&quot;),t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.is-open&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;height&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; auto&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`It&#92;u2019ll open and close just fine, but it won&#92;u2019t animate.
The normal course of action here has been to pull out JavaScript and set the
height explicitly based on the element&#92;u2019s `,t.default.createElement(e.code,null,&quot;scrollHeight&quot;),&quot;.&quot;),`
`,t.default.createElement(e.p,null,`However, I have come across a two different ways to solve the problem without
the help of JavaScript.
One approach transitions an element inside a flex item;
the other approach transitions an element as a grid item.
Each requires adding one or two extra elements to the page to pull it off,
but in many cases, that&#92;u2019s still better than the alternative.`),`
`,t.default.createElement(e.h2,null,&quot;With flexbox&quot;),`
`,t.default.createElement(e.p,null,&quot;I found this solution from Joakim Chiem in &quot;,t.default.createElement(e.a,{href:&quot;https://github.com/w3c/csswg-drafts/issues/626#issuecomment-676230792&quot;},&quot;a comment&quot;),`
buried in the W3C GitHub issue.
(The comment is two and a half years old.
I am so sorry for not writing about it before now!)`),`
`,t.default.createElement(e.p,null,`For this approach, wrap the element you want to transition in two divs: the
outer will be a flexbox and the inner one will be a flex item. With these in
place, you can transition `,t.default.createElement(e.code,null,&quot;max-height&quot;),&quot; from &quot;,t.default.createElement(e.code,null,&quot;0&quot;),&quot; to &quot;,t.default.createElement(e.code,null,&quot;100%&quot;),`.
This leverages a quirk of how height/max-height work inside flexbox.`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.wrapper&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;display&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; flex&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.inner&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;max-height&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;overflow&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; hidden&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;transition&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; max-height &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.5&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;s&quot;),&quot; ease-out&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.wrapper&quot;),t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.is-open&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.inner&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;max-height&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;100&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,&quot;Just be sure there&#39;s an extra div between the &quot;,t.default.createElement(e.code,null,&quot;wrapper&quot;),&quot; and the &quot;,t.default.createElement(e.code,null,&quot;inner&quot;),&quot; element:&quot;),`
`,t.default.createElement(e.pre,{className:&quot;language-html&quot;},t.default.createElement(e.code,{className:&quot;language-html&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;wrapper&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
    `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;inner&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;Expandable content&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`)),`
`,t.default.createElement(e.p,null,&quot;And here&#39;s what it looks like in action:&quot;),`
`,t.default.createElement(f,null,t.default.createElement(g,{query:&quot;prefers-reduced-motion: reduce&quot;},t.default.createElement(e.p,null,t.default.createElement(e.em,null,`This demo won&#92;u2019t show the transition because you have reduced motion
settings enabled.`))),t.default.createElement(S)),`
`,t.default.createElement(e.p,null,`The downside to this approach is that, while the inner element animates smoothly,
its container does not; that just snaps into place instantly.
But that might be prefereable in some situations, since the browser doesn&#39;t
need to constantly reflow all the contents of the page beneath it.`),`
`,t.default.createElement(e.h2,null,&quot;With grid&quot;),`
`,t.default.createElement(e.p,null,`I first saw mention of this technique from Chris Coyier, but he kinda
`,t.default.createElement(e.a,{href:&quot;https://chriscoyier.net/2022/12/21/things-css-could-still-use-heading-into-2023/#animate-to-auto&quot;},&quot;buried the lede&quot;),`
amid a longer wish-list of CSS features.`),`
`,t.default.createElement(e.p,null,`This one is a bit simpler, and probably a little more understandable.
Make a CSS grid with a single grid item.
All you have to do is transition `,t.default.createElement(e.code,null,&quot;grid-template-rows&quot;),&quot; from &quot;,t.default.createElement(e.code,null,&quot;0fr&quot;),&quot; to &quot;,t.default.createElement(e.code,null,&quot;1fr&quot;),`,
so the grid item transitions to its `,t.default.createElement(e.code,null,&quot;auto&quot;),&quot; height:&quot;),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.wrapper&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;display&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; grid&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;grid-template-rows&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;fr&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;transition&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; grid-template-rows &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.5&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;s&quot;),&quot; ease-out&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.wrapper&quot;),t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.is-open&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;grid-template-rows&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;1&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;fr&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`

`,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token class&quot;},&quot;.inner&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;overflow&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; hidden&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,`This does not require the extra div.
Just a grid container and its one grid item:`),`
`,t.default.createElement(e.pre,{className:&quot;language-html&quot;},t.default.createElement(e.code,{className:&quot;language-html&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;wrapper&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
  `,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;&quot;),&quot;div&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token attr-name&quot;},&quot;class&quot;),t.default.createElement(e.span,{className:&quot;token attr-value&quot;},t.default.createElement(e.span,{className:&quot;token punctuation attr-equals&quot;},&quot;=&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;),&quot;inner&quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&#39;&quot;&#39;)),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),&quot;Expandable content&quot;,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`,t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token tag&quot;},t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&lt;/&quot;),&quot;div&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;&gt;&quot;)),`
`)),`
`,t.default.createElement(e.p,null,&quot;Surprisingly, it&#92;u2019s that simple:&quot;),`
`,t.default.createElement(f,null,t.default.createElement(g,{query:&quot;prefers-reduced-motion: reduce&quot;},t.default.createElement(e.p,null,t.default.createElement(e.em,null,`This demo won&#92;u2019t show the transition because you have reduced motion
settings enabled.`))),t.default.createElement(_)),`
`,t.default.createElement(e.p,null,`This approach feels a lot cleaner.
Only two DOM nodes needed, and all the page content beneath moves fluidly
like you probably expect.`),`
`,t.default.createElement(e.p,null,`The one caveat to both of these is you can&#92;u2019t add any padding to the inner
element.
If you need padding, you have to add one more extra element inside the
collapsed element and put the padding on that.`))}function F(n={}){let{wrapper:e}=Object.assign({},(0,q.useMDXComponents)(),n.components);return e?t.default.createElement(e,n,t.default.createElement(X,n)):X(n)}var Me=F,le=ReactDOM.createRoot(document.getElementById(&quot;mdx-root&quot;));le.render(t.default.createElement(F,null));})();
&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>It&#39;s Time to Learn oklch Color</title>
    <link href="https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/"/>
    <published>2023-04-07T18:27:00Z</published>
    <updated>2023-04-07T18:27:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/</id>
    <content xml:lang="en" type="html">&lt;style&gt;.iqEkeL { border: 1px solid rgb(128 128 128 / 0.3); border-radius: 5px; overflow: clip; text-align: center; }
.ksaYcz { block-size: 100px; margin-block-end: 1rem; }
.dXeLGk { display: flex; gap: 1rem; }
.grnIQL { flex-grow: 1; }
.iARwGp { display: block; text-align: center; }
.gEHxaP { display: block; width: 100%; min-height: 10px; -webkit-appearance: none; background-image: linear-gradient(to right, oklch(0% var(--c) var(--h)), oklch(100% var(--c) var(--h))); &amp;::-webkit-slider-thumb { -webkit-appearance: none; border-radius: 50%; height: 15px; width: 15px; background-color: #fff; border: 2px solid #000; } }
.ciMAGs { display: block; width: 100%; min-height: 10px; -webkit-appearance: none; background-image: linear-gradient(to right, oklch(var(--l) 0 var(--h)), oklch(var(--l) 0.37 var(--h))); &amp;::-webkit-slider-thumb { -webkit-appearance: none; border-radius: 50%; height: 15px; width: 15px; background-color: #fff; border: 2px solid #000; } }
.fBZiL { display: block; width: 100%; min-height: 10px; -webkit-appearance: none; background-image: linear-gradient(to right, oklch(var(--l) var(--c) 0), oklch(var(--l) var(--c) 60), oklch(var(--l) var(--c) 120), oklch(var(--l) var(--c) 180), oklch(var(--l) var(--c) 240), oklch(var(--l) var(--c) 300), oklch(var(--l) var(--c) 360)); &amp;::-webkit-slider-thumb { -webkit-appearance: none; border-radius: 50%; height: 15px; width: 15px; background-color: #fff; border: 2px solid #000; } }
.dlhZBo { justify-content: center; display: flex; gap: 1rem; }
.gqhhdV { max-inline-size: 150px; flex-basis: 150px; border-radius: 5px; text-align: center; box-shadow: 0 0 5px 0 rgb(128 128 128 / 0.2); overflow: hidden; }
.fSoeAm { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(217deg 55% 50%); }
.bfVYCr { font-size: 0.7em; padding: 0.3em; overflow: auto; background-color: var(--bg-color--main); display: block; }
.crhjaa { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(110deg 55% 50%); }
.gQlwjo { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(217deg 55% 50%); background-color: oklch(55% 0.15 260); }
.hIA-dqK { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(110deg 55% 50%); background-color: oklch(73% 0.2 141); }
.kiZdEe { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(217.47 56% 50%); background-color: oklch(55% 0.15 260deg); }
.hRkvEH { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(107.11 100% 27%); background-color: oklch(55% 0.2 141deg); }
.gNVuXv { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(111.51 53% 34%); background-color: oklch(55% 0.15 141deg); }
.dEwHvo { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(359 100% 41%); background-color: oklch(50% 0.37 29deg); }
.hlGbzI { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(33.64 100% 45%); background-color: oklch(70% 0.187 60); }
.bJgHnw { margin-block-end: 1em; }
.caapKz { display: flex; align-items: center; padding-inline: 0.5em; height: 2.5em; background-image: linear-gradient(to right, hsl(0deg 70% 50%),hsl(60deg 70% 50%),hsl(120deg 70% 50%),hsl(180deg 70% 50%),hsl(240deg 70% 50%),hsl(300deg 70% 50%),hsl(360deg 70% 50%)); }
.hHgJai { height: 3em; width: 100%; margin-block-end: -0.4em; stroke: currentColor; opacity: 0.5; }
.hzYbGt { display: flex; align-items: center; padding-inline: 0.5em; height: 2.5em; background-image: linear-gradient(to right, hsl(328.63 100% 35%),hsl(32.78 100% 29%),hsl(69.54 100% 21%),hsl(172.43 100% 23%),hsl(199.56 100% 31%),hsl(274.36 100% 44%),hsl(328.63 100% 35%)); background-image: linear-gradient(to right, oklch(55% 0.27 0deg),oklch(55% 0.27 60deg),oklch(55% 0.27 120deg),oklch(55% 0.27 180deg),oklch(55% 0.27 240deg),oklch(55% 0.27 300deg),oklch(55% 0.27 360deg)); }
.larEXS { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(300deg 100% 75%); background-color: oklch(100% 0.37 330deg); }
.hiLjHV { height: 100px; width: 100%; margin-inline: auto; background-color: hsl(120deg 100% 5%); background-color: oklch(0% 0.37 140deg); }&lt;/style&gt;
  &lt;div id=&quot;mdx-root&quot;&gt;&lt;p&gt;If you’re anything like me, looking at all the things happening in CSS lately involving
color, you’re probably a bit overwhelmed. For a long time, HSL &lt;a href=&quot;https://mothereffinghsl.com/&quot;&gt;was promoted&lt;/a&gt;
as the &lt;a href=&quot;http://www.useragentman.com/blog/2010/08/28/coding-colors-easily-using-css3-hsl-notation/&quot;&gt;human readable&lt;/a&gt;
alternative to hex or RGB.
For about a decade, HSL has been the best option for working comfortably with color on the web.&lt;/p&gt;
&lt;p&gt;But now, somewhat suddenly, we’ve got several new options thrown into the mix:
&lt;code&gt;hwb()&lt;/code&gt;, &lt;code&gt;lab()&lt;/code&gt;, &lt;code&gt;lch()&lt;/code&gt;, &lt;code&gt;oklab()&lt;/code&gt;, and &lt;code&gt;oklch()&lt;/code&gt;.
There’s also &lt;code&gt;color()&lt;/code&gt; which sort of fits into the same category and kind of doesn’t.&lt;/p&gt;
&lt;p&gt;This is a lot to take in.
But &lt;code&gt;hsl()&lt;/code&gt; is great!
So why leave what works and what’s comfortable?&lt;/p&gt;
&lt;p&gt;It’s easy to get lost in all this.
So I’m going to try to make it easy:
If you don’t know where to start, or which of these things is going to
be practically useful enough to be worth your time, I think the
biggest bang for your buck is to learn OKLCH
(or, “&lt;a href=&quot;https://front-end.social/@eeeps/109955851076325361&quot;&gt;Oklachroma&lt;/a&gt;”,
if I had my way and could make that catch on).
Hopefully I can convince you that it’s worth diving in and learning.&lt;/p&gt;
&lt;style&gt;.eGziHy {
        --l: 70%;
        --c: 0.19;
        --h: 150;
      }&lt;/style&gt;&lt;div class=&quot;iqEkeL eGziHy&quot;&gt;&lt;style&gt;.bgvLYx {background-color: oklch(var(--l) var(--c) var(--h))}&lt;/style&gt;&lt;div class=&quot;ksaYcz bgvLYx&quot;&gt;&lt;/div&gt;&lt;div class=&quot;dXeLGk&quot;&gt;&lt;div class=&quot;grnIQL&quot;&gt;&lt;label for=&quot;l-slider&quot; class=&quot;iARwGp&quot;&gt;Lightness: 70%&lt;/label&gt;&lt;input id=&quot;l-slider&quot; type=&quot;range&quot; min=&quot;0&quot; max=&quot;100&quot; step=&quot;1&quot; class=&quot;gEHxaP&quot; value=&quot;70&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;grnIQL&quot;&gt;&lt;label for=&quot;c-slider&quot; class=&quot;iARwGp&quot;&gt;Chroma: 0.19&lt;/label&gt;&lt;input id=&quot;c-slider&quot; type=&quot;range&quot; min=&quot;0&quot; max=&quot;0.37&quot; step=&quot;0.01&quot; class=&quot;ciMAGs&quot; value=&quot;0.19&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;grnIQL&quot;&gt;&lt;label for=&quot;h-slider&quot; class=&quot;iARwGp&quot;&gt;Hue: 150deg&lt;/label&gt;&lt;input id=&quot;h-slider&quot; type=&quot;range&quot; min=&quot;0&quot; max=&quot;360&quot; step=&quot;1&quot; class=&quot;fBZiL&quot; value=&quot;150&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-oklch&quot;&gt;Why OKLCH&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/#why-oklch&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’m not going to take a deep dive into all the various color notations
in this post.
But HSL has been king for over a decade now, so I will draw upon that
to make some important comparisons.&lt;/p&gt;
&lt;p&gt;The thing that makes HSL so great is that it is so much more intuitive
than hex or RGB color.
One value for Hue, or color of the rainbow.
One value for Saturation, or how vivid that color is.
And one value for Lightness, ranging from black to white.
For example, &lt;code&gt;hsl(220deg 60% 45%)&lt;/code&gt; — a fairly vivid, medium-dark blue.&lt;/p&gt;
&lt;p&gt;OKLCH follows a very conceptually similar pattern.
The mental model is nearly identical, though the order of the values is reversed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L&lt;/strong&gt;ightness: from 0% to 100%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;hroma: vividness of color, from 0.0 to 0.37 (more on that below)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;H&lt;/strong&gt;ue: from 0deg to 360deg indicating the color of the rainbow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are two key distinctions from HSL, however.&lt;/p&gt;
&lt;h3&gt;It’s based on human perception&lt;/h3&gt;
&lt;p&gt;In HSL, lightness and saturation each range from 0% to 100%, where
0% means none and 100% means the highest amount of light or
saturated color that can be represented in the sRGB gamut—
sRGB is the range of colors that have historically been available
to most color monitors.&lt;/p&gt;
&lt;p&gt;The problem with this is it doesn&amp;#x27;t quite align with the way our eyes
perceive light. Look at these two HSL colors, each of which has an
equal HSL lightness value of 50%:&lt;/p&gt;
&lt;div class=&quot;dlhZBo&quot;&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;fSoeAm&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;slate blue&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;hsl(217deg 55% 50%)&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;crhjaa&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;bright spring green&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;hsl(110deg 55% 50%)&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Both colors have the same specified lightness,
but the second one appears noticeable brighter to our eyes.
Here are the same colors in OKLCH:&lt;/p&gt;
&lt;div class=&quot;dlhZBo&quot;&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;gQlwjo&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;slate blue&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(55% 0.15 260)&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;hIA-dqK&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;bright spring green&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(73% 0.2 141)&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Notice how in OLKCH, the lightness is different (55% and 73%).
Look what happens when we bring the lightness of the green down to match the blue:&lt;/p&gt;
&lt;div class=&quot;dlhZBo&quot;&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;kiZdEe&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;slate blue&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(55% 0.15 260deg)&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;hRkvEH&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;grassy green&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(55% 0.2 141deg)&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;By giving them the same lightness value in OKLCH, the green has been dimmed.
Now they both appear equally bright.
You&amp;#x27;ll also noticed that the specified chroma value is different
between the two colors (0.15 and 0.2).
Let&amp;#x27;s adjust the chroma values to match:&lt;/p&gt;
&lt;div class=&quot;dlhZBo&quot;&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;kiZdEe&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;slate blue&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(55% 0.15 260deg)&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;gNVuXv&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;mossy green&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(55% 0.15 141deg)&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The difference here is a little more subtle, but the green on the right
has been desaturated from the previous example.
Now the two colors both appear to have the same brightness and
the same vividness.&lt;/p&gt;
&lt;p&gt;In HSL, 100% saturation is simply as saturated as that particular color
can be in the sRGB gamut.
In OKLCH, the values aren&amp;#x27;t based on technical limits or a
mathematical definition, but rather on perceived equality.
The amount if lightness indicates exactly how bright the color is,
and the amount of chroma indicates exactly how vivid it is.
The human eye perceives some colors like green or yellow to be
brighter than others, like blue or purple, and OKLCH takes these
details into account.&lt;/p&gt;
&lt;h3&gt;It can define any color&lt;/h3&gt;
&lt;p&gt;I’ve mentioned a couple times that HSL is limited to the sRGB gamut.
But monitors are getting better.
Most smartphones and newer monitors, including most Mac laptops,
support a wider range of colors (a gamut called P3).
And some high-end monitors offer an even wider range of colors
(a gamut called Rec2020).&lt;/p&gt;
&lt;p&gt;The great thing about OKLCH, is it can specify any color these monitors
are capable of—In fact, it can specify any color that the
human eye is capable of seeing.
In CSS, the browser will automatically round any out-of-range colors
to the nearest color the hardware is capable of displaying.&lt;/p&gt;
&lt;h3&gt;Browser support is nearly there&lt;/h3&gt;
&lt;p&gt;At the moment, OKLCH is supported in Chrome, Edge, and Safari.
Firefox supports it only behind a flag at the moment, but it is expected to be
enabled by default soon, with version 113.
See the latest &lt;a href=&quot;https://caniuse.com/mdn-css_types_color_oklch&quot;&gt;browser support at caniuse.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That means it’s probably not ready to completely replace your HSL yet,
but that day is near.
In the meantime, you can use it selectively with feature queries.
I’m doing a little of that on this site right now, where I wanted
to push some of the color accents into a slightly more vivid range
for browsers and monitors that support it:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;73&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.17&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;192&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token pseudo-class&quot;&gt;:root&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;--accent-color-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;73.54&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.169&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;193&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;--accent-color-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;68.15&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.272&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;--accent-color-3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;77.94&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.203&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;62&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;--accent-color-4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;66.67&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.193&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;253&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can just use regular old fallback values:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;179&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;oklch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;73&lt;/span&gt;&lt;span class=&quot;token unit&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.17&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;193&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just be aware that this latter approach does &lt;em&gt;not&lt;/em&gt; work for
custom properties, because the value won’t resolve as invalid
until you reference it later using &lt;code&gt;var()&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-to-use-oklch&quot;&gt;How to use OKLCH&lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/04/its-time-to-learn-oklch-color/#how-to-use-oklch&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So hopefully I’ve convinced you to give OKLCH a shot, even if
it’s just with some simple experimentation to get comfortable
with it.&lt;/p&gt;
&lt;p&gt;The first thing you need to know is that it uses the “new” style of CSS
color syntax.
And by that, I mean there are no commas between the values:
&lt;code&gt;oklch(50% 0.3 280deg)&lt;/code&gt;.
If you want to add an alpha channel for transparency, use a slash to
denote it: &lt;code&gt;oklch(50% 0.3 280deg / 0.5)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The older color functions have all been updated to use this approach as well.
So instead of the old &lt;code&gt;hsl(200deg, 50%, 45%)&lt;/code&gt;, you can now use a comma-free
notation: &lt;code&gt;hsl(200deg 50% 45%)&lt;/code&gt;.
And instead of a separate function for transparency (&lt;code&gt;hsla()&lt;/code&gt;), you can
use the same function with a slash: &lt;code&gt;hsl(200deg 50% 45% / 0.5)&lt;/code&gt;.
For &lt;code&gt;hsl()&lt;/code&gt; and &lt;code&gt;rgb()&lt;/code&gt; the old notation with commas will be supported
for backwards-compatibility, but going forward the new color functions
will not.&lt;/p&gt;
&lt;h3&gt;Chroma&lt;/h3&gt;
&lt;p&gt;There are a few things to be aware of when using &lt;code&gt;oklch()&lt;/code&gt;.
The most important one is the chroma range.
Unlike saturation in HSL, chroma is not a percentage.&lt;/p&gt;
&lt;p&gt;For all intents and purposes, the chroma value is a number between
0 and 0.37.
You can try using a higher chroma value, like 25, but it is going to
round to a color in the monitor’s supported range and the result
can be unpredictable (you may specify a blue hue, but it might end up
selecting a teal if it’s vivid enough to be “closer” to the values
you specified).&lt;/p&gt;
&lt;p&gt;Theoretically, OKLCH color can specify colors with chroma
up to infinity, but I find this nonsensical.
I have a hard time imagining a red much more vivid than this:&lt;/p&gt;
&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;dEwHvo&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;extremely vivid red&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(50% 0.37 29deg)&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Maybe I would believe some hyper-intense paint could push it to
something like 0.5, but that’s about it.
So I don’t find the “infinity” limit helpful in the slightest.
Keep it under 0.37.&lt;/p&gt;
&lt;p&gt;In fact, for most hues, the practical limit is even lower.
This orange, for example, is maxed out at 0.187:&lt;/p&gt;
&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;hlGbzI&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;extremely vivid orange&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(70% 0.187 60)&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;If you were to increase the lightness, you could bump up the chroma a smidge further.
But the point is, the values are all interelated.
If you have a very dark color, monitors can display it with only so much chroma;
there just isn’t enough light emitted to make it any more vivid.
And the specifics of how each hue works are a little different, because our
eyes don’t perceive all the various wavelegths equally.&lt;/p&gt;
&lt;h3&gt;Hue&lt;/h3&gt;
&lt;p&gt;Another thing to keep in mind is that the hue values are not exactly
the same as HSL.
All hues have been shifted up by around 30 degrees, though this amount
varies a bit depending on the color.
Here are gradients across all hues (0–360 degrees) in HSL and OKLCH for comparison:&lt;/p&gt;
&lt;div class=&quot;bJgHnw&quot;&gt;&lt;div class=&quot;caapKz&quot;&gt;HSL&lt;/div&gt;&lt;svg width=&quot;100%&quot; viewBox=&quot;0 0 360 25&quot; preserveAspectRatio=&quot;none&quot; class=&quot;hHgJai&quot;&gt;&lt;path d=&quot;M0,0C0,10 27.25,15 27.25,25&quot; style=&quot;fill:none;stroke-width:0.5px&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M60,0C60,10 109.69,15 109.69,25&quot; style=&quot;fill:none;stroke-width:0.5px&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M120,0C120,10 142.67,15 142.67,25&quot; style=&quot;fill:none;stroke-width:0.5px&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M180,0C180,10 194.85,15 194.85,25&quot; style=&quot;fill:none;stroke-width:0.5px&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M240,0C240,10 269.23,15 269.23,25&quot; style=&quot;fill:none;stroke-width:0.5px&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M300,0C300,10 328.21,15 328.21,25&quot; style=&quot;fill:none;stroke-width:0.5px&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;div class=&quot;hzYbGt&quot;&gt;OKLCH&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Again, these differences come down to human perception, so some
hues get a little more space on the spectrum.
Additionally, these shifts are a little different when comparing
across various brightnesses and saturation levels.&lt;/p&gt;
&lt;p&gt;Some key color points are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Red: 30&lt;/li&gt;
&lt;li&gt;Yellow: 90&lt;/li&gt;
&lt;li&gt;Green: 140&lt;/li&gt;
&lt;li&gt;Cyan/teal: 195&lt;/li&gt;
&lt;li&gt;Blue: 260&lt;/li&gt;
&lt;li&gt;Magenta: 330&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hue can be expressed as an angle (&lt;code&gt;25deg&lt;/code&gt;) or as a number (&lt;code&gt;25&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Lightness&lt;/h3&gt;
&lt;p&gt;Another difference from HSL is that 0% or 100% lightness does not
automatically mean full black or full white.
If there is enough chroma, you can still get some color at these
extremes:&lt;/p&gt;
&lt;div class=&quot;dlhZBo&quot;&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;larEXS&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;vivid light pink&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(100% 0.37 330deg)&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;gqhhdV&quot;&gt;&lt;div class=&quot;hiLjHV&quot;&gt;&lt;/div&gt;&lt;div class=&quot;sr-only&quot;&gt;very dark army green&lt;/div&gt;&lt;code class=&quot;bfVYCr&quot;&gt;oklch(0% 0.37 140deg)&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;alert&quot;&gt;&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; This behavior is the result of browser bugs and does not
match the spec. Lightness of 100% should be pure white an 0% should be pure
black. It has been fixed in Chrome, and will likely change in other browsers
in the near future.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;If you want true black, white, or gray, make sure the chroma value is 0.&lt;/p&gt;
&lt;p&gt;Lightness can be expressed as a percent (&lt;code&gt;45%&lt;/code&gt;) or as a decimal (&lt;code&gt;0.45&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Use a color picker&lt;/h3&gt;
&lt;p&gt;In general, as long as you keep the chroma below 0.37,
you will usually be pretty safe.
It’s pretty easy to specify a color that’s not in range of your monitor,
but as long as your values are within reason, the browser should round it
to a predictable result.&lt;/p&gt;
&lt;p&gt;But when you really want to fine tune things, I suggest you use a color picker.
The one at &lt;a href=&quot;https://oklch.com/&quot;&gt;oklch.com&lt;/a&gt; is fantastic:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/oklch-picker.png&quot; alt=&quot;OKLCH color picker&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This picker provides conversions to most other color formats,
so you can find fallback values easily.
Shift-clicking the color swatch in your browser‘s DevTools also provides
conversions, like it always has.&lt;/p&gt;
&lt;p&gt;Instead of an rgb or hsl picker that you may be used to, you’ll notice the colors
here aren’t displayed in perfect rectangles; it’s like chunks have been cut out of them.
This indicates where colors are out of range of computer monitors.
It’s a little odd at first, but it should start to make sense the more you play around with it.&lt;/p&gt;
&lt;div class=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt; If you really want to dive deeper into
understanding gamuts, color spaces, and the other CSS color functions, I
highly recommend the &lt;a href=&quot;https://developer.chrome.com/articles/high-definition-css-color-guide/&quot;&gt;High Definition CSS Color
Guide&lt;/a&gt;
by Adam Argyle.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;script&gt;(()=&gt;{var U=Object.create;var A=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty;var B=(n,e)=&gt;()=&gt;(e||n((e={exports:{}}).exports,e),e.exports);var Q=(n,e,a,l)=&gt;{if(e&amp;&amp;typeof e==&quot;object&quot;||typeof e==&quot;function&quot;)for(let o of W(e))!Z.call(n,o)&amp;&amp;o!==a&amp;&amp;A(n,o,{get:()=&gt;e[o],enumerable:!(l=Y(e,o))||l.enumerable});return n};var u=(n,e,a)=&gt;(a=n!=null?U(J(n)):{},Q(e||!n||!n.__esModule?A(a,&quot;default&quot;,{value:n,enumerable:!0}):a,n));var h=B((ce,K)=&gt;{K.exports=window.React});var F=B(j=&gt;{var C=u(h());j.useMDXComponents=function(){return{h2:function(a){let{children:l,...o}=a,i=R(l);return C.default.createElement(&quot;h2&quot;,{...o,id:i},l,C.default.createElement(&quot;a&quot;,{className:&quot;header-anchor&quot;,href:`#${i}`},&quot;&#92;xB6&quot;))}}};function R(n){return n.toLowerCase().split(&quot; &quot;).slice(0,6).join(&quot;-&quot;).replace(/[^A-Za-z0-9-]/g,&quot;&quot;)}});var T=u(F()),t=u(h());var V=u(h());var M=new Set([&quot;a&quot;,&quot;abbr&quot;,&quot;address&quot;,&quot;area&quot;,&quot;article&quot;,&quot;aside&quot;,&quot;audio&quot;,&quot;b&quot;,&quot;base&quot;,&quot;bdi&quot;,&quot;bdo&quot;,&quot;big&quot;,&quot;blockquote&quot;,&quot;body&quot;,&quot;br&quot;,&quot;button&quot;,&quot;canvas&quot;,&quot;caption&quot;,&quot;cite&quot;,&quot;code&quot;,&quot;col&quot;,&quot;colgroup&quot;,&quot;data&quot;,&quot;datalist&quot;,&quot;dd&quot;,&quot;del&quot;,&quot;details&quot;,&quot;dfn&quot;,&quot;dialog&quot;,&quot;div&quot;,&quot;dl&quot;,&quot;dt&quot;,&quot;em&quot;,&quot;embed&quot;,&quot;fieldset&quot;,&quot;figcaption&quot;,&quot;figure&quot;,&quot;footer&quot;,&quot;form&quot;,&quot;h1&quot;,&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;,&quot;head&quot;,&quot;header&quot;,&quot;hgroup&quot;,&quot;hr&quot;,&quot;html&quot;,&quot;i&quot;,&quot;iframe&quot;,&quot;img&quot;,&quot;input&quot;,&quot;ins&quot;,&quot;kbd&quot;,&quot;keygen&quot;,&quot;label&quot;,&quot;legend&quot;,&quot;li&quot;,&quot;link&quot;,&quot;main&quot;,&quot;map&quot;,&quot;mark&quot;,&quot;menu&quot;,&quot;menuitem&quot;,&quot;meta&quot;,&quot;meter&quot;,&quot;nav&quot;,&quot;noscript&quot;,&quot;object&quot;,&quot;ol&quot;,&quot;optgroup&quot;,&quot;option&quot;,&quot;output&quot;,&quot;p&quot;,&quot;param&quot;,&quot;picture&quot;,&quot;pre&quot;,&quot;progress&quot;,&quot;q&quot;,&quot;rp&quot;,&quot;rt&quot;,&quot;ruby&quot;,&quot;s&quot;,&quot;samp&quot;,&quot;script&quot;,&quot;section&quot;,&quot;select&quot;,&quot;small&quot;,&quot;source&quot;,&quot;span&quot;,&quot;strong&quot;,&quot;style&quot;,&quot;sub&quot;,&quot;summary&quot;,&quot;sup&quot;,&quot;table&quot;,&quot;tbody&quot;,&quot;td&quot;,&quot;textarea&quot;,&quot;tfoot&quot;,&quot;th&quot;,&quot;thead&quot;,&quot;time&quot;,&quot;title&quot;,&quot;tr&quot;,&quot;track&quot;,&quot;u&quot;,&quot;ul&quot;,&quot;use&quot;,&quot;var&quot;,&quot;video&quot;,&quot;wbr&quot;,&quot;circle&quot;,&quot;clipPath&quot;,&quot;defs&quot;,&quot;ellipse&quot;,&quot;foreignObject&quot;,&quot;g&quot;,&quot;image&quot;,&quot;line&quot;,&quot;linearGradient&quot;,&quot;marker&quot;,&quot;mask&quot;,&quot;path&quot;,&quot;pattern&quot;,&quot;polygon&quot;,&quot;polyline&quot;,&quot;radialGradient&quot;,&quot;rect&quot;,&quot;stop&quot;,&quot;svg&quot;,&quot;text&quot;,&quot;tspan&quot;]);var g=u(h());function v(n){let e=te(n)&gt;&gt;&gt;0;return ae(e)}var ee=8362;function te(n){let e=ee,a=n.length;for(;a;)e=e*33^n.charCodeAt(--a);return e}var ne=/(a)(d)/gi,N=52,G=n=&gt;String.fromCharCode(n+(n&gt;25?39:97));function ae(n){let e=&quot;&quot;,a;for(a=Math.abs(n);a&gt;N;a=a/N|0)e=G(a%N)+e;return(G(a%N)+e).replace(ne,&quot;$1-$2&quot;)}var D={};function H(n,e){D[n]||(D[n]=e)}function x(n,e,a,l){let o=le(e,a,l);return(0,g.useMemo)(()=&gt;{function b(m){let{children:k,css:d,...S}=m,E=[o],y;return d&amp;&amp;(y=v(d),E.push(y)),m.className&amp;&amp;E.push(m.className),g.default.createElement(g.default.Fragment,null,d?g.default.createElement(&quot;style&quot;,null,`.${y} {${d}}`):null,g.default.createElement(n,{...S,className:E.join(&quot; &quot;)},k))}return b.className=o,b},[o])}function q(n,e){let a=_(n,e),l=v(a);H(l,a)}function le(n,e,a){let l=_(n,e),o=a||v(l),i=`.${o} {${l}}`;return H(o,i),o}function _(n,e){return n.reduce((a,l,o)=&gt;`${a}${l}${e[o]||&quot;&quot;}`,&quot;&quot;).replace(/&#92;s+/g,&quot; &quot;)}function L(n,...e){return x(&quot;div&quot;,n,e)}M.forEach(n=&gt;{L[n]=(e,...a)=&gt;x(n,e,a)});L.raw=(n,...e)=&gt;q(n,e);var r=L;function f({children:n}){let e=r`
    justify-content: center;
    display: flex;
    gap: 1rem;
  `;return V.default.createElement(e,null,n)}var s=u(h());function $(){let n=r`
    border: 1px solid rgb(128 128 128 / 0.3);
    border-radius: 5px;
    overflow: clip;
    text-align: center;
  `,e=r`
    block-size: 100px;
    margin-block-end: 1rem;
  `,a=r`
    display: flex;
    gap: 1rem;
  `,[l,o]=(0,s.useState)(70),[i,b]=(0,s.useState)(.19),[m,k]=(0,s.useState)(150);return typeof window&gt;&quot;u&quot;||window.CSS.supports(&quot;color: oklch(0% 0 0deg)&quot;)?s.default.createElement(n,{css:`
        --l: ${l}%;
        --c: ${i};
        --h: ${m};
      `},s.default.createElement(e,{css:&quot;background-color: oklch(var(--l) var(--c) var(--h))&quot;}),s.default.createElement(a,null,s.default.createElement(I,{id:&quot;l-slider&quot;,min:0,max:100,value:l,setValue:o,label:&quot;Lightness&quot;,displayValue:`${l}%`,stops:[&quot;oklch(0% var(--c) var(--h))&quot;,&quot;oklch(100% var(--c) var(--h))&quot;]}),s.default.createElement(I,{id:&quot;c-slider&quot;,min:0,max:.37,step:.01,value:i,setValue:b,label:&quot;Chroma&quot;,stops:[&quot;oklch(var(--l) 0 var(--h))&quot;,&quot;oklch(var(--l) 0.37 var(--h))&quot;]}),s.default.createElement(I,{id:&quot;h-slider&quot;,min:0,max:360,value:m,setValue:k,label:&quot;Hue&quot;,displayValue:`${m}deg`,stops:[&quot;oklch(var(--l) var(--c) 0)&quot;,&quot;oklch(var(--l) var(--c) 60)&quot;,&quot;oklch(var(--l) var(--c) 120)&quot;,&quot;oklch(var(--l) var(--c) 180)&quot;,&quot;oklch(var(--l) var(--c) 240)&quot;,&quot;oklch(var(--l) var(--c) 300)&quot;,&quot;oklch(var(--l) var(--c) 360)&quot;]}))):s.default.createElement(n,null,&quot;Sorry, your browser does not support oklch color&quot;)}function I({id:n,min:e,max:a,step:l=1,value:o,setValue:i,stops:b,label:m,displayValue:k}){let d=r`
  flex-grow: 1;
  `,S=r.label`
    display: block;
    text-align: center;
  `,E=r.input`
    display: block;
    width: 100%;
    min-height: 10px;
    -webkit-appearance: none;
    background-image: linear-gradient(to right, ${b.join(&quot;, &quot;)});

    &amp;::-webkit-slider-thumb {
      -webkit-appearance: none;
      border-radius: 50%;
      height: 15px;
      width: 15px;
      background-color: #fff;
      border: 2px solid #000;
    }
  `;return s.default.createElement(d,null,s.default.createElement(S,{htmlFor:n},m,&quot;: &quot;,k||o),s.default.createElement(E,{id:n,type:&quot;range&quot;,min:e,max:a,step:l,value:o,onChange:y=&gt;i(y.target.value)}))}var w=u(h());function c({color:n,fallback:e,alt:a}){let l=r`
    max-inline-size: 150px;
    flex-basis: 150px;
    border-radius: 5px;
    text-align: center;
    box-shadow: 0 0 5px 0 rgb(128 128 128 / 0.2);
    overflow: hidden;
  `,o=r`
    height: 100px;
    width: 100%;
    margin-inline: auto;
    ${e?`background-color: ${e};`:&quot;&quot;}
    background-color: ${n};
  `,i=r.code`
    font-size: 0.7em;
    padding: 0.3em;
    overflow: auto;
    background-color: var(--bg-color--main);
    display: block;
  `;return w.default.createElement(l,null,w.default.createElement(o,null),w.default.createElement(&quot;div&quot;,{className:&quot;sr-only&quot;},a),w.default.createElement(i,null,n))}var p=u(h());function O(){let n=r`
    margin-block-end: 1em;
  `;return p.default.createElement(n,null,p.default.createElement(X,{stops:[&quot;hsl(0deg 70% 50%)&quot;,&quot;hsl(60deg 70% 50%)&quot;,&quot;hsl(120deg 70% 50%)&quot;,&quot;hsl(180deg 70% 50%)&quot;,&quot;hsl(240deg 70% 50%)&quot;,&quot;hsl(300deg 70% 50%)&quot;,&quot;hsl(360deg 70% 50%)&quot;],label:&quot;HSL&quot;}),p.default.createElement(oe,{values:[{from:0,to:27.25},{from:60,to:109.69},{from:120,to:142.67},{from:180,to:194.85},{from:240,to:269.23},{from:300,to:328.21}]}),p.default.createElement(X,{stops:[&quot;oklch(55% 0.27 0deg)&quot;,&quot;oklch(55% 0.27 60deg)&quot;,&quot;oklch(55% 0.27 120deg)&quot;,&quot;oklch(55% 0.27 180deg)&quot;,&quot;oklch(55% 0.27 240deg)&quot;,&quot;oklch(55% 0.27 300deg)&quot;,&quot;oklch(55% 0.27 360deg)&quot;],fallbacks:[&quot;hsl(328.63 100% 35%)&quot;,&quot;hsl(32.78 100% 29%)&quot;,&quot;hsl(69.54 100% 21%)&quot;,&quot;hsl(172.43 100% 23%)&quot;,&quot;hsl(199.56 100% 31%)&quot;,&quot;hsl(274.36 100% 44%)&quot;,&quot;hsl(328.63 100% 35%)&quot;],label:&quot;OKLCH&quot;}))}function X({stops:n,fallbacks:e,label:a}){let l=r`
    display: flex;
    align-items: center;
    padding-inline: 0.5em;
    height: 2.5em;
    ${e?`background-image: linear-gradient(to right, ${e.join(&quot;,&quot;)});`:&quot;&quot;}
    background-image: linear-gradient(to right, ${n.join(&quot;,&quot;)});
  `;return p.default.createElement(l,null,a)}function oe({values:n}){let e=r.svg`
    height: 3em;
    width: 100%;
    margin-block-end: -0.4em;
    stroke: currentColor;
    opacity: 0.5;
  `;return p.default.createElement(e,{width:&quot;100%&quot;,viewBox:&quot;0 0 360 25&quot;,preserveAspectRatio:&quot;none&quot;},n.map(({from:a,to:l})=&gt;p.default.createElement(&quot;path&quot;,{key:`${a}-${l}`,d:`M${a},0C${a},10 ${l},15 ${l},25`,style:{fill:&quot;none&quot;,strokeWidth:&quot;0.5px&quot;}})))}function z(n){let e=Object.assign({p:&quot;p&quot;,a:&quot;a&quot;,code:&quot;code&quot;,h2:&quot;h2&quot;,ul:&quot;ul&quot;,li:&quot;li&quot;,strong:&quot;strong&quot;,h3:&quot;h3&quot;,pre:&quot;pre&quot;,span:&quot;span&quot;,em:&quot;em&quot;,img:&quot;img&quot;},(0,T.useMDXComponents)(),n.components);return t.default.createElement(t.default.Fragment,null,t.default.createElement(e.p,null,`If you&#92;u2019re anything like me, looking at all the things happening in CSS lately involving
color, you&#92;u2019re probably a bit overwhelmed. For a long time, HSL `,t.default.createElement(e.a,{href:&quot;https://mothereffinghsl.com/&quot;},&quot;was promoted&quot;),`
as the `,t.default.createElement(e.a,{href:&quot;http://www.useragentman.com/blog/2010/08/28/coding-colors-easily-using-css3-hsl-notation/&quot;},&quot;human readable&quot;),`
alternative to hex or RGB.
For about a decade, HSL has been the best option for working comfortably with color on the web.`),`
`,t.default.createElement(e.p,null,`But now, somewhat suddenly, we&#92;u2019ve got several new options thrown into the mix:
`,t.default.createElement(e.code,null,&quot;hwb()&quot;),&quot;, &quot;,t.default.createElement(e.code,null,&quot;lab()&quot;),&quot;, &quot;,t.default.createElement(e.code,null,&quot;lch()&quot;),&quot;, &quot;,t.default.createElement(e.code,null,&quot;oklab()&quot;),&quot;, and &quot;,t.default.createElement(e.code,null,&quot;oklch()&quot;),`.
There&#92;u2019s also `,t.default.createElement(e.code,null,&quot;color()&quot;),&quot; which sort of fits into the same category and kind of doesn&#92;u2019t.&quot;),`
`,t.default.createElement(e.p,null,`This is a lot to take in.
But `,t.default.createElement(e.code,null,&quot;hsl()&quot;),` is great!
So why leave what works and what&#92;u2019s comfortable?`),`
`,t.default.createElement(e.p,null,`It&#92;u2019s easy to get lost in all this.
So I&#92;u2019m going to try to make it easy:
If you don&#92;u2019t know where to start, or which of these things is going to
be practically useful enough to be worth your time, I think the
biggest bang for your buck is to learn OKLCH
(or, &#92;u201C`,t.default.createElement(e.a,{href:&quot;https://front-end.social/@eeeps/109955851076325361&quot;},&quot;Oklachroma&quot;),`&#92;u201D,
if I had my way and could make that catch on).
Hopefully I can convince you that it&#92;u2019s worth diving in and learning.`),`
`,t.default.createElement($),`
`,t.default.createElement(e.h2,null,&quot;Why OKLCH&quot;),`
`,t.default.createElement(e.p,null,`I&#92;u2019m not going to take a deep dive into all the various color notations
in this post.
But HSL has been king for over a decade now, so I will draw upon that
to make some important comparisons.`),`
`,t.default.createElement(e.p,null,`The thing that makes HSL so great is that it is so much more intuitive
than hex or RGB color.
One value for Hue, or color of the rainbow.
One value for Saturation, or how vivid that color is.
And one value for Lightness, ranging from black to white.
For example, `,t.default.createElement(e.code,null,&quot;hsl(220deg 60% 45%)&quot;),&quot; &#92;u2014 a fairly vivid, medium-dark blue.&quot;),`
`,t.default.createElement(e.p,null,`OKLCH follows a very conceptually similar pattern.
The mental model is nearly identical, though the order of the values is reversed:`),`
`,t.default.createElement(e.ul,null,`
`,t.default.createElement(e.li,null,t.default.createElement(e.strong,null,&quot;L&quot;),&quot;ightness: from 0% to 100%&quot;),`
`,t.default.createElement(e.li,null,t.default.createElement(e.strong,null,&quot;C&quot;),&quot;hroma: vividness of color, from 0.0 to 0.37 (more on that below)&quot;),`
`,t.default.createElement(e.li,null,t.default.createElement(e.strong,null,&quot;H&quot;),&quot;ue: from 0deg to 360deg indicating the color of the rainbow&quot;),`
`),`
`,t.default.createElement(e.p,null,&quot;There are two key distinctions from HSL, however.&quot;),`
`,t.default.createElement(e.h3,null,&quot;It&#92;u2019s based on human perception&quot;),`
`,t.default.createElement(e.p,null,`In HSL, lightness and saturation each range from 0% to 100%, where
0% means none and 100% means the highest amount of light or
saturated color that can be represented in the sRGB gamut&#92;u2014
sRGB is the range of colors that have historically been available
to most color monitors.`),`
`,t.default.createElement(e.p,null,`The problem with this is it doesn&#39;t quite align with the way our eyes
perceive light. Look at these two HSL colors, each of which has an
equal HSL lightness value of 50%:`),`
`,t.default.createElement(f,null,t.default.createElement(c,{color:&quot;hsl(217deg 55% 50%)&quot;,alt:&quot;slate blue&quot;}),t.default.createElement(c,{color:&quot;hsl(110deg 55% 50%)&quot;,alt:&quot;bright spring green&quot;})),`
`,t.default.createElement(e.p,null,`Both colors have the same specified lightness,
but the second one appears noticeable brighter to our eyes.
Here are the same colors in OKLCH:`),`
`,t.default.createElement(f,null,t.default.createElement(c,{color:&quot;oklch(55% 0.15 260)&quot;,fallback:&quot;hsl(217deg 55% 50%)&quot;,alt:&quot;slate blue&quot;}),t.default.createElement(c,{color:&quot;oklch(73% 0.2 141)&quot;,fallback:&quot;hsl(110deg 55% 50%)&quot;,alt:&quot;bright spring green&quot;})),`
`,t.default.createElement(e.p,null,`Notice how in OLKCH, the lightness is different (55% and 73%).
Look what happens when we bring the lightness of the green down to match the blue:`),`
`,t.default.createElement(f,null,t.default.createElement(c,{color:&quot;oklch(55% 0.15 260deg)&quot;,fallback:&quot;hsl(217.47 56% 50%)&quot;,alt:&quot;slate blue&quot;}),t.default.createElement(c,{color:&quot;oklch(55% 0.2 141deg)&quot;,fallback:&quot;hsl(107.11 100% 27%)&quot;,alt:&quot;grassy green&quot;})),`
`,t.default.createElement(e.p,null,`By giving them the same lightness value in OKLCH, the green has been dimmed.
Now they both appear equally bright.
You&#39;ll also noticed that the specified chroma value is different
between the two colors (0.15 and 0.2).
Let&#39;s adjust the chroma values to match:`),`
`,t.default.createElement(f,null,t.default.createElement(c,{color:&quot;oklch(55% 0.15 260deg)&quot;,fallback:&quot;hsl(217.47 56% 50%)&quot;,alt:&quot;slate blue&quot;}),t.default.createElement(c,{color:&quot;oklch(55% 0.15 141deg)&quot;,fallback:&quot;hsl(111.51 53% 34%)&quot;,alt:&quot;mossy green&quot;})),`
`,t.default.createElement(e.p,null,`The difference here is a little more subtle, but the green on the right
has been desaturated from the previous example.
Now the two colors both appear to have the same brightness and
the same vividness.`),`
`,t.default.createElement(e.p,null,`In HSL, 100% saturation is simply as saturated as that particular color
can be in the sRGB gamut.
In OKLCH, the values aren&#39;t based on technical limits or a
mathematical definition, but rather on perceived equality.
The amount if lightness indicates exactly how bright the color is,
and the amount of chroma indicates exactly how vivid it is.
The human eye perceives some colors like green or yellow to be
brighter than others, like blue or purple, and OKLCH takes these
details into account.`),`
`,t.default.createElement(e.h3,null,&quot;It can define any color&quot;),`
`,t.default.createElement(e.p,null,`I&#92;u2019ve mentioned a couple times that HSL is limited to the sRGB gamut.
But monitors are getting better.
Most smartphones and newer monitors, including most Mac laptops,
support a wider range of colors (a gamut called P3).
And some high-end monitors offer an even wider range of colors
(a gamut called Rec2020).`),`
`,t.default.createElement(e.p,null,`The great thing about OKLCH, is it can specify any color these monitors
are capable of&#92;u2014In fact, it can specify any color that the
human eye is capable of seeing.
In CSS, the browser will automatically round any out-of-range colors
to the nearest color the hardware is capable of displaying.`),`
`,t.default.createElement(e.h3,null,&quot;Browser support is nearly there&quot;),`
`,t.default.createElement(e.p,null,`At the moment, OKLCH is supported in Chrome, Edge, and Safari.
Firefox supports it only behind a flag at the moment, but it is expected to be
enabled by default soon, with version 113.
See the latest `,t.default.createElement(e.a,{href:&quot;https://caniuse.com/mdn-css_types_color_oklch&quot;},&quot;browser support at caniuse.com&quot;),&quot;.&quot;),`
`,t.default.createElement(e.p,null,`That means it&#92;u2019s probably not ready to completely replace your HSL yet,
but that day is near.
In the meantime, you can use it selectively with feature queries.
I&#92;u2019m doing a little of that on this site right now, where I wanted
to push some of the color accents into a slightly more vivid range
for browsers and monitors that support it:`),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token atrule&quot;},t.default.createElement(e.span,{className:&quot;token rule&quot;},&quot;@supports&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;oklch&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;73&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.17&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;192&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token selector&quot;},t.default.createElement(e.span,{className:&quot;token pseudo-class&quot;},&quot;:root&quot;)),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;{&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token variable&quot;},&quot;--accent-color-1&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;oklch&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;73.54&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.169&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;193&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token variable&quot;},&quot;--accent-color-2&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;oklch&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;68.15&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.272&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;9&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token variable&quot;},&quot;--accent-color-3&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;oklch&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;77.94&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.203&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;62&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
    `,t.default.createElement(e.span,{className:&quot;token variable&quot;},&quot;--accent-color-4&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;oklch&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;66.67&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.193&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;253&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
  `,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`,t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;}&quot;),`
`)),`
`,t.default.createElement(e.p,null,&quot;Or you can just use regular old fallback values:&quot;),`
`,t.default.createElement(e.pre,{className:&quot;language-css&quot;},t.default.createElement(e.code,{className:&quot;language-css&quot;},t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;hsl&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;179&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;100&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;38&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`,t.default.createElement(e.span,{className:&quot;token property&quot;},&quot;background-color&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;:&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token function&quot;},&quot;oklch&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;(&quot;),t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;73&quot;),t.default.createElement(e.span,{className:&quot;token unit&quot;},&quot;%&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;0.17&quot;),&quot; &quot;,t.default.createElement(e.span,{className:&quot;token number&quot;},&quot;193&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;)&quot;),t.default.createElement(e.span,{className:&quot;token punctuation&quot;},&quot;;&quot;),`
`)),`
`,t.default.createElement(e.p,null,&quot;Just be aware that this latter approach does &quot;,t.default.createElement(e.em,null,&quot;not&quot;),` work for
custom properties, because the value won&#92;u2019t resolve as invalid
until you reference it later using `,t.default.createElement(e.code,null,&quot;var()&quot;),&quot;.&quot;),`
`,t.default.createElement(e.h2,null,&quot;How to use OKLCH&quot;),`
`,t.default.createElement(e.p,null,`So hopefully I&#92;u2019ve convinced you to give OKLCH a shot, even if
it&#92;u2019s just with some simple experimentation to get comfortable
with it.`),`
`,t.default.createElement(e.p,null,`The first thing you need to know is that it uses the &#92;u201Cnew&#92;u201D style of CSS
color syntax.
And by that, I mean there are no commas between the values:
`,t.default.createElement(e.code,null,&quot;oklch(50% 0.3 280deg)&quot;),`.
If you want to add an alpha channel for transparency, use a slash to
denote it: `,t.default.createElement(e.code,null,&quot;oklch(50% 0.3 280deg / 0.5)&quot;),&quot;.&quot;),`
`,t.default.createElement(e.p,null,`The older color functions have all been updated to use this approach as well.
So instead of the old `,t.default.createElement(e.code,null,&quot;hsl(200deg, 50%, 45%)&quot;),`, you can now use a comma-free
notation: `,t.default.createElement(e.code,null,&quot;hsl(200deg 50% 45%)&quot;),`.
And instead of a separate function for transparency (`,t.default.createElement(e.code,null,&quot;hsla()&quot;),`), you can
use the same function with a slash: `,t.default.createElement(e.code,null,&quot;hsl(200deg 50% 45% / 0.5)&quot;),`.
For `,t.default.createElement(e.code,null,&quot;hsl()&quot;),&quot; and &quot;,t.default.createElement(e.code,null,&quot;rgb()&quot;),` the old notation with commas will be supported
for backwards-compatibility, but going forward the new color functions
will not.`),`
`,t.default.createElement(e.h3,null,&quot;Chroma&quot;),`
`,t.default.createElement(e.p,null,&quot;There are a few things to be aware of when using &quot;,t.default.createElement(e.code,null,&quot;oklch()&quot;),`.
The most important one is the chroma range.
Unlike saturation in HSL, chroma is not a percentage.`),`
`,t.default.createElement(e.p,null,`For all intents and purposes, the chroma value is a number between
0 and 0.37.
You can try using a higher chroma value, like 25, but it is going to
round to a color in the monitor&#92;u2019s supported range and the result
can be unpredictable (you may specify a blue hue, but it might end up
selecting a teal if it&#92;u2019s vivid enough to be &#92;u201Ccloser&#92;u201D to the values
you specified).`),`
`,t.default.createElement(e.p,null,`Theoretically, OKLCH color can specify colors with chroma
up to infinity, but I find this nonsensical.
I have a hard time imagining a red much more vivid than this:`),`
`,t.default.createElement(c,{color:&quot;oklch(50% 0.37 29deg)&quot;,fallback:&quot;hsl(359 100% 41%)&quot;,alt:&quot;extremely vivid red&quot;}),`
`,t.default.createElement(e.p,null,`Maybe I would believe some hyper-intense paint could push it to
something like 0.5, but that&#92;u2019s about it.
So I don&#92;u2019t find the &#92;u201Cinfinity&#92;u201D limit helpful in the slightest.
Keep it under 0.37.`),`
`,t.default.createElement(e.p,null,`In fact, for most hues, the practical limit is even lower.
This orange, for example, is maxed out at 0.187:`),`
`,t.default.createElement(c,{color:&quot;oklch(70% 0.187 60)&quot;,fallback:&quot;hsl(33.64 100% 45%)&quot;,alt:&quot;extremely vivid orange&quot;}),`
`,t.default.createElement(e.p,null,`If you were to increase the lightness, you could bump up the chroma a smidge further.
But the point is, the values are all interelated.
If you have a very dark color, monitors can display it with only so much chroma;
there just isn&#92;u2019t enough light emitted to make it any more vivid.
And the specifics of how each hue works are a little different, because our
eyes don&#92;u2019t perceive all the various wavelegths equally.`),`
`,t.default.createElement(e.h3,null,&quot;Hue&quot;),`
`,t.default.createElement(e.p,null,`Another thing to keep in mind is that the hue values are not exactly
the same as HSL.
All hues have been shifted up by around 30 degrees, though this amount
varies a bit depending on the color.
Here are gradients across all hues (0&#92;u2013360 degrees) in HSL and OKLCH for comparison:`),`
`,t.default.createElement(O),`
`,t.default.createElement(e.p,null,`Again, these differences come down to human perception, so some
hues get a little more space on the spectrum.
Additionally, these shifts are a little different when comparing
across various brightnesses and saturation levels.`),`
`,t.default.createElement(e.p,null,&quot;Some key color points are:&quot;),`
`,t.default.createElement(e.ul,null,`
`,t.default.createElement(e.li,null,&quot;Red: 30&quot;),`
`,t.default.createElement(e.li,null,&quot;Yellow: 90&quot;),`
`,t.default.createElement(e.li,null,&quot;Green: 140&quot;),`
`,t.default.createElement(e.li,null,&quot;Cyan/teal: 195&quot;),`
`,t.default.createElement(e.li,null,&quot;Blue: 260&quot;),`
`,t.default.createElement(e.li,null,&quot;Magenta: 330&quot;),`
`),`
`,t.default.createElement(e.p,null,&quot;Hue can be expressed as an angle (&quot;,t.default.createElement(e.code,null,&quot;25deg&quot;),&quot;) or as a number (&quot;,t.default.createElement(e.code,null,&quot;25&quot;),&quot;).&quot;),`
`,t.default.createElement(e.h3,null,&quot;Lightness&quot;),`
`,t.default.createElement(e.p,null,`Another difference from HSL is that 0% or 100% lightness does not
automatically mean full black or full white.
If there is enough chroma, you can still get some color at these
extremes:`),`
`,t.default.createElement(f,null,t.default.createElement(c,{color:&quot;oklch(100% 0.37 330deg)&quot;,fallback:&quot;hsl(300deg 100% 75%)&quot;,alt:&quot;vivid light pink&quot;}),t.default.createElement(c,{color:&quot;oklch(0% 0.37 140deg)&quot;,fallback:&quot;hsl(120deg 100% 5%)&quot;,alt:&quot;very dark army green&quot;})),`
`,t.default.createElement(&quot;div&quot;,{className:&quot;alert&quot;},t.default.createElement(e.p,null,t.default.createElement(&quot;em&quot;,null,&quot;Update:&quot;),` This behavior is the result of browser bugs and does not
match the spec. Lightness of 100% should be pure white an 0% should be pure
black. It has been fixed in Chrome, and will likely change in other browsers
in the near future.`)),`
`,t.default.createElement(e.p,null,&quot;If you want true black, white, or gray, make sure the chroma value is 0.&quot;),`
`,t.default.createElement(e.p,null,&quot;Lightness can be expressed as a percent (&quot;,t.default.createElement(e.code,null,&quot;45%&quot;),&quot;) or as a decimal (&quot;,t.default.createElement(e.code,null,&quot;0.45&quot;),&quot;).&quot;),`
`,t.default.createElement(e.h3,null,&quot;Use a color picker&quot;),`
`,t.default.createElement(e.p,null,`In general, as long as you keep the chroma below 0.37,
you will usually be pretty safe.
It&#92;u2019s pretty easy to specify a color that&#92;u2019s not in range of your monitor,
but as long as your values are within reason, the browser should round it
to a predictable result.`),`
`,t.default.createElement(e.p,null,`But when you really want to fine tune things, I suggest you use a color picker.
The one at `,t.default.createElement(e.a,{href:&quot;https://oklch.com/&quot;},&quot;oklch.com&quot;),&quot; is fantastic:&quot;),`
`,t.default.createElement(e.p,null,t.default.createElement(e.img,{src:&quot;/images/2023/oklch-picker.png&quot;,alt:&quot;OKLCH color picker&quot;})),`
`,t.default.createElement(e.p,null,`This picker provides conversions to most other color formats,
so you can find fallback values easily.
Shift-clicking the color swatch in your browser&#92;u2018s DevTools also provides
conversions, like it always has.`),`
`,t.default.createElement(e.p,null,`Instead of an rgb or hsl picker that you may be used to, you&#92;u2019ll notice the colors
here aren&#92;u2019t displayed in perfect rectangles; it&#92;u2019s like chunks have been cut out of them.
This indicates where colors are out of range of computer monitors.
It&#92;u2019s a little odd at first, but it should start to make sense the more you play around with it.`),`
`,t.default.createElement(&quot;div&quot;,{className:&quot;alert&quot;},t.default.createElement(e.p,null,t.default.createElement(&quot;strong&quot;,null,&quot;Further reading:&quot;),` If you really want to dive deeper into
understanding gamuts, color spaces, and the other CSS color functions, I
highly recommend the `,t.default.createElement(e.a,{href:&quot;https://developer.chrome.com/articles/high-definition-css-color-guide/&quot;},`High Definition CSS Color
Guide`),`
by Adam Argyle.`)))}function P(n={}){let{wrapper:e}=Object.assign({},(0,T.useMDXComponents)(),n.components);return e?t.default.createElement(e,n,t.default.createElement(z,n)):z(n)}var Oe=P,re=ReactDOM.createRoot(document.getElementById(&quot;mdx-root&quot;));re.render(t.default.createElement(P,null));})();
&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>Redesign 2023</title>
    <link href="https://keithjgrant.com/posts/2023/03/redesign-2023/"/>
    <published>2023-03-29T17:54:00Z</published>
    <updated>2023-03-29T17:54:00Z</updated>
    <id>https://keithjgrant.com/posts/2023/03/redesign-2023/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Oh man, it always feels good to roll out a fresh site design!
I had a lot of fun with this one, especially since it’s my first chance to
really dig in to some newer CSS features.
And floats.
Who would have guessed I’d be using several of those in 2023?&lt;/p&gt;
&lt;p&gt;I’ve still got some rough edges to clean up, but I’m feeling good enough to
go live with this design.&lt;/p&gt;
&lt;p&gt;While working on this design, I spent a fair amount of time scrolling through
dozens of other blogs from people in the webdev community.
There are some really great sites out there!
So I thought it would be fun to point out some of my sources of inspiration.
These sites are a few of my favorites:&lt;/p&gt;
&lt;h2 id=&quot;henry.codes&quot; tabindex=&quot;-1&quot;&gt;henry.codes &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/03/redesign-2023/#henry.codes&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/blog-henry-codes.png&quot; alt=&quot;screenshot of henry.codes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This was the first design I came across that really struck me.
It leans hard into a clean minimalistic look that feels very reminescent
of the old &lt;a href=&quot;https://subtraction.com/&quot;&gt;subtraction.com&lt;/a&gt;, but with fresh, modernized
aestetic.&lt;/p&gt;
&lt;p&gt;I love all the little touches like the rotating curved text and blur filters.&lt;/p&gt;
&lt;p&gt;» &lt;a href=&quot;https://henry.codes/&quot;&gt;https://henry.codes&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;stephaniewalter.design&quot; tabindex=&quot;-1&quot;&gt;stephaniewalter.design &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/03/redesign-2023/#stephaniewalter.design&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/blog-stephanie-walter.png&quot; alt=&quot;screenshot of stephaniewalter.design&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you told me a site was swimming in vibrant yellow, I wouldn’t expect it to work,
but this site looks fantastic.
I especially love the buttons with a off-center faux screen printing effect.
In the end, I don’t think I borrowed any specific ideas from this site, but I still
found myself coming back and looking at it as I was brainstorming.&lt;/p&gt;
&lt;p&gt;» &lt;a href=&quot;https://stephaniewalter.design/&quot;&gt;https://stephaniewalter.design/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;michellebarker.co.uk&quot; tabindex=&quot;-1&quot;&gt;michellebarker.co.uk &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/03/redesign-2023/#michellebarker.co.uk&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/blog-michelle-barker.png&quot; alt=&quot;screenshot of michellbarker.co.uk&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So many cool little interactive elements and animations!
I love the vivid colors here and how almost everything on the page moves
in response to scrolling into view.&lt;/p&gt;
&lt;p&gt;» &lt;a href=&quot;https://michellebarker.co.uk/&quot;&gt;https://michellebarker.co.uk/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;trentwalton.com&quot; tabindex=&quot;-1&quot;&gt;trentwalton.com &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/03/redesign-2023/#trentwalton.com&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/blog-trent-walton.png&quot; alt=&quot;screenshot of trentwalton.com&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Of course Trent is going to have gorgeous typography.
But the other thing that really stood out to me here was the way he seems
to have designed the page uniquely for each and every post.
That’s definitely something I want to experiment with in the future.&lt;/p&gt;
&lt;p&gt;» &lt;a href=&quot;https://trentwalton.com/&quot;&gt;https://trentwalton.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;joshwcomeau.com&quot; tabindex=&quot;-1&quot;&gt;joshwcomeau.com &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/03/redesign-2023/#joshwcomeau.com&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2023/blog-josh-comeau.png&quot; alt=&quot;screenshot of joshwcomeau&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I came back to this site a lot in particular when I was figuring out how
I want to organize information on my site.
I’m hoping to dive a little deeper into proper tutorials and demos in the
future, and Josh’s site is a fantastic example of this.&lt;/p&gt;
&lt;p&gt;I may have also drawn a lot of inspiration for the mobile nav menu from here.&lt;/p&gt;
&lt;p&gt;» &lt;a href=&quot;https://www.joshwcomeau.com/&quot;&gt;https://www.joshwcomeau.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There were plenty of other excellent sites beyond these.
You all do some great work.
Keep it up!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>React Pays the Bills</title>
    <link href="https://keithjgrant.com/posts/2023/02/react-pays-the-bills/"/>
    <published>2023-02-08T18:02:14Z</published>
    <updated>2023-02-08T18:02:14Z</updated>
    <id>https://keithjgrant.com/posts/2023/02/react-pays-the-bills/</id>
    <content xml:lang="en" type="html">&lt;p&gt;It seems to be trendy to trash on React again.
Look, I know these criticisms aren’t targeted at me,
but I can’t help but feel defensive when I see post after post accusing React of
“taking us back to the days of Flash” or being “Facebook’s way to control JavaScript”
or that it’s full of “cultish worship” and a “goddamn awful technology and community”.&lt;/p&gt;
&lt;p&gt;These are direct quotes, from just some of the many posts I’ve seen, and holy shit are these
ridiculously over the top and melodramatic.&lt;/p&gt;
&lt;h2 id=&quot;you-can-code-in-react-without-being-a-%E2%80%9Creact-scab%E2%80%9D&quot; tabindex=&quot;-1&quot;&gt;You can code in React without being a “React scab” &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/02/react-pays-the-bills/#you-can-code-in-react-without-being-a-%E2%80%9Creact-scab%E2%80%9D&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So I totally acknowledge there are some asshats in the community, some of whom are very vocal.
I’m not going to pretend that isn’t the case.&lt;/p&gt;
&lt;p&gt;But don’t judge the entirety of one of the largest sectors in the web development industry
based on its bad actors.
There’s a reason React is so popular, and it’s not the assholes.
Maybe take a few minutes to step back and consider why.&lt;/p&gt;
&lt;p&gt;I will say this point blank:
I have built things in React that literally were not possible on the web before it came out.
&lt;sup&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2023/02/react-pays-the-bills/#footnote-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;It absolutely changed the landscape for the better;
it absolutely expanded the possibilities of what we can do on the web.&lt;/p&gt;
&lt;p&gt;It’s not the only game in town — several other (better) frameworks have built on what React
started — but it was the first library to offer truly componentized web development
without tons of technical overhead.
It was essentially first to market by several years.
That’s why it dominates the industry today.&lt;/p&gt;
&lt;h2 id=&quot;i%E2%80%99m-not-calling-it-perfect&quot; tabindex=&quot;-1&quot;&gt;I’m not calling it perfect &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2023/02/react-pays-the-bills/#i%E2%80%99m-not-calling-it-perfect&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The framework has its issues.
Single-page apps also have their issues
— though I will be quick to point out
there isn’t some 1:1 correlation between React and SPAs.
So do some of the other aspects of the ecosystem that surrounds it.
It’s totally okay to point out the problems with a technology.
In fact, I’d say it’s a good and productive thing to do.
But don’t disregard all the good it also provides.
And above all, don’t be an asshole about it.&lt;/p&gt;
&lt;p&gt;You aren’t going to make the industry better if your response to bad actors is
to be one yourself.&lt;/p&gt;
&lt;p&gt;Look, &lt;a href=&quot;https://keithjgrant.com/posts/2017/01/its-both&quot;&gt;I’m tired&lt;/a&gt; of &lt;a href=&quot;https://keithjgrant.com/posts/2018/09/wont-you-be-my-neighbor&quot;&gt;writing this same post&lt;/a&gt;
over and over.
Can we stop it already?&lt;/p&gt;
&lt;aside class=&quot;footnotes&quot;&gt;
&lt;span id=&quot;footnote-1&quot;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/span&gt;
I&#39;m getting questions about this comment.
Here&#39;s what I mean: React provides a higher-level abstraction on the web.
This makes the code easier to reason about and less error prone,
thus bringing more complicated problems into attainable reach.
&lt;p&gt;&lt;a href=&quot;https://softwareengineering.stackexchange.com/a/321838/297&quot;&gt;Here is a fantastic explanation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
If all you want is a coffee, then you want to tell the barista &quot;I want a coffee&quot;, not &quot;I want you to take three steps to the left, stretch out your arms, pick up the beans, put the in the grinder, push the button to grind them [...]&quot; and so on. It wouldn&#39;t make the final product any better (in fact, in some cases it&#39;d make it worse since the barista is probably way better than you at making coffee).
&lt;p&gt;High-level languages encourage you to think more about the problem domain and less about the execution platform. There is less ceremony, so you can spend more time on stuff that actually brings you value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/aside&gt;
</content>
  </entry>
  <entry>
    <title>You Should Start a Mastodon Instance</title>
    <link href="https://keithjgrant.com/posts/2022/you-should-start-a-mastodon-instance/"/>
    <published>2022-11-07T16:15:46Z</published>
    <updated>2022-11-07T16:15:46Z</updated>
    <id>https://keithjgrant.com/posts/2022/you-should-start-a-mastodon-instance/</id>
    <content xml:lang="en" type="html">&lt;p&gt;…and I don’t mean a single-user one for your own personal use.&lt;/p&gt;
&lt;h2 id=&quot;this-is-happening&quot; tabindex=&quot;-1&quot;&gt;This is happening &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2022/you-should-start-a-mastodon-instance/#this-is-happening&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hi! I hope you had a great weekend.&lt;/p&gt;
&lt;p&gt;While you were off, you know, having a life in the real world, something crazy happened online: users signed up for Mastodon.
&lt;a href=&quot;https://bitcoinhackers.org/@mastodonusercount/109299342554048578&quot;&gt;More than one person every second&lt;/a&gt;.
All weekend long.&lt;/p&gt;
&lt;p&gt;I logged in this morning to a full feed of content that I still haven’t finished reading.&lt;/p&gt;
&lt;p&gt;Even weirder, on Friday I found myself as one of the admins of a new instance, &lt;a href=&quot;https://front-end.social/&quot;&gt;front-end.social&lt;/a&gt;.
Now don’t go clicking that link to register just yet!&lt;a href=&quot;https://keithjgrant.com/posts/2022/you-should-start-a-mastodon-instance/#footnote&quot;&gt;*&lt;/a&gt;
I want to ask something a little bigger of you: start a new instance of your own.&lt;/p&gt;
&lt;p&gt;This is the Fediverse’s moment, and it has a very serious chance of succeeding.
The thing that will help it (and the internet, and the concept of actual decent, &lt;i&gt;social&lt;/i&gt; social networks) is more, smaller instances.
The big instances are getting buried right now.
Mastodon.social has been struggling with downtime and slow response times because it has suddenly become so popular.&lt;/p&gt;
&lt;h2 id=&quot;small-instances-are-where-mastodon-shines&quot; tabindex=&quot;-1&quot;&gt;Small instances are where Mastodon shines &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2022/you-should-start-a-mastodon-instance/#small-instances-are-where-mastodon-shines&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The “Local” timeline view on small instances are not a undigestable firehose of noise;
it is full of good content shared by people with at least one similar interest to yours.
It’s something “the birdsite” could never hope to be.&lt;/p&gt;
&lt;p&gt;But we need more of that.
When &lt;a href=&quot;https://front-end.social/@mia&quot;&gt;Mia&lt;/a&gt; started front-end.social (after a little prodding from a small group of folks), the idea was not to become &lt;em&gt;the&lt;/em&gt; Mastodon instance for the world of front end web development.
The idea was to be &lt;em&gt;one&lt;/em&gt; instance among many.
There is so much demand for this right now.
We have been flooded with requests to join, and for the sake of maintaining a tight knit community, we’ve dragged our feet a bit in accepting every one of them.&lt;/p&gt;
&lt;p&gt;So please, grab a few friends and start another one.
We’d love to network with you.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I&#39;ve been covering technology since the 90s (and using the internet since the 1980s, before there was a web 😏) and have to say I have never seen anything like the high speed mass migration to Mastodon from Twitter. These things generally happen as a drift not a sudden avalanche and it&#39;s extra-amazing that at this highly commercial moment in internet history, it&#39;s to a decentralised, free and open source software-based community. It&#39;s just 🤯 (in a good way)&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://mastodon.ie/@klillington/109298931503335348&quot;&gt;Karlin Lillington&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;small&gt;&lt;a name=&quot;footnote&quot;&gt;&lt;/a&gt;*Depending when you read this, registrations may actually be closed anyway. We’re having trouble keeping up!&lt;/small&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>What Happened to Unit Tests?</title>
    <link href="https://keithjgrant.com/posts/2021/06/what-happened-to-unit-tests/"/>
    <published>2021-06-29T16:25:49Z</published>
    <updated>2021-06-29T16:25:49Z</updated>
    <id>https://keithjgrant.com/posts/2021/06/what-happened-to-unit-tests/</id>
    <content xml:lang="en" type="html">&lt;p&gt;It seems to me &lt;a href=&quot;https://kentcdodds.com/blog/why-i-never-use-shallow-rendering&quot;&gt;one individual&lt;/a&gt; has convinced our entire industry to stop shallow rendering, and to abandon unit testing in favor of (almost) exclusively using integration tests and I gotta be honest, I don’t buy it.
I agree with many of the arguments in that post, but not the conclusion.
And I think the state of unit testing in React is worse off today than it was three years ago because of this mindset.&lt;/p&gt;
&lt;h2 id=&quot;tests-should-be-fast&quot; tabindex=&quot;-1&quot;&gt;Tests should be fast &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2021/06/what-happened-to-unit-tests/#tests-should-be-fast&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In that article, I believe this is a critical miss:&lt;/p&gt;
&lt;blockquote&gt;
There&#39;s no getting around the fact that shallow rendering is faster than any other form of testing react components.
It&#39;s certainly way faster than mounting a react component.
But we&#39;re talking a handful of milliseconds here.
Yes, it will add up, but I&#39;d gladly wait an extra few seconds or minutes for my tests to finish in exchange for my tests actually giving me confidence that my application will work when I ship it to users.
&lt;cite&gt;&lt;a href=&quot;https://kentcdodds.com/blog/why-i-never-use-shallow-rendering#-it-seems-like-a-waste-&quot;&gt;Kent C. Dodds&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;
&lt;p&gt;In my experience, we’re not talking milliseconds.
We’re talking minutes, often tens of minutes, across the entire test suite.&lt;/p&gt;
&lt;p&gt;Here’s the thing: slow tests don’t get run.
It’s as simple as that.
You will ignore slow tests as you code, because you have to.
You’re holding a bunch of context in your head, and you are working to organize thoughts and get them down, and you don’t have time to stop frequently and wait for tests to run.
You don’t have time to figure out whether the tests are checking the change you just saved, or if they’re still running from a change you made five minutes ago.
Not until it’s time to commit, or worse, merge, will you check test results.
Only then do you catch the error, sometimes even a critical oversight, and have to re-work.&lt;/p&gt;
&lt;p&gt;But fast tests — those you can leave running all the time.
Those give &lt;em&gt;instant&lt;/em&gt; feedback.
Those shorten the iteration cycle from hours to seconds.
Fast tests are worth more than slow tests.&lt;/p&gt;
&lt;h2 id=&quot;tests-should-be-focused&quot; tabindex=&quot;-1&quot;&gt;Tests should be focused &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2021/06/what-happened-to-unit-tests/#tests-should-be-focused&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kent’s post lays out many bad practices common in React unit testing, often seen when using Enzyme:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inspecting component state&lt;/li&gt;
&lt;li&gt;directly manipulating a component’s state&lt;/li&gt;
&lt;li&gt;spying on a component’s internal methods&lt;/li&gt;
&lt;li&gt;getting the component instance and fiddling with it in ways your user never could&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And I absolutely agree with every one of these points.
These are internals, implementation details that are irrelevant and have no business in your tests.
(Kent also makes arguments about accessibility I also agree with, but think are rather beside the point.)
And I also agree that Enzyme probably shouldn’t give us the ability to do many of those things, as it enables bad practices.&lt;/p&gt;
&lt;p&gt;But that doesn’t mean shallow rendering is wrong.
It means that bad test practices are.
These bad habits can exist with deep rendering just as with shallow.&lt;/p&gt;
&lt;p&gt;Kent says, “For example, the &lt;code&gt;&amp;lt;Fade /&amp;gt;&lt;/code&gt; component we have above is an implementation detail of the &lt;code&gt;&amp;lt;HiddenMessage /&amp;gt;&lt;/code&gt; component, but because we’re shallow rendering &lt;code&gt;&amp;lt;Fade /&amp;gt;&lt;/code&gt; isn’t rendered so changes to that component could break our application but not our test.”
But that’s good!
Let the unit tests for the Fade component test whether it works correctly.
Just assert that your component sets the correct props on Fade, and who cares what Fade does with them — those are concerns of a different test suite.&lt;/p&gt;
&lt;p&gt;With shallow rendering, a failure in the HiddenMessage test suite means HiddenMessage has a bug.
With full rendering, a bug in Fade means dozens of tests are going to fail across multiple test suites.
Then you get the fun job of tracking down where the bug originated.
With unit tests, you know exactly where the bug is, because only the test suite for the Fade component will break.
If &lt;code&gt;&amp;lt;Fade /&amp;gt;&lt;/code&gt; is well tested, and you assert that &lt;code&gt;&amp;lt;HiddenMessage /&amp;gt;&lt;/code&gt; passes the correct props to &lt;code&gt;&amp;lt;Fade /&amp;gt;&lt;/code&gt; based on user interaction, then you can be confident in your code.&lt;/p&gt;
&lt;h2 id=&quot;tests-should-be-comprehensive&quot; tabindex=&quot;-1&quot;&gt;Tests should be comprehensive &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2021/06/what-happened-to-unit-tests/#tests-should-be-comprehensive&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Is there a place for integration testing, as Kent explains?
Absolutely.
But not at the expense of unit testing.
Apart from being faster, unit testing also tests something integration tests simply cannot do: complex permutations.&lt;/p&gt;
&lt;p&gt;If a particular component has three key behaviors, it requires three unit tests*.
If its child component has three key behaviors, it requires three unit tests.
If you plan only to test those behaviors via integration tests, you need three times three unit tests, because you have to check how every permutation behaves.&lt;/p&gt;
&lt;p&gt;But your app isn’t just two components, so let’s keep going.
If you have a grandchild component with three key behaviors, you need to multiply all your integration tests by three, so you need 27 integration tests.
Four interacting components mean 81 tests.&lt;/p&gt;
&lt;p&gt;My codebase has hundreds of components.
I asked my calculator how many tests that would need, and it gave me an answer with an &lt;em&gt;e&lt;/em&gt; in it, if that tells you something.
How many components are in your codebase?&lt;/p&gt;
&lt;p&gt;Even a moderately sized app will need tens of thousands of tests to cover all the possible permutations of component behaviors.
If you rely solely on integration tests, you will &lt;em&gt;never&lt;/em&gt; have confidence in the behavior of your application, because you will never be able to write thousands of tests, let alone wait the hours required to run them all.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;*I’m simplifying, of course. In reality, the component probably needs six to ten unit tests, because you need to assert negative conditions as well, and error handling. So you can see that the problem will actually compound much worse that I’ve stated here.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;But with unit tests, it’s much simpler.
Assert that each component does what it should when given certain props, and when certain user interactions occur.
Then assert that each component sets the correct props on its children components.
If those child components are similarly tested, you can be confident with far fewer tests than checking all the ways those behaviors can mix and match in integration.&lt;/p&gt;
&lt;h2 id=&quot;react-has-shot-itself-in-the-foot&quot; tabindex=&quot;-1&quot;&gt;React has shot itself in the foot &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2021/06/what-happened-to-unit-tests/#react-has-shot-itself-in-the-foot&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What really gets me about all this is it’s not just the community that has gone down this road, the React library itself has encouraged it.
They have &lt;a href=&quot;https://github.com/facebook/react/issues/17321&quot;&gt;essentially abandoned their shallow renderer&lt;/a&gt;, and with the rise of hooks, third party libraries such as Enzyme can’t see into React’s internals enough to do it either.
The way React is structured now, you simply can’t exercise some hooks like &lt;code&gt;useEffect&lt;/code&gt; in a shallow render.
And yet nobody with any clout in the community seems to care.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Client Side NPM</title>
    <link href="https://keithjgrant.com/posts/2020/04/client-side-npm/"/>
    <published>2020-04-21T17:00:47Z</published>
    <updated>2020-04-21T17:00:47Z</updated>
    <id>https://keithjgrant.com/posts/2020/04/client-side-npm/</id>
    <content xml:lang="en" type="html">&lt;div class=&quot;alert&quot;&gt;
Disclaimer: this is an _idea_.
At this point, I’m not entirely convinced it’s a _good_ idea, but it’s an idea nonetheless.
This is something I thought of about a year ago, and recent discussions brought it back to mind.
The point of this post is to flush out the idea a bit, as a sort of thought experiment.
Take this with a grain of salt, and I’d gladly welcome feedback and critique.
&lt;/div&gt;
&lt;p&gt;So the web has a performance problem.
Before I get into it, I’ll concede the point that some of this is due to either over-engineered solutions or lazy developers who add dependencies without much thought to the ramifications.
But there are plenty of sites and apps out there that legitimately require complexity, large dependencies, and lots of code.&lt;/p&gt;
&lt;p&gt;This is a proposed solution to help address this problem: Client Side Package Management.&lt;/p&gt;
&lt;h2 id=&quot;goal%3A-dramatically-cut-network-traffic&quot; tabindex=&quot;-1&quot;&gt;Goal: Dramatically Cut Network Traffic &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#goal%3A-dramatically-cut-network-traffic&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On many, many sites, the bulk of the JavaScript bundle comes from dependencies.
This means we spend most of our bandwidth downloading the same code over and over again as we browse the web.
“Client side package management” would be an effort to offload all of that into an efficient caching strategy.
If two completely unrelated sites both use React Router, they can use the same local copy without repeatedly pulling it down the wire.
If a user frequently visits your app, they only need to re-download the code you wrote; not all of its dependencies.&lt;/p&gt;
&lt;p&gt;Conceptually, it would work like this:
Code split your app into two JavaScript files. The first would be your “core” app — all the code your team wrote. The second would be a bundle of all its dependencies.
So when a site needs a dependency such as React, instead of including React in the core bundle, it would be bundled in the dependencies file.
This dependencies bundle serves only as a backup for clients that don’t support package management.&lt;/p&gt;
&lt;p&gt;If the browser does support it, it would fetch only the core script.
Then instead of downloading the dependencies bundle, it would go directly to NPM, fetch the appropriate version of React, and store it locally.
It would do likewise for any other dependencies.
On subsequent visits, the core script can be re-downloaded (or fetched from normal browser cache), but the dependencies would already be available locally in the package manager, so it would not need to be downloaded again.
And because the dependencies are indexed by their name on npm, they can be re-used on other sites that rely on the same packages.&lt;/p&gt;
&lt;p&gt;If the browser does not support client side package management, it would fetch the the dependencies bundle from the page’s server.
The page would effectively work just like the web does today.
This can be a fully backwards-compatible enhancement.&lt;/p&gt;
&lt;p&gt;In some ways, this is an iteration on the idea of a CDN, which among other things attempt to save bandwidth by relying on browser caching.
In practice, though, CDNs very rarely produce hits in the browser cache;
too many sites rely on slightly different version numbers or different host CDNs to ever gain that benefit.
But with package management, the host is always the user’s computer and it is fully aware of semantic versioning;
one site relying on &lt;code&gt;lodash ^4.17.4&lt;/code&gt; and another using &lt;code&gt;lodash ^4.17.10&lt;/code&gt; would share the same resource.
For reliability, packages are sourced directly from the package manager where their author has published them (i.e. npm.com).&lt;/p&gt;
&lt;h2 id=&quot;how-to-accomplish-this&quot; tabindex=&quot;-1&quot;&gt;How to Accomplish This &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#how-to-accomplish-this&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This sounds nice in theory, but how could it actually be accomplished? And I mean, accomplished today, without waiting interminably for standards bodies to do their thing. I believe in the power of the open web platform — but wild new ideas generally need to be proven in the real world before a standards body would ever consider adding them to language specifications.&lt;/p&gt;
&lt;p&gt;I see two potential approaches that could work.
The first approach would be through the use of a browser extension.
The second would be through the installation of a native application that leverages custom protocol URLs (something like &lt;code&gt;package://npmjs.com/react@^16.10.0&lt;/code&gt;).
It’s possible a hybrid of both would be necessary to work around CORS issues and access to enough hard drive space to store downloaded packages.
Either way, web apps that are built for this approach could encourage their users to install the appropriate dependencies:
A banner with a link could appear at the top of the page that reads,
“This site has been optimized for client side package management. For a faster browsing experience, you can install the plugin for free.”&lt;/p&gt;
&lt;p&gt;I’ll be the first to admit, this brings up flashbacks of old sites with the notice, “This site required Flash,” which is not a pleasant memory.
The key difference would be that the plugin is entirely &lt;em&gt;optional&lt;/em&gt;.
The page remains fully functional without it, by downloading dependencies from the server, bundled in a normal JavaScript file.
And, unlike Flash, these dependencies would be built using native web technologies.&lt;/p&gt;
&lt;h3 id=&quot;versioning-and-storage&quot; tabindex=&quot;-1&quot;&gt;Versioning and Storage &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#versioning-and-storage&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This must be fully aware of semantic versioning.
If a page depends on a version &lt;code&gt;^3.14.0&lt;/code&gt; and version &lt;code&gt;3.14.7&lt;/code&gt; is found in the local cache, the already-available package would be used.
Meanwhile, the client could even perform a check in the background if any newer version is available, and if it is, download it in a low-priority network request for use in subsequent visits to pages that rely on it.&lt;/p&gt;
&lt;p&gt;I’m a little hesitant about that last part.
We wouldn’t want to chew through disk space eagerly storing unnecessary files.
But either way, there should probably some sort of cleanup algorithm that takes into account how frequently a specific package is used and how long it has been since last use, automatically deleting those that seem to be outdated.
The user could even customize how much disc space they wish to allocate for package storage.&lt;/p&gt;
&lt;h2 id=&quot;miscellaneous-thoughts%2C-questions%2C-problems&quot; tabindex=&quot;-1&quot;&gt;Miscellaneous Thoughts, Questions, Problems &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#miscellaneous-thoughts%2C-questions%2C-problems&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;bundling&quot; tabindex=&quot;-1&quot;&gt;Bundling &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#bundling&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This would definitely require code splitting to separate the “core” app script from the dependencies (and of course further splitting should be supported).
The exact specifics need to be worked out for how native JavaScript would load the secondary bundle while the plugin, if installed, would prevent that request and use package management instead.
Would this require a custom bundler, or could an existing bundler do this in an acceptable way?&lt;/p&gt;
&lt;h3 id=&quot;testing-%26-debugging&quot; tabindex=&quot;-1&quot;&gt;Testing &amp;amp; Debugging &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#testing-%26-debugging&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This could complicate development as different users could end up with different combinations of package versions.
The plugin should probably provide a user-friendly means of creating a bug report with exact versions in use, and stack traces if available.&lt;/p&gt;
&lt;h3 id=&quot;abuse-by-developers&quot; tabindex=&quot;-1&quot;&gt;Abuse by Developers &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#abuse-by-developers&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://usa.streetsblog.org/2017/06/21/the-science-is-clear-more-highways-equals-more-traffic-why-are-dots-still-ignoring-it/&quot;&gt;More highways equals more traffic&lt;/a&gt;.
I’d hate for developers to see this as a carte blanche for dumping &lt;em&gt;more&lt;/em&gt; dependencies on a site, but that’s sure to happen.
I don’t have a great solution to this at this time, but I would certainly be on the lookout for any ways to incentivize developers to keep their dependencies in check.
The browser must pull them down the first time they’re required, after all.&lt;/p&gt;
&lt;h3 id=&quot;wasm&quot; tabindex=&quot;-1&quot;&gt;WASM &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#wasm&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Client side package management addresses network bloat, but there remains the overhead of parsing those scripts every time they’re used.
Can we leverage (or even require) WASM to speed things up even more?&lt;/p&gt;
&lt;h3 id=&quot;exit-strategy&quot; tabindex=&quot;-1&quot;&gt;Exit Strategy &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#exit-strategy&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I mentioned flashbacks to “this site requires Flash” notices.
This could be amazing now, but I would hate for this to still be the status quo ten years from now.
I would hope some future iteration on the idea (including a formalization of what constitutes a “package”) could make its way into the native platform and then this project could be sunset.&lt;/p&gt;
&lt;h3 id=&quot;security&quot; tabindex=&quot;-1&quot;&gt;Security &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#security&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When it comes down to it, your webpage could be running code that was installed by some other unknown website.
This sounds &lt;em&gt;Really Bad&lt;/em&gt; when put in those terms.
However, this only applies to code installed directly off of a known package repository (e.g. npm), and only if your site explicitly asked for that same package to be run.
The key task here would be to ensure this can’t be spoofed.
I would also presents the user with a big red dialog box about “unfamiliar package repository” if a site ever tried to install a package from anywhere else.
(You could restrict it to npm only, but I want to leave the door open for alternatives like &lt;a href=&quot;https://www.entropic.dev/&quot;&gt;Entropic&lt;/a&gt;.)
Fingerprinting based on which packages load instantly from cache could also be a concern.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2020/04/client-side-npm/#conclusion&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Obviously a lot of the technical details would need to be ironed out, if this is something to pursue.
I’ve resisted the temptation in this post to go down that road, and instead focused on more on the general concept.
So what do you think?
Am I crazy or could this work?&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Adding Webmention Support to a Static Site</title>
    <link href="https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/"/>
    <published>2019-02-01T17:47:33Z</published>
    <updated>2019-02-01T17:47:33Z</updated>
    <id>https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/</id>
    <content xml:lang="en" type="html">&lt;div class=&quot;alert&quot;&gt;
This is Part 3 in a series exploring short-form posting, the IndieWeb, and taking control of your own online social workflow for your statically-generated site:
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/&quot;&gt;A Low-Friction Workflow for Short-form Posting on A Statically Generated Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes&quot;&gt;Preparing Your Site for Posting Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adding Webmention Support to a Static Site&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Using Micropub to Post to a Static Site — &lt;em&gt;Coming soon&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p&gt;Webmentions are a key building block of the IndieWeb.
They are they way for one webpage to notify another when it has linked to it.
This is based on a &lt;a href=&quot;https://www.w3.org/TR/webmention/&quot;&gt;W3C Recomendation spec&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this post, I will show you how to set up webmentions for a statically-generated site.
This is not the only way to do it, but it is how I do it for this site, so I know it works.&lt;/p&gt;
&lt;h2 id=&quot;receiving-webmentions&quot; tabindex=&quot;-1&quot;&gt;Receiving Webmentions &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/#receiving-webmentions&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A webmention is sent as a POST request containing two pieces of information: a &lt;em&gt;source&lt;/em&gt; URL and a &lt;em&gt;target&lt;/em&gt; URL.
The page at the source URL must include a link to the target URL, which the webmention endpoint will verify before accepting the webmention.
(You won’t actually have to implement this, but it’s worth knowing for context.)&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes&quot;&gt;the previous post&lt;/a&gt;, I showed you how to sign in to a service called &lt;a href=&quot;https://webmention.io/&quot;&gt;webmention.io&lt;/a&gt;.
This will provide your site’s webmention endpoint.
When someone links to a page on your site, they can notify the endpoint, and you can choose what to do with that webmention.&lt;/p&gt;
&lt;p&gt;You will have to make this endpoint known so you can receive webmentions.
Log-in to webmention.io, and navigate to the &lt;a href=&quot;https://webmention.io/settings&quot;&gt;settings page&lt;/a&gt;.
This will provide you with two &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags you should add to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; for every page on your site.
They will look something like this (with your site’s URL in place of &lt;code&gt;keithjgrant.com&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;webmention&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://webmention.io/keithjgrant.com/webmention&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pingback&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://webmention.io/keithjgrant.com/xmlrpc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this to your site.
This tells other sites what endpoint to send their webmentions to.
(It also includes a pingback endpoint for sites that support the older standard instead—webmention.io will handle both for you).
Webmention.io provides you with a feed you can subscribe to to see all webmentions you receive.
The url is listed on the settings page.&lt;/p&gt;
&lt;h3 id=&quot;displaying-webmentions-on-your-site&quot; tabindex=&quot;-1&quot;&gt;Displaying webmentions on your site &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/#displaying-webmentions-on-your-site&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Part of the fun of webmentions is being able to display the replies, likes, and shares of your content you receive.
If you want, you can add some JavaScript to your site to fetch these, and add them to the page beneath your posts.&lt;/p&gt;
&lt;p&gt;How you add these to your page will be unique to your site’s design,
so I can’t give you exact code to copy and paste,
but I’ll highlight the key things I do for my site.
Here is my &lt;a href=&quot;https://github.com/keithjgrant/keithjgrant.com/blob/master/themes/shindig/static-src/js/webmentions.js&quot;&gt;source code&lt;/a&gt; for this.&lt;/p&gt;
&lt;p&gt;First, perform a GET to &lt;code&gt;https://webmention.io/api/mentions?perPage=500&amp;amp;jsonp=parseWebmentions&amp;amp;target={PAGE_URL}&lt;/code&gt;,
where &lt;code&gt;{PAGE_URL}&lt;/code&gt; is the current page.
You will need to use &lt;a href=&quot;https://stackoverflow.com/questions/2067472/what-is-jsonp-all-about#2067584&quot;&gt;JSONP&lt;/a&gt; so you can make the request cross-domain.
This url specifies the JSONP function &lt;code&gt;parseWebmentions&lt;/code&gt;, which you will need to include in your page’s script.
This function must be defined as a global, so the webmention.io script can invoke it.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;parseWebmentions()&lt;/code&gt; function will be invoked with an array of objects, each representing a webmention you’ve recieved, including metadata about the source page that sent it.
Each one of these objects will look something like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://example-site.com/2019/01/22/the-secret-weapon-to-learning-css/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;verified&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;verified_date&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2019-01-22T17:06:39+00:00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;580804&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;private&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://example-site.com/2019/01/22/the-secret-weapon-to-learning-css/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;published&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;published_ts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;John Smith&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example-site.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&quot;photo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example-site.com/avatar.jpg&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;activity&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;link&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;sentence&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://example-site.com/2019/01/22/the-secret-weapon-to-learning-css/ posted &#39;&#39; linking to https://keithjgrant.com/posts/2019/01/css-mental-model/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;sentence_html&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;a href=&#92;&quot;https://example-site.com/2019/01/22/the-secret-weapon-to-learning-css/&#92;&quot;&gt;someone&amp;lt;/a&gt; posted &#39;&#39; linking to &amp;lt;a href=&#92;&quot;https://keithjgrant.com/posts/2019/01/css-mental-model/&#92;&quot;&gt;https://keithjgrant.com/posts/2019/01/css-mental-model/&amp;lt;/a&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://keithjgrant.com/posts/2019/01/css-mental-model/&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I sort these chronologically (using &lt;code&gt;webmention.data.published&lt;/code&gt; or &lt;code&gt;webmention.verified_data&lt;/code&gt;).
Then I put them into three different arrays:
one for likes (&lt;code&gt;webmention.activity.type === &#39;like&#39;&lt;/code&gt;),
one for reposts and regular links (&lt;code&gt;webmention.activity.type === &#39;repost&#39; || webmention.activity.type === &#39;link&#39;&lt;/code&gt;),
and one for replies (everything else).
I then have code to convert the metadata into DOM elements and add them to the page in the appropriate place.
Again, feel free to rework &lt;a href=&quot;https://github.com/keithjgrant/keithjgrant.com/blob/master/themes/shindig/static-src/js/webmentions.js&quot;&gt;my code&lt;/a&gt; to suit your purposes for this—I include a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; on my page for these, which I clone into the page and hydrate with the fetched data.&lt;/p&gt;
&lt;p&gt;If you really want to get fancy, you can fetch your webmentions every time your site builds, and add them into the initial render of the page.
If you go this route, however, you should probably make an additional fetch from the client side to pick up any webmentions that you’ve received since the last time you built your site.&lt;/p&gt;
&lt;h2 id=&quot;enhancing-with-microformats&quot; tabindex=&quot;-1&quot;&gt;Enhancing with Microformats &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/#enhancing-with-microformats&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a webmention is sent, it includes only the source URL and the target URL.
But you’ll notice in the payload received from webmention.io
there is a whole lot more information included.
This is all information it was able to parse from the page at the source URL.
How much it is able to figure out will depend largely on whether or not the page uses &lt;em&gt;microformats&lt;/em&gt; in its markup.&lt;/p&gt;
&lt;p&gt;Microformats are a series of class names added to the markup to help indicate the meaning of the parts of a page.
I strongly urge you to add &lt;a href=&quot;http://microformats.org/wiki/microformats2&quot;&gt;microformats support&lt;/a&gt; to your site.
Most tools for working with webmentions expect microformats to be there, so this will be essential for using some IndieWeb tools.&lt;/p&gt;
&lt;p&gt;There are two types of microformat that you should add to your site first: &lt;a href=&quot;http://microformats.org/wiki/microformats2#h-entry&quot;&gt;h-entry&lt;/a&gt; and &lt;a href=&quot;http://microformats.org/wiki/microformats2#h-card&quot;&gt;h-card&lt;/a&gt;.
An h-entry allows you to identify the key elements of a post, including url, title, publish date, and content.
An h-card identifies an author, including name, website url, and an avatar image.&lt;/p&gt;
&lt;p&gt;Add the classes for an h-entry to the markup for every post on your site.
Here is a sample post with h-entry microformat classes added:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;note note--list h-entry&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;note__date&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;u-url dt-published&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://keithjgrant.com/replies/2019/01/yes-do-it/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;10:24 AM EST • 14 Jan 2019&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;metadata text-left&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    in reply to
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;u-in-reply-to&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;in-reply-to&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://twitter.com/SaraSoueidan/status/1084833046140981248&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a post on twitter.com&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;note__body e-content show-embeds&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Yes! DO IT ✨&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The essentials here are the classes &lt;code&gt;h-entry&lt;/code&gt;, &lt;code&gt;u-url&lt;/code&gt;, and &lt;code&gt;e-content&lt;/code&gt;.
&lt;code&gt;h-entry&lt;/code&gt; must be on an element containing the entire post.
The other microformat classes within indicate further metadata on this entry.
&lt;code&gt;u-url&lt;/code&gt; indicates the canonical URL of this post (specified in the &lt;code&gt;href&lt;/code&gt; attribute).
&lt;code&gt;e-content&lt;/code&gt; denotes the body of the post.
&lt;code&gt;p-name&lt;/code&gt;, when present, indicates the title of the post
(this post has no title because it is a short-form note rather than a full blog post).
Some other useful classes an h-entry might have are &lt;code&gt;u-in-reply-to&lt;/code&gt; or &lt;code&gt;u-like-of&lt;/code&gt;, indicating the post is a reply to or “like” of a post elsewhere on the web,
and &lt;code&gt;dt-published&lt;/code&gt; to indicate the publish date.&lt;/p&gt;
&lt;p&gt;The tag names are irrelevant;
use correct semantic HTML elements,
and add microformat classes to indicate what the elements mean as part of the post.&lt;/p&gt;
&lt;p&gt;Your homepage should have an h-card on it somewhere, providing data about you.
Here is an example h-card:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;h-card p-author&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title-bar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;u-url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;p-name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Keith J. Grant&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title-avatar u-photo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/images/keithjgrant.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;h-card&lt;/code&gt; indicates this is an author card.
&lt;code&gt;u-url&lt;/code&gt; denotes my primary url (the homepage of the site).
&lt;code&gt;p-name&lt;/code&gt; indicates my name and &lt;code&gt;u-photo&lt;/code&gt; indicates the image I would like to use as my avatar.&lt;/p&gt;
&lt;p&gt;You can also nest microformats.
It is a good idea to add an author card inside every post.
When this &lt;code&gt;h-card&lt;/code&gt; is placed inside of an &lt;code&gt;h-entry&lt;/code&gt;, the &lt;code&gt;p-author&lt;/code&gt; class indicates that I am the author of that post.
So when I reply to a post elsewhere on the web, the site that recieves my webmention can parse some data about me and my reply, and present them in a way that is well formatted.
This sort of metadata is what allows me to present not just replies to my posts, but also the author’s name and image alongside:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/webmentions-formatted.png&quot; alt=&quot;Two replies to a previous post of mine. Each reply includes the author’s name and small avatar image.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Adding microformats can be a bit troublesome, because it’s hard to know at a glance whether you’ve done it correctly or not.
One tool to help with this is &lt;a href=&quot;https://xray.p3k.io/&quot;&gt;X-Ray&lt;/a&gt;.
As you integrate microformats on your site, plug in the URL to a post into X-Ray and see what kind of metadata it finds.
This will display as a JSON object:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/x-ray-results.png&quot; alt=&quot;A sample JSON result from X-Ray after parsing one of my posts&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Additional microformats can be used to markup things like recipes, events, and products reviews. See &lt;a href=&quot;http://microformats.org/wiki/microformats2&quot;&gt;microformats.org&lt;/a&gt; for details on these if you’re interested.&lt;/p&gt;
&lt;h2 id=&quot;integrating-with-twitter&quot; tabindex=&quot;-1&quot;&gt;Integrating with Twitter &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/#integrating-with-twitter&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Webmentions are fantastic for interacting with other blogs, but what about social media?
Twitter doesn’t support webmentions, but there is a tool called &lt;a href=&quot;https://keithjgrant.com/brid.gy&quot;&gt;Bridgy&lt;/a&gt; that can add this support for you.
If you send Bridgy a webmention, it will post your note to your social network accounts for you.
And if those posts get replies (or likes, or reposts), Bridgy will send you valid webmentions letting you know about it.&lt;/p&gt;
&lt;p&gt;To setup Bridgy for Twitter, visit &lt;a href=&quot;https://brid.gy/&quot;&gt;brid.gy&lt;/a&gt; and click the Twitter button where it says “Connect your accounts”.
This will take you to twitter, and will ask you to grant Bridgy access to post to your Twitter account.
Click “Authorize app” to give it access.&lt;/p&gt;
&lt;p&gt;Next, you’ll need to add a link to Bridgy in your posts, so you can send a webmention to Bridgy.
Add this to your page template for notes.
If you followed the site structure like as I described in the previous post,
a good place to add this would be to the partial template at &lt;code&gt;themes/[theme-name]/partials/note-full.html&lt;/code&gt;.
Place this inside the &lt;code&gt;e-content&lt;/code&gt; of your &lt;code&gt;h-entry&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://brid.gy/publish/twitter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;data&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;p-bridgy-omit-link&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line provides an (empty) link to bridgy, so you can send Bridgy a webmention.
The second line — which is totally optional — provides some metadata to bridgy telling it to include a link back to your post when it syndicates to twitter.&lt;/p&gt;
&lt;h2 id=&quot;send-your-first-webmention&quot; tabindex=&quot;-1&quot;&gt;Send your first webmention &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/#send-your-first-webmention&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point, your site should be able to receive webmentions,
and it should have valid microformats so other sites can parse your content should you send webmentions.&lt;/p&gt;
&lt;p&gt;Your site doesn’t send webmentions yet
— I’ll cover that in the next post —
but in the meantime, you should be able to send them manually with a tool called Telegraph.&lt;/p&gt;
&lt;p&gt;If you haven’t already, publish a Note to your site.
Make sure the resulting page has valid microformats (using &lt;a href=&quot;https://xray.p3k.io/&quot;&gt;X-Ray&lt;/a&gt;) and it includes a link to bridgy.&lt;/p&gt;
&lt;p&gt;Then visit &lt;a href=&quot;https://telegraph.p3k.io/&quot;&gt;Telegraph&lt;/a&gt; and sign in using web sign-in:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/telegraph-sign-in.png&quot; alt=&quot;Web sign in screen for Telegraph&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once logged in, you should see a text input labelled “Find Links”.
Enter the full URL of the note you just published and click the button:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/telegraph-find-links.png&quot; alt=&quot;Telegraph screen to find links on a page. A page url has been entered into the form.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Telegraph will scan the &lt;code&gt;h-entry&lt;/code&gt; on that page and find any links therein, including your link to Bridgy.
It will show you a list of these links and, for those targets that support webmentions, it will provide a button to send a webmention.
Click the button next to ”https://brid.gy/publish/twitter”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/telegraph-send-wm.png&quot; alt=&quot;Telegraph screen listing links found in the given page. It includes a link to brid.gy/publish/twitter and a blue button that reads &amp;quot;Send Webmention&amp;quot;.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If all goes well, your note should show up on Twitter, posted from your account!
Bridgy will monitor your stream for activity, and when it sees some, it will forward it to your site as a webmention.
It polls once every 30 minutes or so, so replies won’t show up immediately.
While testing, though, you can log in to Bridgy and click the “poll now” button to skip this waiting period.&lt;/p&gt;
&lt;p&gt;Hopefully this is helpful!
In the final post, I will walk you through setting up Micropub so you can post notes more quickly to your site,
and I will show you how to automate the sending of webmentions, so you won’t have to open up Telegraph every time you post a new note.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Preparing Your Site for Posting Notes</title>
    <link href="https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/"/>
    <published>2019-01-17T19:23:33Z</published>
    <updated>2019-01-17T19:23:33Z</updated>
    <id>https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/</id>
    <content xml:lang="en" type="html">&lt;div class=&quot;alert&quot;&gt;
This is Part 2 in a series exploring short-form posting, the IndieWeb, and taking control of your own online social workflow for your statically-generated site:
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/&quot;&gt;A Low-Friction Workflow for Short-form Posting on A Statically Generated Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preparing Your Site for Posting Notes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site&quot;&gt;Adding Webmention Support to a Static Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using Micropub to Post to a Static Site — &lt;em&gt;Coming soon&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p&gt;If you’re eager to get going with short-form posting using webmentions and micropub,
it’s worth giving your site structure a little thought so you know where your notes will go.
You’ll also need to enable something called Web Sign-in so you can use some of the tools I will mention in the upcoming posts.&lt;/p&gt;
&lt;p&gt;I’ll walk you through these things below.
My thoughts on site structure may be self-apparent,
but be sure you don’t miss the setup instructions for Web Sign-in,
as this will be an essential prerequisite as we continue in the following posts.&lt;/p&gt;
&lt;h2 id=&quot;static-site-structure&quot; tabindex=&quot;-1&quot;&gt;Static site structure &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/#static-site-structure&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I use Hugo and Netlify for my site.
My site content is stored in a git repository.
For new posts, I create a markdown file anywhere within a &lt;code&gt;content&lt;/code&gt; directory.
Hugo builds these files into web pages, with URLs matching the directory structure.
When a change is pushed to Github, Netlify automatically runs Hugo and publishes the resulting pages.
If you want help getting started with these, Sara Soueidan has
&lt;a href=&quot;https://www.sarasoueidan.com/blog/jekyll-ghpages-to-hugo-netlify/&quot;&gt;a great post on setting those up&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My instructions in this series can be applied to any static site generator and hosting setup.
As long as you have a static site and an automatic build process, it should work for you,
but it you may have to adapt some of the specifics yourself.&lt;/p&gt;
&lt;p&gt;It usually makes sense to have separate places in your site structure for long- and short-form posts.
On my site, I have broken these up into separate directories called &lt;code&gt;posts&lt;/code&gt; and &lt;code&gt;notes&lt;/code&gt;.
I’ve also added additional directories for &lt;code&gt;replies&lt;/code&gt;, &lt;code&gt;likes&lt;/code&gt;, and &lt;code&gt;bookmarks&lt;/code&gt;;
you don’t have to add these if you don’t want to, but I find them useful.&lt;/p&gt;
&lt;p&gt;Inside all five of these directories, have subdirectories for each year, and subdirectories for each month within those.
My directory structure looks &lt;a href=&quot;https://github.com/keithjgrant/keithjgrant.com&quot;&gt;something like this&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ascii&quot;&gt;&lt;code class=&quot;language-ascii&quot;&gt;content
 └- bookmarks
 └- likes
 └- notes
 |   └- 2018
 |   |   └- 11
 |   |   └- 12
 |   └ 2019
 |   |   └- 01
 └- posts
 |   └- 2018
 |   |   └- 11
 |   |   └- 12
 |   └ 2019
 |   |   └- 01
 └- replies
static
 └- images
themes
 └- shindig (my theme name)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My site is run by &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;.
Adjust this as necessary for Jekyll or Eleventy.
Or to suit your own desires, really.&lt;/p&gt;
&lt;p&gt;I prefer my long-form posts to look different than my notes and replies,
so in my Hugo theme, I have some logic to render those using different partial templates.
If you’re comfortable editing your Hugo theme, you can change it to something &lt;a href=&quot;https://github.com/keithjgrant/keithjgrant.com/blob/master/themes/shindig/layouts/_default/single.html&quot;&gt;like mine&lt;/a&gt;.
This is the file &lt;code&gt;themes/[theme-name]/layouts/_default/single.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en-US&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &#92;{&#92;{ partial &quot;meta.html&quot; . }}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &#92;{&#92;{ partial &quot;header.html&quot; . }} &#92;{&#92;{ if or (eq .Section &quot;posts&quot;) (eq
    .Section &quot;&quot;) }}
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;l-article h-entry js-main&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;l-article__inner&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&#92;{&#92;{ partial &quot;post-full.html&quot; .}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;l-note h-entry js-main&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;l-note__inner&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&#92;{&#92;{ partial &quot;note-full.html&quot; . }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &#92;{&#92;{ end }} &#92;{&#92;{ partial &quot;footer.html&quot; . }}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key here is the &lt;code&gt;if&lt;/code&gt; statement near the middle: &lt;code&gt;&#92;{&#92;{ if or (eq .Section &amp;quot;posts&amp;quot;) (eq .Section &amp;quot;&amp;quot;)}}&lt;/code&gt;.
This statement renders the first block for pages that are in the &lt;code&gt;posts&lt;/code&gt; directory (&lt;code&gt;.Section&lt;/code&gt; equals “posts”)
or are top level pages (&lt;code&gt;.Section&lt;/code&gt; equals an empty string).
It renders the second block for everything else, such as my notes and replies.&lt;/p&gt;
&lt;p&gt;The first block includes a partial called &lt;code&gt;post-full.html&lt;/code&gt; which is resides at &lt;code&gt;themes/[theme-name]/partials/post-full.html&lt;/code&gt;.
The second block includes &lt;code&gt;note-full.html&lt;/code&gt; which is at &lt;code&gt;themes/[theme-name]/partials/note-full.html&lt;/code&gt;.
Use these partials to define your page structure for long-form and short-form content, respectively.&lt;/p&gt;
&lt;p&gt;Once that is done, you can create a new note by creating a new file in the appropriately-dated directory:
&lt;code&gt;hugo new notes/2019/01/my-first-note.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This part of your setup is very flexible.
Use my directory structure as an example,
but feel free to modify it as needed so your site’s structure makes sense to you.&lt;/p&gt;
&lt;h2 id=&quot;enable-web-sign-in&quot; tabindex=&quot;-1&quot;&gt;Enable Web Sign-in &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/#enable-web-sign-in&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Several of the tools and services used on the IndieWeb require something called &lt;em&gt;Web Sign-in&lt;/em&gt;.
This is a way to login to a service using your website as a username, proving you are the owner of your website.
Setting this up should be a quick process.&lt;/p&gt;
&lt;h3 id=&quot;link-to-social-profiles&quot; tabindex=&quot;-1&quot;&gt;Link to social profiles &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/#link-to-social-profiles&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, add links to your various social profiles somewhere on your homepage. Include a &lt;code&gt;rel=&amp;quot;me&amp;quot;&lt;/code&gt; attribute for each of these:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;social-links&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://twitter.com/keithjgrant&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;me&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;@keithjgrant on Twitter&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://github.com/keithjgrant&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;me&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Github&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to include GitHub. I’ll explain why in a moment.&lt;/p&gt;
&lt;h3 id=&quot;make-your-social-profiles-link-back-to-your-site&quot; tabindex=&quot;-1&quot;&gt;Make your social profiles link back to your site &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/#make-your-social-profiles-link-back-to-your-site&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Second, on each of the services you link to, edit your profile so the “homepage” field links back to your website:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/settings/profile&quot;&gt;Edit your Twitter profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/settings/profile&quot;&gt;Edit your GitHub profile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/url-in-github-profile.png&quot; alt=&quot;The URL field in my Github profile, set to https://keithjgrant.com&quot; /&gt;&lt;/p&gt;
&lt;p&gt;At this point, your homepage links to your social profiles,
and your social profiles link back to your homepage,
each with a &lt;code&gt;rel=&amp;quot;me&amp;quot;&lt;/code&gt; attribute.
This means, if you log in somewhere using OAuth for one of these accounts,
you also verify that you are the owner of your site.&lt;/p&gt;
&lt;p&gt;Unfortunately, Twitter recently changed the way profile links work with some complicated redirects,
so it’s not straightforward for a service to verify the link.
GitHub is the best widely-used service right now that correctly uses &lt;code&gt;rel=&amp;quot;me&amp;quot;&lt;/code&gt; on a user profile page,
so I recommend you use this service for Web Sign-in.&lt;/p&gt;
&lt;h3 id=&quot;define-a-preferred-web-sign-in-endpoint&quot; tabindex=&quot;-1&quot;&gt;Define a preferred web sign-in endpoint &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/#define-a-preferred-web-sign-in-endpoint&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, you will add some metadata to your homepage to indicate which Web sign-in service you wish to use.
I recommend &lt;a href=&quot;https://indieauth.com/&quot;&gt;IndieAuth.com&lt;/a&gt;, though there are others available.&lt;/p&gt;
&lt;p&gt;Add this code within the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your site’s homepage:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;authorization_endpoint&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://indieauth.com/auth&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;token_endpoint&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://tokens.indieauth.com/token&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells third party services that use web sign-in to use IndieAuth to log you in.
IndieAuth will find the link to your Github account and verify that your GitHub profile links back to your site.
It will then allow you to login to Github,
and will then provide a token to third party services verifying you are the owner of your site.&lt;/p&gt;
&lt;h3 id=&quot;test-it-out&quot; tabindex=&quot;-1&quot;&gt;Test it out &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes/#test-it-out&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To test that this is working, visit &lt;a href=&quot;https://webmention.io/&quot;&gt;Webmention.io&lt;/a&gt; and enter your site’s url in the login box:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/webmention-io-sign-in.png&quot; alt=&quot;Web sign-in form on webmention.io, with https://keithjgrant.com entered into the input field&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This will take you to indieauth, your preferred Web sign-in endpoint. IndieAuth will ask which service you wish to use to sign in:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/indieauth-sign-in.png&quot; alt=&quot;IndieAuth.com showing my Github profile &quot; /&gt;&lt;/p&gt;
&lt;p&gt;Github is the only valid option right now, so click the green Github button. If you’re not already logged into Github, this will ask you to login. Once logged in, it will redirect you back to webmention.io.&lt;/p&gt;
&lt;p&gt;Now you’ve created an account on webmention.io:
your website url is your username, and you didn’t need to create a password or check an email;
your website and Github account are all you need.
In the next post, I’ll show you how you can use this service to receive webmentions on your site.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Low-Friction Workflow for Short-form Posting on a Statically Generated Site</title>
    <link href="https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/"/>
    <published>2019-01-17T15:11:33Z</published>
    <updated>2019-01-17T15:11:33Z</updated>
    <id>https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/</id>
    <content xml:lang="en" type="html">&lt;div class=&quot;alert&quot;&gt;
This is Part 1 in a series exploring short-form posting, the IndieWeb, and taking control of your own online social workflow for a statically-generated site:
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;A Low-Friction Workflow for Short-form Posting on A Statically Generated Site&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/01/preparing-your-site-for-posting-notes&quot;&gt;Preparing Your Site for Posting Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site&quot;&gt;Adding Webmention Support to a Static Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using Micropub to Post to a Static Site — &lt;em&gt;Coming soon&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p&gt;I am thrilled to see
&lt;a href=&quot;https://andy-bell.design/notes/97/&quot;&gt;more&lt;/a&gt; and
&lt;a href=&quot;https://www.zachleat.com/web/notes/&quot;&gt;more&lt;/a&gt; folks are
&lt;a href=&quot;https://twitter.com/SaraSoueidan/status/1084833046140981248&quot;&gt;moving to their blogs&lt;/a&gt; for posting short-form content, usually referred to as &lt;em&gt;Notes&lt;/em&gt;.
These are very brief blog posts, usually about the same length as a Tweet or Facebook update.
Of course, as they do this, they inevitably start grappling with the question:
“How do I make sure these notes are copied to Twitter?”
And maybe also: “How do I make the process quick and seamless?”&lt;/p&gt;
&lt;p&gt;I gave a talk a while back titled &lt;a href=&quot;https://www.recallact.com/presentation/decentralized-social-web&quot;&gt;The Decentralized Social Web&lt;/a&gt; where I covered the general principles to make this work.
This is part of a concept known as the &lt;a href=&quot;https://indieweb.org/&quot;&gt;IndieWeb&lt;/a&gt;.
But I haven’t covered the specifics of how I set this up for my site.
I am frequently asked about my notes and replies on Twitter, and have promised a write-up repeatedly.
It’s taken me far too long, but this is that write-up.&lt;/p&gt;
&lt;h2 id=&quot;getting-on-the-indieweb&quot; tabindex=&quot;-1&quot;&gt;Getting on the IndieWeb &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/#getting-on-the-indieweb&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you’re the type of person who uses a static site generator
(i.e. a developer or otherwise technically literate),
you should be able to set this stuff up yourself.
None of it is too complicated, but there several pieces to it.&lt;/p&gt;
&lt;p&gt;Each piece on its own is fairly straightforward, and there is a sort of exponential return:
the more of these pieces you put in place, the more they work together to give you a smooth workflow.
Which is why I want to start by illustrating the final experience.
I will avoid bogging this down with the &lt;em&gt;how&lt;/em&gt; (that will come in the following posts) and will instead focus on &lt;em&gt;what&lt;/em&gt;. I will get into the specifics in the rest of this post series.&lt;/p&gt;
&lt;p&gt;If you already have a blog or website: Congratulations! You’re already on the IndieWeb. The rest is a series of enhancements you can make to add to that experience.&lt;/p&gt;
&lt;h2 id=&quot;the-magic-in-action&quot; tabindex=&quot;-1&quot;&gt;The magic in action &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/#the-magic-in-action&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I write my notes using the &lt;a href=&quot;https://omnibear.com/&quot;&gt;Omnibear&lt;/a&gt; browser plugin (available for Chrome and Firefox).
I type what I want to say, check the “syndicate to Twitter” box if I want it copied to Twitter as well, and click Post.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/omnibear-note.png&quot; alt=&quot;Screenshot of drafting a short note in the Omnibear Firefox plugin&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This adds the entry to my site, which usually publishes in around 45–60 seconds (the time it takes for Netlify to rebuild my site).
After another minute or so, a copy of the note appears on Twitter,
along with a link back to the original on my site.
If people reply to the post on Twitter, those replies are automatically
&lt;a href=&quot;https://keithjgrant.com/notes/2019/01/i-wish-everybody-arguing-about-the/#comments&quot;&gt;copied back to my site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I can also reply to tweets. On twitter.com, I can right-click on a tweet and select “Reply to entry”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/reply-on-twitter.png&quot; alt=&quot;Screenshot a tweet, highlighted with a yellow outline. A context menu is open with the option &amp;quot;Reply to entry&amp;quot; selected&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This opens the Omnibear window and allows me to write my reply.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/omnibear-reply.png&quot; alt=&quot;Screenshot of drafting a reply to a tweet. The tweet I am replying to is outlined in yellow and a small popup window beneath it contains the Omnibear drafting page.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As before, this &lt;a href=&quot;https://keithjgrant.com/replies/2019/01/yes-do-it/&quot;&gt;posts to my site&lt;/a&gt;. Shortly thereafter, the reply is &lt;a href=&quot;https://twitter.com/keithjgrant/status/1084834425966346242&quot;&gt;copied to twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2019/social-web/reply-syndicated.png&quot; alt=&quot;Screenshot of the reply on Twitter&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hopefully, this sort of workflow sounds appealing to you.
If you want to know how to do this stuff, stay tuned.&lt;/p&gt;
&lt;h2 id=&quot;a-new-type-of-web-standards&quot; tabindex=&quot;-1&quot;&gt;A new type of web standards &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/low-friction-workflow-for-notes/#a-new-type-of-web-standards&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When we developers talk about &lt;em&gt;Web Standards&lt;/em&gt;, we tend to talk about the basic building blocks of web pages: HTML, CSS, JavaScript.
But there are more standards beyond these languages.
The W3C, in fact, has a Social Web Working Group.
This group has published several standards.&lt;/p&gt;
&lt;p&gt;Two key standards to be familiar with are &lt;a href=&quot;https://www.w3.org/TR/webmention/&quot;&gt;Webmention&lt;/a&gt; and &lt;a href=&quot;https://www.w3.org/TR/micropub/&quot;&gt;Micropub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Webmention&lt;/strong&gt; is a way to let another webpage know when you have linked to it.
If you’re familiar with the old Pingback concept, this is similar, but greatly simplified.
In the example above, I replied to a post on Twitter,
but I can just as easily reply to a post on any site that supports Webmentions.
If you write a note on your site, I can reply to it directly, without going through any third party service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Micropub&lt;/strong&gt; is a way to post directly to your site using a third party editor.
In the example above, this editor is Omnibear,
but it could just as easily be an app called &lt;a href=&quot;https://quill.p3k.io/&quot;&gt;Quill&lt;/a&gt; or another similar service.&lt;/p&gt;
&lt;p&gt;The important thing to note here is that I haven’t rigged up a custom script to post to my site and copy to Twitter and crawl through twitter for replies.
I am using open web standards.
Most of the work is done by existing tools that understand these standards, not one-off code.&lt;/p&gt;
&lt;p&gt;If I want, I can swap out Omnibear for a different editor, or change to a new Micropub server if I find an alternative I like better.
Open standards drive it all, so nothing is proprietary.
In the following posts, I will show you how I chose to configure each piece of the puzzle;
but you can just as easily change one piece out to suit your needs, while still doing other parts of it just as I have.&lt;/p&gt;
&lt;p&gt;In short: my process is not an all-or-nothing approach. I’ll show you what I use, and you are free to pick and choose from among the pieces. You can implement them one at a time at your own pace, or all at once. In the rest of this series, I’ll walk you through how to do it.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Teaching a Correct CSS Mental Model</title>
    <link href="https://keithjgrant.com/posts/2019/01/css-mental-model/"/>
    <published>2019-01-08T17:38:52Z</published>
    <updated>2019-01-08T17:38:52Z</updated>
    <id>https://keithjgrant.com/posts/2019/01/css-mental-model/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Almost four years ago, &lt;a href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/&quot;&gt;I chimed in&lt;/a&gt; on the debate surrounding CSS.
Is it good? &lt;a href=&quot;https://keithjgrant.com/posts/2017/03/css-is-not-broken/&quot;&gt;Is it broken&lt;/a&gt;?
Does CSS-in-JS solve anything?&lt;/p&gt;
&lt;p&gt;The discussion has evolved since then, as has CSS-in-JS tooling,
but we are still seeing regularly recurring heated arguments on Twitter.
The most recent happened — &lt;em&gt;checks watch&lt;/em&gt; — an hour ago.&lt;/p&gt;
&lt;p&gt;It’s interesting to read my original article now.
There are elements I still agree with, and some I don’t.
There are some that I now think were beside the point.
At the end, I concluded with this: “you need to learn CSS”.&lt;/p&gt;
&lt;p&gt;For quite a while now, I have distanced myself from that argument:
there are people in the CSS-in-JS community that really do understand the language,
and are using JavaScript tooling to deal with its legitimate shortcomings.
Many of them have learned the language, and those shortcomings are still there.&lt;/p&gt;
&lt;h2 id=&quot;we-need-to-teach-a-mental-model&quot; tabindex=&quot;-1&quot;&gt;We need to teach a mental model &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/css-mental-model/#we-need-to-teach-a-mental-model&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Yet in some ways, I find I’m coming full circle back to that argument:
if you think CSS is a mess or broken, you need to learn CSS.
What I mean by that now, however, this not about learning the specifics of CSS,
but rather building a correct mental model of it.&lt;/p&gt;
&lt;p&gt;Natalya Shelburne articulated this brilliantly in her recent talk “&lt;a href=&quot;https://www.youtube.com/watch?v=MJVRKmA83LU&quot;&gt;CSS at the Intersection&lt;/a&gt;”.
She describes the cognitive dissonance that is an important part of learning:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You try something new outside of your comfort zone and you expect it to work a certain way,
and it does something else.
It breaks, and you have no idea what happened.
You get these unpredictable things.
It makes no sense and you start thinking,
“Why would someone make something so frustrating?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a natural reaction when facing something where we don’t have a correct mental model.
It’s an essential step in learning, to face this, and to learn to push through it
and correct your mental model.
It’s a reaction we see all the time online regarding CSS.
If we’re honest (and we can recall well enough),
it’s a reaction we all felt at one point or another when we were learning CSS.&lt;/p&gt;
&lt;p&gt;The problem is, a surprisingly large number of people seem to have incredible difficulty moving past this point,
and adjusting their mental model of the language.
Some developers like myself were able to do it.
Others stay stuck in this place for years.&lt;/p&gt;
&lt;p&gt;One facet of this whole CSS debate involves one side saying, “Just learn CSS”
and the other side responding, “That’s what I’ve been trying to do!”&lt;/p&gt;
&lt;p&gt;I think it’s high time we the teachers of CSS start discussing how exactly we can teach a correct mental model.
How do we, in specific and practical ways, help developers get past this point of frustration.
Because we have not figured out how to properly teach a mental model of CSS.&lt;/p&gt;
&lt;p&gt;I think, perhaps, we’re beginning to circle closer to this. There seems to be &lt;a href=&quot;https://www.smashingmagazine.com/2019/01/how-to-learn-css/&quot;&gt;more focus lately&lt;/a&gt; on fundamentals of the language like document flow and the box model and formatting contexts.&lt;/p&gt;
&lt;h2 id=&quot;a-%E2%80%9Ccommon-core%E2%80%9D-for-css&quot; tabindex=&quot;-1&quot;&gt;A “Common Core” for CSS &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2019/01/css-mental-model/#a-%E2%80%9Ccommon-core%E2%80%9D-for-css&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Schools in the U.S. have recently begun teaching something called “Common Core.”
It’s controversial in some ways
(and I’m no expert in Common Core, so take this analogy with a grain of salt),
but I like some of the things I’ve seen regarding Common Core math.&lt;/p&gt;
&lt;p&gt;It teaches tricks for making difficult arithmetic easier.
For example, if you need to add 32 + 67, break the problem up to 30 + 60 and 2 + 7,
both of which are much easier to do in your head.
As someone who excelled at math in school,
I have found that most of these Common Core tricks are things I discovered on my own as a student,
and are precisely why I was able to do well in the subject.&lt;/p&gt;
&lt;p&gt;We need common core tricks like this for CSS.
Not “tricks” in the old sense (like how to fake a gradient border),
but mental patterns: ways to frame the problem in our heads,
so we can break problems into their constituent parts and notice recurring patterns.
Those of us who deeply understand the language do this internally.
We need to start working on distilling out these mental patterns we use for understanding layout and positioning and working with relative units, so that we can articulate them to others.&lt;/p&gt;
&lt;p&gt;This will take some deep consideration
and intentional analysis of our internal thought processes while we code.
But I think if we are able to distill out recurring patterns of thinking,
we can find a common set of tools we can teach to those who struggle with webpage layout.&lt;/p&gt;
&lt;p&gt;And maybe, just maybe, we can make the development community a less hostile place for developers in the process.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Won’t You Be My Neighbor</title>
    <link href="https://keithjgrant.com/posts/2018/09/wont-you-be-my-neighbor/"/>
    <published>2018-09-10T12:29:22Z</published>
    <updated>2018-09-10T12:29:22Z</updated>
    <id>https://keithjgrant.com/posts/2018/09/wont-you-be-my-neighbor/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Here we are again. Arguing about CSS, the cascade, and CSS-in-JS. And you know what? We’ll be here again in a few months. Because that seems to be the cycle.&lt;/p&gt;
&lt;p&gt;Somebody &lt;a href=&quot;https://twitter.com/mxstbr/status/1038073603311448064&quot;&gt;tweets something&lt;/a&gt;, usually rather innocuous. This gets spun a few times with various reactions. Then &lt;a href=&quot;https://twitter.com/rhodesjason/status/1038497112869269504&quot;&gt;it escalates&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;So often conflicts arise from lack of communication, false assumptions, or confusion.
&lt;cite&gt;Mr. Rogers&lt;/cite&gt;
&lt;/blockquote&gt;
&lt;!--more--&gt;
&lt;p&gt;Since this is clearly destined to happen again and again in the web developer community, I think it’s time we learn to have these discussions more productively. — And no, &lt;a href=&quot;https://medium.com/@didoo/let-there-be-peace-on-css-8b26829f1be0&quot;&gt;I’m not the first to say this&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-twitter-culture-isn%E2%80%99t-helping&quot; tabindex=&quot;-1&quot;&gt;The Twitter culture isn’t helping &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/09/wont-you-be-my-neighbor/#the-twitter-culture-isn%E2%80%99t-helping&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ve got to stop with the hot-takes. This is a complex topic, now with a lot of argumentative history and misunderstandings. 280 characters just isn’t enough to adequately frame your thoughts any more. Even if your tweet is well-phrased, it will not take long for it to be mis-interpreted and quote tweeted while the conversation spirals out of control.&lt;/p&gt;
&lt;p&gt;Initially, &lt;a href=&quot;https://twitter.com/keithjgrant/status/1038500386783588352&quot;&gt;I tweeted something&lt;/a&gt;, then tried to frame it better with a few follow-up tweets, but the resulting thread quickly became a game of poking at the other side. &lt;a href=&quot;https://twitter.com/meyerweb/status/1038602174211784704&quot;&gt;Eric Meyer tweeted&lt;/a&gt;, and it was interpreted in ways he didn’t intend. To the point where he’s &lt;a href=&quot;https://twitter.com/meyerweb/status/1038905212315086848&quot;&gt;expressed regret for the tweet&lt;/a&gt;. These things just don’t work on Twitter.&lt;/p&gt;
&lt;p&gt;I recently heard how a friend helps their son when he’s being whiney: they tell him to “walk away and come back with a better attitude.” He has to literally walk down the hallway, then back into the room before asking for the thing he wants more politely. And it usually works.&lt;/p&gt;
&lt;p&gt;So maybe it’s time we all practice this, too. Myself included. Instead of a hot-take, take it to your blog. Even if you only write three paragraphs, that will a) be more nuanced than even a thread of tweets, and b) will allow you more time to collect your thoughts and articulate them better.&lt;/p&gt;
&lt;h2 id=&quot;enough-with-picking-sides&quot; tabindex=&quot;-1&quot;&gt;Enough with picking sides &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/09/wont-you-be-my-neighbor/#enough-with-picking-sides&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ve got to stop with the &lt;em&gt;this camp&lt;/em&gt; vs. &lt;em&gt;that camp&lt;/em&gt; stuff. &lt;a href=&quot;https://twitter.com/markdalgleish/status/1038901043780702208&quot;&gt;Mark Dagleish tweeted&lt;/a&gt;, “It’s my life goal to never be part of the technical old guard.” And I tell you what, this got my hackles up.&lt;/p&gt;
&lt;p&gt;Now, to be fair to Mark, he’s far from the only one to make this about sides. He also tweeted several other things on the topic that I thought were very helpful and insightful. But I hate that this has somehow become some “old guard” vs. “new guard” thing.&lt;/p&gt;
&lt;p&gt;The problem with drawing lines like this: whichever side you find yourself on, there are some whackos out there throwing ridiculous arguments into the mix. And now people on the other side associate that viewpoint with you.&lt;/p&gt;
&lt;p&gt;Instead, call out the folks on “your side” who are off in the weeds. To whatever “cascade defender” out there made this a CSS-in-JS bashing party: knock it off. To those on the other side who said the don’t want their employees bothering to learn the cascade: cut it out. Everyone else is somewhere in between. And we don’t have to be old guard &lt;em&gt;or&lt;/em&gt; new guard. We can just be a web dev, and supporter of the open web platform as a whole.&lt;/p&gt;
&lt;h2 id=&quot;just-so-my-cards-are-on-the-table%3A&quot; tabindex=&quot;-1&quot;&gt;Just so my cards are on the table: &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/09/wont-you-be-my-neighbor/#just-so-my-cards-are-on-the-table%3A&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I think every front end developer should learn the cascade, as it’s a fundamental part of the web platform.&lt;/li&gt;
&lt;li&gt;I agree it’s not perfect, and I hope we can continue to iterate and improve on it. But it’s what we have to work with.&lt;/li&gt;
&lt;li&gt;I don’t think you’re an idiot if you don’t know it.&lt;/li&gt;
&lt;li&gt;That question was particularly misleading.&lt;/li&gt;
&lt;li&gt;Despite all the dust I’ve kicked up in the past, I’m not completely opposed to CSS-in-JS. In fact, I brought up &lt;a href=&quot;https://emotion.sh/&quot;&gt;Emotion&lt;/a&gt; as an option for a project as recently as last week.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Getting Started with Micropub</title>
    <link href="https://keithjgrant.com/posts/2018/09/getting-started-with-micropub/"/>
    <published>2018-09-04T16:39:16Z</published>
    <updated>2018-09-04T16:39:16Z</updated>
    <id>https://keithjgrant.com/posts/2018/09/getting-started-with-micropub/</id>
    <content xml:lang="en" type="html">&lt;p&gt;With the recent discontentment on Twitter (and social media in general), I’ve seen a resurgence of people returning to blogging. In particular, I’ve noticed more folks posting short entries, or &lt;em&gt;notes&lt;/em&gt;, to their blog — either in addition to Twitter, or in place of it.&lt;/p&gt;
&lt;p&gt;If you are interested in doing this, &lt;em&gt;micropub&lt;/em&gt; is a great way to make it a frictionless experience. Micropub is a new W3C standard, allowing you to post directly to your site using third-party apps.&lt;/p&gt;
&lt;p&gt;I recently wrote a quick overview how to get started with micropub as part of the &lt;a href=&quot;https://omnibear.com/&quot;&gt;Omnibear&lt;/a&gt; documentation. Check it out.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Resilient, Declarative, Contextual</title>
    <link href="https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/"/>
    <published>2018-06-08T13:39:05Z</published>
    <updated>2018-06-08T13:39:05Z</updated>
    <id>https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I’ve spent a lot of time thinking about what defines a CSS mindset. Some people seem to “get” it, and others don’t. It’s always felt to me that if I could put my finger on that, maybe CSS would make more sense to those who have struggled with it. One piece of my motivation in writing &lt;a href=&quot;https://www.manning.com/books/css-in-depth&quot;&gt;CSS in Depth&lt;/a&gt; was to try to articulate some of those things.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Today I want to take a different tack. I want to look at three key characteristics of CSS that set it apart from conventional programming languages: it’s resilient; it’s declarative; and it’s contextual. Understanding these aspects of the language, I think, is key to becoming proficient in CSS.&lt;/p&gt;
&lt;h2 id=&quot;css-is-resilient&quot; tabindex=&quot;-1&quot;&gt;CSS is resilient &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/#css-is-resilient&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you were to randomly delete a chunk of code out of a JavaScript file, the app or page using it would almost certainly come crashing to a halt and much of the script (if not the page as a whole) would become useless. If you do the same thing to CSS, you might not even notice. Almost everything apart from that specific section of code will continue to work as intended.&lt;/p&gt;
&lt;p&gt;We call this &lt;em&gt;resilience&lt;/em&gt;. HTML and CSS were specifically designed to be fault-tolerant. If there’s a problem, the browser won’t throw an error; instead, it will ignore that part of the code and keep on going.&lt;/p&gt;
&lt;p&gt;This may seem absurd from a debugging perspective: if it doesn’t throw errors, how do you know what went wrong? But this is an essential piece to how CSS works. It’s woven into the fabric of the language itself. It may take some getting used to, I admit. Once you understand this, though, you can safely use features that aren’t supported in all browsers. This is what makes progressive enhancement possible.&lt;/p&gt;
&lt;p&gt;Consider this example of a grid layout. It works in browsers that support grid, and it works in browsers that don’t support grid. It will be slightly imperfect in those that don’t support grid (the exact sizes of the items will probably vary), but it will still layout the page in roughly the same way:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.portfolio&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;auto-fit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;minmax&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;400px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1fr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.portfolio__item&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 600px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A browser that doesn’t understand the two grid declarations will ignore them, and the other rules will do the work. And a browser that does understand grid will use the grid layout and ignore the &lt;code&gt;inline-block&lt;/code&gt; declaration (because that’s how grid was designed to work). Jen Simmons half-jokingly calls this &lt;a href=&quot;https://www.youtube.com/watch?v=u00FY9vADfQ&quot;&gt;“Quantum CSS”&lt;/a&gt;. You can take a feature of CSS and “use it and not use it at the same time. It works and it doesn’t work at the same time.”&lt;/p&gt;
&lt;p&gt;This concept of “fallback” behavior is integral to using CSS, but it is a foreign concept in most conventional programming languages.&lt;/p&gt;
&lt;h2 id=&quot;css-is-declarative&quot; tabindex=&quot;-1&quot;&gt;CSS is declarative &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/#css-is-declarative&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In JavaScript, you give specific, step-by-step instructions how to make something happen. In CSS, you tell the browser what you want to have happen, and it works out the how. This is extremely important to understand. If you get it right, CSS will &lt;a href=&quot;https://adactio.com/journal/13831&quot;&gt;do all the hard work for you&lt;/a&gt;! And if you get it wrong, you’ll be fighting against the grain of the language and you will be frustrated at every turn.&lt;/p&gt;
&lt;p&gt;Writing CSS is effectively setting up a system of constraints. You don’t tell the browser where to put every single element on the page; you tell it how much space to put between them and let it sort out where they belong. You don’t tell it (or at least shouldn’t tell it) how tall to make a container; you let it figure that out at render time when it knows the contents of the container, which other styles are applied, and how much width is available in the viewport.&lt;/p&gt;
&lt;p&gt;There are too many variables to consider. The point of CSS is to make it so you don’t have to worry about them all. Define some constraints. Let the language work out the details.&lt;/p&gt;
&lt;h3 id=&quot;a-simple-example&quot; tabindex=&quot;-1&quot;&gt;A simple example &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/#a-simple-example&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let’s consider this CSS for a moment: &lt;code&gt;font-size: 2em&lt;/code&gt;. What does it do? “It increases the font size,” you say. But that’s not all. It also adjusts the line wrapping of text in the container, as fewer words will now fit on each line. That in turn will often increase the number of lines of text: so it will also increase the container’s height to contain the new lines of text. When the container’s height changes, anything beneath it on the page will be shifted down accordingly. Finally, it also specifies a value for the local meaning of &lt;code&gt;em&lt;/code&gt;. Any other properties defined using ems will have their computed values updated to match.&lt;/p&gt;
&lt;p&gt;That one declaration creates a whole slew of changes on the page. And they’re all exactly what you should want: the content will always fit, elements aren’t going to wind up overlapping oddly, and anything defined in terms of the font size (like padding, perhaps) will adapt. You don’t have to worry about those details. The browser makes all those calculations and does the work by default.&lt;/p&gt;
&lt;p&gt;If you want to stop these things from happening, you can. You could cap the container height with a &lt;code&gt;max-height&lt;/code&gt; and &lt;code&gt;overflow: auto&lt;/code&gt;. You could redefine padding to be in rems or px so it doesn’t adapt to the local font size. This highlights an interesting part of writing CSS: sometimes you’re not telling the browser what to do; you’re effectively telling it what &lt;em&gt;not&lt;/em&gt; to do.&lt;/p&gt;
&lt;h3 id=&quot;griddy-goodness&quot; tabindex=&quot;-1&quot;&gt;Griddy goodness &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/#griddy-goodness&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some of the newer features in CSS do even more. Flexbox and Grid are prime examples of this. With just a few declarations, you can build a grid layout that is extremely flexible and “just works”. You don’t have to worry about countless edge cases. You say, effectively “put these boxes in columns of about 400px wide” and it will do it for you. It takes about three lines of code.&lt;/p&gt;
&lt;p&gt;If you were to do this imperatively, you would need to deal with all sorts of odd scenarios. What if there’s an extremely long word in one of the boxes? What if the viewport is very narrow? What if it’s very wide? What if one box has a ton of content and another contains just a few words? But chances are, in CSS, you don’t need to think about any of these things. All the hard thought for this has already gone into the spec, and the browser takes care of it for you. This is the power of a declarative language.&lt;/p&gt;
&lt;p&gt;This does come with a trade-off: if the declarative language doesn’t support something you want to do (say, a “masonry” layout), you’re left relying on either odd hacks or JavaScript to help accomplish it. And for years, this sort of thing was a large part of CSS development. Thankfully, with the rise of Flexbox and Grid, we can do far more than we could in the past, without any hacks (and yes, floats were a hack). If this limitation still bothers you, I suggest you read up on &lt;a href=&quot;https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/&quot;&gt;CSS Houdini&lt;/a&gt;, which is just beginning to land in browsers.&lt;/p&gt;
&lt;h2 id=&quot;css-is-contextual&quot; tabindex=&quot;-1&quot;&gt;CSS is contextual &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/#css-is-contextual&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the React era, we have embraced the extremely useful approach of modular, component-based development. CSS best-practices do this as well, with BEM and SMACSS and CSS-in-JS. I don’t want to belittle this, because this is an essential way of thinking when building large-scale applications. But I think it’s equally important to acknowledge that CSS is not 100% modular, nor should it be.&lt;/p&gt;
&lt;p&gt;There are two reasons for this. First, and most obvious, is that your app should have some global styles. You will almost always want to set a default typeface and font size at the page level. These values will then be inherited by all descendant elements that don’t explicitly override them. You will also want certain aspects of your design to apply repeatedly throughout the page, such as theme colors, border radii, box shadows, and common margin sizes. More localized styles on the page will then assume these global styles are in place.&lt;/p&gt;
&lt;p&gt;Second, and more subtle, is the way CSS and your styling decisions are informed by the surrounding context of the page. Consider applying the following CSS to an element:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.the-thing&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What will this code do? Without knowledge of where the element is in the DOM and what styles are applied to the rest of the page, there is no way to know. Absolute positioning is done relative to the nearest positioned ancestor; applying it means different things depending on which ancestor, if any, has positioning applied.&lt;/p&gt;
&lt;p&gt;Furthermore, how you can (or cannot) stack one element in front of another is going to be highly dependent on where the two are positioned in the DOM. Shuffling items around in the DOM can cause drastic effects on the way items fit together and stack. This is why document flow and stacking contexts are a vital (and sometimes complicated) topics.&lt;/p&gt;
&lt;p&gt;The contextual nature of CSS is also due in part to the way design works. If an engineer designs a bridge, you can’t just look at the blueprint and say, “this is all good except this one beam here; go ahead and take that out”. Removing that beam has ramifications on the structural integrity of the whole thing. Similarly, changing one part of a design can have ramifications on how other items on the screen are perceived. Frequently, you will need to style multiple elements together, in conjunction.&lt;/p&gt;
&lt;p&gt;If you make the heading in a tile bigger, for instance, it becomes more prominent to the user and therefore makes other items on the screen seem less important. The restrictions aren’t about physics as with the bridge, but there are subtle rules of “soft science” that impact human perception. The parts of the page render in a physical space on screen, and the realities of the physical world (and how we perceive it) are important to be aware of.&lt;/p&gt;
&lt;p&gt;We like to architect software using &lt;a href=&quot;https://freecontent.manning.com/modular-css/&quot;&gt;principles of modularity and encapsulation&lt;/a&gt;. This makes sense in the world of code, because code is complicated and this breaks the problem up into manageable sizes. But we should also be aware that it isn’t always perfect. In CSS, we can never completely disregard what’s going on outside a given module.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/#summary&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These three aspects make CSS different than conventional programming languages. These differences may feel foreign, but it’s these differences that make CSS so powerful. And it’s my suspicion that developers who embrace these things, and have fully internalized them, tend to be far more proficient in CSS.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>HTML5 Sectioning and Landmark Elements</title>
    <link href="https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/"/>
    <published>2018-03-05T17:09:47Z</published>
    <updated>2018-03-05T17:09:47Z</updated>
    <id>https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/</id>
    <content xml:lang="en" type="html">&lt;p&gt;A little while ago, I wrote about the &lt;a href=&quot;https://keithjgrant.com/posts/2018/meet-the-new-dialog-element/&quot;&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element&lt;/a&gt;. But there are plenty of interesting HTML elements that have been around longer, since HTML5 was first introduced, so I’ve decided to expand it into a series.&lt;/p&gt;
&lt;p&gt;Every now and then, I stumble across an element I haven’t heard of before. Some are interactive, and provide a surprising amount of functionality with little to no JavaScript. Others provide helpful semantic meaning and a welcome alternative to “div soup” in complex pages.&lt;/p&gt;
&lt;p&gt;For the next few blog posts, I’m going to explore some aspects of HTML5 that maybe haven’t received as much attention as they deserve.&lt;/p&gt;
&lt;p&gt;As a warm up, I’ll look at some elements from HTML5 that most web developers probably are somewhat familiar with: &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;, and other structural, semantic elements. Even if you already use these elements, you might just learn a few new things along the way (I know I did as I researched this).&lt;/p&gt;
&lt;h2 id=&quot;landmarks&quot; tabindex=&quot;-1&quot;&gt;Landmarks &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#landmarks&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I first learned about HTML5 sectioning elements, I thought they were just a nice alternative to &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. There’s more to the story than this, though.&lt;/p&gt;
&lt;p&gt;Several of these elements define &lt;em&gt;landmarks&lt;/em&gt; on the page. Landmarks are an accessibility feature that help identify the high-level regions of the page. The following elements define landmarks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; — only if it has a title (&lt;code&gt;aria-labelled-by&lt;/code&gt;, &lt;code&gt;aria-label&lt;/code&gt;, or &lt;code&gt;title&lt;/code&gt; attribute)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; — only if it’s not within one of the other landmark sections listed above&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; — only if it’s not within one of the other landmark sections listed above&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The use of landmarks makes it easier for &lt;abbr title=&quot;assistive technology&quot;&gt;AT&lt;/abbr&gt; users to find their way around the page. These don’t need to be the very top level of your DOM, but they should be near the the top. Your top-level landmarks should not be nested within one another, but you can group them using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s if necessary for your layout. &lt;em&gt;All content&lt;/em&gt; on your page should be inside a landmark. The most important landmarks are &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before HTML5, you had to use the &lt;code&gt;role&lt;/code&gt; attribute to define landmarks. For instance, an HTML 4 &lt;code&gt;&amp;lt;div role=&amp;quot;complementary&amp;quot;&amp;gt;&lt;/code&gt; can now be replaced with &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;. It’s one way your markup can be simpler while still maintaining good accessibility practices.&lt;/p&gt;
&lt;div class=&quot;alert&quot;&gt;
When HTML5 was new, many tutorials recommended including the `role` on these elements (`&lt;main role=&quot;main&quot;&gt;`) since screen readers hadn’t yet caught up with the new standard. The current W3C specification recommends you [don’t add the role attribute](https://www.w3.org/TR/html52/dom.html#do-not-set) for these elements, but it doesn’t hurt, especially if you want to support a broader range of browsers/screen readers.
&lt;/main&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; also defines a landmark in most browser/AT combinations, but it can be nested inside the other landmarks listed above.&lt;/p&gt;
&lt;h2 id=&quot;headers-and-footers&quot; tabindex=&quot;-1&quot;&gt;Headers and Footers &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#headers-and-footers&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your page likely has a header and footer. That’s what &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; are for. When used near the top-level of your DOM, outside any landmark elements, they denote landmarks for the page.&lt;/p&gt;
&lt;p&gt;However, these elements aren’t restricted only to use at a high level. If you have an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; on the page, for instance, it can have its own &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; and/or &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;. In this case, it won’t function as a landmark, but it does provide semantic meaning in context. For example:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;My useful blog post&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Published March 5, 2018&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  …
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also add headers and footers to other elements like modals, tiles, and anywhere else it’s appropriate.&lt;/p&gt;
&lt;h2 id=&quot;main-vs.-article-vs.-section&quot; tabindex=&quot;-1&quot;&gt;Main vs. Article vs. Section &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#main-vs.-article-vs.-section&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A lot of developers get tripped up by these three elements. What’s the difference between &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;? Let’s take a look.&lt;/p&gt;
&lt;h3 id=&quot;main&quot; tabindex=&quot;-1&quot;&gt;Main &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#main&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Your page should have only one &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;. This should contain the &lt;em&gt;(ahem)&lt;/em&gt; main content of the page. There’s boilerplate stuff on most pages: the header, the navigation, maybe a sidebar or other peripheral content in one or more &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;s. These regions of the page each belong in their own landmark. And there’s the main section, which is also a landmark.&lt;/p&gt;
&lt;p&gt;If the page is a blog post, the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; should contain the post title, content, and comments section. If the page is on a corporate site, the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; should contain all the photos and informational tiles. Basically, if it’s not part of the nav, header, footer, or asides, it belongs in the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;. (The &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; might have its own header and footer, too. See above.)&lt;/p&gt;
&lt;h3 id=&quot;article&quot; tabindex=&quot;-1&quot;&gt;Article &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#article&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; represents a self-contained piece of content. An article should have a heading (&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;–&lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;) as a child element, indicating its title. A blog post, with its title, byline, and published date belong in an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, while any comments probably belong in a footer or aside.&lt;/p&gt;
&lt;p&gt;The homepage of a corporate site, with photos and informational tiles, might have several &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;s, or might have none at all. The key here is “self-contained”: an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; is something that could be removed from the page and still retain its meaning in isolation.&lt;/p&gt;
&lt;p&gt;An article is not a landmark, so it belongs in one (probably the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&quot;section&quot; tabindex=&quot;-1&quot;&gt;Section &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#section&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If your content has multiple sections, you can use a &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; to contain each of these. You shouldn’t have one lone &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; on a page. A good rule of thumb is this: if the section could be an item in a table of contents, it might make sense as a &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;. An article, for instance, may consist of multiple sections.&lt;/p&gt;
&lt;p&gt;A section is not a landmark. It belongs in one (probably the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;). However, if you give a section a heading with an &lt;code&gt;aria-label&lt;/code&gt;, &lt;code&gt;aria-labelled-by&lt;/code&gt;, or &lt;code&gt;title&lt;/code&gt; attribute, this promotes it to be a landmark. Avoid doing this if you have a large number of sections on the page, as too many landmarks can add to the noise, making them less useful as navigation tools.&lt;/p&gt;
&lt;p&gt;Hopefully, this gives you a better idea of how some of these “bigger” HTML5 elements work. If you want to know more about landmarks and accessibility, check out the links below.&lt;/p&gt;
&lt;p&gt;Next time, I’ll look at some HTML5 elements that are a bit more exotic.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; tabindex=&quot;-1&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/03/html5-sectioning-and-landmark-elements/#references&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.scottohara.me/blog/2018/03/03/landmarks.html&quot;&gt;Accessible landmarks&lt;/a&gt; by Scott O’Hara&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices/examples/landmarks/HTML5.html&quot;&gt;ARIA landmarks example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://html5doctor.com/avoiding-common-html5-mistakes/&quot;&gt;Avoiding common HTML5 mistakes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/html53/sections.html&quot;&gt;Sections, HTML 5.3 working draft&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Introducing Sidecar</title>
    <link href="https://keithjgrant.com/posts/2018/01/introducing-sidecar/"/>
    <published>2018-01-19T13:46:41Z</published>
    <updated>2018-01-19T13:46:41Z</updated>
    <id>https://keithjgrant.com/posts/2018/01/introducing-sidecar/</id>
    <content xml:lang="en" type="html">&lt;img src=&quot;https://keithjgrant.com/images/2018/sidecar/sidecar-home.png&quot; alt=&quot;A smartphone displaying the Sidecar app homescreen. It includes buttons to drinks, tags, ingredients, techniques, and an about page, as well as three featured cocktails.&quot; width=&quot;226&quot; height=&quot;460&quot; /&gt;
&lt;p&gt;A little over a year ago, my wife bought me a copy of &lt;a href=&quot;https://www.amazon.com/Liquid-Intelligence-Science-Perfect-Cocktail/dp/0393089037&quot;&gt;Liquid Intelligence&lt;/a&gt; by Dave Arnold. It’s a book by a renowned bartender on making cocktails. I enjoyed making drinks before this, but this book really got me hooked.&lt;/p&gt;
&lt;p&gt;In the time since, I’ve been honing my skills. And I’ve been collecting cocktail recipes. Testing them, tweaking them, and sharing them. I have a little book of notecards on a ring, full of the recipes I have tested and deemed worthy of making again. I also have a few I’ve created myself.&lt;/p&gt;
&lt;p&gt;Now, I’ve decided to digitize that little booklet, in the form of a little webapp. And I’m making it public at &lt;a href=&quot;https://sidecar.us/&quot;&gt;sidecar.us&lt;/a&gt;. I’ve also added some important techniques and other information that’s essential to know when making drinks.&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2018/sidecar/negroni-sbagliato.png&quot; alt=&quot;A recipe card for a cocktail called Negroni Sbagliato&quot; width=&quot;555&quot; height=&quot;288&quot; /&gt;
&lt;p&gt;It works well on desktop, but I’ve focused on optimizing it for mobile, so I can pull out my phone and look up a recipe wherever I am. If you’re interested, you can install it on your phone with your mobile browser’s “Add to Home Screen” option. &lt;s&gt;I don’t have offline mode working quite yet&lt;/s&gt; &lt;ins&gt;(&lt;strong&gt;Update&lt;/strong&gt;: it works offline!)&lt;/ins&gt;, but it still works great as a &lt;abbr title=&quot;Progressive Web App&quot;&gt;PWA&lt;/abbr&gt;.&lt;/p&gt;
&lt;p&gt;Go ahead and give it a shot. And let me know if you find a recipe I should test out.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sidecar.us/&quot;&gt;Sidecar: A curated collection of cocktails&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Meet the New Dialog Element</title>
    <link href="https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/"/>
    <published>2018-01-11T14:22:12Z</published>
    <updated>2018-01-11T14:22:12Z</updated>
    <id>https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/</id>
    <content xml:lang="en" type="html">&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/html52/&quot;&gt;HTML 5.2&lt;/a&gt; has introduced a new &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element for native modal dialog boxes. At first glance, it seems fairly straightforward (and it is), but as I’ve been playing around with it, I’ve found it has some nice features that might be easy to miss.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;I’ve embedded a full working demo at the end of this article, but if you want to check it out as you read along, &lt;a href=&quot;https://codepen.io/keithjgrant/pen/eyMMVL&quot;&gt;you can see it here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is the markup for a basic dialog box:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dialog&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Native dialog box!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;open&lt;/code&gt; attribute means that the dialog is visible. Without it, the dialog is hidden until you use JavaScript to make it appear. Before any styling is added, the dialog renders as follows:&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2018/native-dialog-basic.png&quot; alt=&quot;Text in a box with a thick black outline&quot; width=&quot;186&quot; height=&quot;76&quot; /&gt;
&lt;p&gt;It’s absolutely positioned on the page, so it will appear in front of other content as you would expect, and is centered horizontally. By default, it’s as wide as the contents within.&lt;/p&gt;
&lt;h2 id=&quot;basic-operation&quot; tabindex=&quot;-1&quot;&gt;Basic Operation &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/#basic-operation&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JavaScript has a few methods and properties to make working with the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element easy. The two methods you will probably need the most are &lt;code&gt;showModal()&lt;/code&gt; and &lt;code&gt;close()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;dialog&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// makes modal appear (adds `open` attribute)&lt;/span&gt;
modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showModal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// hides modal (removes `open` attribute)&lt;/span&gt;
modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you use &lt;code&gt;showModal()&lt;/code&gt; to open the dialog, a backdrop is added to the page, blocking user interaction with the contents outside the modal. By default, this backdrop is fully transparent, but you can make it visible with CSS (more on that below).&lt;/p&gt;
&lt;p&gt;Pressing Esc will close the dialog, and you can provide a close button to trigger the &lt;code&gt;close()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;There is a third method, &lt;code&gt;show()&lt;/code&gt; that also make the modal appear but without the accompanying backdrop. The user will still be able to interact with elements that are visible outside the dialog box.&lt;/p&gt;
&lt;h3 id=&quot;browser-support-and-polyfill&quot; tabindex=&quot;-1&quot;&gt;Browser Support and Polyfill &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/#browser-support-and-polyfill&quot;&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Right now, &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; behavior is only supported in Chrome. Firefox provides default styling, but the JavaScript API is only enabled behind a flag. I suspect Firefox will enable it by default soon.&lt;/p&gt;
&lt;p&gt;Thankfully, there is &lt;a href=&quot;https://github.com/GoogleChrome/dialog-polyfill&quot;&gt;a polyfill&lt;/a&gt; that provides both the JavaScript behavior and a stylesheet with default styling. Install &lt;code&gt;dialog-polyfill&lt;/code&gt; in npm to use it—or use a regular old &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. It works in IE9 and up.&lt;/p&gt;
&lt;p&gt;When using the polyfill, each dialog on the page needs to be initialized:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;dialogPolyfill&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerDialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will not replace native behavior for browsers that have it.&lt;/p&gt;
&lt;h2 id=&quot;styling&quot; tabindex=&quot;-1&quot;&gt;Styling &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/#styling&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Opening and closing a modal is nice, but it doesn’t look very professional at first. Adding styling is as simple as styling any other element. The backdrop can be styled with the new &lt;code&gt;::backdrop&lt;/code&gt; pseudo-element.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;dialog&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.6rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 1em black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;dialog::backdrop&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* make the backdrop a semi-transparent black */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For older browsers using the polyfill, this pseudo-element selector will not work, however. In its place, the polyfill adds a &lt;code&gt;.backdrop&lt;/code&gt; element immediately following the dialog. You can target it with CSS like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;dialog + .backdrop&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a little more markup to provide styling hooks. A common approach to dialog boxes is to break it up into a header, a body, and a footer:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dialog&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;demo-modal&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;modal-header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;A native modal dialog box&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;modal-body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Finally, HTML has a native dialog box element! This is fantastic.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;And a polyfill makes this usable today.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;footer&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;modal-footer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;close&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;close&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add some CSS to this, and you can make the modal look however you want:&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2018/native-dialog-styled.png&quot; alt=&quot;Text in a box with a thick black outline&quot; width=&quot;628&quot; height=&quot;334&quot; /&gt;
&lt;h2 id=&quot;more-control&quot; tabindex=&quot;-1&quot;&gt;More control &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/#more-control&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Often, we want some sort of user feedback from a dialog box. When closing a dialog, you can pass a string value to the &lt;code&gt;close()&lt;/code&gt; method. This value is assigned to the &lt;code&gt;returnValue&lt;/code&gt; property of the dialog DOM element, so it can be read later:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Accepted&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;returnValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// logs `Accepted`&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also some events you can listen for. Two useful ones are &lt;code&gt;close&lt;/code&gt; (triggered when the modal is closed) and &lt;code&gt;cancel&lt;/code&gt; (triggered when the user presses Esc to close the modal).&lt;/p&gt;
&lt;p&gt;One thing that seems to be missing is the ability to close the modal when the backdrop is clicked, but there is a workaround. Clicking the backdrop fires a click event with the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; as the event target. And if you construct the modal such that child elements fill the entire space of the dialog, those child elements will be the target of any clicks inside the dialog. This way, you can listen for clicks on the dialog, and close it when the dialog itself is the target of the click event:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; modal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;cancelled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This isn’t perfect, but it works. Please let me know if you find a better way to detect clicks on the backdrop.&lt;/p&gt;
&lt;h2 id=&quot;full-working-demo&quot; tabindex=&quot;-1&quot;&gt;Full working demo &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/#full-working-demo&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve worked a lot of stuff into the demo below. Play around with and see what else you can do with &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;. This includes the polyfill, so it should work in most browsers.&lt;/p&gt;
&lt;p data-height=&quot;300&quot; data-theme-id=&quot;31665&quot; data-slug-hash=&quot;eyMMVL&quot; data-default-tab=&quot;result&quot; data-user=&quot;keithjgrant&quot; data-embed-version=&quot;2&quot; data-pen-title=&quot;&amp;lt;dialog&amp;gt;&quot; class=&quot;codepen&quot;&gt;See the Pen &lt;a href=&quot;https://codepen.io/keithjgrant/pen/eyMMVL/&quot;&gt;&amp;lt;dialog&amp;gt;&lt;/a&gt; by Keith J. Grant (&lt;a href=&quot;https://codepen.io/keithjgrant&quot;&gt;@keithjgrant&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://production-assets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
</content>
  </entry>
  <entry>
    <title>Shindig</title>
    <link href="https://keithjgrant.com/posts/2018/01/shindig/"/>
    <published>2018-01-02T19:38:37Z</published>
    <updated>2018-01-02T19:38:37Z</updated>
    <id>https://keithjgrant.com/posts/2018/01/shindig/</id>
    <content xml:lang="en" type="html">&lt;p&gt;My new site design is live! I’ve been working on this one for a while, and it just so happened to coincide with the &lt;a href=&quot;https://twitter.com/hashtag/newwwyear?src=hash&quot;&gt;#newwwyear&lt;/a&gt; activity on Twitter. I’m calling this theme &lt;em&gt;Shindig&lt;/em&gt;.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Have a look around. I’ve got some new fonts and colors that I hope I can stick with for a while, even across projects and into new redesigns. I’ve added full page transitions everywhere. (Try it! They’re fun!) And, of course, I still support &lt;a href=&quot;https://webmention.net/&quot;&gt;Webmention&lt;/a&gt; and &lt;a href=&quot;https://indieweb.org/Micropub&quot;&gt;Micropub&lt;/a&gt;, because I’m still loving the &lt;a href=&quot;https://www.recallact.com/presentation/decentralized-social-web&quot;&gt;IndieWeb&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update&lt;/em&gt; — Here’s a screenshot of the design for posterity:
&lt;img src=&quot;https://keithjgrant.com/images/2018/shindig-screenshot.png&quot; alt=&quot;Screenshot of the Shindig theme&quot; /&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>OOCSS and Grid</title>
    <link href="https://keithjgrant.com/posts/2017/12/oocss-and-grid/"/>
    <published>2017-12-13T18:03:47Z</published>
    <updated>2017-12-13T18:03:47Z</updated>
    <id>https://keithjgrant.com/posts/2017/12/oocss-and-grid/</id>
    <content xml:lang="en" type="html">&lt;p&gt;&lt;a href=&quot;https://github.com/stubbornella/oocss/wiki&quot;&gt;OOCSS&lt;/a&gt; was the first of the many CSS methodologies. Since it arrived on the scene, the industry has moved on to newer, more strongly prescriptive methodologies like SMACSS, BEM, and ITCSS. These newer approaches dominate the conventional wisdom today. But CSS grid is here now, and I find it presents some challenges to this wisdom. I think it’s time we give OOCSS a little attention again, because it has an important idea to offer in the world of CSS grid.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;A major theme recently is an emphasis on modular design: a user interface broken up into small modules whose styles are encapsulated. Modules are nested one within another to construct the entire interface. This leads to markup that looks something like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Wash&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The whacky pilot.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;River&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The mysterious prodigy. She kicks serious ass.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Jayne&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The strongman.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have one module responsible for layout (&lt;code&gt;row&lt;/code&gt; and &lt;code&gt;col-4&lt;/code&gt; classes) and another responsible for the look of the of the items within the layout (&lt;code&gt;card&lt;/code&gt; and &lt;code&gt;card__*&lt;/code&gt; classes). I used the classes from the Bootstrap grid system here, but this pattern isn’t limited to bootstrap: it’s common to use one module for layout and another for visual appearance like colors, fonts, and borders.&lt;/p&gt;
&lt;h2 id=&quot;a-limitation-of-css-grid&quot; tabindex=&quot;-1&quot;&gt;A Limitation of CSS Grid &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/12/oocss-and-grid/#a-limitation-of-css-grid&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I work with the new CSS Grid layout, I’m finding that the conventional approach isn’t always feasible. The big restriction with grid is this: to align an element in a grid, it must be a &lt;em&gt;direct child&lt;/em&gt; of the grid container. (This &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/958&quot;&gt;may not be a limitation forever&lt;/a&gt;, but for now, we need to live with it.)&lt;/p&gt;
&lt;p&gt;Take the example above, for instance. If you were to replace the &lt;code&gt;row&lt;/code&gt; and &lt;code&gt;col-4&lt;/code&gt; classes with a proper grid, you could not align the &lt;code&gt;card&lt;/code&gt; elements directly to the grid:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid__item&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Wash&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The whacky pilot.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid__item&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;River&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The mysterious prodigy. She kicks serious ass.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid__item&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Jayne&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The strongman.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you do this, the grid items align to the grid, each filling their respective grid cells, but the cards do not. This is the result:&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2017/oocss-grid-1.png&quot; alt=&quot;Three white cards of different heights&quot; /&gt;
&lt;p&gt;Instead of three neatly-aligned cards, the heights are all different. The grid items each have an equal height, but this is invisible to the user. The cards—and their white backgrounds—within don’t fill the entire height of those grid items. (This example is available &lt;a href=&quot;https://codepen.io/keithjgrant/pen/EoaoxJ&quot;&gt;on Codepen&lt;/a&gt;.)&lt;/p&gt;
&lt;h2 id=&quot;oocss-to-the-rescue&quot; tabindex=&quot;-1&quot;&gt;OOCSS to the Rescue &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/12/oocss-and-grid/#oocss-to-the-rescue&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;abbr title=&quot;Object-Oriented CSS&quot;&gt;OOCSS&lt;/abbr&gt; offers a solution to this problem. Let’s blow the dust off this old book and take a fresh look at what it offers. OOCSS is much simpler than the newer CSS methodologies. It has just two rules:&lt;/p&gt;
&lt;p&gt;First, &lt;em&gt;Separate Container and Content&lt;/em&gt;. This means styles should not be location-dependent. A module should work no matter where you place it in the DOM. If you follow SMACSS and/or BEM, this is exactly what you’re doing with your modules.&lt;/p&gt;
&lt;p&gt;The second rule, however, isn’t followed as often in our newer methodologies: &lt;em&gt;Separate Structure and Skin&lt;/em&gt;. This means one class should be used to apply the structure to an element—its position and shape—while another should be used to apply its “skin”—its colors and stylistic appearance. Ideally, the same skin could be applied to a number of different “structures.”&lt;/p&gt;
&lt;p&gt;After years of working with BEM, this second rule feels foreign. We’re used to each module (or “block,” if you prefer) doing its thing. Any given DOM element belongs only to one module. It feels dirty to apply two different modules to the same element: one for skin and another for structure. But I think this skinning approach going to be useful in the world of CSS grid.&lt;/p&gt;
&lt;p&gt;Watch what happens in our example, when we combine the grid item “structure” with the card “skin”, thereby removing one layer of depth from the DOM:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid__item card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Wash&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The whacky pilot.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid__item card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;River&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The mysterious prodigy. She kicks serious ass.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;grid__item card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Jayne&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;card__body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The strongman.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the card styles are applied directly to the grid items (See this example &lt;a href=&quot;https://codepen.io/keithjgrant/pen/jYEYrx&quot;&gt;on Codepen&lt;/a&gt;):&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2017/oocss-grid-2.png&quot; alt=&quot;Three white cards of equal heights, aligned correctly to the grid&quot; /&gt;
&lt;p&gt;It’s important with this approach to maintain a distinction between “structure” modules and “skin” modules, so you don’t accidentally mix two skins together for conflicting results. On the whole, that’s not too hard to do. It just takes some good old fashioned OOCSS.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Code, Not Clojure</title>
    <link href="https://keithjgrant.com/posts/2017/09/code-not-clojure/"/>
    <published>2017-09-08T15:25:10Z</published>
    <updated>2017-09-08T15:25:10Z</updated>
    <id>https://keithjgrant.com/posts/2017/09/code-not-clojure/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I’m going to make two declarations that might sound contradictory:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CSS is code.&lt;/li&gt;
&lt;li&gt;CSS is not a programming language.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Many developers get these backwards.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;It is code in that it requires discipline and rigor. It requires forethought,
planning, and both big-picture and small-picture thinking. You need to consider
edge cases and strive to keep your CSS logically organized.&lt;/p&gt;
&lt;p&gt;At the same time, it is not a programming language. It is not executed in a
linear fashion. Many of the rules of software architecture do not (and should
not) cleanly apply. It is meant to be global, a cross-cutting concern that
uniformly addresses various circumstances wherever they appear on the page.&lt;/p&gt;
&lt;p&gt;Too many developers try to force it into the mental model of a programming
language, missing out on many of its most powerful features like the cascade and
inheritance. And yet these same developers treat their CSS as an afterthought,
throwing rules haphazardly onto the end of a stylesheet with little
consideration for the selectors they use.&lt;/p&gt;
&lt;p&gt;If you find yourself doing this, reverse your thinking. Don’t expect CSS to be a
programming language, but do treat it like code.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Transitioning Gradients</title>
    <link href="https://keithjgrant.com/posts/2017/07/transitioning-gradients/"/>
    <published>2017-07-05T17:33:51Z</published>
    <updated>2017-07-05T17:33:51Z</updated>
    <id>https://keithjgrant.com/posts/2017/07/transitioning-gradients/</id>
    <content xml:lang="en" type="html">&lt;p&gt;In, CSS, you can’t transition a background gradient. It sure would be nice if you could:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.gradient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    to right&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;211&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;179&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 30%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; background-image 0.5s linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.gradient:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    to bottom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;344&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;31&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 40%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But, no. It jumps from one gradient to the other immediately, with no smooth transition between the two. So let’s hack it!&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;We can achieve this effect with the help of a pseudo-element and an opacity transform instead.&lt;/p&gt;
&lt;p&gt;First, apply one gradient to the element. Then, position its pseudo-element to fill the element and apply the second gradient to that. To transition between the two gradients, transition the opacity of the pseudo-element.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.gradient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    to right&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;211&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;179&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 30%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.gradient::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    to bottom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;344&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;31&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 40%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; opacity 0.5s linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.gradient:hover::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pseudo-element is initially hidden via &lt;code&gt;opacity: 0&lt;/code&gt;. On hover, that transitions to an &lt;code&gt;opacity: 1&lt;/code&gt;. This produces the illusion of the main gradient transitioning to the pseudo-element’s gradient. It also takes a little bit of &lt;code&gt;z-index&lt;/code&gt; work to ensure the pseudo-element stays positioned behind the content of the main element.&lt;/p&gt;
&lt;p&gt;This takes a fair bit of code, unfortunately. But if you need this effect, this is the best (and only) way I’ve found so far to accomplish it.&lt;/p&gt;
&lt;p&gt;Check out the full working example:&lt;/p&gt;
&lt;p data-height=&quot;500&quot; data-theme-id=&quot;31665&quot; data-slug-hash=&quot;OgEdgN&quot; data-default-tab=&quot;css,result&quot; data-user=&quot;keithjgrant&quot; data-embed-version=&quot;2&quot; data-pen-title=&quot;OgEdgN&quot; class=&quot;codepen&quot;&gt;See the Pen &lt;a href=&quot;https://codepen.io/keithjgrant/pen/OgEdgN/&quot;&gt;OgEdgN&lt;/a&gt; on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://production-assets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
</content>
  </entry>
  <entry>
    <title>Thoughts on Self-Documenting CSS</title>
    <link href="https://keithjgrant.com/posts/2017/06/self-documenting-css/"/>
    <published>2017-06-09T17:34:24Z</published>
    <updated>2017-06-09T17:34:24Z</updated>
    <id>https://keithjgrant.com/posts/2017/06/self-documenting-css/</id>
    <content xml:lang="en" type="html">&lt;p&gt;One of the best programming books I’ve ever read is &lt;a href=&quot;https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/&quot;&gt;Clean Code&lt;/a&gt; by Robert C. Martin. If you have never read it, add it to your list.&lt;/p&gt;
&lt;blockquote&gt;
  Every comment represents a failure to make the code self explanatory.
  &lt;cite&gt;Robert C. Martin&lt;/cite&gt;
&lt;/blockquote&gt;
&lt;!--more--&gt;
&lt;p&gt;In one section, Martin discusses code comments, and makes a strong argument against them. I won’t repeat all his arguments, but in short he maintains they have a tendency to fall out of date. The computer ignores them, so nothing guarantees they accurately describe what the code does. It is far better when the code itself is clear; then both the programmer and the computer are reading the same thing.&lt;/p&gt;
&lt;p&gt;Consider the following:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Check to see if the employee is eligible for full benefits&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;employee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flags &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;HOURLY_FLAG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;employee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  …
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is the comment helpful? Absolutely. But this is better:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;employee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEligibleForFullBenefits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  …
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code says what it means and does what it says. Much of the time, a comment can be improved by deleting it and encapsulating meaning in well-named functions or variables. To be clear, Martin does not say comments should &lt;em&gt;never&lt;/em&gt; be used—but you should always strive to render them unnecessary. Every comment represents a failure to do so.&lt;/p&gt;
&lt;h2 id=&quot;what-about-css%3F&quot; tabindex=&quot;-1&quot;&gt;What about CSS? &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#what-about-css%3F&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I agree with Martin regarding comments. However, this thinking raises interesting questions when it comes to a declarative language like CSS. Declarations have to follow a strict pattern. Selectors are determined by the structure of the HTML, at least in part. You have far fewer options regarding code structure. Does this mean your CSS should have comments all over the place?&lt;/p&gt;
&lt;p&gt;Well… maybe. We use comments different ways for a variety of reason. Let’s look at some comments, and consider what they add (or not) to the code. I’ll start with some low-hanging fruit, then move on to the less obvious stuff.&lt;/p&gt;
&lt;h2 id=&quot;bad%3A-obvious-comments&quot; tabindex=&quot;-1&quot;&gt;Bad: Obvious Comments &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#bad%3A-obvious-comments&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In any language, obvious comments are unnecessary. The following are actual examples of comments from an earlier version of Bootstrap 3 source:&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Addresses&lt;/span&gt;
address &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, that does appear to be a selector for addresses.&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Unordered and Ordered lists&lt;/span&gt;
ul&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
ol &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Really?&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Blockquotes&lt;/span&gt;
blockquote &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OMG. Make it stop.&lt;/p&gt;
&lt;p&gt;Don’t do comments like this. Delete that crap. It’s only echoing what’s already there in the code. Thankfully, most of these have been removed in newer versions of Bootstrap.&lt;/p&gt;
&lt;h2 id=&quot;bad%3A-section-separators&quot; tabindex=&quot;-1&quot;&gt;Bad: Section Separators &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#bad%3A-section-separators&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One type of comment that’s mostly unique to CSS are section separators. This sort of thing:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* -----------------
 * TOOLTIPS
 * ----------------- */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These things drive me nuts. Don’t get me wrong; I understand why we have them. Our stylesheets can get really long. When scrolling through a 1000 line file, you need landmarks like this to help navigate.&lt;/p&gt;
&lt;p&gt;But here’s the thing: we don’t work in 1000 line files any more. If your project needs a stylesheet this large, it should be broken out into bite-sized partials and you should be using a preprocessor to piece them all together. You don’t need a big &lt;code&gt;TOOLTIPS&lt;/code&gt; at the top of the file if the file is called &lt;code&gt;tooltips.scss&lt;/code&gt;. If you feel like you need a separator comment, split the code into a new file.&lt;/p&gt;
&lt;h2 id=&quot;bad%3A-explaining-the-language&quot; tabindex=&quot;-1&quot;&gt;Bad: Explaining the Language &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#bad%3A-explaining-the-language&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’m going to pick on Bootstrap again. This is from their &lt;a href=&quot;https://github.com/twbs/bootstrap/blob/v4-dev/scss/_tooltip.scss#L11&quot;&gt;_tooltips.scss&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Allow breaking very long words so they don&#39;t overflow the tooltip&#39;s bounds&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;word-wrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; break-word&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is very close to the category of “obvious comments.” This comment explains what the &lt;code&gt;word-wrap&lt;/code&gt; property does, and no more. There is another rule of code comments that says a comment should explain &lt;a href=&quot;https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/&quot;&gt;why, not what&lt;/a&gt;. This comment breaks that rule. Delete it.&lt;/p&gt;
&lt;p&gt;There might be a fuzzy bound here in CSS, however. There are hundreds of properties, and you probably don’t know them all. If you’re using something &lt;em&gt;really&lt;/em&gt; obscure, maybe a comment like this is okay. Maybe.&lt;/p&gt;
&lt;h2 id=&quot;bad%3A-explaining-the-library&quot; tabindex=&quot;-1&quot;&gt;Bad: Explaining the Library &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#bad%3A-explaining-the-library&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s look at another comment from the same Bootstrap file:&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Our parent element can be arbitrary since tooltips are by default inserted as a&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// sibling of their target element. So reset our font and text properties to avoid&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// inheriting weird values.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reset-text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$font-size-sm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one is interesting. It seems to pass the “why, not what” sniff test. It explains that unexpected font properties might be inherited by this module, then uses a mixin to reset the font properties.&lt;/p&gt;
&lt;p&gt;But upon further reflection, it’s obvious that this is the only reason the mixin exists in the first place. In fact, a search through the codebase reveals an identical comment every place this mixin is used. At the very least, this comment belongs where the mixin is defined, not each and every place it is used. You could move the comment there and cut down the number of comments cluttering the code.&lt;/p&gt;
&lt;p&gt;However, I think even that is unnecessary: the name of the mixin provides enough information. And if it doesn’t, give it a name that does. Call it &lt;code&gt;reset-inherited-font&lt;/code&gt; or something more explicit that makes clear not only what it does, but why you need it. This is a function call; you have total control over the name here. Use that to your advantage and make it say what it does in a way that renders the comment unnecessary.&lt;/p&gt;
&lt;p&gt;Preprocessors are one area where CSS is most like a conventional programming language. When you have the chance, use well-named variables and mixins to make the meaning of the code obvious. This code snippet gets this right with the &lt;code&gt;$font-size-sm&lt;/code&gt; variable: you know at a glance that the font is smaller than the main font size. You can also safely infer that this is a commonly-used font size; it is the same font size as other small text throughout the codebase.&lt;/p&gt;
&lt;h2 id=&quot;bad%3A-old-comments&quot; tabindex=&quot;-1&quot;&gt;Bad: Old Comments &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#bad%3A-old-comments&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.dropdown-header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  …
  &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; // as with &gt; li &gt; a
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/twbs/bootstrap/blob/620257456ed0685cae6b6ff51d2ab1e37f02a4fa/scss/_dropdown.scss#L122&quot;&gt;“as with &amp;gt; li &amp;gt; a”&lt;/a&gt;? What does that mean? My first assumption is that, elsewhere in the file, there is an &lt;code&gt;&amp;gt; li &amp;gt; a&lt;/code&gt; selector and this somehow refers to that. Maybe there’s another comment there explaining the reasoning… But scanning through the file, there is no such selector. There is another &lt;code&gt;nowrap&lt;/code&gt; under a &lt;code&gt;.dropdown-item&lt;/code&gt; selector. Maybe that’s what this refers to? Or maybe it refers to something that has since been deleted or refactored into another file? The only way to know would be to dig through the git history.&lt;/p&gt;
&lt;p&gt;This is an old comment. It presumably meant something at some point in time, but the code has long-since drifted from that. This gets back to one of the main reasons Robert Martin is so hard on comments: the code changes out from underneath them and they become meaningless—or worse: they lie and actively lead you in the wrong direction. When you find a comment like this, delete it. It adds nothing to the code and has in fact has wasted our time trying to make sense of it.&lt;/p&gt;
&lt;h2 id=&quot;sometimes-ok%3A-meaningful-comments&quot; tabindex=&quot;-1&quot;&gt;Sometimes OK: Meaningful Comments &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#sometimes-ok%3A-meaningful-comments&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s another block of code with several comments:&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.dropdown-item &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For `&amp;lt;button&gt;`s&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-item-padding-y&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-item-padding-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; both&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$font-weight-normal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-link-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For `&amp;lt;button&gt;`s&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For `&amp;lt;button&gt;`s&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For `&amp;lt;button&gt;`s&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These comments are meaningful. They tell me that several of these properties are applied specifically to override &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; styles. These are good comments, because that is not immediately obvious.&lt;/p&gt;
&lt;p&gt;But it’s worth asking the question: is there a way to express this in the code itself? You could move those particular declarations into a second ruleset that targets buttons specifically:&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.dropdown-item &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-item-padding-y&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-item-padding-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; both&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$font-weight-normal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-link-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;button.dropdown-item &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is very explicit and easily understood. Unfortunately, it also raises the selector specificity. That’s a side-effect that may not be acceptable.&lt;/p&gt;
&lt;p&gt;Instead, I think this is a strong candidate for a mixin. Refactoring to a mixin could mean cleaning up the code in several other places as well. Consider this version:&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.dropdown-item &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; remove-button-styles&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-item-padding-y&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-item-padding-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; both&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$font-weight-normal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dropdown-link-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s clear what this does without any comments, and it gives me a mixin that performs a fairly common action, so other modules elsewhere could benefit from the same change. I did keep the &lt;code&gt;width: 100%&lt;/code&gt; here rather than moving it into the mixin, because that might cause unexpected breakages if the mixin applied that elsewhere.&lt;/p&gt;
&lt;p&gt;Furthermore, the original ruleset had ten declarations. That’s about as long as I like to get, before I start thinking &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_smell&quot;&gt;“code smell”&lt;/a&gt;. A mixin is a great way to shorten things up. It’s easier to get an overall feel for what this ruleset does at a glance.&lt;/p&gt;
&lt;p&gt;Refactoring to a mixin won’t always be a preferable option, but look for it.&lt;/p&gt;
&lt;h2 id=&quot;good%3A-annotate-obscure-bugfixes&quot; tabindex=&quot;-1&quot;&gt;Good: Annotate Obscure Bugfixes &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#good%3A-annotate-obscure-bugfixes&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve brought the hammer down hard on comments here. But I’m not always opposed. If you’ve ever looked at the source for &lt;a href=&quot;https://github.com/necolas/normalize.css/blob/master/normalize.css&quot;&gt;normalize.css&lt;/a&gt;, you’ll notice it’s chock-full of comments. And I’d say, most of them are &lt;em&gt;great&lt;/em&gt; comments.&lt;/p&gt;
&lt;p&gt;Look at this beauty:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * 1. Add the correct box sizing in Firefox.
 * 2. Show the overflow in Edge and IE.
 */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;hr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; content-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 1 */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 1 */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; visible&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 2 */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without those comments, you would never know why those rules are there. When you code around a particular browser bug, it’s often something obscure and hard to remember. I am fully in support of comments like this. And if you examined this in the wrong browser, you might mistakenly think the rule is no longer needed and delete it.&lt;/p&gt;
&lt;p&gt;Normalize in particular needs a lot of comments because it’s made up entirely of base styles. The selectors are all type selectors and attribute selectors. There are no classnames in sight, because they aren’t naming and styling classnames, so self-documentation is more difficult.&lt;/p&gt;
&lt;p&gt;Here’s another comment from Bootstrap:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A url to an issue on Github! That’s helpful. It tells me, without even following the link, that this was a bug, and it may have been hard to track down. If I need to go read up on it, I can do so and get all the dirty details. And best of all, it hasn’t cluttered up the code with a lengthy paragraph that attempts to summarize the full issue. It gives the browser (and OS) info I need, and tells me where I can find out more. Alternately, if you use a private issue tracker like JIRA, you can put just an associated ticket number in the comment.&lt;/p&gt;
&lt;p&gt;You don’t need to do this to every bug you fix. But if it’s not obvious, and especially if it’s related to a browser quirk, go for it.&lt;/p&gt;
&lt;h2 id=&quot;good%3A-mandatory-comments&quot; tabindex=&quot;-1&quot;&gt;Good: Mandatory Comments &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/06/self-documenting-css/#good%3A-mandatory-comments&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some tools like &lt;a href=&quot;https://github.com/kss-node/kss-node&quot;&gt;KSS&lt;/a&gt; build a styleguide from comments in your CSS:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*
Alerts

An alert box requires a contextual class to specify its importance.

Markup:
&amp;lt;div class=&quot;alert &quot;&gt;
  Take note of this important alert message.
&amp;lt;/div&gt;

alert-success   - Something good or successful
alert-info      - Something worth noting, but not super important
alert-warning   - Something to note, may require attention
alert-danger    - Something important. Usually signifies an error.

Styleguide Alerts
*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not just a comment; it is code. It is parsed by KSS and used to generate HTML output. It is part of your documentation. And, I would say, this is better than a separate hand-build HTML file, because it is co-located in the same file and more likely to stay in sync with the code.&lt;/p&gt;
&lt;p&gt;Another type of mandatory comments are licenses. When you use a third-party library with a license in a comment, you typically need to include that.&lt;/p&gt;
&lt;p&gt;When I pull out &lt;a href=&quot;https://twitter.com/keithjgrant/status/867803638026035200&quot;&gt;Robert Martin quotes&lt;/a&gt; about comments, it tends to get a reaction. I don’t do it to be contrary. I do it because I believe in straightforward code that’s easy to understand quickly. If you’re littering your code with comments, be sure it’s not because you’re doing exactly the opposite.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Memorizing Alignment Properties</title>
    <link href="https://keithjgrant.com/posts/2017/05/memorizing-alignment-properties/"/>
    <published>2017-05-05T13:44:55Z</published>
    <updated>2017-05-05T13:44:55Z</updated>
    <id>https://keithjgrant.com/posts/2017/05/memorizing-alignment-properties/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Do you often find yourself looking up a Flexbox &lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;&gt;cheat sheet&lt;/a&gt;? Wish you could just commit all those properties to memory and be done with it? Here’s how I memorized them.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id=&quot;learn-%E2%80%9Cflex%E2%80%9D&quot; tabindex=&quot;-1&quot;&gt;Learn “flex” &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/05/memorizing-alignment-properties/#learn-%E2%80%9Cflex%E2%80%9D&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you’ve worked with flexbox much, you might already have this one committed to memory. The &lt;code&gt;flex&lt;/code&gt; property is itself shorthand for three other properties: &lt;code&gt;flex-grow&lt;/code&gt;, &lt;code&gt;flex-shrink&lt;/code&gt;, and &lt;code&gt;flex-basis&lt;/code&gt;. But most of the time, the shorthand is all you need (and is even preferable).&lt;/p&gt;
&lt;p&gt;If you’re not familiar with this shorthand yet, learn it first. Learn only it. Don’t worry about any of the rest until you get this down.&lt;/p&gt;
&lt;h2 id=&quot;justify-%3D-horizontal&quot; tabindex=&quot;-1&quot;&gt;Justify = horizontal &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/05/memorizing-alignment-properties/#justify-%3D-horizontal&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Microsoft Word, if you “justify” text, you control how the text is spaced out horizontally. It spaces to be flush against the left and right sides.&lt;/p&gt;
&lt;p&gt;In the same way, &lt;code&gt;justify-content&lt;/code&gt; is used on a flex container to control how its flex items are spaced &lt;em&gt;horizontally&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;align-%3D-vertical&quot; tabindex=&quot;-1&quot;&gt;Align = vertical &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/05/memorizing-alignment-properties/#align-%3D-vertical&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You know there’s a &lt;code&gt;vertical-align&lt;/code&gt; property, right? Maybe it didn’t ever do what you expected it do, but you know it’s there. The same way, the &lt;code&gt;align-*&lt;/code&gt; properties control &lt;em&gt;vertical&lt;/em&gt; alignment.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;align-content&lt;/code&gt; controls how the rows of flex items align vertically within the flex container. (This generally only applies when you have flex-wrap enabled.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;align-items&lt;/code&gt; controls how the flex items align themselves vertically within each row.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;align-self&lt;/code&gt; on a single flex item controls how that item is aligned vertically within a row, overriding its parent’s &lt;code&gt;align-items&lt;/code&gt; setting.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;one-caveat&quot; tabindex=&quot;-1&quot;&gt;One caveat &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/05/memorizing-alignment-properties/#one-caveat&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you can remember this rule, you’ll have a lot more of flexbox ready from memory. Note that &lt;strong&gt;these swap if you change the flex direction&lt;/strong&gt; to “column” or “column reverse”. Technically speaking, &lt;code&gt;justify-content&lt;/code&gt; spaces along the main axis, and &lt;code&gt;align-*&lt;/code&gt; properties space along the cross axis. But learn the default behavior, as it is the most common scenario; and know that rotating these axes changes it.&lt;/p&gt;
&lt;p&gt;As a bonus, these properties also apply to CSS Grid as well. Grid also brings in a &lt;code&gt;justify-items&lt;/code&gt; and &lt;code&gt;justify-self&lt;/code&gt; property to fill out the set. These work much like their &lt;code&gt;align-*&lt;/code&gt; counterparts, but on the horizontal plane.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>What Grid Can Do That Tables Can’t</title>
    <link href="https://keithjgrant.com/posts/2017/04/grid-v-tables/"/>
    <published>2017-04-12T15:52:15Z</published>
    <updated>2017-04-12T15:52:15Z</updated>
    <id>https://keithjgrant.com/posts/2017/04/grid-v-tables/</id>
    <content xml:lang="en" type="html">&lt;p&gt;There’s a kind of narrative out there that’s basically, “Silly CSS wonks: first they tell us tables are bad, now they give us grid.” Nevermind that this is completely ignorant of the original argument against tables for layout (tables were never bad for layout because of the way they looked on screen; they were bad because of what they did to your markup).&lt;/p&gt;
&lt;p&gt;It’s true that grid looks a lot like tables at first glance. But it’s also worth noting the features grid provides that cannot be accomplished using tables. Here are just a few:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://gridbyexample.com/examples/example15/&quot;&gt;Overlapping cells&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://gridbyexample.com/examples/example12/&quot;&gt;Empty cells&lt;/a&gt;, without a bunch of empty tags&lt;/li&gt;
&lt;li&gt;Size columns (or rows!) in &lt;a href=&quot;https://alligator.io/css/css-grid-layout-fr-unit/&quot;&gt;proportion to one another&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://gridbyexample.com/examples/example24/&quot;&gt;Complex alignment&lt;/a&gt; of &lt;a href=&quot;http://gridbyexample.com/examples/example25/&quot;&gt;contents within a cell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Specify a &lt;a href=&quot;https://rachelandrew.co.uk/archives/2016/04/12/flexible-sized-grids-with-auto-fill-and-minmax/&quot;&gt;flexible range of acceptable sizes&lt;/a&gt; for columns &amp;amp; rows&lt;/li&gt;
&lt;li&gt;Provide a full definition of responsive behavior, often without any media queries&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Don’t QWOP Your Way Through CSS</title>
    <link href="https://keithjgrant.com/posts/2017/04/dont-qwop-your-css/"/>
    <published>2017-04-07T19:45:07Z</published>
    <updated>2017-04-07T19:45:07Z</updated>
    <id>https://keithjgrant.com/posts/2017/04/dont-qwop-your-css/</id>
    <content xml:lang="en" type="html">&lt;p&gt;&lt;a href=&quot;http://www.foddy.net/Athletics.html&quot;&gt;QWOP&lt;/a&gt;, if you haven’t played it, is a ridiculous running game. Instead of pressing, say, the right arrow key to run, you must control each of the runner’s muscles independently. Pressing the keys Q, W, O, and P will each extend one leg or bend one knee. Coordinating them all is incredibly difficult, and you are usually lucky if you can make a full stride before falling on your head.&lt;/p&gt;
&lt;p&gt;This is also how a lot of developers treat CSS. Instead of thinking about constructing a robust system, they focus on only one specific part of the desired result. Instead of asking, “How do I run?”, they ask, “How do I flex my knee?” I can teach you how to flex your knee, but if QWOP shows us anything, that knowledge might actually make running more difficult.&lt;/p&gt;
&lt;p&gt;CSS does a lot of work for you, if you will let it. Normal document flow ensures things all fit together on the page; this is why you should be judicious with positioning that breaks out of document flow. CSS automatically sizes your elements to contain their text; this is why you should avoid explicitly setting height. Relative units allow you to define one value in terms of another so they will respond together if the context changes; this is why I sometimes frown on pixel units. Margins collapse so that paragraphs stack with the correct spacing. Certain properties inherit down the DOM tree so you don’t have to specify a font for every single element.&lt;/p&gt;
&lt;p&gt;You can get down to the “bare metal” in CSS, if you want. You can specify absolute positions for everything and set explicit heights and define every single value in pixels and override inheritance. But in the end, this will make more work for yourself and unearth edge cases you didn’t think about.&lt;/p&gt;
&lt;p&gt;The next time you find yourself asking something like, “How do I vertically center this?”, take a step back. Are you having trouble because you set the height on something? Why did you set a height? What are you &lt;em&gt;actually&lt;/em&gt; trying to achieve? Instead of focusing on a specific metric, think about the system as a whole: perhaps you want several items to all have the same height. Now, the problem is defined in terms of some desired &lt;a href=&quot;https://www.youtube.com/watch?v=TGHbkTGVqoU&quot;&gt;system behavior&lt;/a&gt;. Solve for that: flexbox or grid can align multiple elements with the same height (they can also center the contents within, too).&lt;/p&gt;
&lt;p&gt;The automatic behaviors of CSS aren’t always apparent if you aren’t familiar with them. They can result in weird outcomes, and you will be frustrated by them. So, I think, the first tendency is to find a way to turn off this “help.” Don’t.&lt;/p&gt;
&lt;p&gt;Trust CSS; it does good things for you if you let it. Learn how to solve for system behaviors. Unless you like running by pressing Q, W, O, and P.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>CSS is Not Broken</title>
    <link href="https://keithjgrant.com/posts/2017/03/css-is-not-broken/"/>
    <published>2017-03-24T18:15:34Z</published>
    <updated>2017-03-24T18:15:34Z</updated>
    <id>https://keithjgrant.com/posts/2017/03/css-is-not-broken/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Coding in any language is hard before you spend time mastering it. Imagine what your JavaScript would look like if you never took the time to learn about OOP or functional programming principles? Just because you &lt;em&gt;expect&lt;/em&gt; CSS to be easy, doesn’t mean the language is broken when you find it is not.&lt;/p&gt;
&lt;p&gt;A lot of people have &lt;a href=&quot;https://simpleprogrammer.com/2013/05/06/why-javascript-is-doomed/&quot;&gt;gone on about&lt;/a&gt; &lt;a href=&quot;https://medium.com/smalltalk-talk/the-three-worst-programming-languages-b1ec25a232c1#e848&quot;&gt;how horrible JavaScript is&lt;/a&gt;. JavaScript is not horrible. It is an incredible language. It has a few odd quirks, just as CSS does. But if you take the time to actually understand these quirks, you will reap huge reward.&lt;/p&gt;
&lt;p&gt;You cannot be proficient in JavaScript until you understand coercion, prototypal inheritance, and asyncronous flow control. Likewise, in CSS, you have to understand the cascade, inheritance, and the box model. Once you have those down, take a deeper look at the various layout methods. Do you know what stacking contexts and block formatting contexts are? Do you know why setting a height on an element leads to problems and how to accomplish what you need without doing so?&lt;/p&gt;
&lt;p&gt;CSS is hard. But this is not because the language is faulty. Rather, the difficulty lies in what the language seeks to accomplish. I’ve heard many developers say they wish they could “throw out CSS and start over with something better.” I think this betrays a fundamental misunderstanding of the purpose of the language:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;When you code CSS, you’re writing abstract rules to take *unknown* content and organize it in an *unknown* medium. That shit is hard.&lt;/p&gt;&amp;mdash; keith•j•grant (@keithjgrant) &lt;a href=&quot;https://twitter.com/keithjgrant/status/842728744653676544&quot;&gt;March 17, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;Furthermore, CSS isn’t just code, it’s also part of the design. A &lt;a href=&quot;https://snook.ca/archives/html_and_css/css-concerns&quot;&gt;vital concern of CSS is consistency&lt;/a&gt;. You should want “global” styles: colors and spacing should be consistent throughout your application. Similar components need to look similar. Your buttons should all be consistently sized and your box shadows or border radiuses should not be ad-hoc.&lt;/p&gt;
&lt;p&gt;I find it ironic that developers who bemoan the “global” nature of CSS are usually the ones who run into specificity problems because they unnecessarily scope their styles to particular parts of a particular page. When you have selectors like &lt;code&gt;#directory .sorted .sidebar :nth-child(2) button&lt;/code&gt;, your problem isn’t that CSS is global. Your CSS isn’t global enough!&lt;/p&gt;
&lt;p&gt;The next thing you know, you find yourself using &lt;code&gt;!important&lt;/code&gt; to correct specificity problems. This is a red flag that you need to stop and learn the cascade. You can use &lt;code&gt;!important&lt;/code&gt; to sweep specificity problems under the rug once. But you will soon need it a second time, at which point you will face the same specificity problem all over again.&lt;/p&gt;
&lt;p&gt;Design your styles so they can be reused, anywhere in the app. Learn SMACSS and BEM. And don’t stop because you understand the “naming system”—these methodologies are about far more than double-underscores and double-hyphens. They are about code organization, reuse, and refactoring. They are the solution to dead code elimination. They offer ways to utilize the cascade instead of fearing it. They allow you to know precisely where in your code you can find a certain set of styles.&lt;/p&gt;
&lt;p&gt;CSS isn’t broken. But it does require &lt;a href=&quot;https://www.manning.com/books/css-in-depth&quot;&gt;study and skill&lt;/a&gt;. It does require careful thought. As with anything in programming, you can make a mess of the code. Sure, blaming the language is the easy way out. But when your JavaScript is confusing and buggy, you know it’s not the language’s fault.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>More thoughts on CSS in JS</title>
    <link href="https://keithjgrant.com/posts/2017/03/more-thoughts-on-css-in-js/"/>
    <published>2017-03-20T19:07:49Z</published>
    <updated>2017-03-20T19:07:49Z</updated>
    <id>https://keithjgrant.com/posts/2017/03/more-thoughts-on-css-in-js/</id>
    <content xml:lang="en" type="html">&lt;p&gt;At a previous job, I was brought in to a team of Java devs to provide a little JS support. They had been using something called PrimeFaces, basically a bunch of front-end components you drop into JSF pages. Working with it was horrid.&lt;/p&gt;
&lt;p&gt;In short, these devs knew nothing about JavaScript or the front-end, but this tool let them sort of hack a UI together. It abstracted away all the tools needed for really working in the front end. For me, someone adept in JavaScript, working with PrimeFaces was like trying to code without a keyboard.&lt;/p&gt;
&lt;p&gt;I’ve also had to work on projects coded in ExtJS. These feel about the same way: although you are technically editing a JS file, you aren’t really programming “in JavaScript.” Instead, you are basically coding via configuration. Actual understanding of JavaScript doesn’t help you much.&lt;/p&gt;
&lt;p&gt;To me, CSS in JS feels the same way. It feels like a tool so a bunch of JavaScript devs can kinda-sorta hack together styles without actually having to write CSS. Except the syntax is more clunky and you have taken away the cascade. (And, yet, surprise! You still need to learn the most complicated parts of CSS.)&lt;/p&gt;
&lt;p&gt;Now I’ll be fair, CSS in JS libraries typically provide a much thinner buffer between you and the actual CSS when compared to PrimeFaces. But still… it’s an abstraction layer. It gives the illusion of writing styles in perfect isolation, but inheritance still takes place and can interfere. It gets between me and the code I want actual control over.&lt;/p&gt;
&lt;p&gt;I know what I’m doing when I code CSS. I &lt;em&gt;want&lt;/em&gt; the cascade in many instances; removing it feels crippling. I won’t deny CSS in JS offers some benefits. But they are benefits I am not convinced I need at a cost I am not sure I want to pay.&lt;/p&gt;
&lt;p&gt;I won’t say it’s the wrong choice for everyone. Some folks who really do grok CSS are in favor of it. Sometimes ExtJS is the right choice (don’t quote me on that). But dang, please know it’s a compromise that someone might have to clean up down the road.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A better approach to CSS aspect ratios</title>
    <link href="https://keithjgrant.com/posts/2017/03/aspect-ratios/"/>
    <published>2017-03-14T14:12:43Z</published>
    <updated>2017-03-14T14:12:43Z</updated>
    <id>https://keithjgrant.com/posts/2017/03/aspect-ratios/</id>
    <content xml:lang="en" type="html">&lt;p&gt;There’s an old hack for creating elements with a fixed aspect ratio that involves using a percentage-based padding. You may be familiar with it. It looks something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.tile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 25%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bisque&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The element is forced to have no height, then its bottom padding is set to the actual desired height. This produces an element something like this:&lt;/p&gt;
&lt;figure class=&quot;-demo-container&quot;&gt;
  &lt;div class=&quot;-demo1&quot;&gt;
    4:1 aspect ratio
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;This works because of a peculiar quirk of padding: Any padding specified in percent computes to a percentage of the element’s &lt;em&gt;width&lt;/em&gt;—even if it is a top or bottom padding. (The same is true for margin as well.) This is a bit counter-intuitive, but it comes in handy. I think the original reasoning was so you could declare something like &lt;code&gt;padding: 5%&lt;/code&gt; and get an equal padding on all four sides of the element, regardless of its shape.&lt;/p&gt;
&lt;p&gt;Of course, this approach has a problem: overflow is cut off:&lt;/p&gt;
&lt;figure class=&quot;-demo-container&quot;&gt;
  &lt;div class=&quot;-demo1&quot;&gt;
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam laoreet tellus ut erat egestas vestibulum. Aliquam erat volutpat. Fusce ut nibh quis lectus fermentum aliquet. Suspendisse potenti.
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;This approach creates fixed height of the element, which in CSS is an anti-pattern. For this reason, I’ve always felt dirty using it.&lt;/p&gt;
&lt;h2 id=&quot;dealing-with-overflow&quot; tabindex=&quot;-1&quot;&gt;Dealing with overflow &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/03/aspect-ratios/#dealing-with-overflow&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A while back, I stumbled across an approach that is similar, but prevents the overflow problem. Instead of setting our element’s height, we can set the height on a floated &lt;code&gt;::before&lt;/code&gt; pseudo-element. Watch what happens when we do this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.tile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; darkseagreen&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tile::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 25%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.tile::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;::after&lt;/code&gt; is just a familiar clearfix. This produces a very similar result:&lt;/p&gt;
&lt;figure class=&quot;-demo-container&quot;&gt;
  &lt;div class=&quot;-demo2&quot;&gt;
    4:1 aspect ratio
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;However, the element will still grow to contain overflow if necessary:&lt;/p&gt;
&lt;figure class=&quot;-demo-container&quot;&gt;
  &lt;div class=&quot;-demo2&quot;&gt;
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam laoreet tellus ut erat egestas vestibulum. Aliquam erat volutpat. Fusce ut nibh quis lectus fermentum aliquet. Suspendisse potenti.
  &lt;/div&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;how-it-works&quot; tabindex=&quot;-1&quot;&gt;How it works &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/03/aspect-ratios/#how-it-works&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The trick is, instead of explicitly controlling (and thus limiting) the height of the element, we set a known height on the floated pseudo element. By using a padding-based percentage, this will be a ratio of the element’s width. We float it left and leave it empty, producing a width of zero. Here is the floated element, with a black outline added:&lt;/p&gt;
&lt;figure class=&quot;-demo-container&quot;&gt;
  &lt;div class=&quot;-demo3&quot;&gt;
    Floated pseudo element provides a min height to the element.
  &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Then, in the &lt;code&gt;::after&lt;/code&gt; pseudo-element, we clear the float, forcing the element to grow to contain the floated &lt;code&gt;::before&lt;/code&gt;. Since the float has a width of 0, it doesn’t interfere with the content layout in any way. And when the content extends below the bottom of the float, the box simply grows naturally to contain it:&lt;/p&gt;
&lt;figure class=&quot;-demo-container&quot;&gt;
  &lt;div class=&quot;-demo3&quot;&gt;
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam laoreet tellus ut erat egestas vestibulum. Aliquam erat volutpat. Fusce ut nibh quis lectus fermentum aliquet. Suspendisse potenti.
  &lt;/div&gt;
&lt;/figure&gt;
&lt;style&gt;
  .-demo-container {
    max-width: 300px;
    line-height: 1.6;
  }

  .-demo1 {
    height: 0;
    overflow: hidden;
    padding-bottom: 25%;
    background-color: bisque;
  }

  .-demo2 {
    background-color: darkseagreen;
    line-height: 1.6;
  }
  .-demo2::before {
    content: &quot;&quot;;
    float: left;
    padding-bottom: 25%;
  }
  .-demo2::after {
    clear: left;
    content: &quot; &quot;;
    display: table;
  }

  .-demo3 {
    background-color: darkseagreen;
    line-height: 1.6;
  }
  .-demo3::before {
    content: &quot;&quot;;
    float: left;
    padding-bottom: 25%;
    border: 1px solid black;
  }
  .-demo3::after {
    clear: left;
    content: &quot; &quot;;
    display: table;
  }
&lt;/style&gt;
</content>
  </entry>
  <entry>
    <title>It’s Both</title>
    <link href="https://keithjgrant.com/posts/2017/01/its-both/"/>
    <published>2017-01-27T13:54:57Z</published>
    <updated>2017-01-27T13:54:57Z</updated>
    <id>https://keithjgrant.com/posts/2017/01/its-both/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I’m a JavaScript developer. I have experience working in large web applications and dealing with the problems that come from scaling up. I studied Computer Science in college and love solving problems of software architecture.&lt;/p&gt;
&lt;p&gt;I’m also (in case you &lt;a href=&quot;https://www.manning.com/books/css-in-depth&quot;&gt;somehow missed it&lt;/a&gt;) a “CSS Guy”. I learned CSS during its infancy in the mid-nineties and have worked to stay up to speed ever since.&lt;/p&gt;
&lt;p&gt;When I look out at the industry today, it kind of feels &lt;a href=&quot;https://medium.com/javascript-scene/jsx-looks-like-an-abomination-1c1ec351a918#1e2d&quot;&gt;like&lt;/a&gt; &lt;a href=&quot;http://mrmrs.io/writing/2016/03/24/scalable-css/&quot;&gt;watching&lt;/a&gt; &lt;a href=&quot;http://www.zeldman.com/2017/01/03/kiss-my-classname/&quot;&gt;parents&lt;/a&gt; &lt;a href=&quot;https://twitter.com/thejameskyle/status/824795012680421376&quot;&gt;fight&lt;/a&gt;. And I’ll admit it, I’ve &lt;a href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/&quot;&gt;contributed to the problem&lt;/a&gt;. But the more I watch this unfold, the more I feel like we have two camps, talking right past one another, and no meeting in the middle. Both are missing the points the other is trying to make. So I’m shifting my stance:&lt;/p&gt;
&lt;h2 id=&quot;we%E2%80%99re-both-right&quot; tabindex=&quot;-1&quot;&gt;We’re both right &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/01/its-both/#we%E2%80%99re-both-right&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Writing SPAs is complicated business. The page isn’t just a static document you can slap some styles on and call it a day. What if you need to load content dynamically, and it results in unpredictable source order of your CSS? How do you know if styles for deeply-nested dependencies are already loaded? How do you know when you can delete code?&lt;/p&gt;
&lt;p&gt;These are hard problems. You can’t answer them by simply saying “learn CSS”. I personally may not like CSS-in-JS or inline styles, but I do realize they are important experiments. They are quests for answers to these issues. I don’t think they are the ultimate solution. That’s okay. Maybe they will get us to it.&lt;/p&gt;
&lt;p&gt;On the other hand, we have whole teams of developers who fundamentally don’t grok CSS. They code up their JavaScript components, then stick the styles they want into the project stylesheet. These may be great developers when it comes to traditional programming languages, but they get really frustrated with layout problems or specificity battles. Compound this with “too many cooks in the kitchen” and you have a real mess.&lt;/p&gt;
&lt;p&gt;We have sixty-plus years of industry experience about architecting code in traditional programming languages, but we’ve struggled to apply this stuff to CSS. Only in the last seven years or so have we started to figure out best practices for the language, and we are not doing a great job of disseminating them. When we teach that BEM is a “naming convention” rather emphasize it is a way to architect systems with modular code, thousands of developers miss the point entirely. This only drives them further away because the so-called “best practices” don’t work when they aren’t actually followed.&lt;/p&gt;
&lt;p&gt;They don’t understand that BEM is about architecture, not simply namespacing. They don’t see the value of using a pattern library, of centralizing your CSS and imposing order on who controls the CSS rather than treating it like a garbage pile where any random dev can just add haphazard code.&lt;/p&gt;
&lt;p&gt;When JavaScript-centric developers hear, “putting your CSS in JavaScript makes it easier” they clamor to get on board. But this must be made absolutely clear: you still need to understand CSS. If you don’t know what a block formatting context is, CSS in JS will not fix your layout issues. If you don’t understand stacking contexts, you will continue to struggle with futile z-index battles. If you don’t learn custom properties or the difference between cascade and inheritance or margin collapsing, these experiments will not really solve the fundamental problem: most teams don’t know how to develop with CSS.&lt;/p&gt;
&lt;h2 id=&quot;always-bet-on-javascript-the-open-web&quot; tabindex=&quot;-1&quot;&gt;Always bet on &lt;del&gt;JavaScript&lt;/del&gt; the Open Web &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/01/its-both/#always-bet-on-javascript-the-open-web&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So, yes. Hire engineers who know how to deal with system architecture. But also &lt;a href=&quot;https://keithjgrant.com/posts/2016/10/your-team-needs-a-ux-engineer/&quot;&gt;hire someone exclusively for their CSS skills&lt;/a&gt;. Put smart people on your team from both camps. Let them work together to solve these problems. Because your problems are unique and your solutions might not look like someone else’s.&lt;/p&gt;
&lt;p&gt;I can say with confidence that CSS in JS will pass. In our industry, something new always comes along. The saying is, “Always bet on JavaScript” — but this isn’t quite right. When it’s JavaScript vs Java or Ruby, yes, bet on JS. JavaScript wins not because it is inherently better; it wins because it is part of the open web platform.&lt;/p&gt;
&lt;p&gt;The open web platform always wins. And CSS is part of that platform. It’s not CSS versus JS. It’s CSS &lt;em&gt;plus&lt;/em&gt; JS. The platform will evolve. And the platform will win.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hello, World Wide IndieWeb!</title>
    <link href="https://keithjgrant.com/posts/2017/01/hello-indieweb/"/>
    <published>2017-01-23T20:34:37Z</published>
    <updated>2017-01-23T20:34:37Z</updated>
    <id>https://keithjgrant.com/posts/2017/01/hello-indieweb/</id>
    <content xml:lang="en" type="html">&lt;p&gt;It’s a new year, so it’s time for a new design! Only this time, the update is more than just a CSS revamp. This year, I join the “IndieWeb”. If you don’t know what that is, read on.&lt;/p&gt;
&lt;p&gt;Let me introduce you to the newest social network: it’s called the World Wide Web and it’s more than 25 years old.&lt;/p&gt;
&lt;h2 id=&quot;the-decentralized-social-web&quot; tabindex=&quot;-1&quot;&gt;The decentralized social web &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/01/hello-indieweb/#the-decentralized-social-web&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Social networks bother me. I don’t mean the privacy issues or mysterious sorting algorithms. Apart from these things, walled gardens are antithetical to the open standards that made the web successful in the first place. I can’t follow you on Facebook unless I have a Facebook account. You can’t like or reply to my tweet unless you join Twitter. The internet was founded on principles of decentralization, yet here we are, consolidating the bulk of our online activity into a small handful of sites we don’t control.&lt;/p&gt;
&lt;p&gt;I have long wanted an open protocol to bring the key features of social networks (liking, commenting, sharing, following) out into the open web. Now, finally, I have discovered that a fair bit of progress has been made in this regard. The W3C now has a &lt;a href=&quot;https://www.w3.org/wiki/Socialwg&quot;&gt;Social Web working group&lt;/a&gt;, and their specifications are stabilizing and have even been &lt;a href=&quot;http://indieweb.org/&quot;&gt;adopted by many&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This redesign is my first step toward adopting these standards myself. I’ve completed phase one out of two (or possibly three) steps I want to take. Each one corresponds to a key piece of the “Social Web” protocols.&lt;/p&gt;
&lt;h2 id=&quot;phase-i%3A-webmentions&quot; tabindex=&quot;-1&quot;&gt;Phase I: Webmentions &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/01/hello-indieweb/#phase-i%3A-webmentions&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This site now supports comments: you may comment on my blog by writing about it on your own blog. Then your blog should send mine a &lt;a href=&quot;https://indieweb.org/Webmention&quot;&gt;webmention&lt;/a&gt;. These are similar to the old “pingbacks”, except they use a simpler format and they are an official &lt;a href=&quot;https://www.w3.org/TR/webmention/&quot;&gt;W3C Recommendation&lt;/a&gt;. (If your site doesn’t support webmentions, I’ve provided a little form where you can submit this yourself).&lt;/p&gt;
&lt;p&gt;They basically work like this: I write you a post. Then you write a post, with metadata indicating it is a response to my post. Your blog then tells my blog that you replied and provides the URL. If I want, I can then link to your reply. Both our posts now link to each other, and we each own our own data!&lt;/p&gt;
&lt;p&gt;And because we are still beholden to some social networks, there is a service called &lt;a href=&quot;https://brid.gy/&quot;&gt;brid.gy&lt;/a&gt; that will find links to my post (and replies to those links) on Twitter or Facebook, and let my blog know about them, so I can log them here as comments. We can drag the old social network silos out into the open web!&lt;/p&gt;
&lt;h2 id=&quot;phase-ii%3A-micropub&quot; tabindex=&quot;-1&quot;&gt;Phase II: MicroPub &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/01/hello-indieweb/#phase-ii%3A-micropub&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My next goal will be to incorporate something called MicroPub. This basically means I can write short posts (say, 140 characters), called Notes. I will use these notes to post replies to articles on the web. I can also syndicate these to twitter, where webmentions and brid.gy will help me track any conversation that happens there and link to it from my blog.&lt;/p&gt;
&lt;p&gt;This involves a few more moving parts than phase I, but thankfully, most of the tools for this already exist as services online. I just need to wire things up and test them out.&lt;/p&gt;
&lt;h2 id=&quot;phase-iii%3A-activitypub%3F&quot; tabindex=&quot;-1&quot;&gt;Phase III: ActivityPub? &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2017/01/hello-indieweb/#phase-iii%3A-activitypub%3F&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last piece of the puzzle for a decentralized social web is the whole subscribers/followers bit. The old concepts of RSS, PubSub, and feed readers can do this, but that model is more of a one-way publication, not a two-way street. There is a new approaches the W3C has been working on called ActivityPub that involves tracking followers and bringing more of the social network activities into this realm.&lt;/p&gt;
&lt;p&gt;This arena isn’t as mature yet, but I’ve certainly got my eye on it.&lt;/p&gt;
&lt;p&gt;I have high hopes for this will take us. It may take a while — a couple years, at least — but I think we will start to see more discussion happening in this arena. If you want to learn more about this stuff, checkout &lt;a href=&quot;http://indieweb.org/&quot;&gt;indieweb.org&lt;/a&gt; or the &lt;a href=&quot;https://wordpress.org/plugins/indieweb/&quot;&gt;IndieWeb WordPress plugin&lt;/a&gt;. Hello, IndieWeb!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Let’s Talk About Separation of Concerns</title>
    <link href="https://keithjgrant.com/posts/2016/12/separation-of-concerns/"/>
    <published>2016-12-23T05:00:00Z</published>
    <updated>2016-12-23T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/12/separation-of-concerns/</id>
    <content xml:lang="en" type="html">&lt;p&gt;There’s been a lot of talk lately about good old Separation of Concerns — primarily in the context of React and the use of inline styles or CSS-in-JS. Advocates of these approaches argue that the language we use (be it JS, HTML, or CSS) is an arbitrary line to draw. And I would say: Yes, language is, for the most part, an arbitrary line. But that’s beside the point.&lt;/p&gt;
&lt;p&gt;So let’s get one thing straight. This…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import styles from &#39;./tile.css&#39;;
...
render() {
  return &amp;lt;div className={styles.tile}&amp;gt;...&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…is architectually equivalent to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render() {
  return &amp;lt;div className=&amp;quot;tile&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The latter approach, however:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;requires a drastically simpler build process&lt;/li&gt;
&lt;li&gt;is both backwards- and forwards-compatible across all frameworks&lt;/li&gt;
&lt;li&gt;doesn’t try to pretend that those styles exist in perfect encapsulated isolation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stop championing CSS-in-JS as some ground breaking re-thinking of Separation of Concerns. It’s not. The React component will still inherit its font face, color, and size from a parent container. It still exists on the web platform, in an HTML document, where all the rules of styles and inheritance still apply.&lt;/p&gt;
&lt;p&gt;We can debate about class name collision, co-locating stylesheets, &lt;a href=&quot;https://keithjgrant.com/posts/css-first.html&quot;&gt;how styles fit into system archetecture&lt;/a&gt;, and other related topics. But let’s take this one off the table, because it’s a straw man argument.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Your Team Needs a UX Engineer</title>
    <link href="https://keithjgrant.com/posts/2016/10/your-team-needs-a-ux-engineer/"/>
    <published>2016-10-02T05:00:00Z</published>
    <updated>2016-10-02T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/10/your-team-needs-a-ux-engineer/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Recently, after I mentioned that I was the only one on my team that writes the CSS, someone replied, “You’re lucky”.&lt;/p&gt;
&lt;p&gt;It stuck with me, and I’ve been thinking about it since. The thing is, it’s not luck. It was a deliberate decision made early when the team was first put together. And I think the rest of the developers on my team would think themselves lucky for &lt;em&gt;not&lt;/em&gt; having to touch the CSS. It is more valuable for us to have one person on the team who specializes in that.&lt;/p&gt;
&lt;p&gt;We need to rethink something about the way we build our web development teams. We spend a lot of effort hiring good programmers, but for the most part, they are interchangeable. We make sure that everyone we hire knows the fundamentals of software engineering, architecture, and the primary language the team uses. And then we &lt;em&gt;assume&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If a developer has a solid grasp on JavaScript, we think, surely they know CSS. Surely they know how to deal with accessibility. Surely they have a grasp on how and when to use various HTML elements and aria-* properties. These are small beans compared to &lt;em&gt;the code&lt;/em&gt;. And in many ways, they are.&lt;/p&gt;
&lt;p&gt;But, of course, this isn’t how it plays out. Because we hire purely for the bigger task, CSS and accessibility always remain an afterthought. On most teams, nobody really knows what they are doing when it comes to these topics. They team stumbles along, joking amongst themselves how horrible CSS is.&lt;/p&gt;
&lt;p&gt;We need to fix this. On teams larger than four or five developers, I think we can: one person per team should be hired explicitly for these tasks. Most of the team will still be focused on JavaScript (or Python or Java). But one person—call them a “CSS Developer” or “UX Engineer”—should be hired specifically for these “afterthought” concerns.&lt;/p&gt;
&lt;p&gt;Is a developer building a new component and needs some styles? The UX Engineer writes that CSS. The UX Engineer gives a snippet of HTML to the developer for them to incorporate into the component. The UX engineer ensures the correct semantics and can come alongside the developer to ensure keyboard navigation works. The UX Engineer maintains a pattern library for the team to use as a reference to find existing styles.&lt;/p&gt;
&lt;p&gt;This UX Engineer serves an important role, but furthermore, they allow the other developer to offload some work. The developer can focus on the “bigger concern” of getting the business logic right and ensuring the app works correctly. This improves the developer’s productivity.&lt;/p&gt;
&lt;p&gt;This is how things work on my team. I am effectively the UX Engineer, and I think everyone enjoys the process more because of it. In our case, we stumbled upon this setup—I was originally hired for my JavaScript skills. As always, the CSS was an afterthought; I just happened to be good at it, so we settled into the current arrangement.&lt;/p&gt;
&lt;p&gt;Don’t wait for your team to stumble into this. Set it up intentionally. Hire a UX Engineer.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>CSS in Depth</title>
    <link href="https://keithjgrant.com/posts/2016/08/css-in-depth/"/>
    <published>2016-08-24T05:00:00Z</published>
    <updated>2016-08-24T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/08/css-in-depth/</id>
    <content xml:lang="en" type="html">&lt;p&gt;&lt;img src=&quot;https://keithjgrant.com/images/2016/css-in-depth-cover.png&quot; alt=&quot;Cover of CSS in Depth&quot; class=&quot;float-right&quot; /&gt;My book is finally for sale! The &lt;abbr title=&quot;Manning Early Access Program&quot;&gt;MEAP&lt;/abbr&gt; is available on &lt;a href=&quot;https://manning.com/books/css-in-depth&quot;&gt;Manning’s website&lt;/a&gt;. The first three chapters are available now, and others will be rolling out steadily (I’ve actually already completed drafts of six chapters at this point).&lt;/p&gt;
&lt;p&gt;Until the end of August, you can use my code &lt;strong&gt;mlgrant2&lt;/strong&gt; for 50% off. Follow &lt;a href=&quot;https://twitter.com/CSSinDepth&quot;&gt;@CSSinDepth&lt;/a&gt; on Twitter for updates.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Save Scoped CSS</title>
    <link href="https://keithjgrant.com/posts/2016/06/save-scoped-css/"/>
    <published>2016-06-09T05:00:00Z</published>
    <updated>2016-06-09T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/06/save-scoped-css/</id>
    <content xml:lang="en" type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update June 23, 2016:&lt;/strong&gt; The editor’s draft has been updated again. It looks like &lt;code&gt;@scope&lt;/code&gt; is gone for good, so this post is now a moot point. The way forward now lies in the Shadow DOM.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;About &lt;a href=&quot;https://keithjgrant.com/posts/into-the-future-of-css.html#scoped-css&quot;&gt;a year ago&lt;/a&gt;, I wrote about a promising feature of CSS, scoping. I love the idea of this feature, and I think it could be one of the most important changes in the near future of CSS. You know all those complaints about “in CSS everything is global&amp;quot;? This feature is the answer.&lt;/p&gt;
&lt;p&gt;I became excited recently when I noticed some movement in the W3C concerning this spec. So I took a look at what was going on. I found some good news, and I found some bad news.&lt;/p&gt;
&lt;h2 id=&quot;the-good-news&quot; tabindex=&quot;-1&quot;&gt;The good news &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/06/save-scoped-css/#the-good-news&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://drafts.csswg.org/css-scoping/&quot;&gt;editor’s draft of the specification&lt;/a&gt; has recently been updated. The changes, I think, significantly improve the viability of the feature. They more closely resemble the hypothetical changes I wished for last year. Most importantly, scoped styles would no longer need to be defined inline in the HTML document; you can define scoped styles in an external stylesheet.&lt;/p&gt;
&lt;p&gt;The proposed syntax looks like this, using a new &lt;code&gt;@scope&lt;/code&gt; at-rule:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; .foo-module&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; .bar-module&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each scope is defined with a selector (&lt;code&gt;.foo-module&lt;/code&gt;, &lt;code&gt;.bar-module&lt;/code&gt;). This establishes a new scope for matched elements and their descendants. Inside the braces that follow, you can specify any styles you wish. These styles are then applied only to the elements belonging to that scope.&lt;/p&gt;
&lt;p&gt;According to the rules of &lt;a href=&quot;https://www.w3.org/TR/css-cascade-3/#cascade-scope&quot;&gt;the cascade&lt;/a&gt;, rules applied to an inner scope will override those applied to an outer scope, &lt;em&gt;regardless of their selector specificity&lt;/em&gt;. This is precisely the behavior CSS-in-JS and/or CSS Modules users want! And yes, this definition is in the current Candidate Recommendation of the spec.&lt;/p&gt;
&lt;p&gt;This means, given the example above, a &lt;code&gt;foo-module&lt;/code&gt; inside a &lt;code&gt;bar-module&lt;/code&gt; will have blue text; and a &lt;code&gt;bar-module&lt;/code&gt; inside a &lt;code&gt;foo-module&lt;/code&gt; will have red text. This could solve so many developer frustrations.&lt;/p&gt;
&lt;h2 id=&quot;the-bad-news&quot; tabindex=&quot;-1&quot;&gt;The bad news &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/06/save-scoped-css/#the-bad-news&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I got excited when I saw these changes, so I started reading more on what prompted them. I discovered that it is a sort of last-ditch effort to salvage the spec. The problem? Browser vendors have shown no interest in implementing it. Firefox has long-since implemented the earlier version of the spec, but that’s it.&lt;/p&gt;
&lt;p&gt;If we developers want this feature, we need to let the browser vendors know. Otherwise, it’s DOA.&lt;/p&gt;
&lt;p&gt;So tell them, please. Follow these links, and up-vote or leave comments in support of scoped styles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://groups.google.com/a/chromium.org/forum/#!searchin/blink-dev/scoped/blink-dev/R1x18ZLS5qQ/Bjuh_cENhlQJ&quot;&gt;Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/14895573--scope&quot;&gt;Edge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I couldn’t find a channel for Safari. If you know where it is, please let me know and I’ll add it here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s not allow this to die.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Quest for Interchangeable Parts</title>
    <link href="https://keithjgrant.com/posts/2016/02/a-quest-for-interchangeable-parts/"/>
    <published>2016-02-06T05:00:00Z</published>
    <updated>2016-02-06T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/02/a-quest-for-interchangeable-parts/</id>
    <content xml:lang="en" type="html">&lt;p&gt;This is a bolt. &lt;img class=&quot;float-right&quot; src=&quot;https://keithjgrant.com/images/2016/m20-bolt.jpg&quot; height=&quot;200&quot; width=&quot;200&quot; alt=&quot;bolt&quot; /&gt; You may not realize it, but it is a modern marvel. It is a 150mm bolt with an M20 threading. It works in any piece of hardware that is cut with the same size and threading. It can be turned with any 30mm hex wrench. You can reach into a pile of M20-2.50 nuts, made by any manufacturer, pull any one out, and it will fit this bolt.&lt;/p&gt;
&lt;p&gt;This is because of a key principle that made the industrial revolution possible: &lt;a href=&quot;https://en.wikipedia.org/wiki/Interchangeable_parts&quot;&gt;interchangeable parts&lt;/a&gt;. It took countless inventors over the course of hundreds of years to perfect the manufacturing necessary for this. Many people, such as Eli Whitney and Samuel Colt, devoted years of their lives and a great amount of resources in the quest to achieve it.&lt;/p&gt;
&lt;p&gt;Before their efforts, everything had to be hand-made for the particular project. Every gun was uniquely crafted. Every piece of rigging on a ship. And if a part broke, you couldn’t just buy a replacement; one had to be hand-made to fit the parts it integrated with… or the entire device had to be replaced.&lt;/p&gt;
&lt;h2 id=&quot;learn-from-history&quot; tabindex=&quot;-1&quot;&gt;Learn from history &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/02/a-quest-for-interchangeable-parts/#learn-from-history&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a tweet. &lt;img src=&quot;https://keithjgrant.com/images/2016/tweet.png&quot; alt=&quot;screenshot of tweet&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You may not realize it, but it is stuck in the pre-industrial age. Because it is not an interchangeable part. It doesn’t fit into a Facebook wall, or onto a YouTube page.&lt;/p&gt;
&lt;p&gt;Sure, it’s &lt;em&gt;almost&lt;/em&gt; interchangeable. You can post a link to its url in most places, or a screenshot of the tweet. Or you can copy the text and give attribution. But it does not interoperate with other social networks. You can’t retweet it or like it from anywhere but a limited number of places: an embedded tweet (put there by a script provided by Twitter) or a third-party app that uses Twitter’s API. This is because Twitter has a vested interest in keeping a tight reign on where and how a tweet can, and cannot, be used.&lt;/p&gt;
&lt;p&gt;Our digital world is full of these semi-interchangeable parts. You can buy an album from iTunes or Amazon or Google Play. However, if you assemble your music collection using two or more of these, you have to jump through some hoops to keep it all in one place and to sync it to your phone.&lt;/p&gt;
&lt;p&gt;You can subscribe to Hulu and Netflix and Amazon Prime, but you can’t just tune into them on any TV: you need to use something like a Roku that brings them all together. Nothing is standardized.&lt;/p&gt;
&lt;h2 id=&quot;a-better-way&quot; tabindex=&quot;-1&quot;&gt;A better way &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/02/a-quest-for-interchangeable-parts/#a-better-way&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Netflix has to build an app for iOS, an app for Android, an app for Roku and Apple TV and the Amazon Fire Stick. They do all this just so users of those devices can tune in to their service. What if, instead, they just produced their content in a single, standardized web format. Imagine if HBO and Hulu broadcast in the same format. Imagine if subscribers merely had to tune in their TVs to the right “channel” watch. No Apple TV or Roku necessary. Like broadcast television.&lt;/p&gt;
&lt;p&gt;iTunes keeps an iron grip on the whole pipeline of your music: the financial transaction, the download, the files on your hard drive, the sync to your Apple device. What if, instead, iTunes or Amazon were just a store, and they all provided music in the same format for you to save wherever you choose, and sync however you choose. Like it a CD.&lt;/p&gt;
&lt;p&gt;What if, instead of “following” your friend Joseph on Twitter, you could just follow &lt;em&gt;Joseph&lt;/em&gt;. He could tweet on Twitter or post to Facebook and you would get it, on the social network of your choosing, that offers the controls and options you like. There would be a single, common digital format for a social media post, that worked on all networks. You could be on Twitter or Google Plus, and read his updates, wherever they came from. Like email.&lt;/p&gt;
&lt;h2 id=&quot;an-open-web&quot; tabindex=&quot;-1&quot;&gt;An open web &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/02/a-quest-for-interchangeable-parts/#an-open-web&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unlike the centuries-long quest for interchangeable parts in manufacturing, there is nothing technically holding us back from this. We might need to hash out what these standards look like, but that is relatively easy. The forces holding us back are more capitalistic—or rather, &lt;em&gt;monopolistic&lt;/em&gt;. Because the world of open standards I just described would be a much more open, free market: a level playing field where far more companies could innovate and compete, rather than just a small handful of big players. It would be a world where users had more choices and more control over their media, and where they didn’t have to put up with so much headache.&lt;/p&gt;
&lt;p&gt;Has the digital age made life easier? Absolutely. I don’t want to go back to broadcast television or CDs. While email still serves a purpose, it’s different than that of social media.&lt;/p&gt;
&lt;p&gt;But the digital age has still not matured. We need interchangeable parts. It will take some concerted effort to break through the barriers holding us back. I admit don’t know precisely how to do that. But, just as we pushed through the difficulties that held back physical manufacturing in centuries past and ushered in the industrial revolution, we can break through these, and usher in a new digital revolution. The modern digital life is still more complicated than it need be.&lt;/p&gt;
&lt;p&gt;The Internet may be the first true write-once-run-anywhere platform, but as long as these industries are shackled by proprietary lock-in, these parts of the web aren’t open. An open web requires interchangeable parts.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Width and Absolute Positioning</title>
    <link href="https://keithjgrant.com/posts/2016/01/width-and-absolute-positioning/"/>
    <published>2016-01-30T05:00:00Z</published>
    <updated>2016-01-30T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/01/width-and-absolute-positioning/</id>
    <content xml:lang="en" type="html">&lt;p&gt;I recently came across this question on Twitter:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;What&amp;#39;s the diff on a position:absolute el btw {top:0;right:0;bottom:0;left:0;} and {top:0;left:0;height:100%;width:100%;} ?&lt;/p&gt;&amp;mdash; Karl Swedberg (@kswedberg) &lt;a href=&quot;https://twitter.com/kswedberg/status/692720642580295681&quot;&gt;January 28, 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;!--more--&gt;
&lt;p&gt;“That’s easy”, I thought. “They often seem the same in practice, but width and height are based on the parent (or nearest block-level ancestor). Top, right, bottom, and left are based on the nearest &lt;em&gt;positioned&lt;/em&gt; ancestor. Those aren’t necessarily the same element.”&lt;/p&gt;
&lt;p&gt;Shows what I know: my &lt;a href=&quot;http://codepen.io/keithjgrant/pen/obddgy&quot;&gt;quick mockup&lt;/a&gt; to illustrate the difference proved me wrong.&lt;/p&gt;
&lt;p&gt;On first pass, &lt;code&gt;top: 0; right: 0; bottom: 0; left: 0;&lt;/code&gt; and &lt;code&gt;top: 0; left: 0; width: 100%; height: 100%;&lt;/code&gt; are equivalent. Of course, we always &lt;em&gt;treat&lt;/em&gt; them as equivalent, because in most real-world cases, the nearest positioned ancestor is the nearest block-level element (usually the parent). But my codepen showed me an absolutely positioned element whose width and height are derived from the positioned ancestor, not the immediate container.&lt;/p&gt;
&lt;p&gt;In retrospect, this makes a certain kind of sense, because the absolutely positioned element is removed from the normal flow. It’s then “contained” by the positioned ancestor.&lt;/p&gt;
&lt;p&gt;So, the two scenarios are effectively the same. But a quick look at the spec made it clear that padding and margin would weigh in. So it might worth a little experimenting to see what we get.&lt;/p&gt;
&lt;h2 id=&quot;experimenting&quot; tabindex=&quot;-1&quot;&gt;Experimenting &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/01/width-and-absolute-positioning/#experimenting&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So here’s the basic scenario I set up. Three elements: one relatively positioned as the anchor for the absolute positioning, one to serve as the parent, and then the child, which will be absolutely positioned. For better visibility, the positioned container has a faint background color, the parent has a purple border, and the child has a medium gray background (transparent, so the others will show through).&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;positioned&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.positioned&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.parent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px solid #936&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.child&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we’ll play around with the child’s properties to see what happens. We’ll create two variations. First, one with top, right, bottom, and left all set to zero. And second, one with a top and left of zero, and a width and height of 100%.&lt;/p&gt;
&lt;div class=&quot;demo demo--basic&quot;&gt;
  &lt;div class=&quot;demo-col&quot;&gt;
    &lt;pre class=&quot;prettyprint&quot;&gt;.child {
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
&lt;/pre&gt;
    &lt;div class=&quot;demo-1 positioned&quot;&gt;
      &lt;div class=&quot;parent&quot;&gt;
        &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;demo-col&quot;&gt;
    &lt;pre class=&quot;prettyprint&quot;&gt;.child {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}&lt;/pre&gt;
    &lt;div class=&quot;demo-2 positioned&quot;&gt;
      &lt;div class=&quot;parent&quot;&gt;
        &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The two children appear identical. They overflow outside the parent, and fill the size of the positioned container. Visually, you can’t tell the difference between the container and the child. Let’s add some padding to the child and see what happens…&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
  .child {
    padding: 1em;
  }
&lt;/pre&gt;
  &lt;div class=&quot;demo demo--padding&quot;&gt;
    &lt;div class=&quot;demo-col&quot;&gt;
      &lt;div class=&quot;demo-1 positioned&quot;&gt;
        &lt;div class=&quot;parent&quot;&gt;
          &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;demo-col&quot;&gt;
      &lt;div class=&quot;demo-2 positioned&quot;&gt;
        &lt;div class=&quot;parent&quot;&gt;
          &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;p&gt;…nothing. Note, I’ve got &lt;code&gt;box-sizing: border-box&lt;/code&gt; set globally on the page. Let’s turn that off:&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
  .child {
    box-sizing: content-box;
    padding: 1em;
  }
&lt;/pre&gt;
&lt;div class=&quot;demo demo--padding-2&quot;&gt;
  &lt;div class=&quot;demo-col&quot;&gt;
    &lt;div class=&quot;demo-1 positioned&quot;&gt;
      &lt;div class=&quot;parent&quot;&gt;
        &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;demo-col&quot;&gt;
    &lt;div class=&quot;demo-2 positioned&quot;&gt;
      &lt;div class=&quot;parent&quot;&gt;
        &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The padding still has no effect on the size of the first variation. But it adds to the width and height in the second; that child now overflows the positioned container. That makes sense. So there’s a key difference here, if you don’t have a global box-sizing fix in place.&lt;/p&gt;
&lt;p&gt;Now let’s see what a margin does:&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
  .child {
    margin: 1em;
  }
&lt;/pre&gt;
&lt;div class=&quot;demo demo--margin&quot;&gt;
  &lt;div class=&quot;demo-col&quot;&gt;
    &lt;div class=&quot;demo-1 positioned&quot;&gt;
      &lt;div class=&quot;parent&quot;&gt;
        &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;demo-col&quot;&gt;
    &lt;div class=&quot;demo-2 positioned&quot;&gt;
      &lt;div class=&quot;parent&quot;&gt;
        &lt;div class=&quot;child&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now that was unexpected, at least to me. With &lt;code&gt;right: 0; bottom: 0&lt;/code&gt;, the margin is contained inside the positioned descendant; they will shrink the size of the element.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;width: 100%; height: 100%&lt;/code&gt;, the top and left margins are contained, but the element is shifted, maintaining its original size. The right and bottom margins are then added outside the whole structure.&lt;/p&gt;
&lt;p&gt;What’s interesting is that in all these scenarios, the parent element doesn’t affect the shape of the child element at all—Though it does provide the height to the positioned container.&lt;/p&gt;
&lt;h2 id=&quot;take-away&quot; tabindex=&quot;-1&quot;&gt;Take-away &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2016/01/width-and-absolute-positioning/#take-away&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So what does this mean? It seems to me that &lt;code&gt;top: 0; right: 0; bottom: 0; left: 0&lt;/code&gt; is probably the one to favor, as it’s a little more predictable, unless you have a particular reason to use height or width instead.&lt;/p&gt;
&lt;style type=&quot;text/css&quot;&gt;
  .demo-col {
    margin: 1em 0;
  }
  @media screen and (min-width: 900px) {
    .demo {
      display: flex;
      flex-flow: space-between;
      margin: 1em 0;
    }

    .demo-col {
      flex: 1;
      margin: 0;
    }
    .demo-col:not(:first-child) {
      margin-left: 3em;
    }
  }

  .demo .positioned {
    position: relative;
    background-color: rgba(0,0,255, 0.1);
  }
  .demo .parent {
    width: 200px;
    min-height: 200px;
    border: 2px solid #936;
  }
  .demo .child {
    position: absolute;
    top: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.4);
  }

  .demo-1 .child {
    right: 0;
    bottom: 0;
  }

  .demo-2 .child {
    width: 100%;
    height: 100%;
  }

  .demo--padding .child {
    padding: 1em;
  }

  .demo--padding-2 {
    margin-bottom: 2em;
  }
  .demo--padding-2 .child {
    box-sizing: content-box;
    padding: 1em;
  }

  .demo--margin .child {
    margin: 1em;
  }
&lt;/style&gt;
</content>
  </entry>
  <entry>
    <title>Simply White</title>
    <link href="https://keithjgrant.com/posts/2016/01/simply-white/"/>
    <published>2016-01-27T05:00:00Z</published>
    <updated>2016-01-27T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2016/01/simply-white/</id>
    <content xml:lang="en" type="html">&lt;p&gt;&lt;a href=&quot;http://www.benjaminmoore.com/en-us/for-your-home/benjamin-moore-color-trends-2016&quot;&gt;Benjamin
Moore&lt;/a&gt; has declared the Color of the Year for 2016 to be “simply white”. We have reached peak
minimalism, y’all.&lt;/p&gt;
&lt;p&gt;So I’m steering into the skid. Here’s my new design.&lt;/p&gt;
&lt;img src=&quot;https://keithjgrant.com/images/2016/simply-white.png&quot; alt=&quot;screenshot of &#39;Simply White&#39; design&quot; /&gt;
</content>
  </entry>
  <entry>
    <title>CSS First</title>
    <link href="https://keithjgrant.com/posts/2015/08/css-first/"/>
    <published>2015-08-01T15:00:00Z</published>
    <updated>2015-08-01T15:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2015/08/css-first/</id>
    <content xml:lang="en" type="html">&lt;p&gt;In 2003, the &lt;em&gt;CSS Zen Garden&lt;/em&gt; went live, and it spearheaded a revolution. At the time, many web designers were still using tables for layouts, and the battle for semantic markup was underway. The Zen Garden showed the world, in beautiful color, what CSS could do. By changing the CSS, you could make the website retro, postmodern, abstract, or elegant. You could move the sidebar to the left, the right, the top, or the bottom of the page. You could do anything with the font, the colors, the shapes and sizes of the elements on the screen. No changes to the HTML were necessary.&lt;/p&gt;
&lt;p&gt;What we didn’t realize at the time was how much of a disservice this was for the next generation of designers and developers.&lt;/p&gt;
&lt;p&gt;Don’t get me wrong, the CSS Zen Garden was an essential part of moving the web forward. Without it, we might have seen table-based designs for much longer than we did. It did a lot of good for us, but it left us with one bad habit: it taught us to write HTML (or take existing HTML), and then write CSS for it. But this is wrong. The HTML shouldn’t come first; the CSS should.&lt;/p&gt;
&lt;h2 id=&quot;css%3A-your-first-dependency&quot; tabindex=&quot;-1&quot;&gt;CSS: your first dependency &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/08/css-first/#css%3A-your-first-dependency&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many web developers still try to think about how to write their HTML so that it can be sensibly targeted by their CSS. Instead, we aught to be writing selectors that can be used easily by HTML. The HTML depends upon the CSS, not the other way around.&lt;/p&gt;
&lt;p&gt;CSS is the first dependency of your website or web application. The HTML expects it to be there; the JavaScript expects it to be there. Certain things are supposed to happen when they add or remove classes. CSS should be modular enough that it can be reused on a number of different web pages, and ideally even across different applications if needed. If you are using a modern practice like &lt;a href=&quot;https://smacss.com/&quot;&gt;SMACSS&lt;/a&gt; or BEM, this thinking is probably not new to you, but it’s worth unpacking.&lt;/p&gt;
&lt;p&gt;Let’s look at a little CSS. The familiar &lt;a href=&quot;http://www.stubbornella.org/content/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/&quot;&gt;media object&lt;/a&gt; will do:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.media::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* clearfix */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; both&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.media-object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.media-body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is designed for the &lt;code&gt;media&lt;/code&gt; class to go on a container div, and the &lt;code&gt;media-object&lt;/code&gt; and &lt;code&gt;media-body&lt;/code&gt; to go on two child elements. Most likely these will be an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, but you could probably change that up a bit. This can be placed inside other modules, and other modules can be placed inside of this module’s media-body.&lt;/p&gt;
&lt;p&gt;These class names and small bit of implied DOM structure dictate an API contract with the HTML and JavaScript. This way, it’s reusable. You can put as many, or as few, of these on the page as you need, and the CSS never needs to change. Other modules might come with various states: classes like &lt;code&gt;is-open&lt;/code&gt;, &lt;code&gt;is-active&lt;/code&gt;. JavaScript can toggle them and allow the CSS to show, hide, highlight, or do whatever else it needs to do. It’s all part of the API.&lt;/p&gt;
&lt;p&gt;This is the secret to separating the concerns of CSS from those of HTML and JavaScript. The CSS specifies the rules, and it’s up to the other two to follow them. It’s a one-way contract.&lt;/p&gt;
&lt;p&gt;We tried for years to do it the other way around, and that’s what got us into trouble. When you approach web development HTML first, the HTML inevitably becomes coupled with the CSS. The CSS Zen Garden model works… until the HTML changes. And in in the real world, the HTML changes all the time. If the CSS is based on that HTML, the CSS will have to change with it.&lt;/p&gt;
&lt;p&gt;Instead, by developing the CSS first, in a way that is modular, we can apply it again and again to changing HTML, without needing to make changes upstream. The HTML just has to follow the conventions laid out by the API.&lt;/p&gt;
&lt;p&gt;To do this, we need to develop our CSS as if it is for a third party, similar to Bootstrap and other CSS frameworks. If your project (or your team) is moderate to large size, this will begin to necessitate two important features for our CSS: it should be well documented, and it should be versioned.&lt;/p&gt;
&lt;h2 id=&quot;documentation-driven&quot; tabindex=&quot;-1&quot;&gt;Documentation-driven &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/08/css-first/#documentation-driven&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ugh. Documentation. The longest four-letter word in a developer’s life. Bear with me on this.&lt;/p&gt;
&lt;p&gt;Since you are producing an API that other developers will consume, it needs to be documented. That means show each module in action so developers can skim through and find the pieces they need. Publish static HTML pages that organizes them in a reasonable way. Show the variants available. Add some bare-bones JavaScript to illustrate changing states and transition effects. Your CSS will eat its own dog food before you ever use it in your app.&lt;/p&gt;
&lt;p&gt;At my job, we document our styles using &lt;a href=&quot;http://jade-lang.com/&quot;&gt;Jade&lt;/a&gt;, and have some mixins that duplicate portions of markup: once to render on the page, and the second time to print the markup into a &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; tag for reading. But for smaller projects, this may not be necessary; browser dev tools make it easy enough for a developer look under the covers at any of your examples.&lt;/p&gt;
&lt;p&gt;This documentation serves double-duty: it also becomes your unit tests. Every module you build, and the variations thereof, are rendered into a page. Package your documentation with your CSS. Write the documentation for a module as you write the CSS. See it in action during development.&lt;/p&gt;
&lt;p&gt;If you need to verify multiple permutations of module states or test how various modules work together, add appendices to your documentation to do these things, where you won’t bog down developers who are referring to the main docs. Our documentation at work has a series of fully-mocked up static pages (to ensure everything works together as expected) and several “test” pages where our grid system is put through paces and various table styles are mixed and matched in numerous ways to ensure they all work well together.&lt;/p&gt;
&lt;p&gt;Unit tests in CSS are notoriously difficult to do. Some people use tools that take screenshots and compare for changes, but this is prone to all sorts of false positives. Some rely on linting, but that only gets you so far. If you write documentation as you develop, it serves the same purpose. Even better, eyes are on your tests, because developers and content editors will refer to it regularly.&lt;/p&gt;
&lt;h2 id=&quot;versioned&quot; tabindex=&quot;-1&quot;&gt;Versioned &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/08/css-first/#versioned&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Second, version your styles using &lt;a href=&quot;http://semver.org/&quot;&gt;semver&lt;/a&gt;. Your HTML is opting-in to your CSS. You can’t just change things out from underneath it. Deploy your styles to a url with the version in the path, so you always know what you’re getting. Alternately, package your styles in a way that your project can import them at a certain version, whether an npm module or Ruby gem or similar.&lt;/p&gt;
&lt;p&gt;Dead code elimination is a tough problem with CSS. If your CSS is versioned, you can deprecate and delete features with much lower risk.&lt;/p&gt;
&lt;p&gt;Publish a changelog. This can make developers aware of new features, as well as give them a heads up when you deprecate something.&lt;/p&gt;
&lt;h2 id=&quot;write-the-css-in-a-clean-room&quot; tabindex=&quot;-1&quot;&gt;Write the CSS in a clean-room &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/08/css-first/#write-the-css-in-a-clean-room&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;CSS first&lt;/em&gt; doesn’t mean you write all of your CSS before you write any HTML or JavaScript. It applies at a finer-grained level. Each module is first built in the CSS before it can be used in the HTML. When you need styles in your app or website, your first stop should be the existing CSS documentation. Look for something that already meets your needs. Once you have built up a robust toolset of styles, you will often find you don’t need something new after all.&lt;/p&gt;
&lt;p&gt;Only when nothing there fits your situation should you start coding new CSS. Once it’s written, and “tested” (i.e. added to the documentation), bump the version and cut a new release of the CSS.&lt;/p&gt;
&lt;p&gt;This adds a small obstacle to adding new styles, which is intentional. It forces you to stop and think about your CSS outside the context of your particular problem, and encourages you to write a more general-use module. It also encourages developers to use existing styles as much as possible before rolling their own, which will result in a more cohesive look and feel throughout your site.&lt;/p&gt;
&lt;p&gt;This is probably overkill for small projects. I certainly don’t jump through all these hoops for this blog. But the more your project scales up, the more essential they will become, and the more your team will benefit from them.&lt;/p&gt;
&lt;p&gt;The API of your CSS will be much more robust as a result of this work. That API is important. The inner-workings of your CSS are easy to change. Your HTML is easy to change. But that API between them is the most important part. Making significant changes to that is costly, because everything on both sides needs to be updated accordingly. Design that API well. Design it from the viewpoint of the CSS, and then build your HTML to match.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Into the future of CSS</title>
    <link href="https://keithjgrant.com/posts/2015/05/into-the-future-of-css/"/>
    <published>2015-05-30T05:00:00Z</published>
    <updated>2015-05-30T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2015/05/into-the-future-of-css/</id>
    <content xml:lang="en" type="html">&lt;p&gt;In my &lt;a href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js.html&quot;&gt;last post&lt;/a&gt;, I laid out why I think moving our CSS into JavaScript is not a good idea. If am totally honest, however, I have to admit I brushed off the concerns &lt;a href=&quot;https://twitter.com/vjeux&quot;&gt;Christopher Chedeaux&lt;/a&gt; raises in his slidedeck. The seven problems he named can be mitigated by best practices, and most of us can do just fine, because we do not often work in apps of the same scale as Facebook or Google. But that does not mean the problems aren’t worth discussion.&lt;/p&gt;
&lt;p&gt;I may disagree with the solution proposed by the ReactJS team, but I think they are doing something very important by bringing these problems to the forefront. I shot down their ideas in my last post, so now I want to follow up with some constructive ideas.&lt;/p&gt;
&lt;h2 id=&quot;a-way-forward&quot; tabindex=&quot;-1&quot;&gt;A Way Forward &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/into-the-future-of-css/#a-way-forward&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We need to figure out how to solve these problems. Not just mitigate them or work around them, but full solutions that work at scale. And if the current spec does not provide the means, we should work to discover what additions to the spec can get us there.&lt;/p&gt;
&lt;p&gt;I think our best bet is learning from JavaScript, or more specifically, CoffeeScript and Babel. Now that ES6 features are becoming supported more and more in browsers, we can see that CoffeeScript played an important role in making that happen. CoffeeScript brought new syntax and important improvements like auto bound functions (i.e. &lt;code&gt;=&amp;gt;&lt;/code&gt;) and, thanks to a transpiler, made them possible even before browsers supported them. Now, some of these features have made their way into ES6 and with them, a breath of new life. Even more new features are being proposed, and we can use them today with Babel.&lt;/p&gt;
&lt;p&gt;We already have two promising libraries, doing for CSS what Babel does for JavaScript: &lt;a href=&quot;https://github.com/postcss/postcss&quot;&gt;PostCSS&lt;/a&gt; and &lt;a href=&quot;http://cssnext.io/&quot;&gt;cssnext&lt;/a&gt;. These transpile CSS using the latest spec into compatible CSS that works in browsers today. This is huge. Things that we’ve relied on SASS and LESS for can now be done in “pure” CSS, namely variables and advanced color functions. They also include support for things like custom media queries and custom selectors. If you are not familiar with either of these libraries, they are worth reading up on.&lt;/p&gt;
&lt;p&gt;The problem is these additions to spec are not all that exciting. Variables and color functions are nice conveniences, but they do little to solve the real difficulties we face with CSS at scale. I think, instead of emulating Babel, we need to emulate CoffeeScript. It pushed the boundaries of JS, and now, as a result, 60% of ES6 features came straight from CoffeeScript. Let’s do the same thing with CSS. Think outside the box, and dream up new features to help. PostCSS is especially good for this, because it is modular. We just need to start writing some experimental plugins.&lt;/p&gt;
&lt;h2 id=&quot;scoped-css&quot; tabindex=&quot;-1&quot;&gt;Scoped CSS &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/into-the-future-of-css/#scoped-css&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One cutting-edge feature that I think has promise is &lt;a href=&quot;http://davidwalsh.name/scoped-css&quot;&gt;scoped css&lt;/a&gt;. This allows us to add a “scoped” attribute to a style tag, and those styles will only be applied to the parent element of the style tag and its descendants. For example:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;outer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Normal black text&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;inner&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
      &lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Red text!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Normal black text&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These can be nested, and scoped elements can even be targeted in our regular stylesheet with the &lt;code&gt;:scoped&lt;/code&gt; pseudo-class. Scoped styles override styles from a higher-up scope or the global page, regardless of selector specificity.&lt;/p&gt;
&lt;p&gt;The spec for this was proposed in 2011, and not much has happened with it since then. Only Firefox has added support. Chrome added support behind an experimental features flag—but has since removed it. There are polyfills available, but as it is, this has not gone very far, and it’s not hard to see why. Who wants to write a bunch of CSS inline in the document? That feels more like a step backwards.&lt;/p&gt;
&lt;p&gt;So, here’s an idea. What if we assigned a value to the scoped attribute, &lt;code class=&quot;prettyprint&quot;&gt;&amp;lt;style scoped=“widget”/&amp;gt;&lt;/code&gt;, and then create an at-rule to define a named scope, maybe something like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@scope&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;widget&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would define a set of scoped styles and assign them a scope name, then we can use that name to apply those styles to our component wherever we put it on the page. Then we could combine a PostCSS plugin with a polyfill to make it work. Again, this is just an idea. I don’t really know how well it will solve our difficulties in practice, but I would sure like to try. It sure seems to have promise at addressing nearly all of Christopher Chedeaux’s seven problems with CSS at scale.&lt;/p&gt;
&lt;h2 id=&quot;let%E2%80%99s-start-experimenting&quot; tabindex=&quot;-1&quot;&gt;Let’s Start Experimenting &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/into-the-future-of-css/#let%E2%80%99s-start-experimenting&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Maybe my enhanced scope idea won’t work out. Maybe someone else will come up with a better idea. Either way, I think we need to try. Perhaps there is something to be found if we augmented &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@namespace&quot;&gt;@namespace&lt;/a&gt; instead. Maybe the answer lies with the shadow DOM and web components, or something else entirely new.&lt;/p&gt;
&lt;p&gt;My hope here is not that we come up with a postprocessor or polyfill library that makes CSS easier, though that is the first step. My hope is ultimately that we find a way to solve the problems we have, and that the best solution makes its way into the official spec, after we prove it out on production sites. Who knows? Maybe if PostCSS or cssnext keeps gaining traction, we will invent entirely new polyfills for the future that address any number of issues. JavaScript has got a lot of momentum moving forward. It’s time for CSS to do the same.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Against CSS in JS</title>
    <link href="https://keithjgrant.com/posts/2015/05/against-css-in-js/"/>
    <published>2015-05-29T05:00:00Z</published>
    <updated>2015-05-29T05:00:00Z</updated>
    <id>https://keithjgrant.com/posts/2015/05/against-css-in-js/</id>
    <content xml:lang="en" type="html">&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Some folks want a unified language of the web instead of CSS, HTML, and JS.&amp;#10;&amp;#10;It&amp;#39;s increasingly looking like JS will just eat the other two.&lt;/p&gt;&amp;mdash; Henrik Joreteg (@HenrikJoreteg) &lt;a href=&quot;https://twitter.com/HenrikJoreteg/status/603959629425483776&quot;&gt;May 28, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;I have long believed that pieces of our &lt;a href=&quot;https://www.youtube.com/watch?v=x7cQ3mrcKaY&quot;&gt;JavaScript are intimately coupled with the DOM&lt;/a&gt;, especially in the context of web apps. For far too long, we waved our hands and pretended we have a separation of concerns simply because our HTML is in one file and our JavaScript is in another. Not only did this avoid the problem, I think it actually made it worse, because we had to write more and more complicated code to try and abstract away this coupling.&lt;/p&gt;
&lt;p&gt;This coupling is real, and it is unavoidable. We must bind event listeners to elements on the page. We must update elements on the page from our JavaScript. Our code must interact bidirectionally and in real-time with the elements of the DOM. If it doesn’t… then we just have static HTML. Think about it, can you just open up your HTML and change around class names or ids without breaking anything? Of course not. You have to pull up your scripts and see which of those you need to get a handle on various DOM nodes. Likewise, when you make changes to your JavaScript views, you inevitably need to make changes to the markup as well; add a class or id so you can target an element; wrap an extra div around a block so you can animate it a certain way. This is the very definition of tight coupling. You must have an intimate knowledge of both in order to safely make any substantive changes to either.&lt;/p&gt;
&lt;p&gt;Instead, the mantra of React is to stop pretending the DOM and the JavaScript that controls it are separate concerns. Join the two, and instead, separate concerns that are actually different: the dropdown menu is separate from the list of objects; the modal dialog box is separate from the page footer. Why on earth would you put these all in the same HTML document? Obviously, you shouldn’t.&lt;/p&gt;
&lt;p&gt;When React came out, I was more than happy to embrace their controversial claim that pieces of our DOM should live in the JavaScript—because I already knew it to be true. They are the same concern, and every attempt we have ever made at separating them has ended in disaster. Just think about the last time you had to deal with Backbone Views. It actually makes me feel sick to my stomach to think about. There was so much work for so little gain, and it was all boilerplate code to cross that barrier into DOM land.&lt;/p&gt;
&lt;h2 id=&quot;so-what-about-css%3F&quot; tabindex=&quot;-1&quot;&gt;So What About CSS? &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/#so-what-about-css%3F&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This made sense once, so, naturally, there is talk about doing the same thing with CSS. We brought our HTML (sort of) into our JavaScript and it cleaned a lot of things up, why not &lt;a href=&quot;https://medium.com/javascript-scene/jsx-looks-like-an-abomination-1c1ec351a918#1e2d&quot;&gt;do it with our styles&lt;/a&gt; as well?&lt;/p&gt;
&lt;p&gt;This is a really bad idea. Where, with JSX, my reaction was, “Yes, finally!”, my response here is quite the opposite. I’ve had a hard time articulating exactly why I respond this way, especially on the length-constrained and fast-paced atmosphere of Twitter, so I’m setting out now to articulate my reasons here.&lt;/p&gt;
&lt;p&gt;Before I dive in too deep, I do want to say I have interacted some with React Native folks on Twitter regarding this. I acknowledge that their work is primarily in the realm of native applications, not in the context of the web. I think this has ramifications for their use case that I myself am not familiar with. My thoughts may be applicable to them, and they may not; I’m not well-versed in that world enough to speak to them directly. But I do hope others take note of that distinction before they clamber to repeat these practices on the web.&lt;/p&gt;
&lt;h2 id=&quot;we-are-not-solving-the-same-problem-as-before&quot; tabindex=&quot;-1&quot;&gt;We Are Not Solving the Same Problem As Before &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/#we-are-not-solving-the-same-problem-as-before&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The relationship between CSS and JavaScript is not like the relationship between HTML and JavaScript. With HTML, a true separation of concerns between the markup and the corresponding component code is impossible. With CSS, this separation is possible and is in fact vital to clean code organization.&lt;/p&gt;
&lt;p&gt;Your CSS should be wholly independent from your markup (and especially independent from your JavaScript). If you find, as &lt;a href=&quot;http://programmers.stackexchange.com/questions/271294/why-is-it-or-was-it-important-to-separate-css-from-html&quot;&gt;this stackexchange user observed&lt;/a&gt;—that your “commit history shows me the opposite - [you] usually edit both HTML and CSS together”—then you are doing CSS wrong. CSS should not depend on the markup; the markup should depend on the CSS. Or, more accurately, it should depend on the modular, reusable API defined by the CSS.&lt;/p&gt;
&lt;p&gt;If you use the best practices of &lt;a href=&quot;http://oocss.org/&quot;&gt;OOCSS&lt;/a&gt;, &lt;a href=&quot;https://smacss.com/&quot;&gt;SMACSS&lt;/a&gt;, or BEM, you will not need to edit the CSS every time you edit a page. In fact, once you have your basic building blocks of CSS defined, you can build out all sorts of things in the markup before you need to touch the styles again. Far too often, developers write their CSS and HTML together, with selectors that mimic the structure of the DOM. If one changes, then the other must change with it: a tight coupling.&lt;/p&gt;
&lt;p&gt;However, moving the styles into the JavaScript does not address this problem at all. When we moved our HTML into our React components, we redefined which concerns we were separating. Each component is now a unique concern. When we bring CSS into that, we are not further breaking up our concerns; we are just adding more responsibilities to the concerns we already have.&lt;/p&gt;
&lt;p&gt;In a best-case scenario, with well organized code, we require in a style module into our React component and add it to the props. This is roughly equivalent to adding a class name that references a stylesheet–there is no net gain in terms of separation of concerns. But, we don’t always work in a best-case scenario. Now that our JavaScript has taken responsibility for styling, it will have to take care of things that we take for granted with CSS. This will add unnecessary bloat to your codebase, and make separation of concerns more difficult.&lt;/p&gt;
&lt;p&gt;If your stylesheets are well organized and written with best practices, there is no bi-directional dependency between them and the HTML. So we do not need to solve the same problem with our CSS that we had to solve with our markup.&lt;/p&gt;
&lt;h2 id=&quot;beware-framework-lock-in&quot; tabindex=&quot;-1&quot;&gt;Beware Framework Lock-In &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/#beware-framework-lock-in&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CSS is universal. If your page has stylesheets, that’s cool, because everyone’s page has stylesheets. Every JavaScript library and every HTML templating language works with CSS. If you shift all of your styles into JavaScript, you are going to be locked into using JavaScript libraries that can use them. Your styles no longer stand on their own.&lt;/p&gt;
&lt;p&gt;If in 14 months you find a new view library or framework you want to try out, you’re out of luck. You will have to invest a lot of time into pulling styles back out of JavaScript modules and into stylesheets again. The most likely scenario is you would only bring out a portion of it—just enough to use in the components you are building with the new library. Sure, you may continue to make progress over time pulling styles back into regular stylesheets, but during that process, you’re going to have duplication of code, and you may even find yourself running into scenarios where the CSS has to be completely re-written to replicate the behavior you added in JavaScript.&lt;/p&gt;
&lt;h2 id=&quot;all-or-nothing&quot; tabindex=&quot;-1&quot;&gt;All or Nothing &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/#all-or-nothing&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you start bringing your styles into your JavaScript, where do you stop? If you only bring in some of them, how are you going to define that boundary? When you need to change something in the look and feel, do you know whether you need to open up your styles.css or your styles.js? A logical solution here is to bring in all of it and abondon stylesheets altogether.&lt;/p&gt;
&lt;p&gt;The problem with that is, CSS takes care of some things cleanly for us that you are probably taking for granted. This is helpful for fonts in particular: The default font size, the font face and line-height, and the color of your font are all inherited silently from one of the topmost elements on the page. Do you want to explicitly set these things on every single component you build? Because you will have to.&lt;/p&gt;
&lt;p&gt;Not only that, but if you distribute your component, the users of that component need to be all-in on the same paradigm, as well. Your component sets inline styles, which means they can’t be overridden without &lt;code&gt;!important&lt;/code&gt;. To get around this problem, you can allow the user to pass in their own styles, but this means the user of your component must have their styles defined in their JavaScript as well. What if they want to use traditional stylesheets? You’re forcing their hand.&lt;/p&gt;
&lt;h2 id=&quot;missing-css-features&quot; tabindex=&quot;-1&quot;&gt;Missing CSS Features &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/#missing-css-features&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think there is a place for a few inline styles in a React component. I’ll admit I’ve had to do it a few times. Most often, though, it is because I need to accomplish something that can’t be done in pure CSS (yet), such as animating from &lt;code&gt;height: 0&lt;/code&gt; to &lt;code&gt;height: auto&lt;/code&gt;. Animation is a bit of a different topic here, but, honestly, even that is invariably easier to do when it can be accomplished with pure CSS. The power of transitions and keyframe animation is much harder to replicate in JavaScript without the help of yet another dependency. The browser already has this stuff built in. Take advantage of it. It is so much easier to just toggle a classname and not have to think about it when I’m trying to focus on another problem.&lt;/p&gt;
&lt;p&gt;You also lose the ability to do some other things, as well, like media queries and fallback values for older browsers. Unless you want to start browser sniffing and adding conditionals around your styles. Again, this is adding more responsibilities to your components that otherwise they would not have.&lt;/p&gt;
&lt;h2 id=&quot;in-defense-of-css&quot; tabindex=&quot;-1&quot;&gt;In Defense Of CSS &lt;a class=&quot;header-anchor&quot; href=&quot;https://keithjgrant.com/posts/2015/05/against-css-in-js/#in-defense-of-css&quot;&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point, I have to ask: what problem are we trying to solve? There is a lot of baggage and many unknowns if we start doing CSS-in-JS. We had better have a damn good reason before we go down that road. Christopher Chedeau, in the &lt;a href=&quot;https://speakerdeck.com/vjeux/react-css-in-js&quot;&gt;original slidedeck&lt;/a&gt; that introduced this idea, outlines seven “problems” with CSS. The thing is, they are solvable problems. If we as web developers just understand CSS better, and take the time to learn modern best-practices like SMACSS and BEM, these issues almost entirely dissolve, especially at the scale most of us work at. Don’t get me wrong; he does make some good points that are worth discussing, but I believe we have at least partial solutions for all of them (some of the latest proposals to the CSS spec help, as well). If you want to discuss those points directly, feel free to ping me on &lt;a href=&quot;https://twitter.com/keithjgrant&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many web developers fear CSS. They fear it because the do not understand it. And I am worried that, in that fear, we will collectively rush to move our styles into JavaScript in hopes that it solves our problems. It won’t.&lt;/p&gt;
&lt;p&gt;It’s time to truly &lt;a href=&quot;https://github.com/keithjgrant/Taming-CSS&quot;&gt;learn CSS&lt;/a&gt;, because that’s our real problem. And that is a problem we can fix.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;em&gt;Update: I have written a follow-up post: &lt;a href=&quot;https://keithjgrant.com/posts/into-the-future-of-css.html&quot;&gt;Into the Future of CSS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hosting on Github</title>
    <link href="https://keithjgrant.com/posts/2015/05/hosting-on-github/"/>
    <published>2015-05-16T21:26:32Z</published>
    <updated>2015-05-16T21:26:32Z</updated>
    <id>https://keithjgrant.com/posts/2015/05/hosting-on-github/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Well, this is a bit of an experiment… I’ve lost track of how many times I’ve started a redesign of my WordPress blog, only to give up after days of fiddling. This is kind of silly, since I generally finish the &lt;em&gt;design&lt;/em&gt; of my site fairly quickly, but get held up fighting with the implementation minutia.&lt;/p&gt;
&lt;p&gt;I blog very rarely. I also occasionally want to host various other pages that don’t necessarily need to fit with the theme of the rest of my site. WordPress is really overkill for this. What I really need is just some static hosting. So I’m giving GitHub Pages a try. Not only that, but I should be able to play with the design whenever I feel like, without too much time investment.&lt;/p&gt;
&lt;p&gt;So here’s my new design. It took me just a few days not only to build the site, but to also roll out some bare-bones blogging &amp;amp; deployment scripts in gulp; my database is a git repository. I can blog in &lt;a href=&quot;http://jade-lang.com/&quot;&gt;Jade&lt;/a&gt; or MarkDown. I think my wordpress days may be done, at least for now.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Taming CSS</title>
    <link href="https://keithjgrant.com/posts/2015/04/taming-css/"/>
    <published>2015-04-29T21:26:32Z</published>
    <updated>2015-04-29T21:26:32Z</updated>
    <id>https://keithjgrant.com/posts/2015/04/taming-css/</id>
    <content xml:lang="en" type="html">&lt;p&gt;Observation 1: Web developers, in general, don’t know CSS as well as they should.&lt;/p&gt;
&lt;p&gt;Observation 2: There aren’t really any good roadmaps for learning all the essentials of CSS.&lt;/p&gt;
&lt;p&gt;There are great resources for the basics, for styling, for code organization/architecture, for advanced tricks. But to work through them all, you get a lot of overlap, and they still leave you with missing gaps in your knowledge. Often, with CSS, you don’t know what you don’t know, and that makes it hard to move forward. My hope is to fix that, by putting them all together in one place.&lt;/p&gt;
&lt;p&gt;Announcing &lt;a href=&quot;https://github.com/keithjgrant/Taming-CSS&quot;&gt;Taming CSS&lt;/a&gt;, a CSS book for web developers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; My book has been renamed to &lt;a href=&quot;http://manning.com/books/css-in-depth&quot;&gt;CSS in Depth&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>

