Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,36 @@ Keep selector specificity at or below `(0,1,0)`. If you need a compounded select

Only add `@media (forced-colors: active)` if browser defaults are not conveying correct semantic intent, and always put it at the end of the component stylesheet.
→ See [01_component-css#forced-colors-requirements](../../../../CONTRIBUTOR-DOCS/02_style-guide/01_css/01_component-css.md#forced-colors-requirements)

### 8. Browser API selectors

Prefer native CSS pseudo-classes over attribute selectors when one exists for the same state (`:host(:popover-open)` not `:host([open])`; `:host(:disabled)` not `:host([disabled])`). Use attribute selectors for custom attributes and ARIA states with no native pseudo-class.
→ See [01_component-css#state-implementation-patterns](../../../../CONTRIBUTOR-DOCS/02_style-guide/01_css/01_component-css.md#state-implementation-patterns)

### 9. Sub-element inheritance

When a sub-element must always match a variant-driven property on the parent, use `inherit` rather than repeating the `var()` reference in each variant rule.
→ See [01_component-css#variant-implementation-patterns](../../../../CONTRIBUTOR-DOCS/02_style-guide/01_css/01_component-css.md#variant-implementation-patterns)

### 10. Compound pseudo-classes on `:host()` via CSS nesting

CSS nesting inside a `:host([...])` rule — e.g. `&:dir(rtl)` — expands to `:host([...]):dir(rtl)`, which chains the pseudo-class **outside** the `:host()` argument. Browsers do not support this; the rule silently fails with no parse error.

```css
/* ❌ Silent failure: :dir(rtl) is outside :host() */
:host([placement='start']:popover-open) {
&:dir(rtl) {
transform: translateX(1rem);
}
}

/* ✅ All conditions inside the :host() argument */
:host(:dir(rtl)[placement='start']:popover-open) {
transform: translateX(1rem);
}
```

**Exception**: when the parent selector targets a descendant (e.g. `:host([...]) .swc-Child`), nesting `&:dir(rtl)` correctly applies `:dir()` to the inner element — that is valid and fine.

**Migration note**: `:dir()` is a common place this surfaces. Whenever you add `:dir()` to a `:host`-level rule, write a separate `:host(:dir(rtl)[...])` rule instead of nesting.
→ See [05_anti-patterns#9](../../../../CONTRIBUTOR-DOCS/02_style-guide/01_css/05_anti-patterns.md#9-nesting-compound-pseudo-classes-on-host-via-css-nesting)
2 changes: 1 addition & 1 deletion 1st-gen/tools/base/src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/**
* The version of the 1st-gen Spectrum Web Components library.
*/
export const version = '1.12.0';
export const version = '1.12.1';

/**
* The version of the core base package.
Expand Down
30 changes: 22 additions & 8 deletions 2nd-gen/packages/core/components/tooltip/Tooltip.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,19 @@ export abstract class TooltipBase extends SpectrumElement {
public triggerElement: HTMLElement | null = null;

/**
* Whether to apply warm-up and cooldown timing (1500ms each) to hover and focus events.
* Duration in milliseconds of the warm-up delay before the tooltip shows on pointer hover.
* Set to `0` to show immediately on hover. Keyboard focus (`focusin` when `:focus-visible`)
* always shows the tooltip immediately regardless of this value. The cooldown duration (before the next hover must wait again)
* matches this value. Warm-up/cooldown state is shared across all tooltips in the same document,
* so moving quickly between adjacent triggers (e.g. a toolbar) shows each subsequent tooltip
* immediately after the first warm-up elapses.
*
* Additive/deferred: active when `HoverController` is integrated.
*
* @default false
* @default 1500
*/
@property({ type: Boolean, reflect: true })
public delayed: boolean = false;
@property({ type: Number, reflect: true })
public delay: number = 1500;

/**
* When set, prevents automatic trigger wiring from responding to hover and focus events.
Expand Down Expand Up @@ -206,31 +211,40 @@ export abstract class TooltipBase extends SpectrumElement {
);
}

// Guards dispatchAfterEvent so only the first transitionend per open/close cycle fires,
// preventing one after event from firing for each CSS property that transitions.
private afterEventPending = false;

private readonly handleBeforeToggle = (event: Event): void => {
const { newState } = event as ToggleEvent;
const eventName = newState === 'open' ? 'swc-open' : 'swc-close';
this.dispatchEvent(
new CustomEvent(eventName, { bubbles: true, composed: true })
);
// Set here so the flag is set regardless of whether this.open already matches
// newState. handleToggle exits early when open was set externally, which would
// otherwise leave the flag unset and suppress swc-after-open / swc-after-close.
this.afterEventPending = true;
};

private readonly handleToggle = (event: Event): void => {
const { newState } = event as ToggleEvent;
const isOpen = newState === 'open';
if (isOpen === this.open) {
return;
if (isOpen !== this.open) {
this.open = isOpen;
}
this.open = isOpen;
// When no CSS transition is active, dispatch after-* immediately since transitionend will not fire.
if (getComputedStyle(this).transitionDuration === '0s') {
this.afterEventPending = false;
this.dispatchAfterEvent(isOpen);
}
};

private readonly handleTransitionEnd = (event: TransitionEvent): void => {
if (event.target !== this) {
if (event.target !== this || !this.afterEventPending) {
return;
}
this.afterEventPending = false;
this.dispatchAfterEvent(this.open);
};

Expand Down
18 changes: 15 additions & 3 deletions 2nd-gen/packages/swc/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,10 @@ const preview = {
'Rendering and styling migration analysis',
],
'Action button',
['Rendering and styling migration analysis'],
[
'Accessibility migration analysis',
'Rendering and styling migration analysis',
],
'Action group',
['Rendering and styling migration analysis'],
'Action menu',
Expand Down Expand Up @@ -360,9 +363,14 @@ const preview = {
'Rendering and styling migration analysis',
],
'Button group',
['Rendering and styling migration analysis'],
[
'Accessibility migration analysis',
'Rendering and styling migration analysis',
],
'Checkbox',
['Rendering and styling migration analysis'],
'Close button',
['Accessibility migration analysis'],
'Color field',
['Rendering and styling migration analysis'],
'Color loupe',
Expand Down Expand Up @@ -391,12 +399,16 @@ const preview = {
'Rendering and styling migration analysis',
],
'Infield button',
['Rendering and styling migration analysis'],
[
'Accessibility migration analysis',
'Rendering and styling migration analysis',
],
'Infield progress circle',
['Rendering and styling migration analysis'],
'Link',
[
'Accessibility migration analysis',
'Migration plan',
'Rendering and styling migration analysis',
],
'Menu',
Expand Down
1 change: 1 addition & 0 deletions 2nd-gen/packages/swc/components/button/button-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
/* @global-exclude: host display/alignment apply to the custom element wrapper, not the native element */
:host {
display: inline-block;
inline-size: fit-content;
vertical-align: top;
}

Expand Down
8 changes: 6 additions & 2 deletions 2nd-gen/packages/swc/components/tooltip/Tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import styles from './tooltip.css';
* <swc-tooltip for="save-btn">Save your changes</swc-tooltip>
*
* @slot - Text label displayed in the tooltip.
*
* @cssprop --swc-tooltip-background-color - Background color of the tooltip bubble. Defaults to the neutral background color token.
*/
export class Tooltip extends TooltipBase {
// ──────────────────────────────
Expand All @@ -38,8 +40,10 @@ export class Tooltip extends TooltipBase {

protected override render(): TemplateResult {
return html`
<span class="swc-Tooltip-tip"></span>
<span class="swc-Tooltip-label"><slot></slot></span>
<div class="swc-Tooltip">
<span class="swc-Tooltip-tip" aria-hidden="true"></span>
<slot></slot>
</div>
`;
}
}
Loading
Loading