From 5a8b256e4d4b39cd73a40f48a88bcb3b95932dff Mon Sep 17 00:00:00 2001 From: Lea Date: Tue, 7 Jan 2025 16:11:06 +0100 Subject: [PATCH 1/3] chore(components): update overflow layout header in tablet and mobile (#4364) --- .changeset/silent-vans-care.md | 5 +++++ .../src/components/post-header/post-header.scss | 12 +++++++++--- .../src/components/post-header/post-header.tsx | 3 +++ .../post-megadropdown/post-megadropdown.scss | 7 +------ 4 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 .changeset/silent-vans-care.md diff --git a/.changeset/silent-vans-care.md b/.changeset/silent-vans-care.md new file mode 100644 index 0000000000..30fde6b48e --- /dev/null +++ b/.changeset/silent-vans-care.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-components': patch +--- + +Updated header overflow in tablet and mobile for long application titles. diff --git a/packages/components/src/components/post-header/post-header.scss b/packages/components/src/components/post-header/post-header.scss index c6c3eb7b01..32163a160f 100644 --- a/packages/components/src/components/post-header/post-header.scss +++ b/packages/components/src/components/post-header/post-header.scss @@ -11,6 +11,7 @@ --global-header-height: 72px; --global-header-minimal-height: 24px; --main-header-height: 56px; + --main-header-min-height: 56px; --header-height: calc(var(--global-header-height) + var(--main-header-height)); @include media.min(lg) { @@ -27,6 +28,7 @@ @include media.max(lg) { --global-header-height: 64px; --main-header-height: 48px; + --main-header-min-height: 48px; } } @@ -66,7 +68,6 @@ slot[name='post-logo'] { display: flex; align-items: center; gap: var(--post-core-dimension-24); - height: var(--global-header-height); } .align-end { @@ -91,14 +92,15 @@ slot[name='post-logo'] { display: flex; padding-left: 0; gap: 1rem; + flex-shrink: 0 !important; } .title-header { display: flex; align-items: center; + gap: var(--post-core-dimension-8); + min-height: var(--main-header-min-height); justify-content: space-between; - gap: var(--post-core-dimension-4); - height: var(--main-header-height); background: var(--post-core-color-brand-white); @include media.min(lg) { @@ -111,6 +113,8 @@ slot[name='post-logo'] { z-index: 1; inset-block-start: var(--global-header-height); padding-inline: var(--post-core-dimension-8) var(--post-core-dimension-16); + padding-block: var(--post-core-dimension-9); + flex-wrap: wrap; &.title-header-mobile-extended { border-bottom: 1px solid var(--post-core-color-sandgrey-012); @@ -124,6 +128,7 @@ slot[name='post-logo'] { ::slotted(h1) { margin: 0 !important; + flex-shrink: 10; @include media.min(lg) { font-size: var(--post-core-font-size-28) !important; @@ -131,6 +136,7 @@ slot[name='post-logo'] { @include media.max(lg) { font-size: var(--post-core-font-size-20) !important; + max-width: calc(100vw - var(--post-core-dimension-8) - var(--post-core-dimension-16)); } } diff --git a/packages/components/src/components/post-header/post-header.tsx b/packages/components/src/components/post-header/post-header.tsx index 849678ba9f..b71d906cc5 100644 --- a/packages/components/src/components/post-header/post-header.tsx +++ b/packages/components/src/components/post-header/post-header.tsx @@ -113,6 +113,9 @@ export class PostHeader { this.mobileMenuAnimation.finish(); // no animation } + const mhh = this.host.shadowRoot.querySelector('.title-header').clientHeight; + this.host.style.setProperty('--main-header-height', `${mhh}px`); + // Apply only on change for doing work only when necessary if (newDevice !== previousDevice) { this.device = newDevice; diff --git a/packages/components/src/components/post-megadropdown/post-megadropdown.scss b/packages/components/src/components/post-megadropdown/post-megadropdown.scss index c489f8c48b..6a22d4e99f 100644 --- a/packages/components/src/components/post-megadropdown/post-megadropdown.scss +++ b/packages/components/src/components/post-megadropdown/post-megadropdown.scss @@ -26,10 +26,6 @@ } post-popovercontainer { - --post-global-header-height: 72px; - --post-main-header-height: 56px; - --post-header-height: calc(var(--post-global-header-height) + var(--post-main-header-height)); - background-color: #fafafa; border: none; border-top: 1px solid #e1e0dc; @@ -38,9 +34,8 @@ post-popovercontainer { box-shadow: 0 8px 6px rgba(0, 0, 0, 16%); @include media.max(lg) { - --post-global-header-height: 64px; position: absolute; - top: var(--post-header-height) !important; + top: var(--header-height) !important; bottom: 0; left: 0; width: 100%; From bf95c9bded7b0ac5344c4b538aa6c84f75ce8b39 Mon Sep 17 00:00:00 2001 From: Myrta Sakellariou <66249294+myrta2302@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:51:03 +0200 Subject: [PATCH 2/3] feat(styles): implement gutter classes (#4378) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alizé Debray <33580481+alizedebray@users.noreply.github.com> --- .changeset/chatty-pigs-hear.md | 6 + .../snapshots/utilities/gutters.snapshot.ts | 7 + .../foundations/layout/grid/grid.docs.mdx | 5 + .../utilities/gutters/gutters.docs.mdx | 58 ++++++++ .../utilities/gutters/gutters.module.scss | 18 +++ .../gutters/gutters.snapshot.stories.ts | 44 ++++++ .../utilities/gutters/gutters.stories.ts | 130 ++++++++++++++++++ .../utilities/gutters/gutters.styles.scss | 20 +++ packages/styles/src/utilities/_variables.scss | 18 +++ 9 files changed, 306 insertions(+) create mode 100644 .changeset/chatty-pigs-hear.md create mode 100644 packages/documentation/cypress/snapshots/utilities/gutters.snapshot.ts create mode 100644 packages/documentation/src/stories/utilities/gutters/gutters.docs.mdx create mode 100644 packages/documentation/src/stories/utilities/gutters/gutters.module.scss create mode 100644 packages/documentation/src/stories/utilities/gutters/gutters.snapshot.stories.ts create mode 100644 packages/documentation/src/stories/utilities/gutters/gutters.stories.ts create mode 100644 packages/documentation/src/stories/utilities/gutters/gutters.styles.scss diff --git a/.changeset/chatty-pigs-hear.md b/.changeset/chatty-pigs-hear.md new file mode 100644 index 0000000000..7a42efb2f3 --- /dev/null +++ b/.changeset/chatty-pigs-hear.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-styles': minor +--- + +Implemented gutter utility classes. diff --git a/packages/documentation/cypress/snapshots/utilities/gutters.snapshot.ts b/packages/documentation/cypress/snapshots/utilities/gutters.snapshot.ts new file mode 100644 index 0000000000..6a2f1b705b --- /dev/null +++ b/packages/documentation/cypress/snapshots/utilities/gutters.snapshot.ts @@ -0,0 +1,7 @@ +describe('Gutters', () => { + it('Gutters', () => { + cy.visit('/iframe.html?id=snapshots--gutters'); + cy.get('.gutters-example', { timeout: 30000 }).should('be.visible'); + cy.percySnapshot('Gutters', { widths: [320, 780, 1024, 1440] }); + }); +}); diff --git a/packages/documentation/src/stories/foundations/layout/grid/grid.docs.mdx b/packages/documentation/src/stories/foundations/layout/grid/grid.docs.mdx index e1911e644e..ec5911cd3c 100644 --- a/packages/documentation/src/stories/foundations/layout/grid/grid.docs.mdx +++ b/packages/documentation/src/stories/foundations/layout/grid/grid.docs.mdx @@ -106,6 +106,11 @@ To control the space between your columns, add either `.g-*` (horizontal and ver +
+ You can find additional information and examples of grid gutters{' '} + here. +
+ ## Nesting To nest your content with the default grid, add a new `.row` and set of `.col-*` columns within an existing `.col-*` column. Nested rows should include a set of columns that add up to 12 or fewer. diff --git a/packages/documentation/src/stories/utilities/gutters/gutters.docs.mdx b/packages/documentation/src/stories/utilities/gutters/gutters.docs.mdx new file mode 100644 index 0000000000..4022e3497f --- /dev/null +++ b/packages/documentation/src/stories/utilities/gutters/gutters.docs.mdx @@ -0,0 +1,58 @@ +import { Canvas, Controls, Meta } from '@storybook/blocks'; +import * as GutterStories from './gutters.stories'; + +export const firstBreakpoint = Object.values(GutterStories.SCSS_VARIABLES.firstBreakpoint)[0]; +export const breakpoints = Object.values(GutterStories.SCSS_VARIABLES.breakpoints).map( + (breakpoint, i, arr) => [breakpoint, i !== arr.length - 1], +); + + + +# Gutters + +Gutters are the spaces between column content in grid elements (e.g., `.row`, `.col`, `.col-*`). They are implemented by applying an inline-padding to each column, along with matching negative margins to offset the padding at the row's edges. + +## Horizontal gutters + +The `.gx-{size}` classes can be used to set the horizontal gutters within the columns (`.col`) of a `.row`. + +### Example + + +
+ +
+ +## Vertical gutters + +The `.gy-{size}` classes can be used to set the vertical gutter width when a `.row` wraps to new lines. + +### Example + + +
+ +
+ +## Horizontal and vertical gutters + +The `.g-{size}` classes can be used to set both the horizontal and vertical gutter widths. + +### Example + + +
+ +
+ +## No gutters + +Gutters can be eliminated by applying the classes `.g-0`, `.gx-0`, or `.gy-0`, which remove all gutters, horizontal gutters, or vertical gutters, respectively. + +## Relative to breakpoints + +Breakpoint-specific classes allow precise control of horizontal, vertical, and general gutters at various screen sizes. + +- `.gx-{breakpoint}-{size}` (e.g. `gx-lg-24`) +- `.gy-{breakpoint}-{size}` (e.g. `gy-sm-32`) +- `.g-{breakpoint}-{size}` (e.g. `g-md-12`) diff --git a/packages/documentation/src/stories/utilities/gutters/gutters.module.scss b/packages/documentation/src/stories/utilities/gutters/gutters.module.scss new file mode 100644 index 0000000000..0b5a6608cb --- /dev/null +++ b/packages/documentation/src/stories/utilities/gutters/gutters.module.scss @@ -0,0 +1,18 @@ +@use 'sass:list'; +@use 'sass:map'; +@use '@swisspost/design-system-styles/core' as post; +@use '@swisspost/design-system-styles/tokens/utilities' as tokens; + +:export { + @each $key, $value in tokens.$post-spacing { + spacing_#{$key}: #{$value}; + } + @each $breakpoint in post.$grid-breakpoints-list { + @if (map.get(post.$grid-breakpoints, $breakpoint) == 0) { + firstBreakpoint_#{$breakpoint}: $breakpoint; + } + @if (map.get(post.$grid-breakpoints, $breakpoint) != 0) { + breakpoints_#{$breakpoint}: $breakpoint; + } + } +} diff --git a/packages/documentation/src/stories/utilities/gutters/gutters.snapshot.stories.ts b/packages/documentation/src/stories/utilities/gutters/gutters.snapshot.stories.ts new file mode 100644 index 0000000000..e4a5990bca --- /dev/null +++ b/packages/documentation/src/stories/utilities/gutters/gutters.snapshot.stories.ts @@ -0,0 +1,44 @@ +import type { Args, StoryObj } from '@storybook/web-components'; +import { html } from 'lit'; +import { COLOR_SCHEMES, schemes } from '@/shared/snapshots/schemes'; +import meta from './gutters.stories'; +import './gutters.styles.scss'; +import { bombArgs } from '@/utils'; + +const { id, ...metaWithoutId } = meta; + +export default { + ...metaWithoutId, + title: 'Snapshots', +}; + +const sizes = ['0', '1', '2', '4', '8', '12', '24', '32', '48']; +const gutterType = ['g', 'gx', 'gy']; + +export const Gutters: StoryObj = { + render: () => { + return schemes( + () => { + return html` + ${bombArgs({ + gutterType: gutterType, + sizes: sizes, + }).map( + (args: Args) => + html` +
+
+
${args.gutterType}-${args.sizes}
+
${args.gutterType}-${args.sizes}
+
${args.gutterType}-${args.sizes}
+
${args.gutterType}-${args.sizes}
+
+
+ `, + )} + `; + }, + { filter: scheme => scheme === COLOR_SCHEMES.light }, + ); + }, +}; diff --git a/packages/documentation/src/stories/utilities/gutters/gutters.stories.ts b/packages/documentation/src/stories/utilities/gutters/gutters.stories.ts new file mode 100644 index 0000000000..f184325204 --- /dev/null +++ b/packages/documentation/src/stories/utilities/gutters/gutters.stories.ts @@ -0,0 +1,130 @@ +import type { Args, StoryContext, StoryFn, StoryObj } from '@storybook/web-components'; +import { html } from 'lit/static-html.js'; +import { MetaExtended } from '@root/types'; +import { parse } from '@/utils/sass-export'; +import scss from './gutters.module.scss'; +import './gutters.styles.scss'; + +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +export const SCSS_VARIABLES: any = parse(scss); + +const properties = ['gap']; + +const sizes = properties.reduce((options, property) => { + return { + ...options, + [property]: Object.keys(SCSS_VARIABLES.spacing) + .filter((key: string) => key.startsWith(`post-utility-${property}-`)) + .map((key: string) => key.replace(`post-utility-${property}-`, '')), + }; +}, {} as { [property: string]: string[] }); + +const meta: MetaExtended = { + id: '64b63483-79fa-4e9f-9441-f7d6b2eabae2', + title: 'Foundations/Layout/Gutters', + decorators: [ + (story: StoryFn, context: StoryContext) => { + const storyTemplate = html`
+ ${story(context.args, context)} +
`; + return storyTemplate; + }, + ], +}; + +export default meta; + +type Story = StoryObj; + +export const HorizontalGutters: Story = { + argTypes: { + gutterX: { + name: 'Horizontal gutter (gx)', + description: 'Sets the horizontal gutter size.', + control: { + type: 'select', + }, + options: sizes.gap, + }, + }, + args: { + gutterX: '12', + }, + render: (args: Args) => { + // used only for the snapshots + const breakpointClass = args.breakpointClass ? `-${args.breakpointClass}` : ''; + return html` +
+
+
col
+
col
+
col
+
col
+
+
+ + `; + }, +}; + +export const VerticalGutters: Story = { + argTypes: { + gutterY: { + name: 'Vertical gutter (gy)', + description: 'Sets the vertical gutter size.', + control: { + type: 'select', + }, + options: sizes.gap, + }, + }, + args: { + gutterY: '32', + }, + render: (args: Args) => { + // used only for the snapshots + const breakpointClass = args.breakpointClass ? `-${args.breakpointClass}` : ''; + return html` +
+
+
col
+
col
+
col
+
col
+
+
+ + `; + }, +}; + +export const GeneralGutters: Story = { + argTypes: { + gutter: { + name: 'General gutter (g)', + description: 'Sets the general gutter size.', + control: { + type: 'select', + }, + options: sizes.gap, + }, + }, + args: { + gutter: '64', + }, + render: (args: Args) => { + // used only for the snapshots + const breakpointClass = args.breakpointClass ? `-${args.breakpointClass}` : ''; + return html` +
+
+
col
+
col
+
col
+
col
+
+
+ + `; + }, +}; diff --git a/packages/documentation/src/stories/utilities/gutters/gutters.styles.scss b/packages/documentation/src/stories/utilities/gutters/gutters.styles.scss new file mode 100644 index 0000000000..381781e516 --- /dev/null +++ b/packages/documentation/src/stories/utilities/gutters/gutters.styles.scss @@ -0,0 +1,20 @@ +.gutters-example, +.gutters-snapshot-example { + text-align: center; + + .container { + overflow: hidden; + } + + .row { + .col, + .col-6 { + background: linear-gradient(#b8e2f3, #b8e2f3), linear-gradient(#a69ce1, #a69ce1); + background-clip: content-box, border-box; + color: #59718b; + } + } + .row:has(.col-6) { + background: linear-gradient(#a69ce1, #a69ce1); + } +} diff --git a/packages/styles/src/utilities/_variables.scss b/packages/styles/src/utilities/_variables.scss index 04b78b2890..28a3b6fcf1 100644 --- a/packages/styles/src/utilities/_variables.scss +++ b/packages/styles/src/utilities/_variables.scss @@ -29,6 +29,24 @@ $position-values: ( ); $utilities: ( + 'grid-gutter': ( + responsive: true, + property: --post-grid-gutter-x --post-grid-gutter-y, + class: g, + values: from-tokens('spacing', 'gap'), + ), + 'grid-gutter-x': ( + responsive: true, + property: --post-grid-gutter-x, + class: gx, + values: from-tokens('spacing', 'gap'), + ), + 'grid-gutter-y': ( + responsive: true, + property: --post-grid-gutter-y, + class: gy, + values: from-tokens('spacing', 'gap'), + ), 'width': ( responsive: true, property: width, From ea5c63817595c44b3e9542f3e18c7d8865250270 Mon Sep 17 00:00:00 2001 From: Lea Date: Wed, 8 Jan 2025 09:01:46 +0100 Subject: [PATCH 3/3] chore(components): update crypto to nanoid (#4369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alizé Debray <33580481+alizedebray@users.noreply.github.com> --- .changeset/lucky-grapes-wink.md | 5 +++++ .../components/post-accordion-item/post-accordion-item.tsx | 3 ++- .../components/src/components/post-banner/post-banner.tsx | 3 ++- packages/components/src/components/post-list/post-list.tsx | 3 ++- .../src/components/post-tab-header/post-tab-header.tsx | 3 ++- .../src/components/post-tab-panel/post-tab-panel.tsx | 3 ++- 6 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 .changeset/lucky-grapes-wink.md diff --git a/.changeset/lucky-grapes-wink.md b/.changeset/lucky-grapes-wink.md new file mode 100644 index 0000000000..5ff9ef321f --- /dev/null +++ b/.changeset/lucky-grapes-wink.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-components': patch +--- + +Reduced the length of random IDs generated in the components; they are now generated using the [nanoid library](https://github.com/ai/nanoid) instead of the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). diff --git a/packages/components/src/components/post-accordion-item/post-accordion-item.tsx b/packages/components/src/components/post-accordion-item/post-accordion-item.tsx index 235d9ce6e1..60f129dda8 100644 --- a/packages/components/src/components/post-accordion-item/post-accordion-item.tsx +++ b/packages/components/src/components/post-accordion-item/post-accordion-item.tsx @@ -2,6 +2,7 @@ import { Component, Element, h, Host, Listen, Method, Prop, State, Watch } from import { version } from '@root/package.json'; import { HEADING_LEVELS, HeadingLevel } from '@/types'; import { checkEmptyOrOneOf } from '@/utils'; +import { nanoid } from 'nanoid'; /** * @part button - The pseudo-element, used to override styles on the components internal header `button` element. @@ -46,7 +47,7 @@ export class PostAccordionItem { } componentWillLoad() { - this.id = this.host.id || `a${crypto.randomUUID()}`; + this.id = this.host.id || `p${nanoid(6)}`; } componentDidLoad() { diff --git a/packages/components/src/components/post-banner/post-banner.tsx b/packages/components/src/components/post-banner/post-banner.tsx index 26d4f10794..8aed59c285 100644 --- a/packages/components/src/components/post-banner/post-banner.tsx +++ b/packages/components/src/components/post-banner/post-banner.tsx @@ -14,6 +14,7 @@ import { version } from '@root/package.json'; import { fadeOut } from '@/animations'; import { checkEmptyOrOneOf, checkEmptyOrPattern, checkNonEmpty, checkType } from '@/utils'; import { BANNER_TYPES, BannerType } from './banner-types'; +import { nanoid } from 'nanoid'; /** * @slot heading - Slot for placing custom content within the banner's heading. @@ -29,7 +30,7 @@ import { BANNER_TYPES, BannerType } from './banner-types'; export class PostBanner { @Element() host: HTMLPostBannerElement; - @State() bannerId = crypto.randomUUID(); + @State() bannerId = `p${nanoid(6)}`; @State() classes: string; @State() hasActions: boolean; @State() hasHeading: boolean; diff --git a/packages/components/src/components/post-list/post-list.tsx b/packages/components/src/components/post-list/post-list.tsx index 47efe5a797..086859699b 100644 --- a/packages/components/src/components/post-list/post-list.tsx +++ b/packages/components/src/components/post-list/post-list.tsx @@ -1,5 +1,6 @@ import { Component, Element, Prop, Host, State, h } from '@stencil/core'; import { version } from '@root/package.json'; +import { nanoid } from 'nanoid'; /** * @slot default - Slot for placing the list title. @@ -35,7 +36,7 @@ export class PostList { /** * Get the id set on the host element or use a random id by default */ - this.titleId = `title-${this.host.id || crypto.randomUUID()}`; + this.titleId = `title-${this.host.id || nanoid(6)}`; } componentDidLoad() { diff --git a/packages/components/src/components/post-tab-header/post-tab-header.tsx b/packages/components/src/components/post-tab-header/post-tab-header.tsx index 4f84fd6afe..966988162b 100644 --- a/packages/components/src/components/post-tab-header/post-tab-header.tsx +++ b/packages/components/src/components/post-tab-header/post-tab-header.tsx @@ -1,6 +1,7 @@ import { Component, Element, h, Host, Prop, State, Watch } from '@stencil/core'; import { version } from '@root/package.json'; import { checkNonEmpty } from '@/utils'; +import { nanoid } from 'nanoid'; /** * @slot default - Slot for the content of the tab header. @@ -27,7 +28,7 @@ export class PostTabHeader { } componentWillLoad() { - this.tabId = `tab-${this.host.id || crypto.randomUUID()}`; + this.tabId = `tab-${this.host.id || nanoid(6)}`; } render() { diff --git a/packages/components/src/components/post-tab-panel/post-tab-panel.tsx b/packages/components/src/components/post-tab-panel/post-tab-panel.tsx index 7b76213692..7b5a1445e4 100644 --- a/packages/components/src/components/post-tab-panel/post-tab-panel.tsx +++ b/packages/components/src/components/post-tab-panel/post-tab-panel.tsx @@ -1,5 +1,6 @@ import { Component, Element, h, Host, Prop, State } from '@stencil/core'; import { version } from '@root/package.json'; +import { nanoid } from 'nanoid'; /** * @slot default - Slot for placing the content of the tab panel. @@ -22,7 +23,7 @@ export class PostTabPanel { componentWillLoad() { // get the id set on the host element or use a random id by default - this.panelId = `panel-${this.host.id || crypto.randomUUID()}`; + this.panelId = `panel-${this.host.id || nanoid(6)}`; } render() {