<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jonathan Dallas</title><description>Writings on interfaces, mostly web, mostly CSS</description><link>https://jwdallas.com/</link><language>en-us</language><atom:link href="https://jwdallas.com/feed.xml" rel="self" type="application/rss+xml"/><item><title>HTML Interfaces For CSS Libraries</title><link>https://jwdallas.com/posts/HTMLInterfacesForCSSLibraries/</link><guid isPermaLink="true">https://jwdallas.com/posts/HTMLInterfacesForCSSLibraries/</guid><description>Collected ideas around the HTML side of achieving better developer user experiences for component-based CSS libraries.</description><pubDate>Sun, 03 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;I’m using the term “CSS library” to refer to a repository that exists to provide styles for components. Perhaps &quot;component CSS library&quot; would be more accurate? This article is basically a grab bag of loosely held opinions presented as suggestions.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of the big ideas behind pulling in a CSS library for your components is that the consumer of the library should be able to configure presentation of a component without touching CSS. The developer should be able to name the component configuration they want in HTML and the styling should &lt;em&gt;just work&lt;/em&gt;. Let’s talk about some ways that API could be made more intuitive and easier to manage.&lt;/p&gt;
&lt;h2&gt;Avoid using the class attribute as API&lt;/h2&gt;
&lt;p&gt;Component configuration from the perspective of a CSS library typically consists of things like component name, variant, size, etc. These are effectively key-value pairs but I often see this type of API being set via the &lt;code&gt;class&lt;/code&gt; attribute.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Don&apos;t do this --&amp;gt;
&amp;lt;button class=&quot;
  button--default
  button--standard
  button--medium
&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the &lt;code&gt;class&lt;/code&gt; attribute for this has a number of issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All of it is jammed into a single HTML attribute so can be difficult to differentiate one config from another.&lt;/li&gt;
&lt;li&gt;There is no indication of which config each value is setting. What are &quot;default&quot;, &quot;standard&quot;, &quot;medium&quot; referring to? The developer might not know unless they looked at the documentation for the library and even then it might not be obvious.&lt;/li&gt;
&lt;li&gt;No clear way to know what library is being used here. If a developer is new to maintaining a project using this CSS library, it may be difficult for them to know where to begin when looking for documentation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A better approach is to use &lt;code&gt;data-*&lt;/code&gt; attributes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Do this --&amp;gt;
&amp;lt;button
  data-ln-variant=&quot;default&quot;
  data-ln-treatment=&quot;standard&quot;
  data-ln-size=&quot;medium&quot;
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Above example uses &lt;code&gt;ln&lt;/code&gt; short for &quot;Library Name&quot; as a sample library-specific namespace. A project like Spectrum CSS might use &lt;code&gt;sp&lt;/code&gt;, Tailwind &lt;code&gt;tw&lt;/code&gt;, etc.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;data-*&lt;/code&gt; attributes for CSS library APIs makes it clear what each config refers to because there is a clear delineation between key and value.&lt;/p&gt;
&lt;h2&gt;Use library-specific namespacing&lt;/h2&gt;
&lt;p&gt;Consider adding a brief namespace unique to the library so if someone were to paste any of these API names into a search engine they would have a better chance of quickly finding a docs page for the library. This also helps to avoid potential collisions with existing code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Don&apos;t do this --&amp;gt;
&amp;lt;button data-variant=&quot;large&quot;&amp;gt;Launch&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* library styles */
[data-variant=&quot;large&quot;] {
  /* These styles might collide if the project
     importing this library already styles
     a `data-variant` attribute. */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, if a project already styles &lt;code&gt;data-variant&lt;/code&gt;, or imports another CSS library that does, that would collide with this library’s styles.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Do this --&amp;gt;
&amp;lt;button data-ln-variant=&quot;large&quot;&amp;gt;Launch&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* library styles */
[data-ln-variant=&quot;large&quot;] {
  /* The library namespacing helps ensure
     that only this CSS library will style
     this attribute. */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Requiring a short library-specific sigil on the custom data attributes used by the CSS library reduces the risk of code conflicts and makes the code easier to follow.&lt;/p&gt;
&lt;h2&gt;Decide if the library will style tag names or only attributes&lt;/h2&gt;
&lt;p&gt;You may be thinking that putting the &lt;code&gt;data-&lt;/code&gt; prefix in front of all these attributes is pretty verbose. That prefix is required for custom attributes on standard HTML but not for custom HTML elements. Additionally, you can drop the library-specific prefix on the custom attribute since it’s already clear from the library prefix in the tag name where this attribute is defined.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- data prefix needed --&amp;gt;
&amp;lt;button data-ln-variant=&quot;large&quot;&amp;gt;Engage&amp;lt;/button&amp;gt;

&amp;lt;!-- data prefix NOT needed --&amp;gt;
&amp;lt;button-ln variant=&quot;large&quot;&amp;gt;
  &amp;lt;button&amp;gt;Engage&amp;lt;/button&amp;gt;
&amp;lt;/button-ln&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Above example uses &lt;code&gt;ln&lt;/code&gt; short for &quot;Library Name&quot; as a sample library-specific namespace. A project like Spectrum CSS might use &lt;code&gt;sp&lt;/code&gt;, Tailwind &lt;code&gt;tw&lt;/code&gt;, etc.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;While requiring a custom element for every component can be useful for consistency purposes, it will require additional complexity to do that. In the example above, the native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element is still desired for accessibility which makes the component a little more complex.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Native button element hidden in Shadow DOM --&amp;gt;
&amp;lt;button-ln variant=&quot;large&quot;&amp;gt;Engage&amp;lt;/button-ln&amp;gt;

&amp;lt;!-- Shadow DOM declaration --&amp;gt;
&amp;lt;button-ln …&amp;gt;
  &amp;lt;template shadowrootmode=&quot;open&quot;&amp;gt;
    … &amp;lt;!-- focus management galore --&amp;gt;
    &amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;
  &amp;lt;/template&amp;gt;
&amp;lt;/button-ln&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is worth noting that using &lt;code&gt;data-*&lt;/code&gt; attributes gives access to a special &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset&quot;&gt;dataset API&lt;/a&gt; in JavaScript. Not of much use for a CSS library, but something to consider if that would be useful for configuring functionality in JavaScript.&lt;/p&gt;
&lt;p&gt;Whether it’s better for a CSS library to style &lt;code&gt;data-*&lt;/code&gt; attributes or custom elements with custom attributes may depend on the library’s goals.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the CSS library exists only to provide styling for a single component library, requiring a custom tag name for every component and custom attributes for every configuration instead of using &lt;code&gt;data-*&lt;/code&gt; attributes seems like a better fit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the CSS library exists to be more general and support multiple component libraries, using the &lt;code&gt;data-*&lt;/code&gt; approach and never styling tag names directly seems like a better fit.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Consider making namespace secondary to API name&lt;/h2&gt;
&lt;p&gt;Think about the developer user experience of interacting with this library’s API in the web inspector. Ideally information which is repeated should be secondary to information that is unique. To achieve that goal, consider putting API name first and namespace second.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- The eye reads the less significant info first --&amp;gt;
&amp;lt;ln-popover …&amp;gt;
  &amp;lt;ln-toolbar …&amp;gt;
    &amp;lt;ln-button …&amp;gt; … &amp;lt;/ln-button&amp;gt;
    &amp;lt;ln-button …&amp;gt; … &amp;lt;/ln-button&amp;gt;
    &amp;lt;ln-button …&amp;gt; … &amp;lt;/ln-button&amp;gt;
  &amp;lt;/ln-toolbar&amp;gt;
&amp;lt;/ln-popover&amp;gt;

&amp;lt;!-- The eye reads the more significant info first  --&amp;gt;
&amp;lt;popover-ln …&amp;gt;
  &amp;lt;toolbar-ln …&amp;gt;
    &amp;lt;button-ln …&amp;gt; … &amp;lt;/button-ln&amp;gt;
    &amp;lt;button-ln …&amp;gt; … &amp;lt;/button-ln&amp;gt;
    &amp;lt;button-ln …&amp;gt; … &amp;lt;/button-ln&amp;gt;
  &amp;lt;/toolbar-ln&amp;gt;
&amp;lt;/popover-ln&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Above example uses &lt;code&gt;ln&lt;/code&gt; short for &quot;Library Name&quot; as a sample library-specific namespace. A project like Spectrum CSS might use &lt;code&gt;sp&lt;/code&gt;, Tailwind &lt;code&gt;tw&lt;/code&gt;, etc.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;There’s a lot more to say on this topic, but I’m going to end here for now. Please feel free to reach out via the links in the footer. I’d love to continue the discussion!&lt;/p&gt;
</content:encoded></item><item><title>Naming Variables In CSS</title><link>https://jwdallas.com/posts/NamingCSSVariables/</link><guid isPermaLink="true">https://jwdallas.com/posts/NamingCSSVariables/</guid><description>Some collected thoughts around how to name variables in CSS. Ideas, conventions, and some do&apos;s and don&apos;t for consideration.</description><pubDate>Sat, 07 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;“Naming things is hard” goes the software engineering axiom and CSS is no exception. Here are some collected thoughts related to naming &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/--*&quot;&gt;CSS Custom Properties&lt;/a&gt;. I&apos;m going to use use the terms &quot;variable&quot; and &quot;custom property&quot; interchangeably since they are effectively the same thing for the purposes of what to call them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: What follows is not gospel. CSS to me is a very poetic language, there are so many different ways to express the same concepts. I like these conventions but do not consider them the one correct way to name variables in CSS. If you disagree with any of my points below, I would love to learn from your perspective.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Casing&lt;/h2&gt;
&lt;p&gt;In naming variables, the first thing to talk about is what sort of casing to use. The industry seems have settled on kebab-casing (which makes sense) but I think it&apos;s worth considering an alternative.&lt;/p&gt;
&lt;h3&gt;Maybe camelCase isn&apos;t so bad&lt;/h3&gt;
&lt;p&gt;You might be surprised to learn that many of the native values defined within CSS do not use kebab-casing. For example, &lt;code&gt;currentColor&lt;/code&gt; and all of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/named-color&quot;&gt;named colors&lt;/a&gt; (&lt;code&gt;cadetBlue&lt;/code&gt;, &lt;code&gt;rebeccaPurple&lt;/code&gt;, &lt;code&gt;antiqueWhite&lt;/code&gt;, etc).&lt;/p&gt;
&lt;h3&gt;Consider mixing kebab-casing with camelCasing&lt;/h3&gt;
&lt;p&gt;We can use camelCasing mixed with kebab-casing to create variable names that are structurally consistent. The idea is to use hyphens to separate value type and namespace from variable name and then camelCase within each segment. Essentially: &lt;code&gt;namespaceName-valueType-variableName&lt;/code&gt;. Let’s call this &lt;strong&gt;triptych notation&lt;/strong&gt;. In my opinion, this convention makes it clearer at a glance what is the actual name of the variable and what is the metadata encoded in the name.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  /* Harder to scan: */
  --system-control-accent-color: blue;
  --system-focus-ring-color: cadetBlue;
  --system-label-color-quaternary: lightGray;
  --system-heading-title-font-size: 1.5rem;
  --system-subheading-font-size: 1.2rem;
  --system-caption-font-size: 0.65rem;

  /* Easier to scan: */
  --system-color-controlAccent: blue;
  --system-color-focusRing: cadetBlue;
  --system-color-labelQuaternary: lightGray;
  --system-fontSize-headingTitle: 1.5rem;
  --system-fontSize-subheading: 1.2rem;
  --system-fontSize-caption: 0.65rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With triptych notation, camelCase is used to limit the number of hyphens. This allows the middle segment to consistently be the value type and the last segment to consistently be the specific name of the variable. In my opinion, this consistent placement of hyphens makes custom properties easier to read quickly.&lt;/p&gt;
&lt;h2&gt;Namespacing&lt;/h2&gt;
&lt;p&gt;The example above has variable names that are prefixed with ‘system’—short for &apos;design system&apos;. This is called namespacing. Namespaced variable names can help avoid collisions when CSS is shared by multiple projects. In other words, they help to avoid situations where a developer outside of your project accidentally defines a variable with the same name. Another benefit is that namespacing provides a hint in the web inspector as to which project defined the custom property.&lt;/p&gt;
&lt;p&gt;Namespacing your variable names can be important for top level global variable names but I’d argue this type of name scoping is typically &lt;em&gt;not&lt;/em&gt; neccesary or useful when a variable is defined below the top level. This is because CSS handles that for you. A custom property is always scoped to the selector in which the property is defined. If you define a custom property with a CSS selector for a custom element called &lt;code&gt;quiz-library&lt;/code&gt; that custom property will only exist within DOM nodes that match &lt;code&gt;quiz-library&lt;/code&gt; and their children.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
    /* This variable is defined at the root making it shared
       globally so having a namespace is useful. */
    --system-color-labelPrimary: #000;
}

quiz-library {
  /* This variable is defined within quiz-library so it’s not
     shared globally. A namespace of &quot;quizLibrary&quot; would be
     redundant because the variable is only available within
     quiz-library elements and their descendents. */
  --color-questionTitle: var(--system-color-labelPrimary);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Value typing&lt;/h2&gt;
&lt;p&gt;The examples above include the type of the value (&apos;color&apos;, &apos;fontSize&apos;, etc) in the custom property name. Consider including value type information in variable names so that maintainers of the code can have a sense of what kind of value the variable holds. This is often referred to as &lt;a href=&quot;https://en.wikipedia.org/wiki/Hungarian_notation&quot;&gt;Hungarian notation&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;button {
  /* Did they set a font family definition to a font size? */
  font-size: var(--system-elephant);
}

button {
  /* Clear now that the variable sets a font size. */
  font-size: var(--system-fontSize-elephant);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Names that are descriptive&lt;/h2&gt;
&lt;p&gt;There are two fundamental categories of variable names in CSS. Consider these two variables:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;--color-icyBlue&lt;/code&gt; (value-based)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--color-accent&lt;/code&gt; (usage-based)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One of them is labeled as a constant—by name, &quot;Icy Blue&quot; should never hold anything other than a blue color value. The other is more dynamic, the specific color held by &quot;Accent&quot; could be expected to change depending on where it is used; for example, which project the variable is used within.&lt;/p&gt;
&lt;p&gt;People call these categories lots of different names. I&apos;m going to to call them &lt;strong&gt;value-based&lt;/strong&gt;: names that describe a value, and &lt;strong&gt;usage-based&lt;/strong&gt;: names that describe a use.&lt;/p&gt;
&lt;h3&gt;Where to use value-based naming&lt;/h3&gt;
&lt;p&gt;Variables with value-based names can be useful for restricting the number of values in your interface. As an example, it’s good design to limit your interface to a small set of colors. If every part of your UI uses a slightly different shade of gray, your design will look inconsistent and unconsidered. Requiring every use of color in your interface to be a variable allows you to limit your colors to the set defined as variables. The number of font sizes, font weights, animation durations, panel elevations (defined by the presentation of their shadows) can all be useful things to limit.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Value-based variables at the global level */
:root {
  /* Colors */
  --system-color-bondiBlue: rgb(0 58 71);
  --system-color-canaryYellow: rgb(255 239 0);
  --system-color-caribbeanGreen: rgb(0 204 153);
  
  /* Font Sizes */
  --system-fontSize-jumbo: 3.052rem;
  --system-fontSize-large: 1.563rem;
  --system-fontSize-small: 0.8rem;
  
  /* Font Weights */
  --system-fontWeight-bold: 700;
  --system-fontWeight-medium: 400;
  --system-fontWeight-light: 200;
  
  /* Durations */
  --system-duration-presto: 60ms;
  --system-duration-allegro: 125ms;
  --system-duration-andante: 500ms;

  /* Elevation */
  --system-boxShadow-slightlyRaised: 0 1px 2px 0 rgb(0 0 0 / 10%);
  --system-boxShadow-floatingBox: 0 0 30px 0 rgb(0 0 0 / 35%);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Color palettes&lt;/h3&gt;
&lt;p&gt;Many design systems name the colors in their color palette with a &lt;a href=&quot;https://spectrum.adobe.com/page/color-fundamentals/#Contrast-generated-colors&quot;&gt;numeric suffix&lt;/a&gt; to indicate contrast with a base background color. The thinking is that it can be useful for consumers of the palette to be able to easily determine if a particular color will pass &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast&quot;&gt;WCAG requirements for text color contrast&lt;/a&gt;. This is a clever idea but in many projects the colors that are used for text (the only ones which matter for WCAG’s contrast requirements) are very limited so naming the entire color palette that way just for a few colors can be overkill. Additionally I&apos;m unconvinced that these numbers actually make it faster to implement contrast safe UIs. The contrast algorithm used by WCAG is &lt;a href=&quot;https://typefully.com/u/DanHollick/t/sle13GMW2Brp&quot;&gt;likely going to change&lt;/a&gt; and there are a number of ways a color could be transformed in a way that would negate the value of the numeric suffix. If you&apos;re going to need to always double-check contrast-ratio in the rendered UI, no time has been saved using these numbers.&lt;/p&gt;
&lt;p&gt;That said, these numbers do provide a useful utility of being able to see at a glance whether a color is lighter or darker. Though I feel using words rather than numbers is a nicer more human friendly way to accomplish that. Consider using compound names for color variables. One name that refers to the basic color (“red”, “yellow”, “blue”) and another that acts as a differentiator (“cherry”, &quot;sunflower”, “sky”).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  /* Not very human friendly */
  --system-color-red400: hsl(0 100% 50%);
  --system-color-yellow200: hsl(48 100% 50%);
  --system-color-blue300: hsl(200 100% 50%);

  /* Friendlier and easier to understand */
  --system-color-cherryRed: hsl(0 100% 50%);
  --system-color-sunflowerYellow: hsl(48 100% 50%);
  --system-color-skyBlue: hsl(200 100% 50%);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep differentiators analogous to real world things to avoid confusion. Don&apos;t use an abstract name like &lt;code&gt;historyBlue&lt;/code&gt; because it would be unclear what that color would look like.&lt;/p&gt;
&lt;p&gt;The goal is to get to a unique color name format that can support an endless number of tints, shades, and tones but at glance still be clear from the name what the color probably looks like so someone can see if the color was accidentally used in the wrong spot in the code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;button.destructive {
  /* There&apos;s a UX bug in our code if this color isn&apos;t red
     but the variable name below is somewhat ambiguous. */
  color: var(--system-color-ferrari);
}

button.destructive {
  /* Clearer now at a glance that a red color was correctly set */
  color: var(--system-color-ferrariRed);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This naming convention can be expanded to incorporate alpha, though it&apos;s a bit of a stretch. Separately from naming, just as general practice, it’s often better to reach non-opaque color values in UI via layered transformations or usage-based named variables than to put them into the static color palette. That said, when I &lt;em&gt;have&lt;/em&gt; needed to write a value-based variable name for a non-opaque value using this convention I’ve put that info at the start of the color name using terms analogous to real world transparency.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  --system-color-semitransparentBondiBlue: rgb(0 58 71 / 10%);
  --system-color-translucentBondiBlue: rgb(0 58 71 / 30%);
  --system-color-frostedBondiBlue: rgb(0 58 71 / 70%);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Where to use usage-based naming&lt;/h3&gt;
&lt;p&gt;Variable names tied to use provide varying levels of abstraction. Put another way, names can communicate different scopes of capability and utility within the project by describing uses that are more specific or more general. Some are very narrow in use because they describe a very specific thing and some are very wide in use because they describe a general category of things.&lt;/p&gt;
&lt;p&gt;For a very contrived example, consider naming the font weight used in a button that submits a registration form. That variable could be named something like &lt;code&gt;--fontWeight-regFormSubmitButton&lt;/code&gt; but that&apos;s very specific. Typically all submit buttons look the same way in which case the concept of a &apos;submit button font weight&apos; could be abstracted out into a less specific name like &lt;code&gt;--fontWeight-submitButton&lt;/code&gt;. That name is more general and as a result at a higher level abstraction because it doesn&apos;t refer to a specific form anymore.&lt;/p&gt;
&lt;p&gt;It often makes sense to combine variables with multiple levels of abstraction in a project. Here is how that could come into play with control tinting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  /* Color palette defined at root */
  --system-color-bondiBlue: rgb(0 58 71);
  --system-color-canaryYellow: rgb(255 239 0);
}

body {
  /* Custom property for custom controls */
  --color-accentColor: var(--system-color-bondiBlue);
  /* Reflect it below for native controls */
  accent-color: var(--color-accentColor);
}

foobar-custom-control {
  /* Define CSS interface allowing the background to be changed */
  --color-background: var(--accentColor);
  /* Implement the above interface */
  background: var(--color-background);
}

form.tinted {
  /* Define CSS interface for applying tint colors to form */
  --color-formTint: var(--system-color-canaryYellow);
}

form.tinted foobar-custom-control {
  /* Utilize the above interface for foobar-custom-control */
  --color-background: var(--color-formTint);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dark mode is simpler with usage-based variables&lt;/h3&gt;
&lt;p&gt;Consider an implementation of dark mode styling without usage-based variables vs one with them. When using only value-based variables, the code is much more repetitive and verbose.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Dark mode WITHOUT usage-based variables… */

:root {
  --system-color-deepBlack: #333;
  --system-color-offWhite: #eee;
  --system-color-skyBlue: lch(33 111 231.17);
  --system-color-deepBlue: lch(14 111 231.17);
}

/* Because the variables above are named in
   a value-based way we can’t reasonably change
   their values. Instead we fork our CSS below
   to use one or the other depending on the
   root appearance. */

[data-appearance=&quot;light&quot;] body {
  color: var(--system-color-deepBlack);
  background: var(--system-color-offWhite);
}

[data-appearance=&quot;dark&quot;] body {
  color: var(--system-color-offWhite);
  background: var(--system-color-deepBlack);
}

[data-appearance=&quot;light&quot;] a {
  color: var(--system-color-deepBlue);
}

[data-appearance=&quot;dark&quot;] a {
  color: var(--system-color-skyBlue);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With usage-based names you can define color variables as interface concepts that have understood meanings beyond individual UI pieces allowing for those values to be externally changed for dark mode without needing to maintain separate light/dark CSS for each new piece of UI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Dark mode WITH usage-based variables… */

[data-appearance=&quot;light&quot;] {
  --system-color-textPrimary: #333;
  --system-color-fillPrimary: #eee;
  --system-color-link: lch(14 111 231.17);
}
[data-appearance=&quot;dark&quot;] {
  --system-color-textPrimary: #eee;
  --system-color-fillPrimary: #333;
  --system-color-link: lch(33 111 231.17);
}

/* With usage-specific variable names we can
   change the values for the uses they describe
   at a very high level of abstraction allowing
   the lower level code that uses the variables
   not to need to understand the current
   appearance mode. */

body {
  color: var(--system-color-textPrimary);
  background: var(--system-color-fillPrimary);
}

a {
  color: var(--system-color-link);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Levels of hierarchy within usage-based variables&lt;/h3&gt;
&lt;p&gt;Any time you have a design that references the same value across mulitple pieces of UI, I&apos;d suggest that is an opportunity for abstracting that value into a name that better describes the intention of the value in the design.&lt;/p&gt;
&lt;p&gt;For example, if the background color of your sidebar and your info panels are both &lt;code&gt;#eee&lt;/code&gt;, relative to the &lt;code&gt;#fff&lt;/code&gt; of your main background, perhaps your intent for that color from the design perspective is to convey to the user that UI with that background is of a secondary nature.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  --system-color-backgroundPrimary: #fff;
  --system-color-backgroundSecondary: #eee;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The utility of a usage-based name comes in how it guides a developer or designer in its use. Be careful to avoid using names the are too generic. For example, &lt;code&gt;--system-color-primary&lt;/code&gt; is too open-ended in meaning making it unclear where it should be used. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Do not do this */

:root {
  /* What is this for? */
  --system-color-box: var(--system-color-neonBlue);
}

:is(a, button, input):focus-visible {
  /* Was this variable used correctly? */
  background: var(--system-color-box);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* Do this instead */

:root {
  /* Clear what this is for */
  --system-color-focusRing: var(--system-color-neonBlue);
}

:is(a, button, input):focus-visible {
  /* Clear it was used correctly */
  outline-color: var(--system-color-focusRing);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be careful to avoid using names that are too specific. For example, &lt;code&gt;--system-color-mainToolbarBackground&lt;/code&gt; could only be used in one spot which makes the use of a variable superfluous.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Do not do this */

:root {
  /* This can only be used in one place meaning its
     existence needlessly adds complexity to the project */
  --system-color-mainToolbarBackground: #eee;
}

main .toolbar {
  background: var(--system-color-mainToolbarBackground);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* Do this instead */

:root {
  /* This name is general enough in scope that it
     can be used across all UI so it is useful at
     the global level. */
  --system-color-backgroundSecondary: #eee;
}

main .toolbar {
  background: var(--system-color-backgroundSecondary);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;There’s a lot more to say on this topic but I&apos;m going to end here for now. Please feel free to reach out, there are links in the footer. I&apos;d love to continue the discussion!&lt;/p&gt;
</content:encoded></item><item><title>Nested Dark Mode Via CSS Proximity</title><link>https://jwdallas.com/posts/NestedDarkMode/</link><guid isPermaLink="true">https://jwdallas.com/posts/NestedDarkMode/</guid><description>How to arbitrarily nest themes to any depth</description><pubDate>Sun, 07 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Note: This might be more accurately titled &quot;Nested Dark Mode via CSS Inheritance&quot; but I&apos;m going with &apos;proximity&apos; since that term helped me to grok this concept better. &quot;Proximity&quot; isn&apos;t a formally defined term in any CSS spec, as far as I can tell. However, there is a related term called &quot;scope proximity&quot; being &lt;a href=&quot;https://drafts.csswg.org/css-cascade-6/#cascade-proximity&quot;&gt;defined&lt;/a&gt; for &lt;code&gt;@scope&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Nested theming&lt;/h2&gt;
&lt;p&gt;Let&apos;s start by defining some terms. For the purposes of this article I’m considering Light/Dark Mode to be a &lt;a href=&quot;https://en.wikipedia.org/wiki/Theme_(computing)&quot;&gt;theme&lt;/a&gt;. Nested theming is the case where the page is styled in one theme but sub-sections of it use another theme. A practical example of nested theming could be a scrolling product page that has discrete sections, some on a light background and some on a dark background, or maybe a rich editor web app that allows users to preview content in different themes.&lt;/p&gt;
&lt;h2&gt;CSS selectors don&apos;t consider proximity&lt;/h2&gt;
&lt;p&gt;Let’s look at a basic example of nested theming:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div data-theme=&quot;blue&quot;&amp;gt;
  &amp;lt;a&amp;gt;Should be blue&amp;lt;/a&amp;gt;
  &amp;lt;div data-theme=&quot;red&quot;&amp;gt;
    &amp;lt;a&amp;gt;Should be red (but is actually blue)&amp;lt;/a&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[data-theme=&quot;red&quot;] a {
  color: red;
}

[data-theme=&quot;blue&quot;] a {
  color: blue;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, the blue theme link style incorrectly applies within the red theme. This is because CSS is comparing the two selectors in isolation and in isolation both selectors have identical specificity, so last in wins. It is not considering where the component parts of these selectors are in DOM relative to the element targeted by the selector.&lt;/p&gt;
&lt;p&gt;One fix would be to duplicate the styles for the nested theme but this adds significant complexity because it requires describing every combination of nested themes in advance.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Brute force solution that doesn’t scale */
[data-theme=&quot;red&quot;] a,
[data-theme=&quot;blue&quot;] [data-theme=&quot;red&quot;] a {
  color: red;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To find a better solution we need to revisit some CSS fundamentals.&lt;/p&gt;
&lt;h2&gt;Inheritance&lt;/h2&gt;
&lt;p&gt;Some CSS properties &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance&quot;&gt;inherit&lt;/a&gt; by default and some don&apos;t.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;color&lt;/code&gt; is an example of a property that inherits. If the &lt;code&gt;color&lt;/code&gt; property is set on &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, all elements within that one will get that color value by default because all elements by default inherit their value for the &lt;code&gt;color&lt;/code&gt; property from their parent element.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;background&lt;/code&gt; is an example of a property that does not inherit. If the &lt;code&gt;background&lt;/code&gt; property is set on &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; that value will only apply to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element, not its descendants.&lt;/p&gt;
&lt;h2&gt;Proximity is a feature of inheritance&lt;/h2&gt;
&lt;p&gt;This behavior of inheriting the closest set value from ancestors means that the proximity of a given element to an ancestor with a set value determines the resulting value. If two or more ancestor elements have set values, the child element will always use the value from its closest parent. This is exactly the behavior we want for nesting themes. We only want the values from the closest theme container.&lt;/p&gt;
&lt;p&gt;With that in mind, there is a much simpler way to fix the example from above:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[data-theme=&quot;red&quot;] {
  color: red;
}

[data-theme=&quot;blue&quot;] {
  color: blue;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div data-theme=&quot;blue&quot;&amp;gt;
  &amp;lt;a&amp;gt;Should be blue&amp;lt;/a&amp;gt;
  &amp;lt;div data-theme=&quot;red&quot;&amp;gt;
    &amp;lt;a&amp;gt;Should be red (and now actually is)&amp;lt;/a&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Did you spot the difference?&lt;/p&gt;
&lt;p&gt;The selectors still have identical specificity but no longer style individual elements within their respective theme contexts. Now anchor tags are &lt;strong&gt;inheriting&lt;/strong&gt; the color style instead of getting it set directly on them. This is the magic concept: CSS inheritance is based on proximity.&lt;/p&gt;
&lt;h2&gt;Custom properties &lt;em&gt;always&lt;/em&gt; inherit by default&lt;/h2&gt;
&lt;p&gt;Where this gets especially cool with regards to nested theming is that custom property values &lt;em&gt;always&lt;/em&gt; inherit by default and they can be set as values on CSS properties that normally do &lt;em&gt;not&lt;/em&gt; inherit. In other words, via custom properties, we can make nearly all of CSS proximity-dependent.&lt;/p&gt;
&lt;h2&gt;Using proximity for nested dark mode&lt;/h2&gt;
&lt;p&gt;Here is an example that puts all of this together:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;main data-theme=&quot;light&quot;&amp;gt;
  &amp;lt;article data-theme=&quot;dark&quot;&amp;gt;
    &amp;lt;figure data-theme=&quot;light&quot;&amp;gt;…&amp;lt;/figure&amp;gt;
  &amp;lt;/article&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[data-theme=&quot;light&quot;] {
  --color-backgroundPrimary: white;
  --color-backgroundSecondary: ghostwhite;
}

[data-theme=&quot;dark&quot;] {
  --color-backgroundPrimary: black;
  --color-backgroundSecondary: gray;
}

/* Now uses of the custom properties defined above will always
   be correct for the given light/dark context set in DOM */

article {
  background: var(--color-backgroundPrimary);
}

figure {
  background: var(--color-backgroundSecondary);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Behold the magic of CSS proximity. Leveraging proximity via inheritance makes it simple to write styles for any number of theme contexts and nest them arbitrarily to any desired depth.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[data-theme=&quot;red&quot;] {
  --color-primaryText: red;
}

[data-theme=&quot;blue&quot;] {
  --color-primaryText: blue;
}

p {
  color: var(--color-primaryText);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div data-theme=&quot;blue&quot;&amp;gt;
  &amp;lt;p&amp;gt;Blue&amp;lt;/p&amp;gt;
  &amp;lt;div data-theme=&quot;red&quot;&amp;gt;
    &amp;lt;p&amp;gt;Red&amp;lt;/p&amp;gt;
    &amp;lt;div data-theme=&quot;blue&quot;&amp;gt;
      &amp;lt;p&amp;gt;Blue&amp;lt;/p&amp;gt;
      &amp;lt;div data-theme=&quot;red&quot;&amp;gt;
        &amp;lt;p&amp;gt;Red&amp;lt;/p&amp;gt;
        &amp;lt;div data-theme=&quot;blue&quot;&amp;gt;
          &amp;lt;p&amp;gt;Blue&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The inherit keyword&lt;/h2&gt;
&lt;p&gt;At this point, you might be thinking: &lt;em&gt;What about the &lt;code&gt;inherit&lt;/code&gt; keyword?&lt;/em&gt; One of the major issues with using something other than a custom property for nested theming is it requires every ancestor element between theme container and target element to have the same value for that property. In many practical applications of nested theming, this is a poor solution because there will often be many elements between the theme container and target element and many of those elements should &lt;em&gt;not&lt;/em&gt; have a value for that style. Custom properties are a way around this because, when they inherit, they have no effect on element styling until they are invoked as a property value.&lt;/p&gt;
&lt;p&gt;Additionally, for a property like &lt;code&gt;background&lt;/code&gt; which does not inherit by default, the &lt;code&gt;inherit&lt;/code&gt; keyword would need to be set on that property for every element all the way down the chain; further increasing the code complexity.&lt;/p&gt;
&lt;h2&gt;Further reading&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.oddbird.net/authors/miriam/&quot;&gt;Miriam Suzanne&lt;/a&gt; has written articles about the topics described above going back to at least &lt;a href=&quot;https://www.smashingmagazine.com/2019/07/css-custom-properties-cascade/&quot;&gt;2019&lt;/a&gt;. Her article about &lt;a href=&quot;https://css-tricks.com/using-custom-property-stacks-to-tame-the-cascade/&quot;&gt;custom property &quot;stacks&quot;&lt;/a&gt; takes these concepts much further and explores some mind-bendingly clever ways to mix uses of proximity with uses of the cascade. I recommend also reading her fascinating &lt;a href=&quot;https://css.oddbird.net/scope/nesting/#why-combine-lower-boundaries-and-proximity-in-a-single-feature&quot;&gt;thinking&lt;/a&gt; around formalizing concepts of scoping and proximity for her work on &lt;a href=&quot;https://drafts.csswg.org/css-cascade-6/&quot;&gt;CSS Cascading and Inheritance Level 6&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Style APIs As A Last Resort</title><link>https://jwdallas.com/posts/StyleAPIsAsALastResort/</link><guid isPermaLink="true">https://jwdallas.com/posts/StyleAPIsAsALastResort/</guid><description>Some opinions and suggestions around APIs in CSS</description><pubDate>Sat, 15 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last Spring I studied a popular CSS library and took a bunch of notes, originally just for my own purposes but, as I push myself to write more, I&apos;m reformatting them into articles. This is the second of those. The first one was on &lt;a href=&quot;/posts/HTMLInterfacesForCSSLibraries&quot;&gt;HTML interfaces&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This article attempts to explain why style APIs should be avoided wherever possible, suggests how whole categories of style APIs may be unnecessary, and outlines what makes a good style API.&lt;/p&gt;
&lt;h2&gt;What is a style API?&lt;/h2&gt;
&lt;p&gt;I’ve seen others use this term, but I don&apos;t believe it has an established definition anywhere. I’ll do my best to explain what I mean when I use it.&lt;/p&gt;
&lt;p&gt;A style API is an application programming interface for styling. You know that CSS lets us create systems, but the larger or more complex a web project the more the more we want to be thoughtful about the way we structure this system of styles. This is especially true if we expect the CSS to serve more than one web project. Systems of CSS go by many names—theme, library, framework—but the idea is that the consumers of such a CSS system are freed from worrying about CSS implementation details. They can instead work with CSS at a higher level by using an API to apply styles instead of writing them directly.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;project-owned connection points within CSS systems for controlling how CSS is applied&lt;/strong&gt; is what I’m attempting to get at with the term &quot;style API.&quot; In other words, the parts of a CSS system that are maintained to allow external users to use and configure the system. Examples of style APIs include component style variants, component style modifiers, utility classes, and even base design constants such as color palettes, text styles, shadows, glows, etc.&lt;/p&gt;
&lt;h2&gt;All APIs have a cost&lt;/h2&gt;
&lt;p&gt;The number of API entry points directly influences the difficulty of learning and using a system. Each API adds more for a consumer to learn and understand before they have mastered use of the system. Maintaining an API also takes time. As a system adds new API, the risk of regression increases.&lt;/p&gt;
&lt;p&gt;If you’re with me on that, you’ll likely agree that any API should provide value that outweighs the burden its existence brings to the system. A style API should provide a utility that would not be possible without it. If the same result can be achieved without a style API, we’re in a stronger position by not having that style API.&lt;/p&gt;
&lt;p&gt;Josh Clark’s article &lt;a href=&quot;https://bigmedium.com/ideas/design-system-pace-layers-slow-fast.html&quot;&gt;Ship Faster by Building Design Systems Slower&lt;/a&gt; goes into more depth on how being reluctant to add API is necessary for the success of a design system.&lt;/p&gt;
&lt;h2&gt;Let’s talk about Shadow DOM&lt;/h2&gt;
&lt;p&gt;It’s common to see style APIs created using custom properties to work around the style encapsulation of the &lt;a href=&quot;https://open-wc.org/guides/knowledge/styling/styles-piercing-shadow-dom/#styling-styles-piercing-shadow-dom&quot;&gt;Shadow DOM&lt;/a&gt;. CSS inheritance &lt;a href=&quot;https://open-wc.org/guides/knowledge/styling/styles-piercing-shadow-dom/#styling-styles-piercing-shadow-dom&quot;&gt;passes down&lt;/a&gt; into the Shadow DOM and all custom properties are, by default, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Inheritance&quot;&gt;inherited properties&lt;/a&gt; making those properties available on both sides of the shadow boundary.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Web component definition --&amp;gt;
&amp;lt;example-component&amp;gt;
  &amp;lt;template shadowrootmode=&quot;open&quot;&amp;gt;
    &amp;lt;label&amp;gt;Text&amp;lt;/label&amp;gt;
  &amp;lt;/template&amp;gt;
&amp;lt;/example-component&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* -- Project using this web component --- */

/* Does not work, cannot reach in
   and style the Shadow DOM like this. */
example-component label {
  background-color: gold;
}

/* Will work if styles inside of
   this web component use this specific
   API to set the label background color. */
example-component {
  --color-labelBackground: gold;
  /* However, this is a poor custom property name
     because it is just aliasing the `background`
     property. I’ll explain a better approach below. */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s cool that this works, but let’s look at an inherent problem with this pattern of mirroring properties.&lt;/p&gt;
&lt;p&gt;Imagine a label element in the shadow DOM needs to be italic in some cases, and bold in others. Should a style API like &lt;code&gt;--fontStyle-label&lt;/code&gt; be created?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* -- Internal web component styles --- */
:host {
  label {
    font-style: var(--fontStyle-label);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consider a label background that grows darker on hover. Should a style API like &lt;code&gt;--fontStyle-labelBackgroundHovered&lt;/code&gt; be created to support that usecase?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* -- Internal web component styles --- */
:host {
  label {
    font-style: var(--fontStyle-label);
  }
  label:hover {
    background: var(--fontStyle-labelBackgroundHovered);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What if focus styles need to match? Should another style API like &lt;code&gt;--fontStyle-labelHoveredOrFocused&lt;/code&gt; be created for that?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* -- Internal web component styles --- */
:host {
  label {
    font-style: var(--fontStyle-label);
  }
  label:hover {
    background: var(--fontStyle-labelBackgroundHovered);
  }
  label:hover,
  label:focus-visible {
    background: var(--fontStyle-labelHoveredOrFocused);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where to draw the line?&lt;/p&gt;
&lt;p&gt;It’s not hard to imagine that as an API of this sort gets larger and larger it must inevitably recreate a sort of utility-first naming approach (similar to &lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind&lt;/a&gt;) where modifiers for things like pseudo-selectors and media/container queries need to be encoded in the same namespace as CSS property names.&lt;/p&gt;
&lt;h3&gt;Reconsidering styling across the shadow boundary&lt;/h3&gt;
&lt;p&gt;Looking a little closer at how the shadow boundary is defined can be helpful in finding some more congruent ways to style across it without the need for creating APIs.&lt;/p&gt;
&lt;p&gt;Of particular interest is that the Shadow DOM only shields styles from the shadow &lt;em&gt;children&lt;/em&gt; of the web component. In other words, &lt;strong&gt;the shadow root element of the component is not style encapsulated.&lt;/strong&gt; This means internal styles set using &lt;code&gt;:host&lt;/code&gt; can be overridden by consumers of the web component.&lt;/p&gt;
&lt;p&gt;It follows, then, that a component author can intentionally put styles on that shadow root to make those styles available for customization.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Web component definition --&amp;gt;
&amp;lt;some-element&amp;gt;
  &amp;lt;template shadowrootmode=&quot;open&quot;&amp;gt;
    &amp;lt;style&amp;gt;
      :host {
        /* Setting properties on the root of the
           web component enables consumers to
           override and change them. */
        border-radius: 50%;
      }
    &amp;lt;/style&amp;gt;
    …
  &amp;lt;/template&amp;gt;
&amp;lt;/some-element&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* -- Project using this web component --- */
some-element {
  /* We can change this because the web component author
     offered it on the outer element, which is above
     the shadow boundary */
  border-radius: 16px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example we&apos;ve exposed the &lt;code&gt;border-radius&lt;/code&gt; as style configuration without creating a style API.&lt;/p&gt;
&lt;p&gt;I’m sure you can imagine many ways to leverage inheritance and other context-related features in CSS to provide more elegant style configuration across the shadow boundary before resorting to inventing new style API.&lt;/p&gt;
&lt;p&gt;Consider these ways that the CSS language has built in:&lt;/p&gt;
&lt;h4&gt;The &lt;code&gt;inherit&lt;/code&gt; keyword&lt;/h4&gt;
&lt;p&gt;Many native CSS properties are &lt;a href=&quot;https://web.dev/learn/css/inheritance/#which_properties_are_inherited_by_default&quot;&gt;inherited by default&lt;/a&gt; and other properties can be made to inherit by setting the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/inherit&quot;&gt;inherit keyword&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:host {
  text-align: /* This is one of the properties that inherit by default.
                 See the full list of others in the link above. */ ;

  /* This property does not inherit by default but can be told
     to with the inherit keyword */
  background: inherit;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;The &lt;code&gt;currentColor&lt;/code&gt; keyword&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;color&lt;/code&gt; property inherits by default. This unlocks the ability to leverage the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword&quot;&gt;currentColor keyword&lt;/a&gt; for referencing the value on the &lt;code&gt;color&lt;/code&gt; property as a value in any property value. There are many ways this can be used to automatically adapt component colors to an outer color. Gradients, border colors, accent colors, color-mixes, etc., can all be used with &lt;code&gt;currentColor&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:host {
  color: /* This is one of the properties that inherit by default.
            See the full list of others in the link above. */ ;

  /* Using the inherited color */
  border: 1px solid currentColor;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Font relative units&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;font&lt;/code&gt; property is a shorthand. This property and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/font#constituent_properties&quot;&gt;its constituent longhands&lt;/a&gt; (&lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;font-family&lt;/code&gt;, &lt;code&gt;line-height&lt;/code&gt;, etc.) all inherit their values by default. This means that &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_font&quot;&gt;font relative units&lt;/a&gt; (em, ex, ch, lh, etc.) are (by default) relative to whatever font properties are set on the outer scope. This unlocks the ability to change any length within the component’s internally scoped CSS based on what font size is present in the component’s outer scope.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:host {
  font-size: /* This is one of the properties that inherit by default.
                See the full list of others in the link above. */ ;
  /* Using the inherited font size */
  height: 4em;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Container and media queries&lt;/h4&gt;
&lt;p&gt;Beyond direct property inheritance, consider how other forms of context can be used to adapt styles. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries&quot;&gt;Container queries&lt;/a&gt; provide the ability to apply a different style configuration based on the size the parent component is rendered at. This could be used, for example, to automatically change the variant of a component without the need for an API.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:host {
  container-type: size;

  control-group {
    /* Large variant */
    @container (height &amp;gt; 10rem) {
      /* Stack horizontally */
      flex-direction: row;
    }
    /* Small variant */
    @container (height &amp;lt; 10rem) {
      /* Stack vertically */
      flex-direction: column;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Bringing it together&lt;/h4&gt;
&lt;p&gt;Let’s put some of the above ideas together in a practical to example to illustrate how a web component can be style configured without the need for additional API.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Web component --&amp;gt;
&amp;lt;guitar-knob&amp;gt;&amp;lt;/guitar-knob&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/*-- Internal guitar-knob styles --*/
:host {
  /* There is a separate element for a focus ring to allow for
     a more fancy animation on focus */
  &amp;amp;:focus-visible {
    …
    .focus-ring {
      …
      /* The focus ring should match the rounded corners of the
         component. This can be achieved in a congruent way by
         inheriting whatever border-radius is on the outer element. */
      border-radius: inherit;
    }
  }

  label {
    /* The label text needs to be a little bit lighter.
       One way to achieve this in a congruent way is by inheriting
       the color of the outer element and then mixing it with white. */
    background: color-mix(in oklch, currentColor 20%, white);

    /* The space for the label needs to be two lines tall.
       A congruent way to do this is to use a font relative
       unit for the sizing. */
    block-size: 2lh;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/*-- Project using the guitar-knob component --*/
guitar-knob {
  /* We’ve changed this knob to be a rounded square for a more
     unique look. The custom focus ring deep in the shadow tree
     is inheriting and now using this new border-radius. */
  border-radius: 20%;

  /* We’ve changed the text color on our use of this knob
     and, without any APIs, an element deep in the shadow tree
     is inheriting and now using this new color for the label. */
  color: rebeccapurple;
}

guitar-instrument {
  /* On this parent element we’ve decided to change the font,
     this new font is best with a taller line-height. The label
     deep down in the shadow tree of the guitar-knob is inheriting
     the new font and it’s larger line-height and has increased it’s
     block-size accordingly. */
  font-family: &quot;Fira Sans&quot;, sans-serif;
  line-height: 1.6;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, all of the configuration across the shadow boundary is done without creating any style APIs.&lt;/p&gt;
&lt;h2&gt;When creating style APIs&lt;/h2&gt;
&lt;p&gt;Let’s switch gears now and talk about how to craft the style APIs that make up a CSS system. There are a few guidelines here that have formed in my head and I’ll go through them below.&lt;/p&gt;
&lt;h3&gt;Consider again whether the API is still needed&lt;/h3&gt;
&lt;p&gt;Before adding a style API stop and consider. Is there another way that doesn’t incur the cost of a new API? Before updating a style API, think about just removing the API instead.&lt;/p&gt;
&lt;p&gt;CSS is exploding in capability. I wouldn’t be surprised to learn that the changes to CSS since the pandemic now exceed the previous 10 years combined. There are now incredible new features like &lt;a href=&quot;https://caniuse.com/css-cascade-layers&quot;&gt;cascade layers&lt;/a&gt; and &lt;a href=&quot;https://caniuse.com/mdn-css_types_color_color-mix&quot;&gt;color-mix&lt;/a&gt; both of which obviate large structural pieces of many existing CSS system architectures and both now have over 90% support in global browser usage.&lt;/p&gt;
&lt;p&gt;The possibilities and potential for reimagining CSS architecture at scale have never been greater.&lt;/p&gt;
&lt;h3&gt;Avoid directly aliasing CSS properties&lt;/h3&gt;
&lt;p&gt;When naming a style API, use a declarative approach rather than an imperative one. Instead of directly aliasing an existing CSS property, look for ways to allow the consumer of the API to express the intent of their configuration. By being less prescriptive of implementation in the naming, the API is less likely to require changes to maintain semantic accuracy as the component internals get updated over time to add new features or adapt to different environments or design languages.&lt;/p&gt;
&lt;p&gt;Ideally API names should only be expressing higher-level configuration options.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example-component {
  /* Imperative naming.
     Implies that labels in this
     component will set this value for
     the `background` color property. */
  --color-labelBackground: gold;
}

example-component {
  /* Declarative naming.
     Allows more flexibility in how
     it applies to the component. */
  --color-secondary: gold;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Avoid directly aliasing CSS values&lt;/h3&gt;
&lt;p&gt;Unless the CSS library is fully down the &lt;a href=&quot;https://tailwindcss.com/docs/utility-first&quot;&gt;utility-first&lt;/a&gt; rabbit hole, my advice is that it’s generally not a good idea to force consumers to use a library API as an alias for plain CSS.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example-component {
  /* Arguably more utility is lost than
     gained by using a library value over
     just writing 0.75rem. */
  font-size: var(--ln-fontSize-0_75rem);
}

example-component {
  /* This API name is more useful because
     it can hold a different value depending
     on context. */
  font-size: var(--ln-fontSize-caption);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Above example uses &lt;code&gt;ln&lt;/code&gt; short for “Library Name” as a sample library-specific namespace. A project like Spectrum CSS might use &lt;code&gt;sp&lt;/code&gt;, Tailwind &lt;code&gt;tw&lt;/code&gt;, etc.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;h3&gt;Enforce a maximum number of hyphens&lt;/h3&gt;
&lt;p&gt;An easy way to improve a style API is to make it more readable. We like kebab-casing in CSS names but a long string of kebab-cased words can be difficult to parse at glance, especially when a namespace is involved and the semantics come midway into the name.&lt;/p&gt;
&lt;p&gt;In my opinion, custom property APIs are a lot easier to follow with a fixed number of hyphens (and camel-casing in-between) to enforce structure and meaning in predictable places.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  /* Harder to scan: */
  --ln-control-accent-color: blue;
  --ln-focus-ring-color: cadetBlue;
  --ln-label-color-quaternary: lightGray;
  --ln-heading-title-font-size: 1.5rem;
  --ln-subheading-font-size: 1.2rem;
  --ln-caption-font-size: 0.65rem;

  /* Easier to scan: */
  --ln-color-controlAccent: blue;
  --ln-color-focusRing: cadetBlue;
  --ln-color-labelQuaternary: lightGray;
  --ln-fontSize-headingTitle: 1.5rem;
  --ln-fontSize-subheading: 1.2rem;
  --ln-fontSize-caption: 0.65rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Above example uses &lt;code&gt;ln&lt;/code&gt; short for &quot;Library Name&quot; as a sample library-specific namespace.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Both of these groupings hold the same token name but, in my opinion, one is faster to read than the other.&lt;/p&gt;
&lt;p&gt;I’ve been referring to this specific use of two hyphens above (conforming to &lt;code&gt;&amp;lt;namespace&amp;gt;-&amp;lt;valueType&amp;gt;-&amp;lt;valueName&amp;gt;&lt;/code&gt;) as &lt;strong&gt;triptych notation&lt;/strong&gt;, which is a riff on &lt;a href=&quot;https://en.wikipedia.org/wiki/Hungarian_notation&quot;&gt;Hungarian notation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Shadow parts&lt;/h2&gt;
&lt;p&gt;You might have noticed I didn&apos;t address &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_shadow_parts&quot;&gt;shadow parts&lt;/a&gt; above. My thoughts on all of this are still evolving and on the &lt;code&gt;::part&lt;/code&gt; pseudo-element especially. I haven’t played with this feature enough yet to have an opinion on how best to use it.&lt;/p&gt;
&lt;p&gt;My current thinking is that shadow parts seem best fit for components that are intended to be aggressively restyled, but I wonder how useful it actually is in practice since it seems like you’d run into a similar API scaling problem of &apos;well if that’s a part, this other thing should be a part also&apos; until the entire component might as well just be in the light DOM.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Hopefully you found some of my thinking above useful! As always, I&apos;d love to hear from you and continue the discussions using the links in the footer.&lt;/p&gt;
&lt;p&gt;If you enjoyed this, check out my previous article on &lt;a href=&quot;/posts/NamingCSSVariables&quot;&gt;Naming CSS Variables&lt;/a&gt;, which explores many of these same topics from a more general perspective, and this one on &lt;a href=&quot;/posts/HTMLInterfacesForCSSLibraries&quot;&gt;HTML Interfaces&lt;/a&gt;, which gives suggestions around ways component libraries can interact with CSS theming using HTML.&lt;/p&gt;
</content:encoded></item></channel></rss>