Skip to content

Conversation

@aduth
Copy link
Member

@aduth aduth commented Dec 10, 2025

What?

Updates theme package design tokens to include Figma scope extensions, such that importing the tokens will assign the valid scopes in Figma accordingly.

Follow-on to #73859 (specifically #73859 (comment))

Why?

Ensure that tokens are only available for use in relevant contexts (e.g. "stroke" tokens should be used in "stroke" fields in Figma).

How?

Adds a Figma-supported $extensions field specifying scopes for tokens.

Extensions are intended for these sorts of proprietary vendor data.

I could not find any documentation which describes this field, but it was discovered via reverse-engineering by exporting variables out of Figma that already had the scopes assigned.

I was curious to try to effectively "hide" primitive token values by assigning "com.figma.scopes": [], but this did not work as expected. My hunch is that Figma won't allow them to be unscoped since they are aliased by other variables.

Documentation of available scopes can be found on Figma Developers site: https://developers.figma.com/docs/plugins/api/VariableScope/

Testing Instructions

  1. Repeat Figma-specific Testing Instructions from Theme: Restructure theme tokens to embed prefix, flattened modifiers #73859
  2. After variables are imported, right-click and "Edit" a variable to confirm that "Scopes" are assigned as expected (see screenshot below)

Screenshots or screencast

Screenshot 2025-12-10 at 4 53 35 PM

@aduth aduth requested a review from jameskoster December 10, 2025 22:07
@aduth aduth requested a review from a team as a code owner December 10, 2025 22:07
@github-actions
Copy link

github-actions bot commented Dec 10, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: aduth <[email protected]>
Co-authored-by: jameskoster <[email protected]>
Co-authored-by: mirka <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@aduth aduth added [Type] Task Issues or PRs that have been broken down into an individual action to take [Package] Theme /packages/theme labels Dec 10, 2025
@aduth
Copy link
Member Author

aduth commented Dec 10, 2025

Now that I think of it, it probably makes sense to go through our other tokens and add applicable scopes as well. For example, "GAP" for wpds-dimension.gap, etc.

Edit: Added in b22d6e3.

@aduth aduth changed the title Theme: Include Figma scopes extension in color tokens Theme: Include Figma scopes extension in design tokens Dec 10, 2025
Copy link
Member

@mirka mirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need someone to confirm, but this isn't working for me 😕 The screen recording is for dimension, but same with color. It's the latest version of Figma.

CleanShot.2025-12-12.at.19-44-40.mp4

}
},
"$extensions": {
"com.figma.scopes": [ "GAP" ]
Copy link
Contributor

@jameskoster jameskoster Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both gap and padding don't seem to be scoped 🤔

@jameskoster
Copy link
Contributor

jameskoster commented Dec 12, 2025

Colors seem to be scoped correctly for me 👍

Border width interactive doesn't seem to be scoped. Neither do any of the dimension tokens.


Related to this work, do we intend to strip the primitives again, or is that going to be difficult? If so then ideally we scope them so they apply to nothing. Otherwise they end up polluting the color picker UI quite a bit.

Edit: I just read the OP :) Variables can be unscoped in the UI, even when aliased. I suppose we'll have to do this manually in Figma for now.

@aduth
Copy link
Member Author

aduth commented Dec 12, 2025

Related to this work, do we intend to strip the primitives again, or is that going to be difficult? If so then ideally we scope them so they apply to nothing. Otherwise they end up polluting the color picker UI quite a bit.

I would like to, and it feels like something that Figma should support, as in: If I specify scopes as an empty array, those primitives should be effectively hidden, right? Unfortunately that doesn't seem to be working as expected, mentioned in the original comment:

I was curious to try to effectively "hide" primitive token values by assigning "com.figma.scopes": [], but this did not work as expected. My hunch is that Figma won't allow them to be unscoped since they are aliased by other variables.

Although maybe the issues y'all have flagged will lead me to some sudden "ah-ha!" of what I'm doing wrong (unlikely, but there's hope yet 😅 ).

@jameskoster
Copy link
Contributor

Oh, I just noticed that border colors are scoped to 'Effects' rather than 'Stroke'.

@jameskoster
Copy link
Contributor

Typography tokens are also unscoped. I'm wondering if all the unscoped tokens are a 'me' problem 🙈

@aduth aduth force-pushed the add/theme-dtcg-figma-scopes branch from b22d6e3 to 8fbb711 Compare December 18, 2025 20:09
@aduth
Copy link
Member Author

aduth commented Dec 18, 2025

I've not been able to reproduce the issue y'all are seeing with the scopes not being properly assigned 😬

The typography scopes were unscoped though. This was semi-intentional as the typography tokens are very unfinished, though based on the changes I explored separately in #73931, I don't think the structure will need to radically change, so I went ahead and added them here.

@jameskoster
Copy link
Contributor

I asked AI and was able to fix the dimension tokens in 696d5f2.

The issue was that Figma's token import doesn't inherit group-level $extensions when individual tokens already define $extensions. Each gap token had $extensions with mode, and the group-level com.figma.scopes wasn't being applied.

@jameskoster
Copy link
Contributor

Seems we were using the wrong scope name for stroke. I fixed that (and removed effects as I don't think it's needed) in bf1e67f.

@jameskoster
Copy link
Contributor

Tokens are now all importing with correct scoping for me.

@aduth
Copy link
Member Author

aduth commented Dec 19, 2025

Hm, neither of those changes make particular sense to me, but if it's working then it's working and I'm not gonna fight it 😄

Per your quoted AI conversation:

The issue was that Figma's token import doesn't inherit group-level $extensions when individual tokens already define $extensions. Each gap token had $extensions with mode, and the group-level com.figma.scopes wasn't being applied.

The individual tokens didn't define $extensions until you added the referenced changes in 696d5f2, so it should have inherited group-level $extensions.

Seems we were using the wrong scope name for stroke.

Figma's official documentation mentions STROKE_COLOR and EFFECT_COLOR, but does not mention the STROKE value you introduced with bf1e67f .

https://developers.figma.com/docs/plugins/api/VariableScope/

* @param extensions - The extensions object to copy.
* @return A new extensions object without the 'mode' key.
*/
function copyExtensionsWithoutMode(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jameskoster What were these changes for?

Copy link
Contributor

@jameskoster jameskoster Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the changes I made to fix scoping, running npm run --workspace @wordpress/theme build was stripping the scope definitions from the .json files in /modes. This was entirely AI generated so please fix any slop 🙏

Copy link
Member Author

@aduth aduth Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I see now. Yes, without this, the overrides files won't include $extensions. But my understanding of how Figma imports the mode overrides is that they only override differences, i.e. the absence of scopes means it'll inherit the scopes from the value it overrides.

In my own testing, this seems to work as expected. The GAP scope is still applied to "compact" density overrides even without explicit com.figma.scopes extension because it's applied to the original value.

Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in my initial test it was working opposite to how you describe. When the modes weren't scoped, importing was them was unsetting the scope of all tokens.

But I just reverted this change, rebuilt the tokens, and it's working fine 😅 At this point I don't know if it's the importer that's bugged or me... probably the latter.

@jameskoster
Copy link
Contributor

The individual tokens didn't define $extensions

The dimensions tokens did, no? There's a mode extension for the different densities.

Figma's official documentation mentions STROKE_COLOR and EFFECT_COLOR, but does not mention the STROKE value you introduced with bf1e67f .

That's so strange 😅

It goes without saying; hopefully someone else can test these changes before merging.

@aduth
Copy link
Member Author

aduth commented Dec 19, 2025

Figma's official documentation mentions STROKE_COLOR and EFFECT_COLOR, but does not mention the STROKE value you introduced with bf1e67f .

That's so strange 😅

It goes without saying; hopefully someone else can test these changes before merging.

Well, it does seem to still work for me in applying "Stroke" scope using the (seemingly-invalid) STROKE value, so I guess it's okay 🤷

image

@aduth
Copy link
Member Author

aduth commented Dec 19, 2025

At this point the only thing I'm concerned is if we can avoid the changes to the mode plugin. @jameskoster would you mind testing what I described to see if the scopes are still applied for the mode overrides even if the $extension value doesn't exist in those overrides files?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Theme /packages/theme [Type] Task Issues or PRs that have been broken down into an individual action to take

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants