diff --git a/.changeset/weak-jars-rhyme.md b/.changeset/weak-jars-rhyme.md new file mode 100644 index 0000000000..0a81503669 --- /dev/null +++ b/.changeset/weak-jars-rhyme.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-styles': minor +--- + +Added color palettes to easily apply colors to a page section using predefined color sets. diff --git a/packages/documentation/cypress/e2e/components/palette.cy.ts b/packages/documentation/cypress/e2e/components/palette.cy.ts new file mode 100644 index 0000000000..dc38dc8312 --- /dev/null +++ b/packages/documentation/cypress/e2e/components/palette.cy.ts @@ -0,0 +1,13 @@ +describe('Palette', () => { + describe('Accessibility', () => { + beforeEach(() => { + cy.visit('/iframe.html?id=snapshots--palette'); + cy.get('.palette-default', { timeout: 30000 }).should('be.visible'); + cy.injectAxe(); + }); + + it('Has no detectable a11y violations on load for all variants', () => { + cy.checkA11y('#root-inner'); + }); + }); +}); diff --git a/packages/documentation/cypress/snapshots/components/palette.snapshot.ts b/packages/documentation/cypress/snapshots/components/palette.snapshot.ts new file mode 100644 index 0000000000..a0e3bedc09 --- /dev/null +++ b/packages/documentation/cypress/snapshots/components/palette.snapshot.ts @@ -0,0 +1,7 @@ +describe('Palette', () => { + it('default', () => { + cy.visit('/iframe.html?id=snapshots--palette'); + cy.get('.palette-default', { timeout: 30000 }).should('be.visible'); + cy.percySnapshot('Palettes', { widths: [1440] }); + }); +}); diff --git a/packages/documentation/src/stories/foundation/palette/palette.docs.mdx b/packages/documentation/src/stories/foundation/palette/palette.docs.mdx new file mode 100644 index 0000000000..0296bc2bfa --- /dev/null +++ b/packages/documentation/src/stories/foundation/palette/palette.docs.mdx @@ -0,0 +1,27 @@ +import { Canvas, Controls, Meta } from '@storybook/blocks'; +import * as paletteStories from './palette.stories'; + + + +
+ # Palettes +
+ +
+ Easily apply colors to a section of your page using predefined color sets. +
+ +There are four different palettes: +- **Default** - The standard palette used when no other palette is specified. +- **Alternate** - Used to differentiate alternating sections from the body, without strong emphasis or highlighting. +- **Accent** - A complementary color used for highlights and emphasis. +- **Brand** - The primary color for the brand. + +Palettes may also include a specific text color that is different from the body color. +You can apply this text color to any element by using the `.palette-text` class. + + + +
+ +
diff --git a/packages/documentation/src/stories/foundation/palette/palette.snapshot.stories.ts b/packages/documentation/src/stories/foundation/palette/palette.snapshot.stories.ts new file mode 100644 index 0000000000..5fc42a0cb4 --- /dev/null +++ b/packages/documentation/src/stories/foundation/palette/palette.snapshot.stories.ts @@ -0,0 +1,35 @@ +import type { StoryObj } from '@storybook/web-components'; +import { html } from 'lit'; +import meta from './palette.stories'; + +const { id, ...metaWithoutId } = meta; + +export default { + ...metaWithoutId, + title: 'Snapshots', +}; + +type Story = StoryObj; + +export const Palette: Story = { + render: () => { + return html`${['light', 'dark'].map( + mainScheme => html` +
+ ${['', 'light', 'dark'].map( + paletteScheme => html` +
+

Palette scheme: ${paletteScheme || 'none'}

+
+ ${meta.argTypes.palette.options.map(palette => + meta.render({ palette, colorScheme: paletteScheme }), + )} +
+
+ `, + )} +
+ `, + )}`; + }, +}; diff --git a/packages/documentation/src/stories/foundation/palette/palette.stories.ts b/packages/documentation/src/stories/foundation/palette/palette.stories.ts new file mode 100644 index 0000000000..0d54b51955 --- /dev/null +++ b/packages/documentation/src/stories/foundation/palette/palette.stories.ts @@ -0,0 +1,58 @@ +import { Args, Meta, StoryObj } from '@storybook/web-components'; +import { html, nothing } from 'lit'; + +const meta: Meta = { + id: '43481535-5b39-40b5-a273-478b07dc3b31', + title: 'Foundations/Palettes', + tags: ['package:HTML'], + render: renderPalette, + parameters: { + palettes: [], + design: { + type: 'figma', + url: 'https://www.figma.com/file/xZ0IW0MJO0vnFicmrHiKaY/Components-Post?type=design&node-id=18172-73431&mode=design&t=3lniLiZhl7q9Gqgn-4', + }, + }, + args: { + palette: 'default', + }, + argTypes: { + palette: { + name: 'Palette', + description: 'The set of colors used for a section of the page.', + control: { + type: 'radio', + labels: { + default: 'Default', + alternate: 'Alternate', + brand: 'Brand', + accent: 'Accent', + }, + }, + options: ['default', 'alternate', 'accent', 'brand'], + table: { + category: 'General', + }, + }, + }, +}; + +export default meta; + +// RENDERER +function renderPalette(args: Args) { + return html` +
+

+ I use a specific color from the palette (it might be the same as the body color). +

+

I use the main body color.

+ +
+ `; +} + +// STORIES +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/packages/styles/gulpfile.js b/packages/styles/gulpfile.js index 1a663b1e9b..e1eac17f77 100644 --- a/packages/styles/gulpfile.js +++ b/packages/styles/gulpfile.js @@ -27,7 +27,7 @@ gulp.task('copy', () => { * See https://github.com/pnpm/pnpm/issues/8338 for more information and reproduction */ gulp.task('temporarily-copy-token-files', () => { - return gulp.src(['../tokens/dist/*.scss']).pipe(gulp.dest('./src/tokens/temp')); + return gulp.src(['../tokens/dist/**/*.scss']).pipe(gulp.dest('./src/tokens/temp')); }); /** diff --git a/packages/styles/src/cargo-external.scss b/packages/styles/src/cargo-external.scss index 845e06851a..fbeaeffede 100644 --- a/packages/styles/src/cargo-external.scss +++ b/packages/styles/src/cargo-external.scss @@ -2,6 +2,7 @@ @use './tokens/device'; @use './tokens/external'; @use './tokens/cargo-theme'; +@use './palettes/cargo-palettes'; @use './utilities'; @use './elements'; @use './components'; diff --git a/packages/styles/src/cargo-internal.scss b/packages/styles/src/cargo-internal.scss index 8a5de0f563..6e8dcdabb5 100644 --- a/packages/styles/src/cargo-internal.scss +++ b/packages/styles/src/cargo-internal.scss @@ -2,6 +2,7 @@ @use './tokens/device'; @use './tokens/internal'; @use './tokens/cargo-theme'; +@use './palettes/cargo-palettes'; @use './utilities'; @use './elements'; @use './components'; diff --git a/packages/styles/src/palettes/_mixins.scss b/packages/styles/src/palettes/_mixins.scss new file mode 100644 index 0000000000..b98cd0116e --- /dev/null +++ b/packages/styles/src/palettes/_mixins.scss @@ -0,0 +1,74 @@ +@use 'sass:map'; +@use 'sass:meta'; + +@use '../functions/tokens'; +@use '../placeholders/schemes'; +@use '../tokens/elements'; +@use '../tokens/palettes'; + +@use './variables' as *; + +@forward './utilities'; + +@mixin palettes($theme) { + body { + background-color: var(--post-current-palette-bg) !important; + + &:not([data-color-scheme='dark']) { + @include palette-tokens($default-palette, $theme, 'light'); + } + + &[data-color-scheme='dark'] { + @include palette-tokens($default-palette, $theme, 'dark'); + } + } + + @each $palette in $palettes { + .palette-#{$palette} { + // Redefining the body color is necessary to ensure that the new color scheme is applied correctly. + // Known limitation: body color may be incorrect with nested parent with different `data-color-scheme` values. + color: tokens.get('body-color', elements.$post-body); + background-color: var(--post-current-palette-bg) !important; + + // Light scheme explicitly set on the palette: + &[data-color-scheme='light'], + + // No scheme explicitly set on the palette, but parent has a light scheme: + &:where([data-color-scheme='light'] :not([data-color-scheme='dark'])), + + // No scheme explicitly set on the palette, and no scheme on the parent either: + &:not([data-color-scheme='dark']):not([data-color-scheme='dark'] *) { + @include palette-tokens($palette, $theme, 'light', $override-scheme: true); + } + + // Dark scheme explicitly set on the palette: + &[data-color-scheme='dark'], + + // No scheme explicitly set on the palette, but parent has a dark scheme: + &:where([data-color-scheme='dark'] :not([data-color-scheme='dark'])) { + @include palette-tokens($palette, $theme, 'dark', $override-scheme: true); + } + } + } +} + +@mixin palette-tokens($name, $theme, $scheme, $override-scheme: false) { + $palette: map.get(meta.module-variables(palettes), '#{$theme}-#{$scheme}'); + + @if (not $palette) { + @error 'Palette #{$theme}-#{$scheme} not found.'; + } + + --post-current-palette-fg: #{tokens.get('palettes-color-#{$name}-fg', $palette)}; + --post-current-palette-bg: #{tokens.get('palettes-color-#{$name}-bg', $palette)}; + + @if ($override-scheme == true) { + $bg-scheme: tokens.get('palettes-color-#{$name}-bg-scheme', $palette); + + @if ($bg-scheme == 'light') { + @extend %color-scheme-light; + } @else { + @extend %color-scheme-dark; + } + } +} diff --git a/packages/styles/src/palettes/_utilities.scss b/packages/styles/src/palettes/_utilities.scss new file mode 100644 index 0000000000..e2b2e50157 --- /dev/null +++ b/packages/styles/src/palettes/_utilities.scss @@ -0,0 +1,3 @@ +.palette-text { + color: var(--post-current-palette-fg); +} diff --git a/packages/styles/src/palettes/_variables.scss b/packages/styles/src/palettes/_variables.scss new file mode 100644 index 0000000000..facc770217 --- /dev/null +++ b/packages/styles/src/palettes/_variables.scss @@ -0,0 +1,2 @@ +$default-palette: 'default'; +$palettes: $default-palette, 'alternate', 'brand', 'accent'; diff --git a/packages/styles/src/palettes/cargo-palettes.scss b/packages/styles/src/palettes/cargo-palettes.scss new file mode 100644 index 0000000000..5cbbd9d1c4 --- /dev/null +++ b/packages/styles/src/palettes/cargo-palettes.scss @@ -0,0 +1,3 @@ +@use 'mixins' as *; + +@include palettes('cargo'); diff --git a/packages/styles/src/palettes/post-palettes.scss b/packages/styles/src/palettes/post-palettes.scss new file mode 100644 index 0000000000..9d99a8fc14 --- /dev/null +++ b/packages/styles/src/palettes/post-palettes.scss @@ -0,0 +1,3 @@ +@use 'mixins' as *; + +@include palettes('post'); diff --git a/packages/styles/src/post-external.scss b/packages/styles/src/post-external.scss index 10e1746fa7..6a1d30e333 100644 --- a/packages/styles/src/post-external.scss +++ b/packages/styles/src/post-external.scss @@ -2,6 +2,7 @@ @use './tokens/device'; @use './tokens/external'; @use './tokens/post-theme'; +@use './palettes/post-palettes'; @use './utilities'; @use './elements'; @use './components'; diff --git a/packages/styles/src/post-internal.scss b/packages/styles/src/post-internal.scss index 4e06884fe1..c2884c77b0 100644 --- a/packages/styles/src/post-internal.scss +++ b/packages/styles/src/post-internal.scss @@ -2,6 +2,7 @@ @use './tokens/device'; @use './tokens/internal'; @use './tokens/post-theme'; +@use './palettes/post-palettes'; @use './utilities'; @use './elements'; @use './components'; diff --git a/packages/styles/src/tokens/_palettes.scss b/packages/styles/src/tokens/_palettes.scss new file mode 100644 index 0000000000..930a16d2fb --- /dev/null +++ b/packages/styles/src/tokens/_palettes.scss @@ -0,0 +1,9 @@ +@use './temp/palettes/post-light' as post-light; +@use './temp/palettes/post-dark' as post-dark; +@use './temp/palettes/cargo-light' as cargo-light; +@use './temp/palettes/cargo-dark' as cargo-dark; + +$post-light: post-light.$post-palettes; +$post-dark: post-dark.$post-palettes; +$cargo-light: cargo-light.$post-palettes; +$cargo-dark: cargo-dark.$post-palettes; diff --git a/packages/styles/tests/palettes/cargo-palettes.test.scss b/packages/styles/tests/palettes/cargo-palettes.test.scss new file mode 100644 index 0000000000..40ea983fa8 --- /dev/null +++ b/packages/styles/tests/palettes/cargo-palettes.test.scss @@ -0,0 +1,10 @@ +@use 'src/palettes/cargo-palettes'; + +/* stylelint-disable scss/at-extend-no-missing-placeholder */ +.palettes { + @extend .palette-text; + @extend .palette-default; + @extend .palette-alternate; + @extend .palette-brand; + @extend .palette-accent; +} diff --git a/packages/styles/tests/palettes/post-palettes.test.scss b/packages/styles/tests/palettes/post-palettes.test.scss new file mode 100644 index 0000000000..10c75532e8 --- /dev/null +++ b/packages/styles/tests/palettes/post-palettes.test.scss @@ -0,0 +1,10 @@ +@use 'src/palettes/post-palettes'; + +/* stylelint-disable scss/at-extend-no-missing-placeholder */ +.palettes { + @extend .palette-text; + @extend .palette-default; + @extend .palette-alternate; + @extend .palette-brand; + @extend .palette-accent; +}