All notes

Design Tokens Should Name Meaning Before Values

Treat semantic roles, consumers and theme mappings as a versioned interface.

English edition, edited for length. Read the complete Chinese original.

Why Does the Same Green Need Two Names?

Imagine a management dashboard where the submit button background, success message text, and chart growth lines are all set to green. The resulting light-colored screenshot looks consistent. However, when a dark theme is applied, the button needs to maintain brand recognition, the success message text must be readable against the dark background, and the chart needs to be distinguishable from the other five data lines. As consumers begin to compete for the same green-600, the more granular the color palette, the more likely it is that conflicts will arise.

The problem isn’t that there aren’t enough green values; it’s that the reasons for the changes in the three values are different. The main action, success status, and data series don’t have the same meaning, and even if they happen to use the same values at a particular moment, they shouldn’t be forced to change in sync. This article uses this assumption to model, generate, and migrate background-driven tokens. The code is a local illustration and does not claim to create a complete standard format converter.

Inferring Semantic Interfaces from Consumer Behavior

First, determine exactly what the buttons, messages, tables, and charts actually need, rather than starting with a large, abstract naming tree. The submit button requires background, foreground, and focus feedback; the success message requires status text and background; and the charts require colors that can be stably distinguished by data category. Each role record uses location, background, and allowed status, and this list defines the boundaries of abstraction.

The base layer stores the color palette, the semantic layer stores the intended uses, and the component layer handles specific behaviors only when there are distinct constraints. Components directly reference semantic roles and do not independently choose original color codes. After a designer changes the brand color palette, it’s still possible to discuss which uses should follow the new palette and which should remain independent, rather than having to search through dozens of pages for a specific green color.

:root {
  --palette-brand-600: #166534;
  --palette-neutral-950: #171717;
  --action-primary-background: var(--palette-brand-600);
  --action-primary-foreground: #ffffff;
  --text-body: var(--palette-neutral-950);
  --status-success-text: #166534;
  --status-success-background: #f0fdf4;
}

[data-theme="dark"] {
  --action-primary-background: #86efac;
  --action-primary-foreground: #171717;
  --text-body: #f5f5f5;
  --status-success-text: #bbf7d0;
  --status-success-background: #14532d;
}

.primary-action {
  color: var(--action-primary-foreground);
  background: var(--action-primary-background);
}

The colors presented here are simply a demonstration of the mapping, and do not necessarily mean that all combinations already meet the contrast requirements. The foreground and background change in pairs, and the “success” state has its own mapping. The key is not to mechanically invert the lighter values, but to re-select values that can effectively take on the same role.

If a name like sidebar-left-green is used to bind a location and appearance, but not its purpose, changes to the layout will cause the name to become invalid. Conversely, using border-default for all boundaries could lead to over-generalization: changes in editable input boundaries and purely decorative separator lines may not be consistent. A good name should be clear and understandable, explaining who relies on it and why it is being changed.

Type and Reference Graphs are More Important Than File Hierarchies

Color, length, duration, and font are distinct data types. The generation tool should validate the type and units, and distinguish between the final value and any referenced aliases. Storing all values as plain strings can lead to errors, such as referencing “duration” with “color,” which may only manifest when the browser ignores the declaration. When using standard formats, implement the specifications supported by the specific tool, and do not assume compatibility based on similar names.

The dependency graph is constructed based on aliases. During the construction phase, at least the following checks should be performed:

  • Existence of references
  • Type compatibility
  • Absence of circular dependencies

After this, the graph is parsed to generate output for the target platform.

The assumption is that the primary color references primarily text, and the primary text references the primary color. If both names exist, but there is no definitive value that can be determined, a circular dependency should be detected, preventing silent output of empty values or the selection of any default color.

Runtime fallback for custom CSS properties cannot replace build-time checks. Missing references can render declarations invalid, and the final appearance will depend on inheritance and cascading, which may not match the team’s expected default. The development environment may be saved by default browser styles, while another theme or nested container may display unreadable text. Checks must be performed during the build process to identify and reject issues before they reach the user, rather than waiting for the user to take a screenshot.

The generated output should be consistently ordered, retain traceable names, and be linked to the source definition version. Large differences resulting from the same input producing different orders will increase review noise. Design tools and code tools do not need to output the same files, but they must share role definitions and version records. Sharing only hexadecimal color values is not sufficient to prove that both sides are referring to the same interface.

Component changes must have a valid, independent reason.

The compact buttons in the table may require smaller padding, but this doesn’t necessarily necessitate a completely new brand color palette. Increasing the component size mapping has a logical basis, while copying all action colors lacks justification. The purpose of the component layer is to express stable local constraints, not to isolate each component from the semantic system.

You can use a single change to test for abstraction: When making changes across the entire site, which elements should be modified? If the text, form instructions, and main content are all affected, this is a reasonable coordination. However, if the chart data encoding is also changed, this indicates that the dependency crosses semantic boundaries. Conversely, if a specific component that clearly belongs to the main content remains unchanged, it may indicate an undocumented hardcoded value.

Local exceptions do not need to be automatically blocked. If a third-party embedded or experimental module cannot be accessed temporarily, it is more honest to record the owner, reason, and conditions for re-evaluation, rather than pretending that everything is unified. However, the exception cannot be copied to other pages to create a second standard. When scanning direct values, you should first determine the responsibility and then decide whether to migrate or retain, rather than deleting everything indiscriminately.

How to Successfully Migrate to a New Color Palette

Assume that the old version only contains --green-600. All three types of consumers are using it. Instead of directly renaming it as the primary action color and then having the success text and charts follow, first create new roles and reference the users according to their actual usage. For consumers with unclear categories, the maintainer should confirm the category rather than guessing based on filenames.

Phase 1: Introduce new roles while retaining existing interfaces, and output deprecation information.

Phase 2: Gradually migrate components to use the new roles, with each migration comparing the normal, hovered, focused, and error states.

Phase 3: Confirm that all supported consumers have been migrated before planning to remove the old interfaces. When removing, also consider independent component packages: if the current repository does not reference them, it does not mean that external users have upgraded.

The compatibility guarantee here applies to the interface, not to screenshots. Aliases can temporarily maintain the old appearance, but they cannot simultaneously alias the old green appearance to two future, separate semantic meanings, and then claim that all consumers can migrate without loss. During the transition phase, it must be specified which of the old behaviors to retain, and if necessary, the original values should be retained until the user completes the categorization.

The rollback process also involves two layers. You can restore a previous version definition only when modifying the mapping; however, if you have already deleted a name or modified the generator, you may need to restore both the interface and the consumer simultaneously. The release record lists the definition version, generator version, and component dependencies, which allows you to restore a set of compatible products instead of simply overwriting a single old CSS file.

Accessibility Acceptance Occurs in Real-World Combinations

The prominence of the foreground in comparison to a standard page does not guarantee that it remains readable when placed on a selected row, a highlighted area, or an image. The check should focus on the combination of foreground, background, purpose, and status, rather than individual color blocks. Different requirements apply to standard text, large text, and non-text controls that need to be recognized, and these should be evaluated based on the corresponding success criteria in WCAG, rather than using a single threshold to apply to all objects.

Color alone cannot serve as the sole means of conveying information. Success messages require clear text, error messages require understandable explanations and associations, and charts require labels or other distinguishing methods. The addition of a “success color” token does not achieve these goals. The focus style also involves boundary positioning, occlusion, and interaction order, and the token can only provide some of these visual parameters.

The design acceptance should cover both light and dark themes, as well as user preferences for product support. Don’t just rely on the mockups in the design tool as the final proof: actual fonts, transparency layering, and browser rendering will all affect the results. Also, the disabled state should be confirmed to ensure that the information is still readable, and it shouldn’t be faded out completely just because the control is not interactive.

Approval should differentiate between numerical adjustments and semantic changes

Changing the foreground color of the text from one value to another, and expanding the foreground of the text to a common interface for all chart labels, are two distinct changes. The former primarily involves checking existing combinations, while the latter expands the consumer set, requiring confirmation of future changes. The instructions for review should clearly state which type of change is being made, rather than simply listing a new and old value table.

Similarly, adding a name doesn’t always guarantee compatibility with changes. If the generator changes its parsing method or output scope, new tokens may overwrite the values of existing components. If a new theme becomes the default theme, it will also change the appearance of the user’s initial view. Version rules should be based on observable behavior, not just the presence of new lines in the file.

The responsibilities can be broken down into three key questions: Who approves the role definitions, who maintains the generation tools, and who is responsible for component migration? While a small team can handle all these responsibilities, each question still needs to be answered separately. Otherwise, the designer approves the colors, while the developer merges the roles, and both parties believe that the other has already reviewed the semantics. Instead of holding lengthy meetings to discuss all the changes, it’s more effective to document the decisions made.

Defining access requirements for new roles can also be helpful: Does it clearly identify at least one consumer? Does it explain the differences between this role and existing ones? Does it provide a mapping of the theme and examples? If the answer is currently unavailable, it’s best to retain the local implementation or experiment name to avoid prematurely committing to an unverified style. Once a second consumer emerges, you can then abstract based on the actual shared constraints, rather than creating dozens of empty interfaces in advance.

Maintainers should also regularly check unused names, but the scan can only cover known code. External component packages, design files, and runtime patching may not be included in the scan. Decisions to remove names should clearly state the boundaries of the scan and the compatibility period. A report of zero references is evidence, not automatic removal authorization. For interfaces that are still in deprecated status, it is possible to stop new consumers while allowing existing consumers to migrate according to the planned schedule.

Assessing the Maintainability of a Change Simulation System

Select the foreground element (main text), the background element (primary action), and the success text element. Before making any changes, write down the expected target audience. Then, make one change at a time and compare the actual impact with the predicted impact. Extra impact hints suggest semantic coupling, while missing impacts suggest alternative implementations. The differences in the screenshots are just clues for positioning, and you still need to examine the actual reference relationships.

When regenerating missing aliases, circular references, and error types, the tool should clearly indicate failure. Verifying deprecation checks with a component that still relies on the old interface should demonstrate that it can detect the issue, not just normal input passing through. Ensure that the design documents and browser examples match the same version to avoid reviewing new canvases or delivering old packages.

The initial version does not need to tokenize every pixel. Instead, focus on covering frequently occurring and clearly defined roles, completing a real theme switch and migrating the old interface. The ability to predict the impact of a change, identify illegal dependencies, and revert to a compatible version demonstrates that the system has established a maintainable contract. The number of variables alone does not provide this evidence.

Different platforms may share the same meaning, but not necessarily the same value.

Web pages, mobile clients, and email templates may all require a primary action, but their fonts, backgrounds, and rendering capabilities differ. The common semantics do not require direct copying of the same output file. Instead, roles and applicable conditions can be shared, and the platform can then map them to select the appropriate values. If a client does not support a particular expression, the generator should output the format that it can use, rather than simply inserting browser variables and claiming that everything is unified.

The length and font size, in particular, require clear units. Relative units on a webpage may vary depending on the container or user settings, while native clients have their own sizing and scaling systems. A role named “body text size” can have different mappings across platforms, but it should retain common functionalities, such as continuous reading and system zoom. Conversion tools should not unconditionally convert all values to pixels, thereby undermining the platform’s original support for user preferences.

The duration of the animation is not an isolated parameter. Users who prefer fewer dynamic effects may prefer to disable motion, change the transition method, rather than simply setting the duration to zero. The character should clarify whether it serves as feedback or decoration, and how to maintain information visibility when reducing motion. Status completion should not rely on the animation ending to notify the business; the token system is responsible for presentation parameters and does not handle the actual state machine.

Creating a platform capability checklist helps to avoid false unification: specifying supported types, how aliases are parsed, when topics switch, and how user preferences are prioritized. The checklist should also clearly define supported roles, including explicit rejection or adoption of downgrades based on evidence, and should not silently output seemingly valid but invalid values. The goal of unification is predictable behavior, not that every repository has a file with the same name.

Ensure that theme scope checks for nested and external components.

The overall page is light-colored, with a preview area requiring a darker color, and a pop-up menu rendered to the document root node via a portal. If the theme variables are only defined within the preview container, the menu might read the global light color, which could be inconsistent with the triggering area. The theme scope doesn’t necessarily correspond to the visual location; the actual DOM, inheritance relationships, and component mounting positions all affect the parsing.

You can choose to explicitly pass the theme identity to the floating element, or to place it within an appropriate theme container. The specific approach depends on how the component is implemented. It’s important not to simply increase the style priority and fix the immediate color issue, then have the next floating element repeat the error. Testing should include modals, menus, prompts, and third-party embeds, verifying that they correctly read the role source. The focus and selected state within nested themes should also be re-verified to ensure proper combination.

There needs to be a clear hierarchy between user settings, system preferences, and localized page themes. When a user explicitly chooses a dark theme, the system preferences should not override this setting, and this behavior should be defined and consistently implemented by the product. Initializing with a different theme might cause flickering, and the solution involves loading styles and reading states. However, it is crucial not to arbitrarily inject sensitive user configurations into publicly accessible cached pages solely to eliminate flickering.

Theme switching acceptance isn’t just about checking the main content. The loading state, disabled state, error messages, chart labels, and floating elements all fall under the category of “consumers.” While each role has the same meaning within these contexts, the mapping can be different. If a specific area must maintain its own independent theme, it’s important to explain the reason and define its boundaries, rather than creating an unmanageable, hard-coded exception.

Define the output identity for both “change” and “generate”

While the semantic definitions remain constant, upgrading the generator can still alter CSS scope, unit conversions, or name escaping. Conversely, if the generator remains unchanged, changes to aliases within the definitions will have a wider impact. The release records can at least distinguish between the source definition identity, the tool version, and the output identity, allowing you to determine whether the issue is due to a rule change or a conversion change.

Stable output allows for the identification of genuine differences. When the input is the same but the output is different, it’s important to investigate potential issues with timestamps, sorting, environmental factors, or tool behavior. It’s crucial not to assume that large amounts of noise automatically indicate a problem with token generation. Comparisons can be made between normalized outputs, but normalization cannot mask semantic changes related to scope or units. The goal is to improve the ability to identify subtle differences, not to eliminate important changes.

Error handling must also be clear. If a reference is missing, the build should not proceed using the last successful output without indicating that the build has failed. Otherwise, the preview will appear normal, but the submitted content will not be included in the output. While keeping the old version as an explicit rollback is reasonable, the interface and the pipeline should clearly indicate which version is being used. Success should be confirmed for each stage: success definition, successful generation, and successful delivery.

A repeatable exercise involves using a fixed input to generate two platform outputs, followed by upgrading the tool and comparing semantic role mappings. Changes are explained individually, and any unexplained differences are initially held back. This does not prove that all rendering environments are correct, but it can reduce the uncertainty in the build layer, providing a reliable foundation for subsequent real-world validation.

The token directory should clearly state the conditions for use and disabling.

In addition to a name and value, a role can also list applicable contexts, allowed states, known consumers, and scenarios to avoid. For example, a “dangerous action” context does not equal all error messages, and a data series color does not carry the semantic meaning of success or failure. Prohibitory conditions help new consumers avoid misuse, and are often more effective than simply adding more similar names.

The example documents and code examples reference the same version and demonstrate a realistic combination. The color block display illustrates the color palette, while the component display illustrates the roles. These should not be confused; maintenance costs come from interface commitments, so the role directory should be based on actual usage organizations. There should be no consumer or boundary-based names, even if the categories are well-organized, as this would only create future maintenance burdens.

Consumer registration can help identify abstract errors

When applying for a new component, the background should clearly state what status it represents, in which context it is used, and why it changes with the success rules. If the component is simply for branding purposes, it should not rely on the success semantics. Consumer registration should not be overly burdensome; instead, it should consist of a clear statement of the intended use and an example of how it would be used, allowing for predictable impact when modifications are made in the future.

Regularly compare the register with code references to identify undocumented side effects and unused roles. The tool is only for identifying these issues. The maintainer then determines whether to migrate, record an exception, or split the semantics. This allows for abstraction to be corrected based on real-world needs, rather than being permanently blocked from review simply because a name has been added to the catalog.

References