diff --git a/.changeset/proud-actors-knock.md b/.changeset/proud-actors-knock.md
new file mode 100644
index 0000000000..96980d3ac4
--- /dev/null
+++ b/.changeset/proud-actors-knock.md
@@ -0,0 +1,6 @@
+---
+'@swisspost/design-system-styles': major
+'@swisspost/design-system-documentation': minor
+---
+
+Updated the margin, padding, and gap utility classes to use the pixel values (1, 2, ... , 112) instead of size names (hair, line, ..., bigger-giant).
diff --git a/packages/documentation/cypress/snapshots/utilities/spacing.snapshot.ts b/packages/documentation/cypress/snapshots/utilities/spacing.snapshot.ts
new file mode 100644
index 0000000000..ad8f398489
--- /dev/null
+++ b/packages/documentation/cypress/snapshots/utilities/spacing.snapshot.ts
@@ -0,0 +1,13 @@
+describe('Spacing', () => {
+ it('margin and padding', () => {
+ cy.visit('/iframe.html?id=snapshots--margin-and-padding');
+ cy.get('.margin-padding-example', { timeout: 30000 }).should('be.visible');
+ cy.percySnapshot('Margin and Padding', { widths: [320, 1440] });
+ });
+
+ it('gap', () => {
+ cy.visit('/iframe.html?id=snapshots--gap');
+ cy.get('.gap-example', { timeout: 30000 }).should('be.visible');
+ cy.percySnapshot('Gap', { widths: [320, 1440] });
+ });
+});
diff --git a/packages/documentation/src/stories/utilities/spacing/spacing.docs.mdx b/packages/documentation/src/stories/utilities/spacing/spacing.docs.mdx
index ecd61e9877..b6d2cb7ed9 100644
--- a/packages/documentation/src/stories/utilities/spacing/spacing.docs.mdx
+++ b/packages/documentation/src/stories/utilities/spacing/spacing.docs.mdx
@@ -1,63 +1,76 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';
import * as SpacingStories from './spacing.stories';
+export const firstBreakpoint = Object.values(SpacingStories.SCSS_VARIABLES.firstBreakpoint)[0];
+export const breakpoints = Object.values(SpacingStories.SCSS_VARIABLES.breakpoints).map((breakpoint, i, arr) => [breakpoint, i !== arr.length - 1]);
+
# Spacing
-Our spacing utility brings uniform and consistent spacing to your elements,
+Ensure consistent spacing across all pages.
+
-By adhering to standardized spacing guidelines, we maintain visual alignment and
-improve the overall user interface.
+## Margin and Padding
+
-
${story(args, context)}
-
-
Resize the browser window to see changes.
+ args: {
+ property: 'gap',
+ size: '16',
+ },
+ render: (args: Args) => {
+ // used only for the snapshots
+ const breakpointClass = args.breakpointClass ? ` ${args.breakpointClass}` : '';
+ return html`
+
+
First child
+
Second child
+
Third child
+
Fourth child
+
Fifth child
+
Sixth child
- `,
- ],
-};
-
-export const AutomaticResponsiveExample: Story = {
- render: () => {
- return html`
`;
+ `;
},
decorators: [
- (story: StoryFn, { args, context }: StoryContext) => html`
-
-
${story(args, context)}
-
-
Resize the browser window to see changes.
-
- `,
+ (story: StoryFn, context: StoryContext) => {
+ const storyTemplate = html`
${story(context.args, context)}
`;
+ return withLegend(storyTemplate, 'gap');
+ },
],
};
diff --git a/packages/documentation/src/stories/utilities/spacing/spacing.styles.scss b/packages/documentation/src/stories/utilities/spacing/spacing.styles.scss
index 4e6328d3c1..f9afc38bd2 100644
--- a/packages/documentation/src/stories/utilities/spacing/spacing.styles.scss
+++ b/packages/documentation/src/stories/utilities/spacing/spacing.styles.scss
@@ -1,24 +1,55 @@
+@use 'sass:color';
@use '@swisspost/design-system-styles/core' as post;
-.spacing-example {
+$margin-color: color.adjust(post.$coral-bright, $lightness: 30%);
+$padding-color: color.complement($margin-color);
+$gap-color: color.adjust(post.$aubergine-bright, $lightness: 30%);
+$content-color: post.$white;
+$border: 1px solid post.$gray-40;
+
+.margin-padding-example {
+ display: flex;
+ width: fit-content;
+ background-color: $margin-color;
+
+ > div {
+ height: 100px;
+ width: 100px;
+ background-image: linear-gradient($content-color, $content-color), linear-gradient($padding-color, $padding-color);
+ background-clip: content-box, padding-box;
+ border: $border;
+ }
+}
+
+.gap-example {
+ > div {
+ width: fit-content;
+ background-color: $gap-color;
+ border: $border;
+ text-align: center;
+
+ > div {
+ background-color: $content-color;
+ padding: 12px 16px;
+ }
+ }
+}
+
+.legend {
.margin {
- background-color: #f4cea3;
+ background-color: $margin-color;
}
+
.padding {
- background-color: #c4dab9;
- border: 1px solid post.$gray-40;
- }
- .content {
- background-color: post.$white;
+ background-color: $padding-color;
}
- .legend {
- .padding {
- border: 0 none;
- }
+ .gap {
+ background-color: $gap-color;
+ }
- .content {
- border: 1px solid post.$gray-40;
- }
+ .element {
+ background-color: $content-color;
+ border: $border;
}
}
diff --git a/packages/styles/src/basics.scss b/packages/styles/src/basics.scss
index 065e4439cb..b89c93e1f3 100644
--- a/packages/styles/src/basics.scss
+++ b/packages/styles/src/basics.scss
@@ -1,7 +1,8 @@
@forward './variables/options';
-@use './elements';
+@use 'elements';
+@use 'utilities';
+
@use 'components/elevation';
-@use 'components/utilities';
@use 'components/sizing';
@use 'components/grid';
diff --git a/packages/styles/src/components/_index.scss b/packages/styles/src/components/_index.scss
index 21c9c85e48..60ccbcc3fb 100644
--- a/packages/styles/src/components/_index.scss
+++ b/packages/styles/src/components/_index.scss
@@ -38,7 +38,6 @@
@use 'tooltip';
@use 'topic-teaser';
@use 'transitions';
-@use 'utilities';
@use 'tag';
// Imports depending on source order to override bootstrap styles
diff --git a/packages/styles/src/components/sizing.scss b/packages/styles/src/components/sizing.scss
index 68bf75e297..228e836d8e 100644
--- a/packages/styles/src/components/sizing.scss
+++ b/packages/styles/src/components/sizing.scss
@@ -6,47 +6,16 @@
@use './../variables/breakpoints';
@use './../mixins/utilities';
-// Post margins, paddings and sizes
+// Post sizes
@each $breakpoint in map.keys(breakpoints.$grid-breakpoints) {
@include media-breakpoint-up($breakpoint) {
$infix: if($breakpoint == 'xs', '', '-#{$breakpoint}');
@each $prop,
$abbrev
- in (
- margin: m,
- padding: p,
- /* Deprecated */ line-height: lh,
- height: h,
- max-height: mh,
- width: w,
- max-width: mw,
- gap: gap
- )
+ in (/* Deprecated */ line-height: lh, height: h, max-height: mh, width: w, max-width: mw)
{
@each $size, $length in spacing.$post-sizes {
- @if ($prop == margin or $prop == padding) {
- .#{$abbrev}t#{$infix}-#{$size},
- .#{$abbrev}y#{$infix}-#{$size} {
- #{$prop}-top: $length !important;
- }
-
- .#{$abbrev}e#{$infix}-#{$size},
- .#{$abbrev}x#{$infix}-#{$size} {
- #{$prop}-right: $length !important;
- }
-
- .#{$abbrev}b#{$infix}-#{$size},
- .#{$abbrev}y#{$infix}-#{$size} {
- #{$prop}-bottom: $length !important;
- }
-
- .#{$abbrev}s#{$infix}-#{$size},
- .#{$abbrev}x#{$infix}-#{$size} {
- #{$prop}-left: $length !important;
- }
- }
-
.#{$abbrev}#{$infix}-#{$size} {
#{$prop}: $length !important;
}
diff --git a/packages/styles/src/functions/_string.scss b/packages/styles/src/functions/_string.scss
new file mode 100644
index 0000000000..1c79f06f99
--- /dev/null
+++ b/packages/styles/src/functions/_string.scss
@@ -0,0 +1,13 @@
+@use 'sass:string';
+
+@function replace($string, $term, $replacement: '') {
+ $index: string.index($string, $term);
+
+ @if $index {
+ $before: string.slice($string, 1, $index - 1);
+ $after: string.slice($string, $index + string.length($term));
+ @return $before + $replacement + replace($after, $term, $replacement);
+ }
+
+ @return $string;
+}
diff --git a/packages/styles/src/index.scss b/packages/styles/src/index.scss
index 7bf28ea879..fd78bce402 100644
--- a/packages/styles/src/index.scss
+++ b/packages/styles/src/index.scss
@@ -1,5 +1,6 @@
@forward './variables/options';
+@use './utilities';
@use './elements';
@use './components';
diff --git a/packages/styles/src/intranet.scss b/packages/styles/src/intranet.scss
index 15e5ec70d3..5738064522 100644
--- a/packages/styles/src/intranet.scss
+++ b/packages/styles/src/intranet.scss
@@ -1,5 +1,6 @@
@forward './variables/options';
-@use './elements';
+@use 'utilities';
+@use 'elements';
@use 'components';
@use 'components/intranet-header';
diff --git a/packages/styles/src/post-external.scss b/packages/styles/src/post-external.scss
index cc042736d1..87f2a3fa55 100644
--- a/packages/styles/src/post-external.scss
+++ b/packages/styles/src/post-external.scss
@@ -1,3 +1,4 @@
@use './post-tokens-external';
+@use './utilities';
@use './elements';
@use './components';
diff --git a/packages/styles/src/post-internal.scss b/packages/styles/src/post-internal.scss
index 41a9ca2447..834c79f621 100644
--- a/packages/styles/src/post-internal.scss
+++ b/packages/styles/src/post-internal.scss
@@ -1,3 +1,4 @@
@use './post-tokens-internal';
+@use './utilities';
@use './elements';
@use './components';
diff --git a/packages/styles/src/themes/bootstrap/_utilities.scss b/packages/styles/src/themes/bootstrap/_utilities.scss
index cff122af29..01cb191d0c 100644
--- a/packages/styles/src/themes/bootstrap/_utilities.scss
+++ b/packages/styles/src/themes/bootstrap/_utilities.scss
@@ -6,4 +6,24 @@
// manipulate $utilities before you import utilities/api
$utilities: map.remove($utilities, 'background-color');
+$utilities: map.remove($utilities, 'margin');
+$utilities: map.remove($utilities, 'margin-x');
+$utilities: map.remove($utilities, 'margin-y');
+$utilities: map.remove($utilities, 'margin-top');
+$utilities: map.remove($utilities, 'margin-end');
+$utilities: map.remove($utilities, 'margin-bottom');
+$utilities: map.remove($utilities, 'margin-start');
+
+$utilities: map.remove($utilities, 'padding');
+$utilities: map.remove($utilities, 'padding-x');
+$utilities: map.remove($utilities, 'padding-y');
+$utilities: map.remove($utilities, 'padding-top');
+$utilities: map.remove($utilities, 'padding-end');
+$utilities: map.remove($utilities, 'padding-bottom');
+$utilities: map.remove($utilities, 'padding-start');
+
+$utilities: map.remove($utilities, 'gap');
+$utilities: map.remove($utilities, 'row-gap');
+$utilities: map.remove($utilities, 'column-gap');
+
@import 'bootstrap/scss/utilities/api';
diff --git a/packages/styles/src/utilities/_mixins.scss b/packages/styles/src/utilities/_mixins.scss
new file mode 100644
index 0000000000..01e84ab529
--- /dev/null
+++ b/packages/styles/src/utilities/_mixins.scss
@@ -0,0 +1,23 @@
+@use '../mixins/media';
+@use '../variables/breakpoints';
+
+@mixin generate-utilities($properties, $value, $prefix, $suffix, $infix: '') {
+ .#{$prefix}#{$infix}#{$suffix} {
+ @each $property in $properties {
+ #{$property}: #{$value};
+ }
+ }
+}
+
+@mixin generate-responsive-utilities($properties, $value, $prefix, $suffix) {
+ @each $breakpoint, $min-width in breakpoints.$grid-breakpoints {
+ @if ($min-width == 0) {
+ @include generate-utilities($properties, $value, $prefix, $suffix);
+ } @else {
+ @include media.min($min-width) {
+ $infix: '-#{$breakpoint}';
+ @include generate-utilities($properties, $value, $prefix, $suffix, $infix);
+ }
+ }
+ }
+}
diff --git a/packages/styles/src/utilities/_variables.scss b/packages/styles/src/utilities/_variables.scss
new file mode 100644
index 0000000000..d7c1bca0e7
--- /dev/null
+++ b/packages/styles/src/utilities/_variables.scss
@@ -0,0 +1,86 @@
+@use '../tokens/utilities' as tokens;
+
+/*
+ Add new utilities using the following structure:
+ [set]: (
+ tokens: map (required),
+ classes: (
+ [group]: (
+ responsive: boolean (optional),
+ prefixes: map (required),
+ )
+ )
+ )
+
+ - `set`:
+ The name of the token set (e.g., if the tokens are contained in the "$post-spacing" map, the set is "spacing").
+
+ - `tokens`:
+ The map of tokens that should be used to generate the utility classes.
+
+ - `group`:
+ The group name used in the token keys (e.g., if the tokens are named "post-utility-margin-*", the group is "margin").
+
+ - `responsive`:
+ If set to `true`, the utility classes will be generated for all breakpoints (e.g., `-sm`, `-md`, `-lg`, etc.).
+ If set to `false` or omitted, utilities will be generated without a breakpoint infix.
+
+ - `prefixes`:
+ A map where each key is the class name prefix and the value is the CSS property (or properties) that the class will set.
+
+ Example:
+ spacing: (
+ tokens: tokens.$post-spacing, // Refers to the token map containing all spacing values
+ classes: (
+ margin: ( // Refers to the token "post-utility-margin-*" token in above map
+ classes: (
+ m: margin, // Generates `.m-*` classes to set the `margin` property
+ mx: margin-left margin-right, // Generates `.mx-*` classes to set `margin-left` and `margin-right` properties
+ ...
+ ),
+ responsive: true, // Generates responsive classes
+ )
+ )
+ )
+*/
+
+$utilities: (
+ spacing: (
+ tokens: tokens.$post-spacing,
+ classes: (
+ margin: (
+ responsive: true,
+ prefixes: (
+ m: margin,
+ mx: margin-inline,
+ ms: margin-inline-start,
+ me: margin-inline-end,
+ my: margin-block,
+ mt: margin-block-start,
+ mb: margin-block-end,
+ ),
+ ),
+ padding: (
+ responsive: true,
+ prefixes: (
+ p: padding,
+ px: padding-inline,
+ ps: padding-inline-start,
+ pe: padding-inline-end,
+ py: padding-block,
+ pt: padding-block-start,
+ pb: padding-block-end,
+ ),
+ ),
+ gap: (
+ responsive: true,
+ prefixes: (
+ gap: gap,
+ row-gap: row-gap,
+ column-gap: column-gap,
+ ),
+ ),
+ ),
+ ),
+ // IMPORTANT: When adding new utilities here, please ensure to remove the corresponding bootstrap utilities in `src/themes/bootstrap/_utilities.scss`.
+);
diff --git a/packages/styles/src/utilities/index.scss b/packages/styles/src/utilities/index.scss
new file mode 100644
index 0000000000..4faeb72b77
--- /dev/null
+++ b/packages/styles/src/utilities/index.scss
@@ -0,0 +1,30 @@
+@use 'sass:map';
+
+@use '../functions/string';
+
+@use './mixins' as *;
+@use './variables' as *;
+
+@use './temp/legacy';
+
+@each $set, $config in $utilities {
+ $tokens: map.get($config, tokens);
+ $classes: map.get($config, classes);
+
+ @each $group, $classesConfig in $classes {
+ $responsive: map.get($classesConfig, responsive);
+ $prefixes: map.get($classesConfig, prefixes);
+
+ @each $key, $value in $tokens {
+ $suffix: string.replace($key, 'post-utility-#{$group}');
+
+ @each $prefix, $properties in $prefixes {
+ @if $responsive {
+ @include generate-responsive-utilities($properties, $value, $prefix, $suffix);
+ } @else {
+ @include generate-utilities($properties, $value, $prefix, $suffix);
+ }
+ }
+ }
+ }
+}
diff --git a/packages/styles/src/components/utilities.scss b/packages/styles/src/utilities/temp/_legacy.scss
similarity index 79%
rename from packages/styles/src/components/utilities.scss
rename to packages/styles/src/utilities/temp/_legacy.scss
index 4ab4e9c196..3a963fc06c 100644
--- a/packages/styles/src/components/utilities.scss
+++ b/packages/styles/src/utilities/temp/_legacy.scss
@@ -1,16 +1,14 @@
-@forward './../variables/options';
-
@use 'sass:map';
@use 'sass:color' as sass-color;
@use 'sass:list';
-@use './../themes/bootstrap/core' as *;
-@use './../themes/bootstrap/utilities' as bu; // TODO: Move Design System utilities to utilities folder.
-@use './../mixins/color' as color-mx;
-@use './../mixins/utilities';
-@use './../variables/color';
-@use './../variables/spacing';
-@use './../variables/grid';
+@use '../../themes/bootstrap/core' as *;
+@use '../../themes/bootstrap/utilities' as bu; // TODO: Move Design System utilities to utilities folder.
+@use '../../mixins/color' as color-mx;
+@use '../../mixins/utilities';
+@use '../../variables/color';
+@use '../../variables/spacing';
+@use '../../variables/grid';
.bold {
font-weight: 700;
diff --git a/packages/styles/tests/components/utilities.test.scss b/packages/styles/tests/components/utilities.test.scss
deleted file mode 100644
index c6f8ac82dd..0000000000
--- a/packages/styles/tests/components/utilities.test.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@use 'sass:map';
-@use 'sass:meta';
-@use 'tests/jest';
-@use 'src/components/utilities';
-
-// Check if component forwards options
-@include jest.true(map.has-key(meta.module-variables('utilities'), 'font-base-path'));
diff --git a/packages/styles/tests/functions/string.test.scss b/packages/styles/tests/functions/string.test.scss
new file mode 100644
index 0000000000..2247cf29a6
--- /dev/null
+++ b/packages/styles/tests/functions/string.test.scss
@@ -0,0 +1,22 @@
+@use 'tests/jest';
+@use 'src/functions/string';
+
+$paragraph: "I think Ruth's dog is cuter than your dog!";
+
+// it should replace a term by another
+@include jest.equal(
+ 'I think my dog is cuter than your dog!',
+ string.replace($paragraph, "Ruth's", 'my')
+);
+
+// it should replace all occurrences
+@include jest.equal(
+ "I think Ruth's cat is cuter than your cat!",
+ string.replace($paragraph, "dog", 'cat')
+);
+
+// it should return the original string when it does not contain the term to replace
+@include jest.equal(
+ $paragraph,
+ string.replace($paragraph, "Sandy's", 'my')
+);
diff --git a/packages/styles/tests/utilities/index.test.scss b/packages/styles/tests/utilities/index.test.scss
new file mode 100644
index 0000000000..65b6554c4f
--- /dev/null
+++ b/packages/styles/tests/utilities/index.test.scss
@@ -0,0 +1,16 @@
+@use 'sass:map';
+@use 'sass:meta';
+@use 'tests/jest';
+@use 'src/utilities';
+
+
+.test {
+ // it should include legacy utilities
+ @extend .bold;
+
+ // it should correctly create utilities from tokens
+ @extend .m-16;
+ @extend .mt-24;
+ @extend .m-lg-8;
+ @extend .my-sm-48;
+}
diff --git a/packages/styles/tests/utilities/mixins.test.scss b/packages/styles/tests/utilities/mixins.test.scss
new file mode 100644
index 0000000000..8792c4c959
--- /dev/null
+++ b/packages/styles/tests/utilities/mixins.test.scss
@@ -0,0 +1,11 @@
+@use 'sass:map';
+@use 'sass:meta';
+@use 'tests/jest';
+@use 'src/utilities/mixins';
+
+
+.test {
+ @include mixins.generate-utilities('font-weight', '400', 'fw', 'normal');
+ @include mixins.generate-utilities('font-size', '1.5rem', 'fs', 'large', 'sm');
+ @include mixins.generate-responsive-utilities('row-gap', '48px', 'rg', '48');
+}