diff --git a/www/src/pages/docs/guides/modes.md b/www/src/pages/docs/guides/modes.md index a1a30141..ecd9b020 100644 --- a/www/src/pages/docs/guides/modes.md +++ b/www/src/pages/docs/guides/modes.md @@ -5,6 +5,10 @@ layout: ../../../layouts/docs.astro # Modes + Theming +:::note +As of Dec 2024, the DTCG spec is in the process of reviewing a new proposal that can tackle modes and theming at a greater scale. These docs outline the approach that Terrazzo pioneered in 2021, and still supports today. [See the Resolver Proposal section](#dtcg-resolver-proposal) to learn more. +::: + Modes are **alternate values of a token.** Token modes aren’t supported by the DTCG spec yet, but even so are a common part of most design systems. Modes can solve common problems like theming, responsive design, accessibility options, user preference, and more! ## Modes in everyday use @@ -379,3 +383,44 @@ Don’t use modes for things that can be used on the same page: - Localized state (e.g. disabled or active) - Color shades/hues - Components (e.g. Card or Button) + +## DTCG Resolver proposal + +The [Resolver proposal](https://resolver-spec.netlify.app/info/rationale/) is a currently-under-review proposal that is the next iteration on the modes idea. It keeps all of the functionality, while solving for additional problems in an elegant and performant way. While this modes approach was pioneered by Cobalt in 2021, and Figma Variables took the same approach, there were some problems left to be solved by this method. + +### Response to modes + +This section is a background of why the Resolver proposal was sought due to limitations with modes. + +#### Problem 1: multi-axis modes + +The main problem with modes is that in many ways they can only represent one axis. This is why many systems, including Figma Variables, represent the values in a table—rows represent tokens, columns represent modes. But what if you need “columns” in a different dimension? + +Consider GitHub’s color example just above—you have light and dark mode, and high contrast and colorblind modes. You could interpret those as 2 “axes:” theme and diminished color vision. Though it’s possible to manage those with modes, you have to calculate every permutation of both axes, which results in a _lot_ of tokens to manage. Multiply that across your entire design system, and you [just have a headache](https://www.youtube.com/watch?v=-qlAjXbbn6k). + +#### Problem 2: fallbacks + +Continuing the same train of thought, say along the diminished color vision axis, many modes shared several values. You would have to redeclare and duplicate those values over-and-over again, since modes only allow for a single default fallback\*. The Resolver proposal allows you, for every axis, to declare the fallback order, so you can create more of a “tree” of tokens. + +While that may sound more complex, in practice it results in an exponentially-reduced number of tokens to manage. And allows you to more-easily scale your design system, because you “opt in” to complexity, rather than having to engineer your way out of day 1 complexity. + +:::note + +Terrazzo 1.0 (Cobalt) experimented with a way to use modes in aliases (`{token.foo#mode}`), but the value was ambiguous! Saying “pretend we’re in mode X while we’re in mode Y” is doable across a single mode. But opening up that can of worms doesn’t stop people from having _those_ alias to other modes as well, and you end up in a state of confusion where neither user nor machine is quite sure what mode to apply if the aliases can jump back-and-forth across multiple modes at any time (_“If everything is a mode, nothing is!“_). + +::: + +#### Problem 3: which mode when? + +The last major problem of modes is that there’s no clear boundary as to what triggers what mode, and when—that is up for you to decide. While there are best practices outlined here, that mechanism is opaque. That can lead to unnecessary complexity when you have “mixed modes,” pulling tokens from multiple modes at once. + +While the Resolver Proposal doesn’t fully outline the mode changes in code form, it does do some work of providing stronger “hints” around the mechanisms to load a certain set of tokens. It implements the principles of [functional purity](https://en.wikipedia.org/wiki/Pure_function), and makes it simple to tell given a couple “inputs” (e.g. “light + high contrast” or “dark + deuteranopia”), what the outputs (tokens) will be. + +### Resources + +So while we’ve outlined some of the shortfalls of modes, the actual syntax is still under editor review and will be shared more in detail later. The current draft version is **able to solve all problems mentioned here, plus some.** But making sure other problems don’t sneak in accidentally, and simplifying the syntax to be as easy-to-use as possible, is still in progress. So rather than go into further detail on the “how,” you can consult the following resources until more is ready to share: + +- [Resolver proposal site, with examples](https://resolver-spec.netlify.app/) +- [Resolver proposal early prototype proof of concept](https://0lrskm.csb.app/) + +Look forward to hear more about the Resolver proposal in early 2025! diff --git a/www/src/styles/docs.css b/www/src/styles/docs.css index 6ac5cb7c..8b3bbc27 100644 --- a/www/src/styles/docs.css +++ b/www/src/styles/docs.css @@ -1,14 +1,7 @@ .md-content { - display: flex; - flex-direction: column; - > * { - margin-block: var(--tz-space-400); + margin-block: 2em; order: 99; - - @media (width >= 1600px) { - margin-block: var(--tz-space-600); - } } h1, @@ -18,13 +11,12 @@ h5, h6 { --icon-size: 0.875em; - --padding: var(--tz-space-800); + --padding: 2em; cursor: pointer; font-family: var(--tz-typography-family-sans); letter-spacing: -0.03125em; - margin: 0; - padding-block-start: var(--padding); + margin-block-start: 2em; position: relative; a { @@ -34,7 +26,8 @@ left: 0; opacity: 0; position: absolute; - top: calc(var(--padding) + 0.125em); + top: 50%; + transform: translateY(-50%); transition: opacity var(--tz-timing-default) var(--tz-ease-linear); width: 100%; @@ -239,7 +232,7 @@ width: calc(100% + 2 * var(--padding)); @media (width >= 600px) { - --padding: var(--tz-space-500); + --padding: var(--tz-space-600); } p:first-of-type { @@ -435,8 +428,6 @@ .toc { background: var(--tz-color-bg-1); - border: 1px solid var(--grid-color); - bottom: calc(-1 * var(--tz-space-500)); font-family: var(--tz-typography-200-regular-font-family); font-size: var(--tz-typography-200-regular-font-size); height: min-content; @@ -444,11 +435,12 @@ line-height: var(--tz-typography-200-regular-line-height); margin: 0; order: 1; - padding-block: calc(0.5 * var(--grid-size)); - padding-inline: calc(0.5 * var(--grid-size)); @media (width >= 1200px) { + border: 1px solid var(--grid-color); + bottom: calc(-1 * var(--tz-space-500)); overflow-y: auto; + padding: calc(0.5 * var(--grid-size)); position: fixed; right: var(--tz-space-400); top: 0;