Skip to content

Commit f605a1e

Browse files
authored
fix(sbb-navigation): fix navigation actions contrast ratio (#2481)
1 parent 580b56f commit f605a1e

File tree

8 files changed

+82
-6
lines changed

8 files changed

+82
-6
lines changed

src/components/navigation/common/navigation-action-common.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type { SbbNavigationLinkElement } from '../navigation-link';
99
import type { SbbNavigationMarkerElement } from '../navigation-marker';
1010
import type { SbbNavigationSectionElement } from '../navigation-section';
1111

12+
import '../../icon';
13+
1214
import style from './navigation-action.scss?lit&inline';
1315

1416
export type SbbNavigationActionSize = 's' | 'm' | 'l';
@@ -76,10 +78,11 @@ export const SbbNavigationActionCommonElementMixin = <
7678

7779
// Check if the current element is nested inside a navigation section.
7880
this._navigationSection = this.closest('sbb-navigation-section');
81+
this.toggleAttribute('data-section-action', !!this._navigationSection);
7982
}
8083

8184
protected override renderTemplate(): TemplateResult {
82-
return html` <slot></slot> `;
85+
return html`<sbb-icon name="dash-small"></sbb-icon> <slot></slot>`;
8386
}
8487
}
8588
return SbbNavigationActionCommonElement as unknown as AbstractConstructor<SbbNavigationActionCommonElementMixinType> &

src/components/navigation/common/navigation-action.scss

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,29 @@
1010
outline: none !important;
1111

1212
--sbb-navigation-action-color: var(--sbb-color-cloud);
13+
--sbb-navigation-action-icon-display: none;
1314
}
1415

15-
:host(:is([data-action-active])) {
16+
:host([data-section-action][data-action-active].sbb-active) {
17+
--sbb-navigation-action-icon-display: block;
18+
}
19+
20+
:host([data-action-active]) {
1621
--sbb-navigation-action-color: var(--sbb-color-storm);
1722

1823
@include sbb.if-forced-colors {
1924
--sbb-navigation-action-color: Highlight;
2025
}
2126
}
2227

28+
sbb-icon {
29+
display: var(--sbb-navigation-action-icon-display);
30+
position: absolute;
31+
inset-inline-start: calc((var(--sbb-size-icon-ui-small) + var(--sbb-spacing-fixed-1x)) * -1);
32+
inset-block-start: calc(1em * (var(--sbb-typo-line-height-body-text) / 2));
33+
transform: translateY(-50%);
34+
}
35+
2336
@include sbb.hover-mq($hover: true) {
2437
:host(:hover) {
2538
--sbb-navigation-action-color: var(--sbb-color-storm);
@@ -36,6 +49,7 @@
3649
@include sbb.title-4($exclude-spacing: true);
3750

3851
cursor: pointer;
52+
position: relative;
3953
text-decoration: none;
4054
display: flex;
4155
user-select: none;

src/components/navigation/navigation-button/navigation-button.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ describe('sbb-navigation-button', () => {
1515
expect(root).shadowDom.to.be.equal(
1616
`
1717
<span class="sbb-action-base sbb-navigation-button">
18+
<sbb-icon
19+
aria-hidden="true"
20+
data-namespace="default"
21+
name="dash-small"
22+
role="img"
23+
>
24+
</sbb-icon>
1825
<slot></slot>
1926
</span>
2027
`,

src/components/navigation/navigation-link/navigation-link.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ describe('sbb-navigation-link', () => {
2121
role="presentation"
2222
tabindex="-1"
2323
>
24+
<sbb-icon
25+
aria-hidden="true"
26+
data-namespace="default"
27+
name="dash-small"
28+
role="img"
29+
>
30+
</sbb-icon>
2431
<slot></slot>
2532
<sbb-screenreader-only>
2633
. Link target opens in a new window.

src/components/navigation/navigation-section/navigation-section.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '../../core/overlay';
3131
import type { SbbNavigationElement } from '../navigation';
3232
import type { SbbNavigationButtonElement } from '../navigation-button';
33+
import type { SbbNavigationLinkElement } from '../navigation-link';
3334
import '../../divider';
3435
import '../../button/transparent-button';
3536

@@ -215,6 +216,7 @@ export class SbbNavigationSectionElement extends UpdateScheduler(LitElement) {
215216
this._state = 'closed';
216217
this._navigationSectionContainerElement.scrollTo(0, 0);
217218
this._windowEventsController?.abort();
219+
this._resetLists();
218220
this._setNavigationInert();
219221
if (this._isZeroToLargeBreakpoint() && this._triggerElement) {
220222
setModalityOnNextFocus(this._triggerElement);
@@ -224,6 +226,13 @@ export class SbbNavigationSectionElement extends UpdateScheduler(LitElement) {
224226
this.completeUpdate();
225227
}
226228

229+
private _resetLists(): void {
230+
const activeActions = Array.from(
231+
this.querySelectorAll('[data-section-action][data-action-active]'),
232+
) as (SbbNavigationButtonElement | SbbNavigationLinkElement)[];
233+
activeActions?.forEach((action) => action.toggleAttribute('data-action-active', false));
234+
}
235+
227236
private _attachWindowEvents(): void {
228237
this._windowEventsController = new AbortController();
229238
window.addEventListener('keydown', (event: KeyboardEvent) => this._onKeydownEvent(event), {

src/components/navigation/navigation/navigation.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
}
9595
}
9696

97-
:host([disable-animation]) {
97+
:host(:is([data-resize-disable-animation], [disable-animation])) {
9898
--sbb-navigation-animation-duration: 0.1ms;
9999
}
100100

src/components/navigation/navigation/navigation.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '../../core/dom';
1414
import { EventEmitter, ConnectedAbortController } from '../../core/eventing';
1515
import { i18nCloseNavigation } from '../../core/i18n';
16-
import { AgnosticMutationObserver } from '../../core/observers';
16+
import { AgnosticMutationObserver, AgnosticResizeObserver } from '../../core/observers';
1717
import type { SbbOverlayState } from '../../core/overlay';
1818
import {
1919
removeAriaOverlayTriggerAttributes,
@@ -35,6 +35,7 @@ const navigationObserverConfig: MutationObserverInit = {
3535
};
3636

3737
let nextId = 0;
38+
const DEBOUNCE_TIME = 150;
3839

3940
/**
4041
* It displays a navigation menu, wrapping one or more `sbb-navigation-*` components.
@@ -137,9 +138,11 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
137138
private _focusHandler = new FocusHandler();
138139
private _scrollHandler = new ScrollHandler();
139140
private _isPointerDownEventOnNavigation: boolean = false;
141+
private _resizeObserverTimeout: ReturnType<typeof setTimeout> | null = null;
140142
private _navigationObserver = new AgnosticMutationObserver((mutationsList: MutationRecord[]) =>
141143
this._onNavigationSectionChange(mutationsList),
142144
);
145+
private _navigationResizeObserver = new AgnosticResizeObserver(() => this._onNavigationResize());
143146
private _navigationId = `sbb-navigation-${++nextId}`;
144147

145148
/**
@@ -154,6 +157,7 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
154157
return;
155158
}
156159
this._state = 'opening';
160+
this._checkActiveActions();
157161
this._checkActiveSection();
158162
this.startUpdate();
159163

@@ -169,6 +173,13 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
169173
activeAction?.connectedSection?.open();
170174
}
171175

176+
private _checkActiveActions(): void {
177+
const activeActions = Array.from(
178+
this.querySelectorAll(':is(sbb-navigation-button, sbb-navigation-link).sbb-active'),
179+
) as (SbbNavigationButtonElement | SbbNavigationLinkElement)[];
180+
activeActions?.forEach((action) => action.marker?.select(action));
181+
}
182+
172183
/**
173184
* Closes the navigation.
174185
*/
@@ -234,6 +245,7 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
234245
if (event.animationName === 'open' && this._state === 'opening') {
235246
this._state = 'opened';
236247
this._didOpen.emit();
248+
this._navigationResizeObserver.observe(this);
237249
applyInertMechanism(this);
238250
this._focusHandler.trap(this, { filter: this._trapFocusFilter });
239251
this._attachWindowEvents();
@@ -246,6 +258,7 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
246258
// To enable focusing other element than the trigger, we need to call focus() a second time.
247259
this._triggerElement?.focus();
248260
this._didClose.emit();
261+
this._navigationResizeObserver.unobserve(this);
249262
this._resetMarkers();
250263
this._windowEventsController?.abort();
251264
this._focusHandler.disconnect();
@@ -258,7 +271,9 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
258271

259272
private _resetMarkers(): void {
260273
const activeActions = Array.from(
261-
this.querySelectorAll('sbb-navigation-button[data-action-active]'),
274+
this.querySelectorAll(
275+
':is(sbb-navigation-button, sbb-navigation-link)[data-action-active]:not(.sbb-active)',
276+
),
262277
) as (SbbNavigationButtonElement | SbbNavigationLinkElement)[];
263278
activeActions?.forEach((action) => action.marker?.reset());
264279
}
@@ -332,6 +347,24 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
332347
}
333348
}
334349

350+
private _onNavigationResize(): void {
351+
if (this._state !== 'opened') {
352+
return;
353+
}
354+
355+
if (this._resizeObserverTimeout) {
356+
clearTimeout(this._resizeObserverTimeout);
357+
}
358+
359+
this.toggleAttribute('data-resize-disable-animation', true);
360+
361+
// Disable the animation when resizing the navigation to avoid strange height transition effects.
362+
this._resizeObserverTimeout = setTimeout(
363+
() => this.toggleAttribute('data-resize-disable-animation', false),
364+
DEBOUNCE_TIME,
365+
);
366+
}
367+
335368
public override connectedCallback(): void {
336369
super.connectedCallback();
337370
const signal = this._abort.signal;
@@ -353,6 +386,7 @@ export class SbbNavigationElement extends UpdateScheduler(LitElement) {
353386
this._windowEventsController?.abort();
354387
this._focusHandler.disconnect();
355388
this._navigationObserver.disconnect();
389+
this._navigationResizeObserver.disconnect();
356390
removeInertMechanism();
357391
}
358392

src/components/notification/notification.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const notificationTypes = new Map([
2222
['error', 'circle-cross-small'],
2323
]);
2424

25+
const DEBOUNCE_TIME = 150;
26+
2527
/**
2628
* It displays messages which require a user's attention without interrupting its tasks.
2729
*
@@ -172,7 +174,7 @@ export class SbbNotificationElement extends LitElement {
172174
// Disable the animation when resizing the notification to avoid strange height transition effects.
173175
this._resizeObserverTimeout = setTimeout(
174176
() => this.toggleAttribute('data-resize-disable-animation', false),
175-
150,
177+
DEBOUNCE_TIME,
176178
);
177179
}
178180

0 commit comments

Comments
 (0)