Skip to content

Conversation

rise-erpelding
Copy link
Collaborator

@rise-erpelding rise-erpelding commented Oct 10, 2025

Description

This PR migrates the next-gen Divider component to Spectrum 2 styles and updates Storybook accordingly.

It also introduces a new Storybook decorator for handling static color backgrounds.

Motivation and context

This PR migrates the Divider component from first-gen to the second-gen architecture, following the established pattern of separating core component logic from Spectrum-specific implementation.

Changes:

  • Migrated CSS to align with Spectrum CSS structure
  • Added type definitions for size and static color variants
  • Created comprehensive Storybook stories with new static color decorator

What changed

First-gen:

  • first-gen/packages/divider/src/Divider.ts - Remains unchanged for backward compatibility

Core (Base):

  • second-gen/packages/core/components/divider/Divider.base.ts - Updates shared functionalities
  • second-gen/packages/core/components/divider/Divider.types.ts - Creates types file for size and static color variants
  • second-gen/packages/core/components/divider/index.ts - Imports and exports types

Second-gen:

  • second-gen/packages/swc/components/divider/Divider.ts - Updates render method to accommodate classes
  • second-gen/packages/swc/components/divider/divider.css - Adds CSS from spectrum-css
  • second-gen/packages/swc/components/divider/stories/divider.stories.ts - Adds comprehensive story coverage
  • second-gen/packages/swc/.storybook/decorators/static-color-background.ts - Creates decorator for static color backgrounds
  • second-gen/packages/swc/.storybook/preview.ts - Registers the static color decorator

Related issue(s)

  • SWC-1259

Screenshots (if appropriate)

image

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 added automated tests to cover my changes.
  • 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 a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features (Note: I did not add a changeset, I wasn't sure that this was set up for barebones and didn't see any for progress circle or badge, but I'm happy to add one if I'm mistaken!)
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

Run second-gen Storybook:

yarn workspace @swc/components storybook

Storybook functionality:

  • All controls work correctly on Default story
  • Static color control applies appropriate backgrounds via decorator
  • Size control accepts string values (s, m, l) to change size
  • Vertical control toggles orientation correctly

Documentation:

  • All stories render correctly on Docs page
  • Story descriptions are clear and informative
  • Examples demonstrate all component variants (sizes, orientations, static colors) - see component analysis docs for a clearer picture of what changes for Spectrum 2

Static color decorator:

  • Works in Default story when using controls to set static-color (as seen in Divider and Progress Circle)
  • Works in StaticBlack and StaticWhite stories on Docs page (applies to Divider only), and only on those stories

First-gen verification (use the PR preview or run locally):

yarn start
# or
yarn docs:start
  • First-gen Divider remains unchanged and functional (VRT failures should be the same as on Barebones #5785)

Code review checklist:

  • TypeScript types are correct and properly exported
  • CSS follows second-gen patterns
  • No linter errors
  • Proper separation of concerns between core and SWC packages

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

Copy link

changeset-bot bot commented Oct 10, 2025

⚠️ No Changeset found

Latest commit: 94a0864

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

@rise-erpelding rise-erpelding self-assigned this Oct 10, 2025
@rise-erpelding rise-erpelding added 2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. Component: Divider Spectrum CSS Spectrum 2 Issues related to Spectrum 2 labels Oct 10, 2025
Copy link
Contributor

github-actions bot commented Oct 10, 2025

📚 Branch Preview

🔍 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-5798

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
Contributor

Tachometer results

Currently, no packages are changed by this PR...

Copy link
Collaborator Author

@rise-erpelding rise-erpelding Oct 10, 2025

Choose a reason for hiding this comment

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

Keeping this separate so it can be rolled back easily if we don't want to add it at this time. I noticed that the static color controls on progress circle didn't apply a background making the static white invisible, so this would address that as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is great! I think we should definitely keep it for now. It is one of those things we can always revisit once we dive deeper into our Storybook setup.

Comment on lines +29 to +40
/*
* @todo This is properly configuring the Select, but the control doesn't
* seem to work; need to investigate.
*/

// argTypes.size = {
// ...argTypes.size,
// control: { type: 'select' },
// options: Divider.VALID_SIZES,
// };
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the same situation as with Badge and Progress Circle, copying this over from those components.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could it be related to us not defining the size property on the component? What I understand from Cursor is that we get the argTypes using getStorybookHelpers, but getStorybookHelpers doesn't bind the size attribute to the component in the "regular" template(args) render we're trying to use.

So because divider extends the SizedMixin(), but we're only pulling the argTypes from divider specifically, is getStorybookHelpers not seeing the size property?

While I was experimenting with this in status light, if I use a different render method that binds the size property:

render: (args) =>
        html`<swc-status-light .size=${args.size} variant=${args.variant}
            >${args['default-slot']}</swc-status-light
        >`,

I think I can get the commented out code to work as we're expecting. Not sure if that's something we want to do right now though.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@marissahuysentruyt Thank you for digging into this! I think the solution you came up with is a good workaround, and one we may ultimately end up adopting, but I'd like to hold off for now so we can troubleshoot our Custom Element Manifest tooling pipeline and figure out why mixin-defined properties aren't propagating as we'd like them to.

@rise-erpelding rise-erpelding marked this pull request as ready for review October 10, 2025 16:32
@rise-erpelding rise-erpelding requested a review from a team as a code owner October 10, 2025 16:32
@rise-erpelding rise-erpelding changed the title feat(divider): migrate to second-gen style and architecture feat(divider): migrate to second-gen styles and architecture Oct 10, 2025
}

/* windows high contrast mode */
@media (forced-colors: active) {
Copy link
Collaborator Author

@rise-erpelding rise-erpelding Oct 10, 2025

Choose a reason for hiding this comment

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

If you test WHCM or forced-colors (even adjusting the Rendering in Chrome will reveal this), you'll see the divider on the docs page looks something like this:
image

I did investigate this, and it looks like it's coming from Storybook styling that's adding an 8px transparent border that shows up in forced colors. We might want to remove that eventually, but that feels a little out of scope for now. And the border isn't visible when you view the default story.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, is this something we can override with forced specificity in the Storybook styles?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Was this what you meant? I added some styles to a new preview-head.html file in f0d7607 but the class selector needs to be tripled in order to increase the specificity enough to override.

Copy link
Contributor

Choose a reason for hiding this comment

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

Perfect!

Copy link
Contributor

@rubencarvalho rubencarvalho left a comment

Choose a reason for hiding this comment

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

Looking GREAT!

I will leave the CSS folks to chime in on the styling aspect of it 😄

validSizes: DIVIDER_VALID_SIZES,
noDefaultSize: true,
}) {
static readonly STATIC_COLORS = DIVIDER_STATIC_COLORS;
Copy link
Contributor

Choose a reason for hiding this comment

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

We’re still missing the contributing documentation that will clarify these nuances (SWC-1234). However, in this specific case, we don’t need to add this static variable. It is only needed to handle cases where these values differ from S1/S2 and we want to throw dev warnings (e.g., deprecation, invalid value) at runtime. By making these values class properties, we can access them through the constructor and retrieve the correct value being loaded, whether it’s from the first or second generation. Hopefully, we can make this information clear enough in the aforementioned documentation! 😛

Copy link
Collaborator

@graynorton graynorton Oct 13, 2025

Choose a reason for hiding this comment

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

@rise-erpelding: I discussed this briefly offline with @rubencarvalho, and we agreed that for now we'd like to consistently apply the pattern where we hang structured Spectrum data-type arrays off of the component's constructor.

Let's do this even in cases where the data type doesn't vary from S1 to S2, and even in cases where we don't have a specific need for the structured types at runtime.

At minimum, doing this consistently means we should essentially never have to separately import the structured type arrays into a component's stories—we can always just import the constructor and access the type arrays from there.

The fundamental reasoning behind this is to minimize cognitive load for component authors and maximize consistency.

In the case of Divider, this would mean defining static readonly properties on the base component for both VALID_SIZES and STATIC_COLORS.

In the case of Divider, this would mean defining a static readonly property on the base component for STATIC_COLORS. (It's not necessary to define one for VALID_SIZES, since SizedMixin does that for you.)

Let me know if any of this isn't clear!

Copy link
Contributor

Choose a reason for hiding this comment

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

This is great! I think we should definitely keep it for now. It is one of those things we can always revisit once we dive deeper into our Storybook setup.

@rubencarvalho
Copy link
Contributor

Also, that screenshot in the description is :chefkiss:

"types": "./src/index.d.ts",
"dependencies": {
"@spectrum-web-components/base": "1.8.0",
"@spectrum-web-components/shared": "1.8.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure we need the shared dependency 🤔

Suggested change
"@spectrum-web-components/shared": "1.8.0",

Copy link
Contributor

@castastrophe castastrophe left a comment

Choose a reason for hiding this comment

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

This looks great to me! Would you mind updating the S2 styles to remove the modifiers and then it's an approve from me. I do wish we had VRTs or baselines to use to ensure these changes are showing up the way we expect for all our variants.

/**
* Static color background settings - matching spectrum-css gradients
*/
const staticColorSettings = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks so much for adding this! After this merges, I could go back into progress circle and update it to use this. 👯


import type { ElementSize } from '@swc/core/shared/base';

export const DIVIDER_VALID_SIZES: ElementSize[] = ['s', 'm', 'l'] as const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice job here!

protected override render(): TemplateResult {
return html``;
return html`
<div
Copy link
Contributor

Choose a reason for hiding this comment

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

😍

);
overflow: visible;

&:not(&.spectrum-Divider--vertical) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
&:not(&.spectrum-Divider--vertical) {
&:not(.spectrum-Divider--vertical) {

I don't think the amper used inside the not selector is going to give the result you are looking for here.

Copy link
Collaborator Author

@rise-erpelding rise-erpelding Oct 13, 2025

Choose a reason for hiding this comment

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

Nice catch! I had copied over my CSS without cleaning it up (thus the not removing of the mods). Do you have a preference on nesting/no nesting, for instance with something like:

.spectrum-Divider--staticWhite {
	--spectrum-divider-background-color: var(--spectrum-transparent-white-200);

	&.spectrum-Divider--sizeL {
		--spectrum-divider-background-color: var(--spectrum-transparent-white-800);
	}
}

It didn't look like badge and progress circle CSS were using it, so I will be removing it here as well (relying more on what's in the dist index.css), but let me know if that's too much!

Comment on lines 96 to 99
min-block-size: var(
--mod-divider-block-minimum-size,
var(--spectrum-divider-block-minimum-size)
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
min-block-size: var(
--mod-divider-block-minimum-size,
var(--spectrum-divider-block-minimum-size)
);
min-block-size: var(--spectrum-divider-block-minimum-size);

Would you mind going through here and cleaning up the mods? Since we're not supporting mods in S2, the idea was to remove them in Spectrum CSS before we bring over the code but we haven't had a chance to do that yet.

}

/* windows high contrast mode */
@media (forced-colors: active) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, is this something we can override with forced specificity in the Storybook styles?

handles: events,
},
},
tags: ['migrated'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Yay!

Copy link
Contributor

@Rajdeepc Rajdeepc left a comment

Choose a reason for hiding this comment

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

Good work. Some observations on few things like

  1. Type separation, double check the removed hr tag if that is intentional?
  2. Can you also add a runtime validation for user-facing props like if the user passes invalid value for size or static-color does the component fail gracefully?

Comment on lines +15 to +16
export const DIVIDER_VALID_SIZES: ElementSize[] = ['s', 'm', 'l'] as const;
export const DIVIDER_STATIC_COLORS = ['white', 'black'] as const;
Copy link
Contributor

Choose a reason for hiding this comment

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

How do you feel separating the types and the runtime values in .consts.ts and .types.ts files?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done! 1c40f78

Copy link
Collaborator

Choose a reason for hiding this comment

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

@Rajdeepc @rise-erpelding While I'm definitely open to evolving in this direction as we work our way through more components and refine our approach to defining structured Spectrum data types, I'd prefer to keep it simple for now by maintaining just one file.

My reasoning here is that I could see us evolving in various directions from this initial starting point, so doing this refactor now seems premature.

class=${classMap({
['spectrum-Divider']: true,
[`spectrum-Divider--size${this.size?.toUpperCase()}`]:
typeof this.size !== 'undefined',
Copy link
Contributor

Choose a reason for hiding this comment

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

You can also use this.size != null for both null and undefined.

Comment on lines 25 to 28
hr {
border: none;
margin: 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please double check this is not a breaking change?

Copy link
Collaborator Author

@rise-erpelding rise-erpelding Oct 13, 2025

Choose a reason for hiding this comment

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

I don't see this component rendering an <hr>, it renders an empty template for first-gen (and then our div wrapper with the classes on it for second-gen), so it doesn't seem like those styles were doing anything there, unless I'm missing something.

I will say that I see the CSS implementation optionally using <hr> though (and while the styles do set border: none, the margin isn't corrected for the horizontal divider), so I'm not sure if SWC once used <hr> as well and this is remaining from that.

@rise-erpelding rise-erpelding force-pushed the rise-erpelding/swc-1259--divider-barebones branch from 5966e68 to e3af985 Compare October 13, 2025 17:44
Copy link
Collaborator

@graynorton graynorton left a comment

Choose a reason for hiding this comment

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

Looks great! 😃

Per specific comments, I'd just like to request a few changes:

  1. Add the readonly static property STATIC_COLORS to the base class, and update the stories to access both STATIC_COLORS and VALID_SIZES from the component's constructor, instead of importing them directly into module scope.

  2. Collapse const definitions back into Divider.types.ts for now.

  3. Back the newly added validation code for staticColor out of this PR and add a @todo instead.

  4. Change the API OVERRIDES comment header to RENDERING & STYLING

Comment on lines 38 to 54
public get staticColor(): DividerStaticColor | undefined {
return this._staticColor;
}

public set staticColor(value: DividerStaticColor | undefined) {
if (
value != null &&
!(DIVIDER_STATIC_COLORS as readonly string[]).includes(value)
) {
// Silently ignore invalid values
this._staticColor = undefined;
return;
}
this._staticColor = value;
}

private _staticColor?: DividerStaticColor;
Copy link
Collaborator

Choose a reason for hiding this comment

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

A couple of things:

  1. As a matter of course, I'd prefer that we not include functional changes as part of the migration PRs; I'd rather that, when we come across a potential fix or improvement like this during migration, we add a @todo and come back to it in a separate PR.

  2. When we do need to do runtime validation against structured Spectrum type data, I'd like to consistently access that data off of the component's constructor. Accessing it directly from module scope like this means that, if things were to change down the road and this particular data type started varying from one concrete implementation to another, this logic would not be accessing the correct data.

Comment on lines 32 to 35
// ────────────────────
// API OVERRIDES
// ────────────────────

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 this should actually be RENDERING & STYLING, not API OVERRIDES.

Comment on lines 18 to 21
import {
DIVIDER_STATIC_COLORS,
DIVIDER_VALID_SIZES,
} from '@swc/core/components/divider';
Copy link
Collaborator

Choose a reason for hiding this comment

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

These are the imports we can avoid if we systematically hang the structured Spectrum data type arrays off of the component's constructor.

Side note: You can actually already avoid the import of DIVIDER_VALID_SIZES, because SizedMixin itself already hangs VALID_SIZES off of the constructor.

Copy link
Collaborator

@graynorton graynorton left a comment

Choose a reason for hiding this comment

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

Looks great!

validSizes: DIVIDER_VALID_SIZES,
noDefaultSize: true,
}) {
static readonly STATIC_COLORS = DIVIDER_STATIC_COLORS;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry, missed this earlier, but we should include a doc block and mark it as @internal, and explicitly type it to be readonly string[], as in this example from Badge:

    /**
     * @internal
     */
    static readonly FIXED_VALUES: readonly string[] = FIXED_VALUES;

In cases like this, where it isn't being overridden, the explicit typing isn't strictly necessary, but I'd like to do it as a matter of consistency, as TypeScript does complain if we don't explicitly type and then override in a subclass.

Since it's late in the afternoon and you're on PTO starting tomorrow, I'm going to go ahead and approve the PR rather than request another change; we can always add a commit or edit after merging.

}

/* windows high contrast mode */
@media (forced-colors: active) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perfect!

@marissahuysentruyt marissahuysentruyt added the ready-for-merge Will auto-update until merged label Oct 15, 2025
@marissahuysentruyt marissahuysentruyt merged commit 5dcdae8 into barebones Oct 15, 2025
16 of 22 checks passed
@marissahuysentruyt marissahuysentruyt deleted the rise-erpelding/swc-1259--divider-barebones branch October 15, 2025 15:56
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. Component: Divider ready-for-merge Will auto-update until merged Spectrum CSS Spectrum 2 Issues related to Spectrum 2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants