-
Notifications
You must be signed in to change notification settings - Fork 566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reference is not added to transformed border and shadow token #1415
Comments
If I look at this https://github.com/amzn/style-dictionary/blob/main/lib/utils/references/outputReferencesTransformed.js it seems like objects in tokens are not supported? Is the |
Yeah unfortunately this is a limitation of outputReferences. When you are converting from Object to String, you no longer "know" which property of the object is where exactly in the string, so it's always fundamentally unsafe to then try to put back refs. Let's take an example e.g. shadow where we can see this problem: {
offsetX: '{spacing.1}', // this resolves to 1px
offsetY: '{spacing.11}', // this resolves to 11px
blur: '21px',
color: '#000'
} This would be transformed to You might see the problem here, we would take the original value (the object one), check for references, we will find two references, the first one is So, if we ignore this problem, we would take the resolved final value and replace occurences of Oops... that's not what we wanted... So long story short, OutputReferencesTransformed prevents this from happening by not allowing you to output references for tokens that were originally objects but were simplified to strings, because it is unsafe unless you know precisely e.g. through the use of an Abstract Syntax Tree (AST) the exact locations where a object property is sitting inside the string value. |
I should note that we could also think outside the box here and allow:
which would theoretically let you end up with this value: Which you could then format in the CSS formatter to: So it would be theoretically possible if you could hook into the resolve references and deny certain tokens from resolving altogether, and relying on the format hook to convert the token refs to CSS vars. The downside of that approach is that transitive transforms and regular transforms would never work on these tokens, since they normally have to wait for references to be resolved in order to do their work. Two use cases are Studio's transitive color modifers and resolve math transforms, but those can also be handled in CSS itself using color-mix() and calc(), but these have their own limitations as well. So even if this is made possible e.g. in v5 of Style Dictionary, you'll still find yourself struggling. Hence why my personal recommendation is to not use outputReferences for anything other than tokens that are solely references (I call them dumb tokens). https://github.com/tokens-studio/lion-example?tab=readme-ov-file#tokens-structure documents this a bit for my example repo, where I do use outputReferences, but only for my component token layer which are pure references to the semantic layer (which is theme-dependent/dynamic, hence why CSS vars are useful). |
Ahh, okay that does make sense. Is this why it is working if there is only one reference in the output? The above issue could be solved by simply searching for Another option would be, that a transformer could provide some kind of map like this: I understand that this could be an issue, but is there a way that I can implement it for myself? Or would I do what you seem to suggest above, that I just replace the resolved value with the reference in my border transformer? |
What about: {
offsetX: '{spacing.foo}', // this resolves to 1px
offsetY: '{spacing.bar}', // this also resolves to 1px
blur: '1px',
color: '#000'
} Even if we had a map It feels like every time I dive deep into this topic and try to come up with solutions, I find more edge cases that aren't covered and it feels like putting bandages on open wounds. Whereas if you rethink the way you use references through your token taxonomy/structure and selectively output refs only for dumb tokens, all of these issues can easily be prevented. That said, outputReferences is very flexible now as you can pass in your own function and selectively enable/disable it, but you cannot dictate unfortunately how the refs are outputted, only whether they are or not for a given token. For you to completely control also the "how" would mean you'd have to create a custom format and put in place your own outputReferences feature. For inspiration, the createPropertyFormatter is the most sophisticated example of outputReferences that's built-in for Style Dictionary, so for example for the css/variables format: Edit: no, I think if you replace the value with the references in your border transformer, you will still end up with the same problem because in order to put back refs in the createPropertyFormatter helper, it refers to the token's original value which is still the object. What I meant is that you could have another layer of abstraction in your tokens itself:
|
Does this mean I could try to enable it and see if the output works? Or will this definitely not give me the desired result?
Maybe I am not getting what you mean, but this sounds like not using tokens to the max because of a technical issue? I want references so that I can adjust them on the fly (in the browser). I understand that offering this solution as the tool is problematic, because you have to deal with all the edge cases. But as a consumer, I may be able to avoid all the edge cases, so it would be fine. |
In the particular use case you mentioned, I think it would work, but the odds that you will run into cases where it creates invalid CSS is pretty high, it depends on a few factors but comes down to how many transitive transforms are applied to your tokens that can cause the exact issue I outlined where the find & replace may make mistakes
It is a fundamental issue: One entity cannot be a reference to another entity and yet simultaneously be a transformed or otherwise processed or computed variant of itself. Once you do the transformation, the reference is lost because you have just diverged from the referenced value. We have to think outside the box quite a bit in order to work around that problem. For example, if you would output refs first, and then do the transformation from object to string, then you're fine. So perhaps the lifecycle should be:
That way, if a pre-transform changes the value, outputting refs will still be disallowed due to the fundamental issue, but your border object to string transformation can just be done at the very end, after all, it's just a syntax-only transformation, it doesn't change the inherent value of the token, it just reorders it or stringifies it in your case. Similar to converting from hex to rgb, it doesn't change the value, just the syntax. It's something I would be open to playing with, but completely rethinking this part of the lifecycle is very costly even for v5 milestone. I just don't know if I'm convinced we really need to do this when it is my opinion that you should never end up in a spot where you should need this.
Yeah, my point is that you should adjust only 1 layer of your tokens on the fly (e.g. on theme switch), usually your semantics, and have everything else reference those. "everything else" is what you want to output references for, but the semantic layer of tokens that are "smart" (e.g. can contain math, color modifiers, object tokens with properties that contain/are references) doesn't benefit from outputting as CSS variables unless the thing that it references is also dynamic. In summary, usually people build their tokens in this way:
There's many variations of this but it always tends to come down to a form |
Btw I'm open to having my view changed on all this but I'd have to see more clearly why outputting refs is so important for these tokens that imo wouldn't really benefit from it |
This is exactly how our tokens are built. And the last layer is where I want the references. But it does not work because border is a component token. Within this last layer there are references. From my experience expecting to only have one token is not realistic for even semi-complex systems. Consider the following, you have a color: I don't think this is a pretty complex or "edge-casey" idea. Let's say Now you would either have to build a complex logic that replaces all uses of a specific hex value in your css, OR you just set the Other use cases include adjusting contrast, choosing custom colors for components, etc. |
Hey,
I am not sure if I am missing something.
I use this in my settings for the file:
My border token looks like this:
The
borderColor
token is:And the
borderWidth
token is:My transformer
borderToCss
looks like this:The expect output is:
However, the actual output is:
What am I doing wrong?
// Addition
I just noticed that it is working for shadows without a color and for text shorthands. I am beginning to expect it to be related to the color value.
The text was updated successfully, but these errors were encountered: