-
Notifications
You must be signed in to change notification settings - Fork 201
feat(tag): migrate to S2 #3682
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
feat(tag): migrate to S2 #3682
Conversation
🦋 Changeset detectedLatest commit: 67a2f8d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
File metricsSummaryTotal size: 1.38 MB*
tag
* An ASCII character in UTF-8 is 8 bits or 1 byte. |
🚀 Deployed on https://pr-3682--spectrum-css.netlify.app |
937827f
to
f5cf2f3
Compare
/** | ||
* Tags have a read-only option for when content in the disabled state still needs to be shown. | ||
* Read-only tags cannot be interacted with or changed. | ||
*/ | ||
export const ReadOnly = TagsDefaultOptions.bind({}); | ||
ReadOnly.storyName = "Read only"; | ||
ReadOnly.tags = ["!dev"]; | ||
ReadOnly.args = { | ||
isReadOnly: true, | ||
}; | ||
ReadOnly.parameters = { | ||
chromatic: { disableSnapshot: true }, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a follow-up item also, it wasn't in the design spec but has existed in the S1 design guidelines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The guidelines also show the label text as being selectable and copyable, but currently it can't be selected and copied. The intended behavior needs some clarification; maybe something else to note for the follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed user-select: none;
from the tag, so now the text is selectable (but not copy-able, is there a native way to do that in CSS?). Happy to restore it if that causes unintended issues though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe that functionality is native to HTML or CSS; making text copy-able to the clipboard would need JavaScript. I don't think I'm mentioning that in the docs anywhere but should I? I would hope that consumers would look at the design guidelines instead of solely trusting our Storybook documentation, but I know sometimes we mention that a tooltip or other thing that's outside of the scope of our implementation should be available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another PR, back from the dead! Thanks for the explanation on the padding/margin stuff with all of the new options- very helpful. I think overall the tokens are in a really good spot- there was only one I didn't see in the CSS (about the down state).
I found some style criss-crossing when I started to really mix up the controls, so I left some ideas on how to expand the test file so we covered more of those.
But I had just one extra thing to add, that I couldn't add to the code...
- There's a note about the tooltip on hover to reveal the full text in the docs for text overflow. Is it helpful at all the mention that our component doesn't do that? 😆
color: var(--spectrum-tag-content-color-active); | ||
|
||
/* stylelint-disable-next-line spectrum-tools/no-unknown-custom-properties -- height and width are set by implementations */ | ||
transform: perspective(max(var(--spectrum-downstate-height), var(--spectrum-downstate-width) * var(--spectrum-component-size-width-ratio-down))) translateZ(var(--spectrum-component-size-difference-down)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't see component-size-minimum-perspective-down
token in the CSS. Is that supposed to be added to the smallest size tag, sort of like the extra small and small action buttons use that token?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding based on how this is documented on our Foundations page is that we use minimum perspective for little components, and calculated perspective (which needs the down state decorator) for anything with a width larger than 24px. It's true that it's possible for tag-minimum-width-small
to be 21px for desktop, but I believe in that case the height for the small tag would be 24px and so, using the max formula to determine whether the height or width is larger, we'd get the same perspective value as if we'd used the --component-size-minimum-perspective-down
token (which is 24px).
My guess is that --component-size-minimum-perspective-down
might be included in the spec as sort of a catchall that they add to all components that receive the down state treatment?
|
||
border-radius: var(--mod-tag-corner-radius, var(--spectrum-tag-corner-radius)); | ||
border-width: var(--mod-tag-border-width, var(--spectrum-tag-border-width)); | ||
border-style: solid; | ||
|
||
padding-inline-start: calc(var(--mod-tag-spacing-inline-start, var(--spectrum-tag-spacing-inline-start)) - var(--mod-tag-border-width, var(--spectrum-tag-border-width))); | ||
padding-inline-start: calc(var(--mod-tag-label-spacing-inline, var(--spectrum-tag-label-spacing-inline)) - var(--mod-tag-border-width, var(--spectrum-tag-border-width))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this explanation- it's pretty wild to get these spacing values correct.
I did notice an inconsistency with the label's margin-inline-end when it's next to the clear button. I think maybe we don't need to subtract the border width? The medium and large spacing between the label text and clear button is off by one if I compare to Figma. Technically, the small spacing checks out correctly, but that's because the token is set to 9px instead of 8px like Figma says.
Unfortnately, I don't really have a solution to offer. I can spend some time and see what I come up with.
/** | ||
* Tags have a read-only option for when content in the disabled state still needs to be shown. | ||
* Read-only tags cannot be interacted with or changed. | ||
*/ | ||
export const ReadOnly = TagsDefaultOptions.bind({}); | ||
ReadOnly.storyName = "Read only"; | ||
ReadOnly.tags = ["!dev"]; | ||
ReadOnly.args = { | ||
isReadOnly: true, | ||
}; | ||
ReadOnly.parameters = { | ||
chromatic: { disableSnapshot: true }, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The guidelines also show the label text as being selectable and copyable, but currently it can't be selected and copied. The intended behavior needs some clarification; maybe something else to note for the follow-up.
components/tag/stories/tag.test.js
Outdated
isSelected: true, | ||
}, | ||
{ | ||
testHeading: "Emphasized, selected", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good callout on this one Marissa. I was wondering the same thing when looking this over. Looking at the desktop file and the playground, Figma only allows turning on emphasized in combination with selected. We don't have updated guidelines but I think you may be right that is only affects the selected state.
In that case, the emphasized Docs entry could be "Default - selected + emphasized" with "The emphasized option uses emphasized colors only when the tag is selected". And tests would need a few updates. Along with styles to make sure just emphasized or emphasized + removable doesn't show emphasized colors.
components/tag/stories/tag.test.js
Outdated
{ | ||
testHeading: "Emphasized, read-only", | ||
isReadOnly: true, | ||
isEmphasized: true, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one was showing emphasized colors but I would expect it to show read-only colors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
components/tag/stories/template.js
Outdated
@focusin=${function() { | ||
if (isReadOnly || isDisabled) return; | ||
updateArgs({ isFocused: true }); | ||
}} | ||
@focusout=${function() { | ||
updateArgs({ isFocused: false }); | ||
}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend removing this, as the focus events are not the same behavior as :focus-visible
. So currently if you click on the tag in the Default story, the focus indicator will appear when it shouldn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok! I'm using the keyboard focused instead of focused control now as well. I think that I'd thought it would be nice to see the control toggling when you keyboard focus, but that does have unintended side effects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs addition suggestion: I think it's worth mentioning in the Truncated story description that tags have a maximum width set. Since that might not be expected until it was encountered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't have had a maximum width set on the custom styles, since tag already has a max inline size set. I updated this story to show the truncated, max-width tag for all 3 sizes.
components/tag/index.css
Outdated
background-color: var(--highcontrast-tag-background-color-focus, var(--mod-tag-background-color-focus, var(--spectrum-tag-background-color-focus))); | ||
color: var(--highcontrast-tag-content-color-focus, var(--mod-tag-content-color-focus, var(--spectrum-tag-content-color-focus))); | ||
&:not(.is-disabled, .is-readOnly):focus-visible, | ||
&:not(.is-disabled, .is-readOnly).is-focused { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need .is-focused
for S2 if it's already handled by the :focus-visible
? Is this a legacy thing? If needed for the implementation to apply, it should probably be documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a reason that it needs .is-focused
, it's now using :focus-visible
only and keyboard focused in the controls.
components/tag/index.css
Outdated
--spectrum-tag-background-color-selected: var(--highcontrast-tag-background-color-selected, var(--mod-tag-background-color-selected, var(--spectrum-neutral-background-color-default))); | ||
--spectrum-tag-background-color-selected-hover: var(--highcontrast-tag-background-color-selected-hover, var(--mod-tag-background-color-selected-hover, var(--spectrum-neutral-background-color-hover))); | ||
--spectrum-tag-background-color-selected-active: var(--highcontrast-tag-background-color-selected-active, var(--mod-tag-background-color-selected-active, var(--spectrum-neutral-background-color-down))); | ||
--spectrum-tag-background-color-selected-focus: var(--highcontrast-tag-background-color-selected-focus, var(--mod-tag-background-color-selected-focus, var(--spectrum-neutral-background-color-key-focus))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could "selected" tokens be used for the value of existing custom properties in .spectrum-Tag.is-selected
, instead of creating new custom properties? For example:
&.is-selected {
--spectrum-tag-background-color: var(--highcontrast-tag-background-color-selected, var(--mod-tag-background-color-selected, var(--spectrum-neutral-background-color-default)));
--spectrum-tag-content-color: var(--highcontrast-tag-content-color-selected, var(--mod-tag-content-color-selected, var(--spectrum-gray-25)));
--spectrum-tag-border-color: var(--highcontrast-tag-border-color-selected, var(--mod-tag-border-color-selected, transparent));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have to come back to this one, I made an attempt but started getting tangled when it came to hover/focus/active states. Is button a good component to reference to see where else it's done like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 9624755
51c77f3
to
eec2584
Compare
outline: none; | ||
user-select: none; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to keep this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rise-erpelding I think it's worth keeping it. user-select: none
prevents selection or highlighting of text elements. If you double click it you'll see the text element is highlighted by the browser. If you add user-select: none
the highlight disappears.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rise-erpelding I agree with the removal, and don't see a reason to keep user-select: none
. If we want to be able to copy and highlight that text, like the guidelines say for read-only, and I think you and Josh already we talking about, removing user-select: none
should give us that ability.
The only "behavior" I'm unsure of is if the disabled tag is copy-able. Maybe user-select: none
has to be on a disabled tag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think slapping a user-select: none;
next to the pointer-events: none;
for disabled is a good call. Made that addition.
|
||
.spectrum-Thumbnail { | ||
/* prevent thumbnail shrinking when label is longer */ | ||
flex-shrink: 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear why only thumbnail seems to shrink when the label is long.
I did think about adding thumbnail/avatar/icon to the truncated section of tests and didn't do it because it would involve moving several things around the test file and the testing grid is already starting to feel a little bloated to me, I'm open to the idea though.
7e8269c
to
4ce8d19
Compare
6534c35
to
7094320
Compare
.changeset/khaki-icons-hammer.md
Outdated
|
||
Tag now uses Spectrum 2 tokens and specifications: | ||
|
||
- The invalid variant has been removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add it's been removed because there are no individual invalid tags and errors are displayed in tag groups.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks- a bunch of my initial feedback is complete and addressed!
I think my biggest curiosity, that I haven't quite worked out yet, is the truncation and close button issue I mentioned. Maybe we need
position: absolute;
inset-inline-end: calc(that-you-already-have); // margin-inline-end changes to inset-inline-end
And then maybe some max-inline-size control for the label itself, so it doesn't "go past" the clear button? Sort of like the wacky padding we have for inputs to accommodate icons within fields?
components/tag/stories/tag.test.js
Outdated
isSelected: true, | ||
}, | ||
{ | ||
testHeading: "Emphasized, selected", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** | ||
* Tags have a read-only option for when content in the disabled state still needs to be shown. | ||
* Read-only tags cannot be interacted with or changed. | ||
*/ | ||
export const ReadOnly = TagsDefaultOptions.bind({}); | ||
ReadOnly.storyName = "Read only"; | ||
ReadOnly.tags = ["!dev"]; | ||
ReadOnly.args = { | ||
isReadOnly: true, | ||
}; | ||
ReadOnly.parameters = { | ||
chromatic: { disableSnapshot: true }, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
border-radius: var(--mod-tag-corner-radius, var(--spectrum-tag-corner-radius)); | ||
border-width: var(--mod-tag-border-width, var(--spectrum-tag-border-width)); | ||
border-style: solid; | ||
|
||
padding-inline-start: calc(var(--mod-tag-spacing-inline-start, var(--spectrum-tag-spacing-inline-start)) - var(--mod-tag-border-width, var(--spectrum-tag-border-width))); | ||
padding-inline-start: calc(var(--mod-tag-label-spacing-inline, var(--spectrum-tag-label-spacing-inline)) - var(--mod-tag-border-width, var(--spectrum-tag-border-width))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rise-erpelding I'm not certain where the discrepancy is, so don't take the fact that I'm commenting here to heart too much! This felt sort of like a similar conversation...
I'm wondering about the truncation and the clear button now. I can really smoosh that clear button to the edge of the tag with a long label. How do we avoid that?


.spectrum-Thumbnail { | ||
/* set visual inline-start spacing, but take away label spacing, this will result in negative margin - this calc does not need border width */ | ||
margin-inline-start: calc(var(--mod-tag-visual-spacing-inline-start, var(--spectrum-tag-visual-spacing-inline-start)) - var(--mod-tag-label-spacing-inline, var(--spectrum-tag-label-spacing-inline))); | ||
margin-inline-end: var(--mod-tag-visual-spacing-inline-end, var(--spectrum-tag-visual-spacing-inline-end)); | ||
} | ||
|
||
/* clear button */ | ||
.spectrum-Tag-clearButton { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rise-erpelding, I also spotted what @marissahuysentruyt discovered when the longer the string in truncation mode the clear button magically slides over to the edge. One thing I tried to add:
.spectrum-Tag-clearButton {
inset-inline-end: var(--spectrum-tag-edge-to-clear-icon);
position: relative;
padding-inline-start: var(--spectrum-tag-label-to-clear-icon);
}
The longer the text it doesn't move over.
- updates to support visual content in storybook template: - icon and avatar controls moved to content section - thumbnail added - add visual control menu to choose visual -remove invalid variant references and options - support active, focus, hover - add documentation - minor refactoring and cleanup - support readonly
- remove selected template - add removable tag to grouped template - prevent showing of duplicate removable tag for in removable story - use grouped template for sizing
ADJUST FOCUS BEHAVIOR: - remove .is-focused states from CSS to use native pseudo-classes - use keyboard focused global state instead of focused global state in Storybook - change of arg from isFocused to isKeyboardFocused in template and tests - removal of focus event listeners to update args ADJUST EMPHASIZED STYLING - .is-emphasized is now .spectrum-Tag--emphasized in CSS and template - emphasized styles only take effect if also selected - update tests and test headings to better display the emphasized variant - small adjustments to story names on docs page to put selected stories next to each other and show emphasized and selected rather than just emphasized ADJUST READ ONLY STYLING - Remove user-select: none to make tag text selectable per guidelines - prevent selected styles from having effect if read-only or disabled - adjust emphasized & read-only tests to include selected state MISC OTHER FIXES - hasClearButton control has been changed to isRemovable - Update of Truncated story to remove max width and rely on component's max width
- change avatar size to 75 for medium - use flex-shrink to prevent thumbnail width shrinking when the label is long - use size map in the template for avatar and thumbnail sizing - fix removable tag functionality - remove focusability on clear button (it doesn't remove on delete but is that functionality that's needed here?), revert margin on clear button back to padding where it's not negative - add default sizing info on sizing story - combine inset focus indicator properties to use shorthand
This commit adjusts the spacing of the clear button to match the spec. Between the label and clear button the spacing should be as follows: S: 8px (Desktop) M: 12px (Desktop) L: 15px (Desktop) The label subtracts border width from the spacing, but when clear button exists, the label doesn't need to subtract the border width, so we'll add border width to the clear button spacing to cancel this out. The spacing between the label and clear button is the sum of the padding at the end of the label and the start of the clear button. I think the best way to test this is to add a mod for the border width, and verify that the spacing at the end of the label and start of the clear button continues to match the spec as mentioned above.
- puts clear button spacing tokens into calc for clear button size in order to expand target area - removes older clear button calcs, modified calcs serve to cancel out unneeded spacing from item label
cd7ce1c
to
d02e832
Compare
New changes! ✨
|
withHeading: false, | ||
withBorder: false, | ||
...args, | ||
}, context); | ||
TextOverflow.tags = ["!dev"]; | ||
TextOverflow.args = { | ||
hasIcon: true, | ||
iconName: "CheckmarkCircle", | ||
label: "An example of text overflow behavior. When the button text is too long for the horizontal space available, it will truncate and stay on one line.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super minor, but you mentioned "when the tag text" in the JS Doc but says "button text" here in the label prop - should be "tag text" too
- "--mod-tag-avatar-spacing-inline-end" --> "--mod-tag-visual-spacing-inline-end", | ||
- "--mod-tag-icon-spacing-inline-end" --> "--mod-tag-visual-spacing-inline-end", | ||
- "--mod-tag-spacing-inline-start" --> "--mod-tag-label-spacing-inline", | ||
- "--mod-tag-label-spacing-inline-end" --> "--mod-tag-label-spacing-inline" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comma at the end here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nifty fix using the flex-shrink
on the clear button there! Overall this PR is so well done and I like the test cases you added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonderful work on this, Rise! I'm going to approve, but I do suggest making the change on the location of the status
object for the migrated badge before we merge! Looks like there's a few from Aziz from Friday, but overall, the migration looks great to me, and all of my concerns from the clear button are ffffiiiixxxed! Thanks for all your hard work!
status: { | ||
type: "migrated", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This status
object should go in parameters
(at line 109ish) in order to get picked up properly- sorry I didn't clarify in my last comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely should have been checking to see if this was doing anything 😂
outline: none; | ||
user-select: none; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rise-erpelding I agree with the removal, and don't see a reason to keep user-select: none
. If we want to be able to copy and highlight that text, like the guidelines say for read-only, and I think you and Josh already we talking about, removing user-select: none
should give us that ability.
The only "behavior" I'm unsure of is if the disabled tag is copy-able. Maybe user-select: none
has to be on a disabled tag?
${args.isRemovable ? "" : Template({ | ||
...args, | ||
isRemovable: true, | ||
}, context)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Description
There was a previous version of this work in #2649 that had been shelved because icons had not been updated yet. That PR will be closed once this one merges.
This work migrates the tag component to Spectrum 2 tokens and specifications:
Additionally, Tag has an expanded testing grid to test more states and state combinations, an updated Docs page in Storybook, and updated Storybook controls to demonstrate Tag's supported content and states.
There are a few outstanding questions that I have, but I don't consider these to be blocking and will make follow-up tickets as needed to address these items:
neutral-selected-background-color-default
and its accompanying hover, key-focus, down tokens don't appear to exist, I used--spectrum-neutral-background-color-default
colors, which seem to mostly match the spec colors.Questions I have now:
How and where has this been tested?
Please tag yourself on the tests you've marked complete to confirm the tests have been run by someone other than the author.
Validation steps
Here are some things that I generally look at when reviewing other people's migrations that could be used to review this migration, plus a few specific callouts for this work that I'd love feedback on:
Regression testing
Validate:
Screenshots
Spectrum 2:

Spectrum 1:

To-do list