diff --git a/packages/web/src/scss/components/Grid/_tools.scss b/packages/web/src/scss/components/Grid/_tools.scss index f60a318ffa..f8c7516020 100644 --- a/packages/web/src/scss/components/Grid/_tools.scss +++ b/packages/web/src/scss/components/Grid/_tools.scss @@ -4,25 +4,6 @@ @use '@tokens' as tokens; @use '../../tools/breakpoint'; -// Function to get the breakpoint name as a infix or suffix -// Example: -get-breakpoint-infix('tablet', 768px) will return 'tablet--' -// Example: -get-breakpoint-infix('mobile', 0) will return '' -// Parameters are: -// * $name: the breakpoint name -// * $value: the breakpoint value -// * $infix: whether to return the suffix or infix -@function -get-breakpoint-name($name, $value, $infix: true) { - @if $value > 0 { - @if $infix { - @return unquote($name + '--'); - } - - @return unquote('-' + $name); - } - - @return ''; -} - // Generates grid-gap values for each breakpoint // Parameters are: // * $gutters: the gutters map @@ -46,7 +27,7 @@ @each $breakpoint-name, $breakpoint-value in $breakpoints { @include breakpoint.up($breakpoint-value) { @each $column in $column-count { - .Grid--#{-get-breakpoint-name($breakpoint-name, $breakpoint-value)}cols-#{$column} { + .Grid--#{breakpoint.get-modifier('infix', $breakpoint-name, $breakpoint-value)}cols-#{$column} { grid-template-columns: repeat(#{$column}, 1fr); } } @@ -63,7 +44,7 @@ @each $breakpoint-name, $breakpoint-value in $breakpoints { @include breakpoint.up($breakpoint-value) { @each $option in $grid-span-variants { - .Grid__span--#{-get-breakpoint-name($breakpoint-name, $breakpoint-value)}over-#{$option} { + .Grid__span--#{breakpoint.get-modifier('infix', $breakpoint-name, $breakpoint-value)}over-#{$option} { $start: 1 + math.div(($grid-columns - $option), 2); grid-column: $start / span $option; @@ -83,7 +64,7 @@ $row-end: 'initial'; @each $breakpoint-name, $breakpoint-value in $breakpoints { - $suffix: -get-breakpoint-name($breakpoint-name, $breakpoint-value, false); + $suffix: breakpoint.get-modifier('suffix', $breakpoint-name, $breakpoint-value); @include breakpoint.up($breakpoint-value) { $column-start: var(--grid-item-column-start#{$suffix}, $column-start); @@ -102,7 +83,7 @@ :where(.GridItem .GridItem) { @each $breakpoint-name, $breakpoint-value in $breakpoints { - $suffix: -get-breakpoint-name($breakpoint-name, $breakpoint-value, false); + $suffix: breakpoint.get-modifier('suffix', $breakpoint-name, $breakpoint-value); --grid-item-column-start#{$suffix}: initial; --grid-item-column-end#{$suffix}: initial; diff --git a/packages/web/src/scss/components/Toast/README.md b/packages/web/src/scss/components/Toast/README.md new file mode 100644 index 0000000000..5d4ce9aaa8 --- /dev/null +++ b/packages/web/src/scss/components/Toast/README.md @@ -0,0 +1,259 @@ +# Toast + +Toast displays a brief, temporary notification that appears at a prescribed location of an application window. + +Toast is a composition of a few subcomponents: + +- [Toast](#toast) + - [ToastBar](#toastbar) + +## Toast + +The Toast component is a container responsible for positioning the [ToastBar](#toastbar) component. It is capable of +handling even multiple toast messages at once, stacking them in a [queue](#toast-queue). + +```html +
+
+ +
+
+``` + +### Accessibility + +The wrapping Toast container has the [`role="log"`][mdn-role-log] attribute set (which results in an implicit +[`aria-live`][mdn-aria-live] value of `polite`). Assistive technologies then announce any **dynamic changes** inside the +container as they happen. In order for this to work, the Toast component **must be present in the DOM** on the initial +page load, even when empty. + +👉 Unless you are absolutely sure that your toast messages are critical to interrupt the user, you should not change the +(implicit) `polite` value of the [`aria-live`][mdn-aria-live] attribute. When set to `assertive`, assistive technologies +immediately notify the user, potentially clearing the speech queue of previous updates. + +### Alignment + +The Toast component is positioned at the bottom of the screen by default. It is also fixed to the bottom of the screen, +so it will always be visible, even when the user scrolls. Available alignment options are derived from the +[AlignmentX and AlignmentY][dictionary-alignment] dictionaries and are as follows: + +- `top` `left`, +- `top` `center`, +- `top` `right`, +- `bottom` `left`, +- `bottom` `center` (default), +- `bottom` `right`. + +Use the `Toast--` and `Toast--` modifiers to change the alignment of the Toast component: + +| AlignmentX/Y | left | center | right | +| ------------ | --------------------------- | ----------------------------- | ---------------------------- | +| top | `Toast--top Toast--left` | `Toast--top Toast--center` | `Toast--top Toast--right` | +| bottom | `Toast--bottom Toast--left` | `Toast--bottom Toast--center` | `Toast--bottom Toast--right` | + +ℹ️ The `center` vertical alignment is not supported, as it would not make sense for a toast notification to be in the +middle of the screen. + +Example: + +```html +
+
+ +
+
+``` + +### Responsive Alignment + +The Toast container can be aligned differently on different screen sizes. Use the `Toast----` +modifiers to change the alignment of the Toast component starting on a specific screen size, e.g. `Toast--tablet--top`, +`Tablet--desktop--left`, etc. (leave the breakpoint empty for alignment on all screen sizes, including mobile screens). + +Example: + +```html +
+
+ +
+
+``` + +### Mobile Screens + +Positioning becomes trickier on mobile screens due to the presence of notches, rounded corners, and the virtual +keyboard. The Toast component tries to find the best position to be visible using the following detection mechanisms: + +1. On devices with rounded displays and/or notches (e.g. iPhone X and newer), the Toast component is pushed inwards to + avoid the rounded corners. The `viewport-fit="cover"` meta tag is required for this feature to work: + + ```html + + ``` + +2. Android Chrome only: When the vertical alignment is set to `bottom` and the virtual keyboard is open, the Toast + component is pushed upwards to avoid being covered by the keyboard. This feature requires the following JavaScript + snippet and is currently supported only in Chrome 94 on Android and later. + + ```js + // Enable CSS to detect the presence of virtual keyboard: + if ('virtualKeyboard' in navigator) { + navigator.virtualKeyboard.overlaysContent = true; + } + ``` + +### Toast Queue + +When multiple ToastBar components are present, they stack up in a queue, separated by a gap. The ToastBar components are +sorted from top to bottom for the `top` vertical alignment, and from bottom to top for the `bottom` vertical alignment. + +#### Toast Queue Limitations + +While the Toast queue becomes scrollable when it does not fit the screen, we recommend displaying only a few toasts at +once for several reasons: + +⚠️ **We strongly discourage from displaying too many toasts at once as it may cause the page to be unusable, +especially on mobile screens. As of now, there is no automatic stacking of the toast queue items. It is the +responsibility of the developer to ensure that the Toast queue does not overflow the screen.** + +⚠️ Please note that scrolling is only available on pointer-equipped devices (mouse, trackpad). Furthermore, scrolling is +only possible when the cursor is placed over the toast message boxes. This way the page content behind the toast +messages can remain accessible. + +👉 Please note that the initial scroll position is always at the **top** of the queue. + +## ToastBar + +The ToastBar component is the actual toast notification. It is a simple container with a message and a few optional +elements. + +Minimum example: + +```html +
+
+
Message only
+
+
+``` + +### Optional Icon + +An icon can be added to the ToastBar component: + +```html +
+
+ +
Message with icon
+
+
+``` + +### Action Link + +An action link can be added to the ToastBar component: + +```html +
+
+
+ Message with action + Action +
+
+
+``` + +👉 **Do not put any important actions** like "Undo" in the ToastBar component (unless there are other means to perform +said action), as it is very hard (if not impossible) to reach for users with assistive technologies. Read more about +[Toast accessibility](#scott-o-hara-toast) at Scott O'Hara's blog. + +### Colors + +The ToastBar component is available in all [emotion colors][dictionary-color], plus the `inverted` variant (default). +Use the `ToastBar--` modifier class to change the color of the ToastBar component. + +For example: + +```html +
+
+
Success message
+
+
+``` + +### Dismissible ToastBar + +To make the ToastBar dismissible, add the `ToastBar--dismissible` modifier class, a unique `id` attribute, and a close +button: + +```html +
+
+
Dismissible message
+
+ +
+``` + +👉 Please keep in mind that the Button color should match the ToastBar color. + +⚠️ The JavaScript functionality for dismissing the ToastBar is yet to be implemented. + +## Full Example + +```html + +
+
+ +
+
+ +
+ Toast message + Action +
+
+ +
+ +
+
+ +``` + +[mdn-role-log]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/log_role +[mdn-aria-live]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live +[dictionary-alignment]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#alignment +[dictionary-color]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#color +[scott-o-hara-toast]: https://www.scottohara.me/blog/2019/07/08/a-toast-to-a11y-toasts.html diff --git a/packages/web/src/scss/components/Toast/_Toast.scss b/packages/web/src/scss/components/Toast/_Toast.scss new file mode 100644 index 0000000000..b563899a55 --- /dev/null +++ b/packages/web/src/scss/components/Toast/_Toast.scss @@ -0,0 +1,135 @@ +// 1. Spread the `Toast` container over the viewport. +// +// a) For top-to-bottom languages, the `inset-inline` of `0` is a safe way to spread the container horizontally. +// b) For the best scrolling experience on mobile devices with dynamic viewports, only one of the `inset-block` +// values can be set to `0` at a time. Having both set to `0` would lead to sluggish scrolling on mobile devices. +// c) The `height` property is set to `100lvh` to spread the container over the entire viewport so the toast messages +// (and their shadows) are not clipped, even during dynamic viewport changes (again, using the `dvh` unit would +// lead to sluggish scrolling on mobile devices). +// +// 2. Use grid layout to control horizontal (x) and vertical (y) alignment of the messages. +// +// a) Horizontal alignment is controlled by the `justify-content` property of the grid container. This is further +// reinforced by the `align-items` property of the toast queue (7). +// b) Vertical alignment is controlled by the `align-self` property of the toast queue itself (7). The reason for +// this is that vertical scrolling of the toast container is not possible with `align-content: end` (more on that +// below). This is also the reason why the initial scroll position is at the top of the container in both cases. +// +// ⚠️ CAUTION: Vertical scrolling of grid containers works only with `align-content` other than `end`. +// While the CSS spec speaks [1] about the interaction of `overflow` and `align-content` properties, there may be +// a blind spot in the spec because all major browsers (Firefox, Chrome, Safari) do not allow scrolling for +// `align-content: end`. +// +// [1] https://drafts.csswg.org/css-align-3/#overflow-scroll-position +// +// A similar Chromium bug with flex containers is tracked here: +// +// https://issues.chromium.org/issues/41130651 +// +// 3. Allow scrolling when the messages do not fit the screen. Scrolling is only possible over the toast message boxes +// and not over the entire container. This is because the `pointer-events` property of the container is set to `none` +// (4) so neither the toast container, nor the toast queue stands in the way of the user's interaction with the page +// content beneath it. +// +// (The alternative would be to scroll the toast queue itself, but this would go against (4) by blocking user's +// interaction with the underlying content.) +// +// ⚠️ CAUTION: As a result of this construction, vertical scrolling of the toast queue only works with pointers, not +// with touch events. +// +// 4. Because the `Toast` container is present on the page all the time, we must allow interaction with the page content +// beneath it. The same applies to the toast queue element (7). +// +// 5. On devices with rounded displays (like iPhone X and later), prefer the safe area value over our spacing, if +// bigger. The `viewport-fit="cover"` viewport meta must be present in HTML for this to work. Zero value is used as +// a fallback. +// +// 6. EXPERIMENTAL: When the virtual keyboard is open, the toast container should be pushed up to make space for the +// keyboard. This is achieved by setting the bottom margin of the container to the height of the keyboard. This +// feature requires the following JavaScript snippet and is currently supported only in Chrome 94 on Android and +// later. +// +// ```js +// if ('virtualKeyboard' in navigator) { +// navigator.virtualKeyboard.overlaysContent = true; +// } +// ``` +// +// ℹ️ Similar behavior can be achieved with the `interactive-widget=resizes-content` viewport meta, but then it would +// affect the entire page, not just the toast container. Again, this feature is currently supported only in Chrome +// 108 on Android and later. +// +// https://ishadeed.com/article/virtual-keyboard-api/ +// https://developer.chrome.com/blog/viewport-resize-behavior/ +// +// 7. The toast queue element is in place so we can reverse the visual order of child toast messages when vertical +// alignment is set to `bottom`. Reversing order of all child elements is only possible with flex containers. +// +// 8. Generate: +// +// a) horizontal (x) and +// b) vertical (y) alignment classes for each breakpoint. + +@use 'sass:map'; +@use '../../tools/breakpoint'; +@use 'theme'; + +.Toast { + --toast-padding-x: #{theme.$padding-x}; + --toast-padding-y: #{theme.$padding-y}; + + position: fixed; // 1. + inset-inline: 0; // 1.a + inset-block: var(--toast-inset-top, 0) var(--toast-inset-bottom, 0); // 1.b + display: grid; // 2. + justify-content: var(--toast-alignment-x); // 2.a + height: 100lvh; // 1.c + + // 5. + padding: max(var(--toast-padding-y), env(safe-area-inset-top, 0)) + max(var(--toast-padding-x), env(safe-area-inset-right, 0)) + max(var(--toast-padding-y), env(safe-area-inset-bottom, 0)) + max(var(--toast-padding-x), env(safe-area-inset-left, 0)); + margin-bottom: env(keyboard-inset-height, 0); // 6. + overflow-y: auto; // 3. + pointer-events: none; // 4. + + @include breakpoint.up(map.get(theme.$breakpoints, tablet)) { + --toast-padding-x: #{theme.$padding-x-tablet}; + } + + @include breakpoint.up(map.get(theme.$breakpoints, desktop)) { + --toast-padding-x: #{theme.$padding-x-desktop}; + } +} + +.Toast__queue { + display: flex; + flex-direction: var(--toast-queue-direction, column-reverse); // 7. + row-gap: theme.$gap; + align-self: var(--toast-alignment-y, end); // 2.b + align-items: var(--toast-alignment-x, center); // 2.a +} + +@each $breakpoint-name, $breakpoint-value in theme.$breakpoints { + $infix: breakpoint.get-modifier('infix', $breakpoint-name, $breakpoint-value); + + @include breakpoint.up($breakpoint-value) { + // 8.a + @each $alignment-name, $alignment-value in theme.$alignments-x { + .Toast--#{$infix}#{$alignment-name} { + --toast-alignment-x: #{$alignment-value}; + } + } + + // 8.b + @each $alignment-name, $alignment-value in theme.$alignments-y { + .Toast--#{$infix}#{$alignment-name} { + --toast-alignment-y: #{$alignment-value}; + --toast-inset-top: #{if($alignment-name == 'top', 0, 'auto')}; + --toast-inset-bottom: #{if($alignment-name == 'bottom', 0, 'auto')}; + --toast-queue-direction: #{if($alignment-name == 'bottom', 'column-reverse', 'column')}; + } + } + } +} diff --git a/packages/web/src/scss/components/Toast/_ToastBar.scss b/packages/web/src/scss/components/Toast/_ToastBar.scss new file mode 100644 index 0000000000..a2e3e6d698 --- /dev/null +++ b/packages/web/src/scss/components/Toast/_ToastBar.scss @@ -0,0 +1,56 @@ +// 1. Restore pointer events that have been disabled by parent `Toast` component. +// 2. If the message is short enough, align it vertically to the center of the bar. +// 3. Allow the action to wrap to the next line if the message is too long. +// 4. Add margin to the action to separate it from the dismiss button. + +@use '../../tools/dictionaries'; +@use '../../tools/typography'; +@use 'theme'; + +.ToastBar { + max-width: theme.$bar-max-width; + padding: theme.$bar-padding; + border-radius: theme.$bar-border-radius; + box-shadow: theme.$bar-box-shadow; + pointer-events: initial; // 1. +} + +.ToastBar--dismissible { + display: grid; + grid-template-columns: 1fr auto; + column-gap: theme.$bar-gap; + align-items: start; +} + +.ToastBar--dismissible > .ToastBar__content { + align-self: center; // 2. +} + +.ToastBar__content:has(> svg:first-child) { + display: grid; + grid-template-columns: auto 1fr; + column-gap: theme.$bar-content-gap; +} + +.ToastBar__message { + @include typography.generate(theme.$bar-typography); + + display: flex; + flex-wrap: wrap; // 3. + gap: theme.$bar-message-gap-y theme.$bar-message-gap-x; +} + +.ToastBar__message > :is(a, button):last-child { + font-weight: 400; +} + +.ToastBar--dismissible .ToastBar__message > :is(a, button):last-child { + margin-inline-end: theme.$bar-action-margin-inline-end; // 4. +} + +@include dictionaries.generate-colors( + $class-name: 'ToastBar', + $dictionary-values: theme.$color-dictionary, + $config: theme.$color-dictionary-config, + $overrides: theme.$color-dictionary-overrides +); diff --git a/packages/web/src/scss/components/Toast/_theme.scss b/packages/web/src/scss/components/Toast/_theme.scss new file mode 100644 index 0000000000..40d144b636 --- /dev/null +++ b/packages/web/src/scss/components/Toast/_theme.scss @@ -0,0 +1,43 @@ +@use 'sass:list'; +@use '@tokens' as tokens; +@use '../../settings/dictionaries'; + +$alignments-x: ( + left: start, + center: center, + right: end, +); +$alignments-y: ( + top: start, + bottom: end, +); +$breakpoints: tokens.$breakpoints; + +$gap: tokens.$space-600; +$padding-x: tokens.$space-700; +$padding-x-tablet: tokens.$space-800; +$padding-x-desktop: tokens.$space-900; +$padding-y: tokens.$space-700; + +$bar-typography: tokens.$body-small-text-bold; +$bar-max-width: 480px; +$bar-gap: tokens.$space-600; +$bar-padding: tokens.$space-600; +$bar-border-radius: tokens.$radius-200; +$bar-box-shadow: tokens.$shadow-400; +$bar-content-gap: tokens.$space-500; +$bar-message-gap-x: tokens.$space-700; +$bar-message-gap-y: tokens.$space-500; +$bar-action-margin-inline-end: tokens.$space-400; + +$color-dictionary: list.join('inverted', dictionaries.$emotion-colors); +$color-dictionary-config: ( + color: tokens.$text-primary-inverted-default, + background-color: 'default', +); +$color-dictionary-overrides: ( + inverted: ( + color: tokens.$text-primary-inverted-default, + background-color: tokens.$background-inverted, + ), +); diff --git a/packages/web/src/scss/components/Toast/index.html b/packages/web/src/scss/components/Toast/index.html new file mode 100644 index 0000000000..c30aef4990 --- /dev/null +++ b/packages/web/src/scss/components/Toast/index.html @@ -0,0 +1,354 @@ +{{#> layout/plain }} + +
+ +

Alignment

+ +
+ + + + +
+
+ Vertical alignment: + + +
+
+ Horizontal alignment: + + + +
+
+ + + + +
+
+ Virtual keyboard interaction: +
+ + +
+
+
+ + +
+
+ +
+
+ +
+ I was first! + Action +
+
+ +
+ +
+
+ +
+ I appeared later. This toast has a long message that wraps automatically. + Action +
+
+ +
+ +
+
+ +
+ +
+ +
+ +

Content Variations

+ +
+ +
+
+
+ Message only +
+
+
+ +
+
+
+ Message with action + Action +
+
+
+ +
+
+
+ When the text is long and reaches the maximum width limit, the action automatically wraps to the next line. + Action +
+
+
+ +
+
+ +
+ Message with icon and action + Action +
+
+
+ +
+
+
+ Dismissible message +
+
+ +
+ +
+
+ +
+ Dismissible message with icon and action + Action +
+
+ +
+ +
+ +
+
+ +

Colors

+ +
+ +
+
+ +
+ Inverted + Action +
+
+ +
+ +
+
+ +
+ Informative + Action +
+
+ +
+ +
+
+ +
+ Success + Action +
+
+ +
+ +
+
+ +
+ Warning + Action +
+
+ +
+ +
+
+ +
+ Danger + Action +
+
+ +
+ +
+ +
+ +{{/layout/plain}} diff --git a/packages/web/src/scss/components/Toast/index.scss b/packages/web/src/scss/components/Toast/index.scss new file mode 100644 index 0000000000..cb2d09399d --- /dev/null +++ b/packages/web/src/scss/components/Toast/index.scss @@ -0,0 +1,2 @@ +@forward 'Toast'; +@forward 'ToastBar'; diff --git a/packages/web/src/scss/components/index.scss b/packages/web/src/scss/components/index.scss index 8e4644a793..b6ac714359 100644 --- a/packages/web/src/scss/components/index.scss +++ b/packages/web/src/scss/components/index.scss @@ -22,4 +22,5 @@ @forward 'Tag'; @forward 'TextArea'; @forward 'TextField'; +@forward 'Toast'; @forward 'Tooltip'; diff --git a/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss b/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss index 391f99d227..01180b5034 100644 --- a/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss +++ b/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss @@ -81,3 +81,17 @@ } } } + +@include true.describe('get-modifier mixin') { + @include true.it('should output empty string for a zero breakpoint value') { + @include true.assert-equal(breakpoint.get-modifier('infix', 'mobile', 0), ''); + } + + @include true.it('should output an infix for a non-zero breakpoint value') { + @include true.assert-equal(breakpoint.get-modifier('infix', 'tablet', 768px), 'tablet--'); + } + + @include true.it('should output a suffix for a non-zero breakpoint value') { + @include true.assert-equal(breakpoint.get-modifier('suffix', 'tablet', 768px), '-tablet'); + } +} diff --git a/packages/web/src/scss/tools/_breakpoint.scss b/packages/web/src/scss/tools/_breakpoint.scss index a83a2997f8..e84dee2c7a 100644 --- a/packages/web/src/scss/tools/_breakpoint.scss +++ b/packages/web/src/scss/tools/_breakpoint.scss @@ -1,3 +1,10 @@ +// Mixin to get the min-width media query for a breakpoint value. +// +// Example: up(0) { … } will return … +// Example: up(768px) { … } will return @media (min-width: 768px) { … } +// +// Parameters are: +// * $breakpoint-value: the breakpoint value (in pixels) @mixin up($breakpoint-value) { @if $breakpoint-value > 0 { @media (min-width: $breakpoint-value) { @@ -8,6 +15,13 @@ } } +// Mixin to get the max-width media query for a breakpoint value. +// +// Example: down(0) { … } will return … +// Example: down(768px) { … } will return @media (max-width: 767px) { … } +// +// Parameters are: +// * $breakpoint-value: the breakpoint value (in pixels) @mixin down($breakpoint-value) { @if $breakpoint-value > 0 { @media (max-width: ($breakpoint-value - 1px)) { @@ -17,3 +31,25 @@ @content; } } + +// Function to get the breakpoint modifier as an infix or a suffix. +// +// Example: get-modifier('infix, 'mobile', 0) will return '' +// Example: get-modifier('infix', 'tablet', 768px) will return 'tablet--' +// Example: get-modifier('suffix', 'tablet', 768px) will return '-tablet' +// +// Parameters are: +// * modifier: [ infix | suffix ] +// * $name: the breakpoint/modifier name to return (e.g. 'tablet') +// * $breakpoint-value: the breakpoint value to decide by (in pixels) +@function get-modifier($modifier, $name, $breakpoint-value) { + @if $breakpoint-value > 0 { + @if $modifier == 'infix' { + @return unquote($name + '--'); + } + + @return unquote('-' + $name); + } + + @return ''; +} diff --git a/tests/e2e/demo-components-compare.spec.ts-snapshots/offcanvas-chromium-linux.png b/tests/e2e/demo-components-compare.spec.ts-snapshots/offcanvas-chromium-linux.png index 3fc540cc3b..a25caeb2e4 100644 Binary files a/tests/e2e/demo-components-compare.spec.ts-snapshots/offcanvas-chromium-linux.png and b/tests/e2e/demo-components-compare.spec.ts-snapshots/offcanvas-chromium-linux.png differ diff --git a/tests/e2e/demo-components-compare.spec.ts-snapshots/toast-chromium-linux.png b/tests/e2e/demo-components-compare.spec.ts-snapshots/toast-chromium-linux.png new file mode 100644 index 0000000000..4f70f6bb72 Binary files /dev/null and b/tests/e2e/demo-components-compare.spec.ts-snapshots/toast-chromium-linux.png differ diff --git a/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png b/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png index 3fc540cc3b..a25caeb2e4 100644 Binary files a/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png and b/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png differ