Skip to content

Commit 7998df6

Browse files
marissahuysentruytcdransf
authored andcommitted
feat(statuslight): ensure visual fidelity for status light (#6133)
* feat(statuslight): export status light type for sizes * feat(statuslight): map through status light sizes in story * fix(statuslight): padding block uses padding-vertical tokens * docs(status-light): add argType defaults and clean up story args - Declare defaultValue summaries in argTypes for variant and size so Storybook's controls table shows the defaults - Remove redundant hardcoded args from Playground, Overview, and Anatomy now that defaults are reflected through argTypes - Add semantic variant usage note to SemanticVariants story * docs(statuslight): extra a11y explanations * style(statuslight): fixes according to style guides * docs: update status light contributor examples * docs: update more status light examples in guides * fix(statuslight): use new line-height tokens * docs(statuslight): clarify where to see i18n status light story * docs(statuslight): remove tags from i18n story * fix(statuslight): remove exposed color prop and redundant properties
1 parent 8819601 commit 7998df6

7 files changed

Lines changed: 63 additions & 38 deletions

File tree

2nd-gen/packages/core/components/status-light/StatusLight.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,5 @@ export type StatusLightColorVariant =
9393
export type StatusLightVariantS1 = (typeof STATUSLIGHT_VARIANTS_S1)[number];
9494
export type StatusLightVariantS2 = (typeof STATUSLIGHT_VARIANTS_S2)[number];
9595
export type StatusLightVariant = StatusLightVariantS1 | StatusLightVariantS2;
96+
97+
export type StatusLightSize = (typeof STATUSLIGHT_VALID_SIZES)[number];

2nd-gen/packages/swc/components/status-light/status-light.css

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,21 @@
2222
}
2323

2424
.swc-StatusLight {
25-
--_swc-statuslight-top-to-text: var(--swc-statuslight-top-to-text, token("component-top-to-text-100"));
26-
--_swc-statuslight-bottom-to-text: var(--swc-statuslight-bottom-to-text, token("component-bottom-to-text-100"));
25+
--_swc-statuslight-padding-block: var(--swc-statuslight-padding-block, token("component-padding-vertical-100"));
2726
--_swc-statuslight-top-to-dot: var(--swc-statuslight-top-to-dot, token("status-light-top-to-dot-medium"));
2827
--_swc-statuslight-text-to-visual: var(--swc-statuslight-text-to-visual, token("status-light-text-to-visual-100"));
28+
--_swc-statuslight-line-height: var(--swc-statuslight-line-height, token("line-height-font-size-100"));
2929

3030
display: flex;
3131
gap: var(--_swc-statuslight-text-to-visual);
3232
align-items: flex-start;
3333
min-block-size: var(--swc-statuslight-height, token("component-height-100"));
34-
padding-block-start: var(--_swc-statuslight-top-to-text);
35-
padding-block-end: var(--_swc-statuslight-bottom-to-text);
34+
padding-block: var(--_swc-statuslight-padding-block);
3635
font-size: var(--swc-statuslight-font-size, token("font-size-100"));
3736
font-style: token("default-font-style");
3837
font-weight: token("regular-font-weight");
39-
line-height: token("line-height-100");
40-
color: var(--swc-statuslight-content-color, token("neutral-content-color-default"));
38+
line-height: var(--_swc-statuslight-line-height);
39+
color: token("neutral-content-color-default");
4140

4241
&:lang(ja),
4342
&:lang(zh),
@@ -49,50 +48,48 @@
4948
&::before {
5049
--_swc-statuslight-dot-size: var(--swc-statuslight-dot-size, token("status-light-dot-size-medium"));
5150

52-
box-sizing: border-box;
53-
display: inline-block;
5451
flex-grow: 0;
5552
flex-shrink: 0;
5653
inline-size: var(--_swc-statuslight-dot-size);
5754
block-size: var(--_swc-statuslight-dot-size);
58-
margin-block-start: calc(var(--_swc-statuslight-top-to-dot) - var(--_swc-statuslight-top-to-text));
55+
margin-block-start: calc(var(--_swc-statuslight-top-to-dot) - var(--_swc-statuslight-padding-block));
5956
background-color: var(--swc-statuslight-dot-color);
6057
border-radius: token("corner-radius-full");
6158
content: "";
6259
}
6360
}
6461

6562
:host([size="s"]) {
66-
--swc-statuslight-top-to-text: token("component-top-to-text-75");
67-
--swc-statuslight-bottom-to-text: token("component-bottom-to-text-75");
63+
--swc-statuslight-padding-block: token("component-padding-vertical-75");
6864
--swc-statuslight-top-to-dot: token("status-light-top-to-dot-small");
6965
--swc-statuslight-text-to-visual: token("status-light-text-to-visual-75");
7066
--swc-statuslight-height: token("component-height-75");
7167
--swc-statuslight-dot-size: token("status-light-dot-size-small");
7268
--swc-statuslight-font-size: token("font-size-75");
69+
--swc-statuslight-line-height: token("line-height-font-size-75");
7370
}
7471

7572
:host([size="l"]) {
76-
--swc-statuslight-top-to-text: token("component-top-to-text-200");
77-
--swc-statuslight-bottom-to-text: token("component-bottom-to-text-200");
73+
--swc-statuslight-padding-block: token("component-padding-vertical-200");
7874
--swc-statuslight-top-to-dot: token("status-light-top-to-dot-large");
7975
--swc-statuslight-text-to-visual: token("status-light-text-to-visual-200");
8076
--swc-statuslight-height: token("component-height-200");
8177
--swc-statuslight-dot-size: token("status-light-dot-size-large");
8278
--swc-statuslight-font-size: token("font-size-200");
79+
--swc-statuslight-line-height: token("line-height-font-size-200");
8380
}
8481

8582
:host([size="xl"]) {
86-
--swc-statuslight-top-to-text: token("component-top-to-text-300");
87-
--swc-statuslight-bottom-to-text: token("component-bottom-to-text-300");
83+
--swc-statuslight-padding-block: token("component-padding-vertical-300");
8884
--swc-statuslight-top-to-dot: token("status-light-top-to-dot-extra-large");
8985
--swc-statuslight-text-to-visual: token("status-light-text-to-visual-300");
9086
--swc-statuslight-height: token("component-height-300");
9187
--swc-statuslight-dot-size: token("status-light-dot-size-extra-large");
9288
--swc-statuslight-font-size: token("font-size-300");
89+
--swc-statuslight-line-height: token("line-height-font-size-300");
9390
}
9491

95-
/* Semantic Colors */
92+
/* Semantic colors */
9693
:host([variant="neutral"]) {
9794
--swc-statuslight-content-color: token("gray-600");
9895
--swc-statuslight-dot-color: token("neutral-visual-color");
@@ -114,7 +111,7 @@
114111
--swc-statuslight-dot-color: token("positive-visual-color");
115112
}
116113

117-
/* Non-Semantic Colors */
114+
/* Non-semantic colors */
118115
.swc-StatusLight--yellow {
119116
--swc-statuslight-dot-color: token("yellow-visual-color");
120117
}

2nd-gen/packages/swc/components/status-light/stories/status-light.stories.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers';
1616

1717
import { StatusLight } from '@adobe/spectrum-wc/status-light';
1818
import {
19+
STATUSLIGHT_VALID_SIZES,
1920
STATUSLIGHT_VARIANTS_COLOR_S2,
2021
STATUSLIGHT_VARIANTS_SEMANTIC_S2,
2122
StatusLightColorVariantS2,
2223
StatusLightSemanticVariantS2,
24+
type StatusLightSize,
2325
} from '@spectrum-web-components/core/components/status-light';
2426

2527
import '@adobe/spectrum-wc/status-light';
@@ -38,20 +40,29 @@ argTypes.variant = {
3840
...argTypes.variant,
3941
control: { type: 'select' },
4042
options: StatusLight.VARIANTS,
43+
table: {
44+
category: 'attributes',
45+
defaultValue: {
46+
summary: 'info',
47+
},
48+
},
4149
};
4250

4351
argTypes.size = {
4452
...argTypes.size,
4553
control: { type: 'select' },
4654
options: StatusLight.VALID_SIZES,
55+
table: {
56+
category: 'attributes',
57+
defaultValue: {
58+
summary: 'm',
59+
},
60+
},
4761
};
4862

4963
/**
5064
* Status lights describe the condition of an entity. Much like [badges](../?path=/docs/components-badge--readme), they can be used to convey semantic meaning, such as statuses and categories.
5165
*/
52-
args['default-slot'] = 'Status light';
53-
args.size = 'm';
54-
5566
export const meta: Meta = {
5667
title: 'Status light',
5768
component: 'swc-status-light',
@@ -109,28 +120,31 @@ const nonSemanticLabels = {
109120
silver: 'Version 1.2.10',
110121
} as const satisfies Record<StatusLightColorVariantS2, string>;
111122

123+
const sizeLabels = {
124+
s: 'Small',
125+
m: 'Medium',
126+
l: 'Large',
127+
xl: 'Extra-large',
128+
} as const satisfies Record<StatusLightSize, string>;
129+
112130
// ────────────────────
113131
// AUTODOCS STORY
114132
// ────────────────────
115133

116134
export const Playground: Story = {
117135
tags: ['autodocs', 'dev'],
118136
args: {
119-
size: 'm',
120-
variant: 'info',
121137
'default-slot': 'Active',
122138
},
123139
};
124140

125141
// ────────────────────
126-
// OVERVIEW STORY
142+
// OVERVIEW STORIES
127143
// ────────────────────
128144

129145
export const Overview: Story = {
130146
tags: ['overview'],
131147
args: {
132-
size: 'm',
133-
variant: 'info',
134148
'default-slot': 'Active',
135149
},
136150
};
@@ -158,9 +172,6 @@ export const Anatomy: Story = {
158172
})}
159173
`,
160174
tags: ['anatomy'],
161-
args: {
162-
size: 'm',
163-
},
164175
};
165176

166177
// ──────────────────────────
@@ -179,10 +190,13 @@ export const Anatomy: Story = {
179190
*/
180191
export const Sizes: Story = {
181192
render: (args) => html`
182-
${template({ ...args, size: 's', 'default-slot': 'Small' })}
183-
${template({ ...args, size: 'm', 'default-slot': 'Medium' })}
184-
${template({ ...args, size: 'l', 'default-slot': 'Large' })}
185-
${template({ ...args, size: 'xl', 'default-slot': 'Extra-large' })}
193+
${STATUSLIGHT_VALID_SIZES.map((size) =>
194+
template({
195+
...args,
196+
size,
197+
'default-slot': sizeLabels[size],
198+
})
199+
)}
186200
`,
187201
parameters: { 'section-order': 1 },
188202
tags: ['options'],
@@ -196,6 +210,8 @@ export const Sizes: Story = {
196210
* - **`positive`**: Approved, complete, success, new, purchased, licensed
197211
* - **`notice`**: Needs approval, pending, scheduled, syncing, indexing, processing
198212
* - **`negative`**: Error, alert, rejected, failed
213+
*
214+
* Semantic status lights should never be used for color coding categories or labels, and vice versa.
199215
*/
200216
export const SemanticVariants: Story = {
201217
render: (args) => html`
@@ -286,11 +302,19 @@ export const TextWrapping: Story = {
286302
*
287303
* - Semantic variants provide consistent color associations for common statuses
288304
* - Text labels provide clear context for all users
305+
* - Disabled status lights are deprecated for Spectrum 2. Content like "Unavailable" may be used to communicate that concept instead.
306+
*
307+
* #### Non-interactive element
308+
*
309+
* - Status lights have no interactive behavior and are not focusable
310+
* - Screen readers will announce the status light content as static text
311+
* - No keyboard interaction is required or expected
289312
*
290313
* ### Best practices
291314
*
292315
* - Always provide a descriptive text label that explains the status
293316
* - Use semantic variants (`info`, `positive`, `negative`, `notice`, `neutral`) when the status has specific meaning
317+
* - Status lights are not interactive elements - for interactive status indicators, consider using buttons, tags, or links instead
294318
* - Use meaningful, specific labels (e.g., "Approved" instead of "Green")
295319
* - Ensure sufficient color contrast between the status light and its background
296320
* - For non-semantic variants, ensure the text label provides complete context
@@ -363,6 +387,7 @@ export const Accessibility: Story = {
363387
* textfield.value from translations.json based on context.globals.lang.
364388
* Used by the Fonts guide and for locale/font demos. This story is "docs-only."
365389
*/
390+
// @todo: a withLocaleWrapper could be pulled up into a global decorator/helper to be implemented by more components. SWC-1872
366391
function withLocaleWrapperRender(
367392
args: Record<string, unknown> & { lang?: string; 'default-slot'?: string },
368393
context: { globals: { lang?: string } }
@@ -384,7 +409,9 @@ function withLocaleWrapperRender(
384409
/**
385410
* Status light with label driven by the Language toolbar and translations.json.
386411
* Use this story in the Fonts guide to demonstrate font loading and translated copy.
412+
* Learn more about [loading the expected fonts](/docs/guides-customization-fonts--readme).
387413
*/
414+
// @todo: this story is docs-only, but we should start capturing Chromatic baselines for internationalized content in components. SWC-1871
388415
export const WithLocaleWrapper: Story = {
389416
render: withLocaleWrapperRender,
390417
parameters: {

CONTRIBUTOR-DOCS/02_style-guide/01_css/01_component-css.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ Size variants (s, m, l, xl) use `:host([size="..."])` and update custom properti
269269

270270
```css
271271
:host([size="s"]) {
272-
--swc-statuslight-top-to-text: token("component-top-to-text-75");
272+
--swc-statuslight-padding-block: token("component-padding-vertical-75");
273273
--swc-statuslight-height: token("component-height-75");
274274
--swc-statuslight-dot-size: token("status-light-dot-size-small");
275275
--swc-statuslight-font-size: token("font-size-75");

CONTRIBUTOR-DOCS/02_style-guide/01_css/02_custom-properties.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,9 @@ CSS custom properties *normally* can't actually be "private". However, due to sh
7171

7272
```css
7373
.swc-StatusLight {
74-
--_swc-statuslight-top-to-text: var(--swc-statuslight-top-to-text, token("component-top-to-text-100"));
75-
--_swc-statuslight-bottom-to-text: var(--swc-statuslight-bottom-to-text, token("component-bottom-to-text-100"));
74+
--_swc-statuslight-padding-block: var(--swc-statuslight-padding-block, token("component-padding-vertical-100"));
7675

77-
padding-block-start: var(--_swc-statuslight-top-to-text);
78-
padding-block-end: var(--_swc-statuslight-bottom-to-text);
76+
padding-block: var(--_swc-statuslight-padding-block);
7977
}
8078
```
8179

CONTRIBUTOR-DOCS/02_style-guide/01_css/06_property-order-quick-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ From [status-light.css](../../../2nd-gen/packages/swc/components/status-light/st
104104
inline-size: var(--_swc-statuslight-dot-size);
105105
block-size: var(--_swc-statuslight-dot-size);
106106
/* Spacing */
107-
margin-block-start: calc(var(--_swc-statuslight-top-to-dot) - var(--_swc-statuslight-top-to-text));
107+
margin-block-start: calc(var(--_swc-statuslight-top-to-dot) - var(--_swc-statuslight-padding-block));
108108
/* Decoration */
109109
background-color: var(--swc-statuslight-dot-color);
110110
border-radius: token("corner-radius-full");

CONTRIBUTOR-DOCS/03_project-planning/03_components/badge/rendering-and-styling-migration-analysis.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- [CSS => SWC mapping](#css--swc-mapping)
2020
- [Summary of changes](#summary-of-changes)
2121
- [CSS => SWC implementation gaps](#css--swc-implementation-gaps)
22+
- [TODOs](#todos)
2223
- [CSS Spectrum 2 changes](#css-spectrum-2-changes)
2324
- [Resources](#resources)
2425

0 commit comments

Comments
 (0)