Skip to content

Conversation

@5t3ph
Copy link
Contributor

@5t3ph 5t3ph commented Dec 16, 2025

Description

This is PR number 5 of 5

This PR has two main bodies of work:

  • updates the theme/component class prefix to swc (most of the file changes)
  • wraps-up consumer docs for Storybook and developer/contributor docs

Motivation and context

  • prefix was tenatively agreed upon in recent team discussion
  • docs provide context and maintenance plans for all changes in the related feature PRs

Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features

Manual review test cases

Please thoroughly read the docs to make sure they are understandable and useful, both for ourselves and contributors.

@5t3ph 5t3ph requested a review from a team as a code owner December 16, 2025 16:28
@5t3ph 5t3ph added Feature New feature or request CSS Processing 2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. labels Dec 16, 2025
@changeset-bot
Copy link

changeset-bot bot commented Dec 16, 2025

⚠️ No Changeset found

Latest commit: fed3db0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Contributor

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-5944

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

Copy link
Member

@cdransf cdransf left a comment

Choose a reason for hiding this comment

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

Copy link
Contributor

@nikkimk nikkimk left a comment

Choose a reason for hiding this comment

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

Very informative and easy to understand. I love it when I learn things from a PR.

Comment on lines +9 to +14
1. use of style related attributes per component such as `variant` and `size`
2. providing [overrides of component custom properties](#overriding-component-custom-properties)
3. _coming soon:_ writing styles for specific component shadow parts
4. _coming soon:_ providing granular customization via the theme object
5. extending the components to include custom styles
6. [globally overriding design token](#globally-override-design-tokens) custom properties
Copy link
Contributor

Choose a reason for hiding this comment

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

I love that these are in sequence of simplest to complex to encourage consumers to try the simplest, often most robust choice.

}
```

If your override is applied too broadly, re-assess how the selector is scoped.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Can we provide an example and explain the impact?

Comment on lines +40 to +70
## Rule Order

Follow this outline for ordering rulesets within component stylesheets. This will provide consistency across component stylesheets, as well as help mitigate common specificity issues.

1. *If applicable*: `@layer` , `@keyframes`
2. `:host`
- Considered as a "container" for the component, and should not directly manage component styles itself. Its only concern is how the web component participates in the layout.
- Primarily includes a `display` property to match the layout flow intent, using either `inline-block` or `block`
- Avoid `inline` which would prevent consumers applying reasonable layout properties such as `margin`
- May also include "defensive" styles for `inline-block` elements
- `place-self: start` to avoid stretch behaviors if they are placed in a flex or grid context
- `vertical-align: middle` to keep a vertically centered position next to other `inline-block` elements (ex. a row of badges outside of a flex or grid context)
3. `* { box-sizing: border-box; }`
- Unless there is a strong reason not to, this rule should be included in all components. Expand to pseudo-elements if in use.
- Required if the component or its descendants set `padding` and/or `border` to avoid the compounding affect against the element's size.
4. component styles
- base class: `.swc-ComponentName`
- subcomponents: `.swc-ComponentName-sub-component`
- t-shirt sizes: `:host([size="s"])`
- Uses `:host()` to maintain exposure of size-related custom properties
- other variants
- `.swc-ComponentName--variant` - used for variants excluded from custom property exposure
- `:host([variant="value"])` - used for variants that should maintain custom property exposure
- states: `:host([aria-expanded])` , `:host:focus-visible` , etc.
- states should be attached to `:host/:host()` unless the WC expresses the state on internal elements
- include within variant styles if needed for specificity reasons
- `::slotted()` and `slot` styles
- `@media (forced-colors: active)`
- **First ensure that the styles are actually necessary**. Do not include styles if the browser defaults achieve visibility of critical component parts and states.
- If needed, this media query should *always* be at the end of the stylesheet to take priority over other styles.
- Use direct internal selectors, not host selectors, to enforce the style and prevent accidental consumer overrides.
Copy link
Contributor

Choose a reason for hiding this comment

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

<3 this section. Very helpful and easy to follow.

- `:host` is for defining how the container participates in the global layout, not the core component styles
- Follow the prescribed rule order
- Strive to keep selector specificity ≤ `\(0,1,0\)`
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Can we link to an anchor to the Managing Specificity section here?

Comment on lines +85 to +93
To keep specificity low, clauses beyond a single class can be wrapped in `:where()` which nulls the specificity of that clause to zero. Use of `:where()` is encouraged, has ample browser support, and will be expected in PR reviews for compounding class selectors.

```css
/* Before */
.swc-Divider--staticWhite.swc-Divider--sizeL

/* After */
.swc-Divider--staticWhite:where(.swc-Divider--sizeL)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

I love learning new things via PR reviews!


**Exceptions to max-specificity rule**:

- use of pseudo-classes and pseudo-elements are an acceptable bump to specificity, and should be applied outside of `:where()`
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Add inline examples here?

Copy link
Collaborator

@rise-erpelding rise-erpelding left a comment

Choose a reason for hiding this comment

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

Leaving some comments, I'm planning to come back to this to digest it a bit more later though! I'd also love to see some of this documentation presented/discussed at a future sync meeting, since it is very thorough but very important!

Overall, this is amazing work and will be such a huge improvement to our system, how we write CSS, and the consistency of the styles we ship!

5. extending the components to include custom styles
6. [globally overriding design token](#globally-override-design-tokens) custom properties

SWC strongly recommends using the least intrusive method possible for assigning custom styles. This will help keep your component styles overall inline with Spectrum design recommendations, and reduce the liklihood of compatibilty issues as the library evolves.
Copy link
Collaborator

Choose a reason for hiding this comment

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

[nit] a couple of small typos I noticed!

Suggested change
SWC strongly recommends using the least intrusive method possible for assigning custom styles. This will help keep your component styles overall inline with Spectrum design recommendations, and reduce the liklihood of compatibilty issues as the library evolves.
SWC strongly recommends using the least intrusive method possible for assigning custom styles. This will help keep your component styles overall inline with Spectrum design recommendations, and reduce the likelihood of compatibility issues as the library evolves.

- `vertical-align: middle` to keep a vertically centered position next to other `inline-block` elements (ex. a row of badges outside of a flex or grid context)
3. `* { box-sizing: border-box; }`
- Unless there is a strong reason not to, this rule should be included in all components. Expand to pseudo-elements if in use.
- Required if the component or its descendants set `padding` and/or `border` to avoid the compounding affect against the element's size.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- Required if the component or its descendants set `padding` and/or `border` to avoid the compounding affect against the element's size.
- Required if the component or its descendants set `padding` and/or `border` to avoid the compounding effect against the element's size.


Often, specs will be very prescriptive about what equates to `padding` for an element. That padding may vary for scenarios such as between the top of the component to it's text label vs. from the top to an optional icon.

It is tempting to use those values as prescribed in order to match the specs. However, the `display` choice of `grid` or `flex` should be taken into account first.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think there's a double space after display here that seems like it could be a typo.

Suggested change
It is tempting to use those values as prescribed in order to match the specs. However, the `display` choice of `grid` or `flex` should be taken into account first.
It is tempting to use those values as prescribed in order to match the specs. However, the `display` choice of `grid` or `flex` should be taken into account first.

- Prefer `gap` over overly prescriptive selectors that apply or remove margin
- *Exception*: if using `grid-template-areas` , the `gap` will still exist even if the grid area is not populated, so `margin` may be more appropriate
- Prefer alignment properties in coordination with min/max sizes before overly prescribing `padding` values
- *Example*: for Badge, there is a `min-block-size` and the specs provide different block padding values for an icon vs. a text label. By using flexbox and `align-items: center` , we only really need to set the *text-relative* block padding, which is more of a defense mechanism in case the badge label needs to wrap to prevent the text touching the component edge.
Copy link
Collaborator

Choose a reason for hiding this comment

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

There are also spaces between align-items: center and the comma following it.

Suggested change
- *Example*: for Badge, there is a `min-block-size` and the specs provide different block padding values for an icon vs. a text label. By using flexbox and `align-items: center` , we only really need to set the *text-relative* block padding, which is more of a defense mechanism in case the badge label needs to wrap to prevent the text touching the component edge.
- *Example*: for Badge, there is a `min-block-size` and the specs provide different block padding values for an icon vs. a text label. By using flexbox and `align-items: center`, we only really need to set the *text-relative* block padding, which is more of a defense mechanism in case the badge label needs to wrap to prevent the text touching the component edge.

For example:

- Prefer `gap` over overly prescriptive selectors that apply or remove margin
- *Exception*: if using `grid-template-areas` , the `gap` will still exist even if the grid area is not populated, so `margin` may be more appropriate
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to remove this space between grid-template-areas and the comma?

Suggested change
- *Exception*: if using `grid-template-areas` , the `gap` will still exist even if the grid area is not populated, so `margin` may be more appropriate
- *Exception*: if using `grid-template-areas`, the `gap` will still exist even if the grid area is not populated, so `margin` may be more appropriate

- This distinction directly affects which selector type is used (`:host()` vs internal class selectors). See [Variant Selectors and Inheritance](01_component-css.md#shadow-dom-specificity-and-custom-property-inheritance).
- May be exposed via inclusion in private property, or inline with CSS property
- Include in private property if value has repeated usage throughout base (non-variant) component styles
- In migrated components, legacy `--mod-* `properties should not be preserved; instead, collapse the chain into a single component-level property.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- In migrated components, legacy `--mod-* `properties should not be preserved; instead, collapse the chain into a single component-level property.
- In migrated components, legacy `--mod-*` properties should not be preserved; instead, collapse the chain into a single component-level property.


## Adding Global Tokens

Additional global tokens or token overrides may be necessary if values are unique to SWC, and not available - currently or planned - in the design token source package, `@adobe/spectrum-tokens` .
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Additional global tokens or token overrides may be necessary if values are unique to SWC, and not available - currently or planned - in the design token source package, `@adobe/spectrum-tokens` .
Additional global tokens or token overrides may be necessary if values are unique to SWC, and not available - currently or planned - in the design token source package, `@adobe/spectrum-tokens`.


<!-- Document content (editable) -->

Use this checklist when opening or reviewing a PR.
Copy link
Collaborator

Choose a reason for hiding this comment

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

THIS 👏 IS 👏 AMAZING 👏 , what a great idea to include a checklist!


## Contributor TL;DR

> For examples of all these rules in practice, review the [reference migration for Badge](#reference-migration-badge) as well as examples for avoiding issues in the [anti-patterns guide](05_anti-patterns.md).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would this link for Badge reference migration work better?

Suggested change
> For examples of all these rules in practice, review the [reference migration for Badge](#reference-migration-badge) as well as examples for avoiding issues in the [anti-patterns guide](05_anti-patterns.md).
> For examples of all these rules in practice, review the [reference migration for Badge](04_spectrum-swc-migration.md#reference-migration-badge) as well as examples for avoiding issues in the [anti-patterns guide](05_anti-patterns.md).


## Variants & States

- [ ] Variants that should expose custom properties use `:host([variant=&quot;…&quot;])`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was the use of &quot; intentional here? GitHub Markdown doesn’t unescape entities in backticks, so it renders literally.

Copy link
Collaborator

@rise-erpelding rise-erpelding left a comment

Choose a reason for hiding this comment

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

Leaving more comments as I'm wrapping my head around CSS authoring!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would this component be an exception to the "always apply * { box-sizing: border-box; }" rule?

3. `* { box-sizing: border-box; }`
- Unless there is a strong reason not to, this rule should be included in all components. Expand to pseudo-elements if in use.
- Required if the component or its descendants set `padding` and/or `border` to avoid the compounding affect against the element's size.
4. component styles
Copy link
Collaborator

Choose a reason for hiding this comment

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

To what degree is the order of the component styles listed here important?

For instance, it makes sense that base class should always be first, forced-colors should always be last, t-shirt sizing should be pretty high up, but states should be pretty low. When I look at the CSS we have in 2nd gen, it seems like there might be some wiggle room for the others?

For instance, badge.css doesn't respect a specific order between the non-exposed variant selectors (like .swc-Badge--fixed-inline-start) and exposed variant selectors (like :host([variant='neutral'])), but to me the variants are overall laid out in an order that makes sense (border radius variants, then color variants, then subtle and outline variants).

Or, divider.css has base class first (.swc-Divider), then the vertical variant (.swc-Divider:not(.swc-Divider--vertical) and .swc-Divider--vertical), then t-shirt sizing (:host([size='s'])) and then more variants (.swc-Divider--staticWhite).


Most components are scoped enough that following the prescribed rule order will avoid specificity clashes. However, in some cases such as compounded variants or variants plus states, selectors can still start to bump up specificity.

The issue with bumping up specificity is that is makes valid overrides - such as for a `:disabled` state - more challenging.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
The issue with bumping up specificity is that is makes valid overrides - such as for a `:disabled` state - more challenging.
The issue with bumping up specificity is that it makes valid overrides - such as for a `:disabled` state - more challenging.

.swc-Divider--staticWhite.swc-Divider--sizeL

/* After */
.swc-Divider--staticWhite:where(.swc-Divider--sizeL)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Checking my understanding:

  • Even though we use the variant selector for other large styles (:host([size='l'])), here we'd need to use the class selector (.swc-Divider--sizeL)
  • The reason we use the class selector for static white (.swc-Divider--staticWhite) is because we don't want to expose its custom properties, it's a variant where we shouldn't really be changing the divider color
  • We can't combine variant/attribute selectors because they target different elements in the DOM (for instance .swc-Divider--staticWhite:where(:host([size='l')) or :host([static-color="white"]):where(.swc-Divider--sizeL))
  • Even if exposure of custom properties wasn't a consideration in whether we use a variant selector or a class selector, we could do something like :host([size="l"]):where([static-color="white"]) but that's not as desirable because it increases specificity, but
  • There isn't really a benefit to using :where() for attribute selectors anyway. If it's necessary to use attribute selectors over class selectors for exposure reasons, for instance in badge's :host([outline][variant='neutral']), increasing specificity for the host attribute selectors doesn't matter to the class selectors, since class selectors win in that situation due to inheritance. We do increase specificity from something like :host([variant='neutral']), but that's desirable to be able to override it?


.spectrum-Badge--subtle {
&.spectrum-Badge--gray {
.swc-Badge--subtle {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason for this being nested/a reason to bump specificity? It seems like this would be a place where .spectrum-Badge--subtle:where(.spectrum-Badge--gray) would do the trick.


### Using Cascade Layers (`@layer`)

> *[DRAFT] guidance - formal RFC in the works*
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are there specific components that we're looking at using layers with? Like button/action button?

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

Labels

2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. CSS Processing Feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants