From 5f712444074587e6d78ac0d7776d7108b52c7e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= Date: Fri, 17 May 2024 18:44:24 +0200 Subject: [PATCH 01/10] chore(styles): update horizontal stepper styles --- .changeset/pretty-bugs-change.md | 5 + packages/styles/src/components/stepper.scss | 326 +++++++++--------- packages/styles/src/mixins/_utilities.scss | 10 + .../src/variables/components/_stepper.scss | 28 +- 4 files changed, 197 insertions(+), 172 deletions(-) create mode 100644 .changeset/pretty-bugs-change.md diff --git a/.changeset/pretty-bugs-change.md b/.changeset/pretty-bugs-change.md new file mode 100644 index 0000000000..1406bd2349 --- /dev/null +++ b/.changeset/pretty-bugs-change.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-styles': patch +--- + +Updated the stepper styles: changed the colors and font-weights, as well as the current step label position on smaller screens. diff --git a/packages/styles/src/components/stepper.scss b/packages/styles/src/components/stepper.scss index 6f73e5ca20..61ce4df535 100644 --- a/packages/styles/src/components/stepper.scss +++ b/packages/styles/src/components/stepper.scss @@ -2,225 +2,225 @@ @use './../mixins/icons' as icons-mx; @use './../mixins/utilities'; -@use './../variables/spacing'; @use './../variables/color'; +@use './../variables/commons'; +@use './../variables/spacing'; @use './../variables/components/stepper'; @use './../themes/bootstrap/core' as *; -.stepper-container { - @include size-mx.responsive-size('big', 'margin-bottom'); - position: relative; -} - -.stepper-bar { - height: stepper.$stepper-bar-height; - margin-top: stepper.$stepper-indicator-height * 0.5; - margin-bottom: -1 * (stepper.$stepper-bar-height + stepper.$stepper-indicator-height) * 0.5; - - .progress-bar { - background-color: color.$yellow; - } -} - .stepper { + // start a counter for the step numbers counter-reset: step-index; - list-style: none; - padding: 0 (0.5 * stepper.$stepper-indicator-height); - margin: 0; + @include utilities.reset-list; display: grid; - grid-template-columns: repeat(auto-fit, minmax(#{0.5 * stepper.$stepper-indicator-height}, 1fr)); - justify-items: center; + + // the first column is half a step wide to make sure the separators are the same size even on small screens + grid-template-columns: stepper.$stepper-indicator-height / 2; + + // all other columns are 1 fraction of the available space ase we don't know the number of steps + grid-auto-columns: minmax(0, 1fr); + + // we use a padding and negative margin on the last step for the same reason we need the first column + padding-inline-end: stepper.$stepper-indicator-height / 2; } .stepper-item { - counter-increment: step-index; + grid-row: 1; position: relative; - display: flex; - flex-direction: column; - transition: color 250ms; - // Stepper indicator styles + &:not(:last-child) { + grid-column: span 2; + } +} + +.stepper-link { + text-decoration: none; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + color: stepper.$stepper-link-color; + width: fit-content; + line-height: stepper.$stepper-link-line-height; + + // not adding ellipsis if the like doesn't have a title for accessibility reasons + &:not([title]) { + -webkit-line-clamp: none; + } + + .stepper-item:not(:first-child, :last-child) > & { + margin-inline: auto; + text-align: center; + } + + .stepper-item:last-child > & { + margin-inline-start: auto; + margin-inline-end: -1 * stepper.$stepper-indicator-height / 2; // negative margin matching the container padding + text-align: end; + } + + .stepper-item[aria-current='step'] > & { + color: stepper.$stepper-link-current-color; + font-weight: stepper.$stepper-link-current-font-weight; + } + + // stepper indicator &::before { - content: ''; - pointer-events: none; + counter-increment: step-index; + content: counter(step-index); display: flex; align-items: center; justify-content: center; height: stepper.$stepper-indicator-height; width: stepper.$stepper-indicator-height; - margin-bottom: -1 * stepper.$stepper-indicator-height; + box-sizing: border-box; + margin-block-end: stepper.$stepper-link-gap; color: stepper.$stepper-indicator-color; background-color: stepper.$stepper-indicator-bg; border: stepper.$stepper-indicator-border-width solid stepper.$stepper-indicator-border-color; border-radius: 50%; - font-size: stepper.$stepper-indicator-font-size; font-weight: stepper.$stepper-indicator-font-weight; - transition: background 250ms; - } - - &[aria-current] ~ ::before { - color: stepper.$stepper-indicator-future-color; - background-color: stepper.$stepper-indicator-future-bg; - } - - &:is(:focus-visible, :focus-within)::before { - outline: stepper.$stepper-indicator-hover-outline; - } - - // Check icon (for completed steps only) - &::after { - @include icons-mx.icon(2105); - content: ''; - position: absolute; - display: block; - top: 0.5 * (stepper.$stepper-indicator-height - stepper.$stepper-indicator-check-icon-size); - height: stepper.$stepper-indicator-check-icon-size; - width: stepper.$stepper-indicator-check-icon-size; - } + text-indent: initial; - &[aria-current], - &[aria-current] ~ & { - pointer-events: none; - - &::before { - content: counter(step-index); + .stepper-item:not(:first-child, :last-child) > & { + margin-inline: auto; } - &::after { - content: unset; + .stepper-item:last-child > & { + margin-inline-start: auto; } - } - - // First stepper item - left aligned - &:first-child { - justify-self: start; - align-items: start; - transform: translateX(#{-0.5 * stepper.$stepper-indicator-height}); - &::after { - left: 0.5 * (stepper.$stepper-indicator-height - stepper.$stepper-indicator-check-icon-size); + // completed steps with check mark + .stepper-item:not( + .stepper-item[aria-current='step'], + .stepper-item[aria-current='step'] ~ .stepper-item + ) + > & { + background-image: stepper.$stepper-indicator-check-icon; + background-size: stepper.$stepper-indicator-check-icon-size; + background-position: center; + color: transparent; } - } - - // Last stepper item - right aligned - &:last-child { - justify-self: end; - align-items: end; - text-align: right; - margin-right: -0.5 * stepper.$stepper-indicator-height; - &::after { - right: 0.5 * (stepper.$stepper-indicator-height - stepper.$stepper-indicator-check-icon-size); + // future steps with grey background + .stepper-item[aria-current='step'] ~ .stepper-item > & { + color: stepper.$stepper-indicator-future-color; + background-color: stepper.$stepper-indicator-future-bg; } } - // Other stepper items - centered - &:not(:first-child):not(:last-child) { - grid-column-end: span 2; - align-items: center; - - &::after { - left: calc(50% - #{0.5 * stepper.$stepper-indicator-check-icon-size}); + // progress bar + &::after { + content: ''; + display: block; + position: absolute; + inset-block-start: (stepper.$stepper-indicator-height - stepper.$stepper-bar-height) / 2; + inset-inline-start: 0; + height: stepper.$stepper-bar-height; + width: 100%; + z-index: -1; + + // completed steps with filled bar + .stepper-item:not( + .stepper-item[aria-current='step'], + .stepper-item[aria-current='step'] ~ .stepper-item + ) + > & { + background-color: stepper.$stepper-bar-fill-color; } - } - - &:hover { - color: stepper.$stepper-indicator-hover-color; - &::before { - background-color: stepper.$stepper-indicator-hover-bg; + // current step with half-filled bar + .stepper-item[aria-current='step'] > & { + background: linear-gradient( + 90deg, + stepper.$stepper-bar-fill-color 50%, + stepper.$stepper-bar-color 50% + ); } - } -} -.stepper-link { - z-index: 1; - padding-top: stepper.$stepper-indicator-height + stepper.$stepper-link-gap; - text-decoration: none; - color: stepper.$stepper-link-color; - transition: color 250ms; - text-align: center; - - .stepper-item[aria-current] > & { - color: stepper.$stepper-link-current-color; - font-size: stepper.$stepper-link-current-font-size; - font-weight: stepper.$stepper-link-current-font-weight; + // future steps with unfilled bar + .stepper-item:is(.stepper-item[aria-current='step'] ~ .stepper-item) > & { + background-color: stepper.$stepper-bar-color; + } } - @at-root a:hover#{&}, - :focus-visible#{&} { - color: stepper.$stepper-link-hover-color; - } + // focus/hover states + &:is(a[href]) { + @include utilities.focus-style { + border-radius: commons.$border-radius; + } - &:focus-visible { - outline: none; + @include utilities.focus-hover-style-custom('::before') { + &, + .stepper-item[aria-current='step'] ~ .stepper-item > & { + color: #fff; + background-color: #000; + } + + .stepper-item:not( + .stepper-item[aria-current='step'], + .stepper-item[aria-current='step'] ~ .stepper-item + ) + > & { + background-image: stepper.$stepper-indicator-hover-check-icon; + } + } } } @include media-breakpoint-down(rg) { .stepper-item { - &:not([aria-current]) .stepper-link { - overflow: hidden; + .stepper-link { white-space: nowrap; - text-indent: 100%; - width: stepper.$stepper-indicator-height; + width: 100%; } - } -} - -@include utilities.high-contrast-mode() { - .stepper-bar { - background-color: CanvasText; - border: 0 none; - .progress-bar { - background-color: Highlight; + &[aria-current='step'] { + // using "display: contents" on the stepper-item and stepper-link so that label, indicator and progress bar can be directly placed in the grid + display: contents; + + > .stepper-link { + display: contents; + + &::before { + grid-row: -1; + transform: translateX(50%); + margin-inline-end: 0 !important; + } + + &::after { + grid-row: -1; + width: 201%; + transform: translateX(-50%); + margin-block-start: (stepper.$stepper-indicator-height - stepper.$stepper-bar-height) / 2; + margin-block-end: -1 * (stepper.$stepper-indicator-height - stepper.$stepper-bar-height) / + 2; + position: static; + } + } } - } - .stepper-item { - &::before { - width: calc(#{stepper.$stepper-indicator-height} + 2 * #{spacing.$size-line}); - height: calc(#{stepper.$stepper-indicator-height} + 2 * #{spacing.$size-line}); - line-height: calc(#{stepper.$stepper-indicator-height} + 2 * #{spacing.$size-line}); - color: ButtonText; - background-color: ButtonFace; - border: 0; - outline: spacing.$size-line solid VisitedText; - outline-offset: -3 * spacing.$size-line; - margin-top: -1 * spacing.$size-line; - } - - &[aria-current] ~ &::before { - outline-color: ButtonBorder; - } + &:not(&[aria-current='step']) { + grid-row: -1; + justify-self: stretch; - &[aria-current]::before { - outline-color: Highlight; - } - - &:is(:focus-visible, :focus-within)::before { - border: spacing.$size-line solid Highlight; - line-height: stepper.$stepper-indicator-height; - outline-color: VisitedText; - } - - &[aria-current] ~ &:is(:focus-visible, :focus-within)::before { - outline-color: ButtonBorder; + // hide completed and future step labels + > .stepper-link { + -webkit-line-clamp: none; + line-height: 0; + text-indent: 100%; + } } } +} - .stepper-link { - color: VisitedText; - - .stepper-item[aria-current] ~ .stepper-item > & { - color: CanvasText; - } +// for backward compatibility +.stepper-container { + @include size-mx.responsive-size('big', 'margin-bottom'); - .stepper-item[aria-current] > & { - color: Highlight; - } + > .stepper-bar { + display: none; } } diff --git a/packages/styles/src/mixins/_utilities.scss b/packages/styles/src/mixins/_utilities.scss index a48ebb623e..67826ecdac 100644 --- a/packages/styles/src/mixins/_utilities.scss +++ b/packages/styles/src/mixins/_utilities.scss @@ -136,6 +136,16 @@ } } +@mixin focus-hover-style-custom($additional-selector: '') { + @include focus-style-custom($additional-selector) { + @content; + } + + &:hover#{$additional-selector} { + @content; + } +} + @mixin disabled-style($additional-selector: '') { &:disabled#{$additional-selector} { pointer-events: none; diff --git a/packages/styles/src/variables/components/_stepper.scss b/packages/styles/src/variables/components/_stepper.scss index aca41ff0c4..283960ba5f 100644 --- a/packages/styles/src/variables/components/_stepper.scss +++ b/packages/styles/src/variables/components/_stepper.scss @@ -7,28 +7,38 @@ $stepper-bar-height: spacing.$size-micro; +$stepper-bar-color: color.$gray-40; +$stepper-bar-fill-color: color.$yellow; + $stepper-indicator-height: spacing.$size-bigger-big; $stepper-indicator-border-width: spacing.$size-micro; $stepper-indicator-border-color: color.$white; -$stepper-indicator-font-size: type.$font-size-regular; -$stepper-indicator-font-weight: type.$font-weight-base; +$stepper-indicator-font-weight: type.$font-weight-bold; $stepper-indicator-bg: color.$yellow; $stepper-indicator-color: color.$black; + +$stepper-indicator-check-icon: url('#{icon-fn.get-colored-svg-url('2105', $stepper-indicator-color)}'); $stepper-indicator-check-icon-size: spacing.$size-large; + +$stepper-indicator-future-bg: color.$gray-60; +$stepper-indicator-future-color: color.$white; + $stepper-indicator-hover-color: color.$white; $stepper-indicator-hover-bg: color.$black; $stepper-indicator-hover-outline: forms.$input-focus-outline-thickness solid var(--post-contrast-color); -$stepper-indicator-future-bg: color.$gray-60; -$stepper-indicator-future-color: color.$white; +$stepper-indicator-hover-check-icon: url('#{icon-fn.get-colored-svg-url('2105',$stepper-indicator-hover-color)}'); + +$stepper-link-line-height: type.$line-height-sm; $stepper-link-gap: spacing.$size-mini; $stepper-link-color: color.$gray-60; -$stepper-link-hover-color: color.$black; -$stepper-link-current-color: color.$black; -$stepper-link-current-font-size: type.$font-size-regular; + +$stepper-link-current-color: color.$gray-80; $stepper-link-current-font-weight: type.$font-weight-bold; +$stepper-link-hover-color: color.$black; + // DEPRECATED -$stepper-indicator-check-icon: url('#{icon-fn.get-colored-svg-url('2105', $stepper-indicator-color)}'); -$stepper-indicator-hover-check-icon: url('#{icon-fn.get-colored-svg-url('2105',$stepper-indicator-hover-color)}'); +$stepper-indicator-font-size: type.$font-size-16; +$stepper-link-current-font-size: type.$font-size-16; From 653bda020c79c5e9b291ce04c8f73cd55f7d0759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= Date: Mon, 20 May 2024 09:45:55 +0200 Subject: [PATCH 02/10] Fix linting issue --- packages/styles/src/components/stepper.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/styles/src/components/stepper.scss b/packages/styles/src/components/stepper.scss index 61ce4df535..38f90a8a9a 100644 --- a/packages/styles/src/components/stepper.scss +++ b/packages/styles/src/components/stepper.scss @@ -36,11 +36,13 @@ } .stepper-link { - text-decoration: none; + // -webkit-box is needed for line-clamp: https://caniuse.com/css-line-clamp + // stylelint-disable-next-line value-no-vendor-prefix display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; + text-decoration: none; color: stepper.$stepper-link-color; width: fit-content; line-height: stepper.$stepper-link-line-height; From fd02ef205e3aae4a85a468a565b9de4a0853b91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= Date: Mon, 20 May 2024 14:38:08 +0200 Subject: [PATCH 03/10] chore(docs): add CSS-only stepper docs --- .changeset/shaggy-weeks-grab.md | 5 + .../stepper/stepper-component.sample.ts | 18 --- .../stepper/stepper-template.sample.html | 23 ---- .../components/stepper/stepper.docs.mdx | 65 ++++------ .../components/stepper/stepper.stories.ts | 120 +++++++++++++++++- 5 files changed, 149 insertions(+), 82 deletions(-) create mode 100644 .changeset/shaggy-weeks-grab.md delete mode 100644 packages/documentation/src/stories/components/stepper/stepper-component.sample.ts delete mode 100644 packages/documentation/src/stories/components/stepper/stepper-template.sample.html diff --git a/.changeset/shaggy-weeks-grab.md b/.changeset/shaggy-weeks-grab.md new file mode 100644 index 0000000000..ef640463f0 --- /dev/null +++ b/.changeset/shaggy-weeks-grab.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-documentation': minor +--- + +Added documentation for the CSS-only stepper and deprecated the stepper based on ng-bootstrap progress bar. diff --git a/packages/documentation/src/stories/components/stepper/stepper-component.sample.ts b/packages/documentation/src/stories/components/stepper/stepper-component.sample.ts deleted file mode 100644 index 5df6b5129d..0000000000 --- a/packages/documentation/src/stories/components/stepper/stepper-component.sample.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'post-stepper', - templateUrl: 'template.html', -}) -export class StepperDemoComponent { - steps = ['Sender', 'Product', 'Other details', 'Order summary']; - currentIndex = 2; - - isCurrent(step: string): boolean { - return step === this.steps[this.currentIndex]; - } - - getPathTo(step: string): string { - return step.toLowerCase().split(' ').join('-'); - } -} diff --git a/packages/documentation/src/stories/components/stepper/stepper-template.sample.html b/packages/documentation/src/stories/components/stepper/stepper-template.sample.html deleted file mode 100644 index b91caf9142..0000000000 --- a/packages/documentation/src/stories/components/stepper/stepper-template.sample.html +++ /dev/null @@ -1,23 +0,0 @@ -
-

Order progress, step 3 of 4

- - -
    -
  1. - - Complete: - {{ step }} - - {{ step }} -
  2. -
-
diff --git a/packages/documentation/src/stories/components/stepper/stepper.docs.mdx b/packages/documentation/src/stories/components/stepper/stepper.docs.mdx index 39be7982fa..d806a44f93 100644 --- a/packages/documentation/src/stories/components/stepper/stepper.docs.mdx +++ b/packages/documentation/src/stories/components/stepper/stepper.docs.mdx @@ -1,56 +1,47 @@ -import { Meta, Source } from '@storybook/blocks'; -import { PostTabHeader, PostTabPanel, PostTabs } from '@swisspost/design-system-components-react'; -import StylesPackageImport from '@/shared/styles-package-import.mdx'; +import { Canvas, Controls, Meta } from '@storybook/blocks'; import PostComponentDemoLink from '@/shared/post-component-demo-link.mdx'; -import NgbComponentImport from '@/shared/nb-bootstrap/ngb-component-import.mdx'; -import * as stepperStories from './stepper.stories'; -import stepperTemplate from './stepper-template.sample.html?raw'; -import stepperComponent from './stepper-component.sample?raw'; +import StylesPackageImport from '@/shared/styles-package-import.mdx'; +import * as StepperStories from './stepper.stories'; - +
# Stepper
-

Conveys progress through numbered steps.

+

The stepped progression component provides an interactive visual overview a process. It shows at a glance the amount of steps a user is required to go trough, and serves as a guide for each step, indicating its status.

+ +
+

The stepper previously used ng-bootstrap's progressbar component, this has been deprecated in favor of the CSS-only stepper documented below.

+
+
-
-

The stepper uses ng-bootstrap's progressbar component.

+ +
+
- + + +## Examples + +### Navigational Stepper - +You can use a stepper to allow users to navigate to specific steps. +To do so, use `a` elements for the `.stepper-link` of the steps you want to be navigable, and use `span` for the step you want not to be navigable. +Then, wrap the stepper inside a `
-

The stepped progression component provides an interactive visual overview a process. It shows at a glance the amount of steps a user is required to go trough, and serves as a guide for each step, indicating its status.

+

The stepped progression component provides an interactive visual overview of a process. It shows at a glance the amount of steps a user is required to go through, and serves as a guide for each step, indicating its status.

The stepper previously used ng-bootstrap's progressbar component, this has been deprecated in favor of the CSS-only stepper documented below.

From 84a2c4527b8c76231e5bb953f6fc439e806e8559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aliz=C3=A9=20Debray?= <33580481+alizedebray@users.noreply.github.com> Date: Wed, 22 May 2024 08:30:02 +0200 Subject: [PATCH 07/10] Update packages/documentation/src/stories/components/stepper/stepper.docs.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Oliver Schürch --- .../src/stories/components/stepper/stepper.docs.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/documentation/src/stories/components/stepper/stepper.docs.mdx b/packages/documentation/src/stories/components/stepper/stepper.docs.mdx index 1d7399035a..be24d1ff55 100644 --- a/packages/documentation/src/stories/components/stepper/stepper.docs.mdx +++ b/packages/documentation/src/stories/components/stepper/stepper.docs.mdx @@ -32,7 +32,7 @@ import * as StepperStories from './stepper.stories'; ### Navigational Stepper You can use a stepper to allow users to navigate to specific steps. -To do so, use `a` elements for the `.stepper-link` of the steps you want to be navigable, and use `span` for the step you want not to be navigable. +To do so, use `a` elements for the `.stepper-link` of the steps you want to be navigable, and use `span` for the steps you want not to be navigable. Then, wrap the stepper inside a `