Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions first-gen/packages/divider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
],
"types": "./src/index.d.ts",
"dependencies": {
"@spectrum-web-components/base": "1.8.0",
"@swc/core": "workspace:*"
},
"keywords": [
Expand Down
15 changes: 13 additions & 2 deletions second-gen/packages/core/components/divider/Divider.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ import { property } from 'lit/decorators.js';

import { SizedMixin, SpectrumElement } from '@swc/core/shared/base';

import {
DIVIDER_STATIC_COLORS,
DIVIDER_VALID_SIZES,
type DividerStaticColor,
} from './Divider.types';

/**
* @element swc-divider
*/
export abstract class DividerBase extends SizedMixin(SpectrumElement, {
validSizes: ['s', 'm', 'l'],
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
Contributor

@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.

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.

/**
* Whether the divider is vertical. If false, the divider is horizontal. The default is false.
*/
Expand All @@ -30,9 +37,13 @@ export abstract class DividerBase extends SizedMixin(SpectrumElement, {

/**
* The static color variant to use for the divider.
*
* @todo Add runtime validation separately. When implementing,
* access STATIC_COLORS from this.constructor.STATIC_COLORS to ensure
* correct values are used.
*/
@property({ reflect: true, attribute: 'static-color' })
public staticColor?: 'white' | 'black';
public staticColor?: DividerStaticColor;

protected override firstUpdated(changed: PropertyValues<this>): void {
super.firstUpdated(changed);
Expand Down
18 changes: 18 additions & 0 deletions second-gen/packages/core/components/divider/Divider.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

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!

export const DIVIDER_STATIC_COLORS = ['white', 'black'] as const;
Comment on lines +15 to +16
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
Contributor

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.


export type DividerStaticColor = (typeof DIVIDER_STATIC_COLORS)[number];
1 change: 1 addition & 0 deletions second-gen/packages/core/components/divider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
* governing permissions and limitations under the License.
*/
export * from './Divider.base';
export * from './Divider.types';
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import { html } from 'lit';
import { makeDecorator } from '@storybook/preview-api';
import type { DecoratorFunction } from '@storybook/types';

/**
* 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. 👯

black: 'linear-gradient(45deg, rgb(255 241 246), rgb(238 245 255))',
white: 'linear-gradient(45deg, rgb(64 0 22), rgb(14 24 67))',
} as const;

/**
* Decorator that applies background colors based on static-color arg.
* Wraps the story in a div with the appropriate background when static-color is set.
*/
export const withStaticColorBackground: DecoratorFunction = makeDecorator({
name: 'withStaticColorBackground',
parameterName: 'staticColorBackground',
wrapper: (StoryFn, context) => {
const { args } = context;
const staticColor = args?.[
'static-color'
] as keyof typeof staticColorSettings;

const background =
staticColor && staticColorSettings[staticColor]
? staticColorSettings[staticColor]
: '';

// If no static color is set, just return the story as-is
if (!background) {
return StoryFn(context);
}

// Wrap the story with the background
return html`
<div style="background: ${background}; padding: 24px;">
${StoryFn(context)}
</div>
`;
},
});
9 changes: 9 additions & 0 deletions second-gen/packages/swc/.storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<style>
/* Fix for Storybook's zoom wrapper interfering with forced-colors mode */
@media (forced-colors: active) {
.innerZoomElementWrapper.innerZoomElementWrapper.innerZoomElementWrapper
> * {
border: none !important;
}
}
</style>
2 changes: 2 additions & 0 deletions second-gen/packages/swc/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
type Options,
} from '@wc-toolkit/storybook-helpers';
import customElements from './custom-elements.json';
import { withStaticColorBackground } from './decorators/static-color-background';

const options: Options = {
categoryOrder: [
Expand All @@ -31,6 +32,7 @@ setStorybookHelpersConfig(options);
setCustomElementsManifest(customElements);

const preview = {
decorators: [withStaticColorBackground],
parameters: {
layout: 'centered',
controls: {
Expand Down
28 changes: 26 additions & 2 deletions second-gen/packages/swc/components/divider/Divider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,42 @@
*/

import { CSSResultArray, html, TemplateResult } from 'lit';
import { classMap } from 'lit/directives/class-map.js';

import { DividerBase } from '@swc/core/components/divider';

import styles from './divider.css';

// @todo Pull this up into a utility function for all components to leverage
function capitalize(str?: string): string {
if (typeof str !== 'string') {
return '';
}
return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
* @element sp-divider
* @element swc-divider
*/
export class Divider extends DividerBase {
// ────────────────────
// RENDERING & STYLING
// ────────────────────

public static override styles: CSSResultArray = [styles];

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.

😍

class=${classMap({
['spectrum-Divider']: true,
[`spectrum-Divider--size${this.size?.toUpperCase()}`]:
this.size != null,
[`spectrum-Divider--static${capitalize(this.staticColor)}`]:
this.staticColor != null,
[`spectrum-Divider--vertical`]: this.vertical,
})}
></div>
`;
}
}
124 changes: 45 additions & 79 deletions second-gen/packages/swc/components/divider/divider.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,111 +12,77 @@

:host {
display: block;

--spectrum-divider-background-color: var(--spectrum-gray-300);
--spectrum-divider-background-color-static-white: var(
--spectrum-transparent-white-300
);
--spectrum-divider-background-color-static-black: var(
--spectrum-transparent-black-300
);
}

hr {
border: none;
margin: 0;
}

@media (forced-colors: active) {
:host {
--highcontrast-divider-background-color: CanvasText;
}
}

:host {
.spectrum-Divider {
--spectrum-divider-background-color: var(--spectrum-gray-200);
--spectrum-divider-thickness: var(--spectrum-divider-thickness-medium);
--spectrum-divider-inline-minimum-size: var(
--spectrum-divider-horizontal-minimum-width
);
--spectrum-divider-block-minimum-size: var(
--spectrum-divider-vertical-minimum-height
);
}

:host([size='s']) {
.spectrum-Divider--sizeS {
--spectrum-divider-thickness: var(--spectrum-divider-thickness-small);
}

:host([size='l']) {
.spectrum-Divider--sizeL {
--spectrum-divider-thickness: var(--spectrum-divider-thickness-large);
--spectrum-divider-background-color: var(--spectrum-gray-800);
}

:host([static-color='white']) {
--mod-divider-background-color: var(
--mod-divider-background-color-medium-static-white,
var(--spectrum-divider-background-color-static-white)
);
}

:host([static-color='white'][size='s']) {
--mod-divider-background-color: var(
--mod-divider-background-color-small-static-white,
var(--spectrum-divider-background-color-static-white)
);
}

:host([static-color='white'][size='l']) {
--mod-divider-background-color: var(
--mod-divider-background-color-large-static-white,
var(--spectrum-transparent-white-800)
);
/* static white variant colors */
.spectrum-Divider--staticWhite {
--spectrum-divider-background-color: var(--spectrum-transparent-white-200);
}

:host([static-color='black']) {
--mod-divider-background-color: var(
--mod-divider-background-color-medium-static-black,
var(--spectrum-divider-background-color-static-black)
);
.spectrum-Divider--staticWhite.spectrum-Divider--sizeL {
--spectrum-divider-background-color: var(--spectrum-transparent-white-800);
}

:host([static-color='black'][size='s']) {
--mod-divider-background-color: var(
--mod-divider-background-color-small-static-black,
var(--spectrum-divider-background-color-static-black)
);
/* static black variant colors */
.spectrum-Divider--staticBlack {
--spectrum-divider-background-color: var(--spectrum-transparent-black-200);
}

:host([static-color='black'][size='l']) {
--mod-divider-background-color: var(
--mod-divider-background-color-large-static-black,
var(--spectrum-transparent-black-800)
);
.spectrum-Divider--staticBlack.spectrum-Divider--sizeL {
--spectrum-divider-background-color: var(--spectrum-transparent-black-800);
}

:host {
block-size: var(--mod-divider-thickness, var(--spectrum-divider-thickness));
.spectrum-Divider {
block-size: var(--spectrum-divider-thickness);
inline-size: 100%;

/* Show the overflow for hr in Edge and IE. */
overflow: visible;
border: none;
border-width: var(
--mod-divider-thickness,
var(--spectrum-divider-thickness)
);
border-radius: var(
--mod-divider-thickness,
var(--spectrum-divider-thickness)
);
border-width: var(--spectrum-divider-thickness);
border-radius: var(--spectrum-divider-thickness);
background-color: var(
--highcontrast-divider-background-color,
var(
--mod-divider-background-color,
var(--spectrum-divider-background-color)
)
var(--spectrum-divider-background-color)
);
overflow: visible;
}

:host([vertical]) {
inline-size: var(
--mod-divider-thickness,
var(--spectrum-divider-thickness)
);
.spectrum-Divider:not(.spectrum-Divider--vertical) {
min-inline-size: var(--spectrum-divider-inline-minimum-size);
}

/* vertical dividers */
.spectrum-Divider--vertical {
inline-size: var(--spectrum-divider-thickness);
min-block-size: var(--spectrum-divider-block-minimum-size);
margin-block: 0;
block-size: 100%;
block-size: var(--mod-divider-vertical-height, 100%);
margin-block: var(--mod-divider-vertical-margin);
align-self: var(--mod-divider-vertical-align);
align-self: flex-start;
}

/* 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!

.spectrum-Divider {
--highcontrast-divider-background-color: CanvasText;
}
}
Loading
Loading