-
Notifications
You must be signed in to change notification settings - Fork 236
feat(divider): migrate to second-gen styles and architecture #5798
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
Changes from 15 commits
99923f4
12605f2
c15952b
07e7925
6e12ae4
b25ba03
c785f25
8c85c59
1c40f78
e3af985
f0d7607
d09e908
f5a6283
fedb16a
d2fb0ed
ae7081a
94a0864
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
||
| /** | ||
| * Whether the divider is vertical. If false, the divider is horizontal. The default is false. | ||
| */ | ||
|
|
@@ -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); | ||
|
|
||
| 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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do you feel separating the types and the runtime values in
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! 1c40f78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]; | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
| `; | ||
| }, | ||
| }); | ||
| 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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
| `; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perfect! |
||
| .spectrum-Divider { | ||
| --highcontrast-divider-background-color: CanvasText; | ||
| } | ||
| } | ||

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.
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! 😛
Uh oh!
There was an error while loading. Please reload this page.
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 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 forVALID_SIZES, sinceSizedMixindoes that for you.)Let me know if any of this isn't clear!