Removed some spacing utilities' classes (margin and padding
{m/p}{x/y/s/e/t/b}-*)
- breaking
+ breaking🪄 migration rule
*-small-large
@@ -293,7 +293,7 @@ export class MigrationV99Component extends LitElement {
Changed the spacing utilities' classes (margin and padding
{m/p}{x/y/s/e/t/b}-*) naming to pixel-based names
- breaking
+ breaking🪄 migration rule
*-hair is now *-1
diff --git a/packages/eslint/docs/rules/html/migrations/no-deprecated-spacing-utilities.md b/packages/eslint/docs/rules/html/migrations/no-deprecated-spacing-utilities.md
new file mode 100644
index 0000000000..00b3be7ab2
--- /dev/null
+++ b/packages/eslint/docs/rules/html/migrations/no-deprecated-spacing-utilities.md
@@ -0,0 +1,36 @@
+# `no-deprecated-spacing-utilities`
+
+Flags all deprecated spacing utility classes and replaces them with the new ones.
+
+- Type: problem
+- 🔧 Supports autofix (--fix)
+
+## Class list
+
+The classes that are handled by this rule are the following ones:
+
+- Starts with one of those margin/padding class names: `m-*`, `mx-*`, `my-*`, `mt-*`, `mb-*`, `ms-*`, `me-*`, `p-*`, `px-*`, `py-*`, `pt-*`, `pb-*`, `ps-*`, `pe-*`
+- Then, has either no breakpoint or one of the following breakpoint: `*sm-*`,`*md-*`,`*lg-*`, `*xl-*`
+- Ends with one of the following class values: `*1`, `*2`, `*3`, `*4`, `*5`, `*hair`, `*line`, `*micro`, `*mini`, `*small-regular`, `*regular`, `*small-large`, `*large`, `*big`, `*bigger-big`, `*small-huge`, `*huge`, `*small-giant`, `*giant`, `*bigger-giant`
+
+**Note that the classes that have, as a value, `*small-large` and `*bigger-giant` don't have an exact matching in the new spacing utility classes. Therefore, they should be reviewed carefully.**
+
+## Rule Options
+
+This rule does not have any configuration options.
+
+## Example
+
+### ❌ Invalid Code
+
+```html
+
Content
+
Content
+```
+
+### ✅ Valid Code
+
+```html
+
Content
+
Content
+```
diff --git a/packages/eslint/src/rules/html/migrations/index.ts b/packages/eslint/src/rules/html/migrations/index.ts
index 7740713a0b..764c19cec0 100644
--- a/packages/eslint/src/rules/html/migrations/index.ts
+++ b/packages/eslint/src/rules/html/migrations/index.ts
@@ -2,8 +2,16 @@ import noDeprecatedBtnRgRule, { name as noDeprecatedBtnRgRuleName } from './no-d
import noUnnumberedBorderRadiusRule, {
name as noUnnumberedBorderRadiusRuleName,
} from './no-unnumbered-border-radius';
+import {
+ rulePhase1 as noDeprecatedSpacingUtilitiesRulePhase1,
+ rulePhase2 as noDeprecatedSpacingUtilitiesRulePhase2,
+ namePhase1 as noDeprecatedSpacingUtilitiesRulePhase1Name,
+ namePhase2 as noDeprecatedSpacingUtilitiesRulePhase2Name,
+} from './no-deprecated-spacing-utilities';
export const htmlMigrationRules = {
[noDeprecatedBtnRgRuleName]: noDeprecatedBtnRgRule,
[noUnnumberedBorderRadiusRuleName]: noUnnumberedBorderRadiusRule,
+ [noDeprecatedSpacingUtilitiesRulePhase1Name]: noDeprecatedSpacingUtilitiesRulePhase1,
+ [noDeprecatedSpacingUtilitiesRulePhase2Name]: noDeprecatedSpacingUtilitiesRulePhase2,
};
diff --git a/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts b/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts
new file mode 100644
index 0000000000..14c15a5894
--- /dev/null
+++ b/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts
@@ -0,0 +1,75 @@
+import { createClassUpdateRule } from '../../../utils/create-class-update-rule';
+import { setUpClassesMutations, TwoPhasesData } from '../../../utils/two-phases-classes-update';
+
+// Class names
+const classNames = [
+ 'm-',
+ 'mx-',
+ 'my-',
+ 'mt-',
+ 'mb-',
+ 'ms-',
+ 'me-',
+ 'p-',
+ 'px-',
+ 'py-',
+ 'pt-',
+ 'pb-',
+ 'ps-',
+ 'pe-',
+];
+
+// Empty string means no middle part
+const breakpoints = ['sm-', 'md-', 'lg-', 'xl-', ''];
+
+// Previous values mapped to the new values
+const classValuesMap: { [key: string]: number } = {
+ '1': 4,
+ '2': 8,
+ '4': 24,
+ '3': 16,
+ '5': 48,
+ 'hair': 1,
+ 'line': 2,
+ 'micro': 4,
+ 'mini': 8,
+ 'small-regular': 12,
+ 'regular': 16,
+ 'small-large': 24,
+ 'large': 24,
+ 'big': 32,
+ 'bigger-big': 40,
+ 'small-huge': 48,
+ 'huge': 56,
+ 'small-giant': 78,
+ 'giant': 80,
+ 'bigger-giant': 80,
+};
+
+export const data: TwoPhasesData = setUpClassesMutations(
+ classNames,
+ breakpoints,
+ classValuesMap,
+ 'deprecatedSpacingUtilities',
+);
+
+export const namePhase1 = 'no-deprecated-spacing-utilities-phase-1';
+export const namePhase2 = 'no-deprecated-spacing-utilities-phase-2';
+
+export const rulePhase1 = createClassUpdateRule({
+ name: namePhase1,
+ type: 'problem',
+ description:
+ 'Flags deprecated named and numbered spacing utility classes and replaces them with pixel ones with a temporary name (phase 1).',
+ messages: data.messagesPhase1,
+ mutations: data.mutationsPhase1,
+});
+
+export const rulePhase2 = createClassUpdateRule({
+ name: namePhase2,
+ type: 'problem',
+ description:
+ 'Flags deprecated named and numbered spacing utility classes and replaces the temporary class names with the final ones.',
+ messages: data.messagesPhase2,
+ mutations: data.mutationsPhase2,
+});
diff --git a/packages/eslint/src/utils/two-phases-classes-update.ts b/packages/eslint/src/utils/two-phases-classes-update.ts
new file mode 100644
index 0000000000..6a1a441b68
--- /dev/null
+++ b/packages/eslint/src/utils/two-phases-classes-update.ts
@@ -0,0 +1,66 @@
+export interface TwoPhasesData {
+ messagesPhase1: Record;
+ mutationsPhase1: Record;
+ messagesPhase2: Record;
+ mutationsPhase2: Record;
+}
+
+/**
+ * Since some classes are identical between before and after the migration but with different values,
+ * we have to do the migration in two phase:
+ * - First, migrate all `oldClassName` classes to `_tmp-newClassName`
+ * - Second, migrate all `_tmp-newClassName` to `newClassName`
+ *
+ * This ensures that we don't get any deprecation errors when running the tests on those identical classes
+ */
+export function setUpClassesMutations(
+ classNames: Array,
+ breakpoints: Array,
+ classValuesMap: { [key: string]: number },
+ messageId: string,
+): TwoPhasesData {
+ const returnData: TwoPhasesData = {
+ messagesPhase1: {},
+ mutationsPhase1: {},
+ messagesPhase2: {},
+ mutationsPhase2: {},
+ };
+
+ let index = 0;
+
+ // Temporary prefix to differenciate old and new class names
+ const tempPrefix = '_tmp-';
+
+ // Generate all the possible classes based on the class names, breakpoint and class values
+ for (const className of classNames) {
+ for (const bp of breakpoints) {
+ for (const classValue in classValuesMap) {
+ const oldClass = className + bp + classValue;
+ const finalNewClass = className + bp + classValuesMap[classValue];
+
+ // Add the index to the tempClass to avoid issues with having the wrong error msg when running tests
+ const tempClass = tempPrefix + index + finalNewClass;
+
+ const keyPhase1 = `${messageId}Phase1_${index}`;
+
+ returnData.messagesPhase1[
+ keyPhase1
+ ] = `The "${oldClass}" class is deprecated. Please replace it with "${finalNewClass}".`;
+ // Mutate from `oldClass` to `_tmp-newClass`
+ returnData.mutationsPhase1[keyPhase1] = [oldClass, tempClass];
+
+ const keyPhase2 = `${messageId}Phase2_${index}`;
+
+ returnData.messagesPhase2[
+ keyPhase2
+ ] = `The "${oldClass}" class is deprecated. Please replace it with "${finalNewClass}".`;
+ // Mutate from `_tmp-newClass` to `newClass`
+ returnData.mutationsPhase2[keyPhase2] = [tempClass, finalNewClass];
+
+ index++;
+ }
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts b/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts
new file mode 100644
index 0000000000..d4df518c38
--- /dev/null
+++ b/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts
@@ -0,0 +1,38 @@
+import { RuleListener, RuleModule } from '@typescript-eslint/utils/ts-eslint';
+import {
+ rulePhase1,
+ namePhase1,
+ rulePhase2,
+ namePhase2,
+ data,
+} from '../../../../src/rules/html/migrations/no-deprecated-spacing-utilities';
+import { htmlRuleTester } from '../../../utils/html-rule-tester';
+import { RuleDocs } from '../../../../src/utils/create-rule';
+
+function runTests(
+ name: string,
+ rule: RuleModule,
+ data: Record,
+) {
+ // Generate all of the invalid use cases
+ const invalidData = Object.entries(data).map(([key, [oldClass, newClass]]) => ({
+ code: `
@@ -221,7 +221,7 @@ export class MigrationV99Component extends LitElement {
Changed the pixel sizing utility classes (w-*, h-*,
mh-*, mw-*) to pixel-based names
- breaking
+ breaking🪄 migration rule
*-hair is now *-1
@@ -242,7 +242,7 @@ export class MigrationV99Component extends LitElement {
Changed the sizing utility classes max-height and max-width naming
- breaking
+ breaking🪄 migration rule
mh-* is now max-h-*
diff --git a/packages/eslint/docs/rules/html/migrations/no-deprecated-sizing-utilities.md b/packages/eslint/docs/rules/html/migrations/no-deprecated-sizing-utilities.md
new file mode 100644
index 0000000000..8495f38968
--- /dev/null
+++ b/packages/eslint/docs/rules/html/migrations/no-deprecated-sizing-utilities.md
@@ -0,0 +1,40 @@
+# `no-deprecated-sizing-utilities`
+
+Flags all deprecated sizing utility classes and replaces them with the new ones.
+
+- Type: problem
+- 🔧 Supports autofix (--fix)
+
+## Class list
+
+The classes that are handled by this rule are the following ones:
+
+- Starts with one of those sizing class names: `w-*`, `mw-*`, `h-*`, `mh-*`
+- Then, has either no breakpoint or one of the following breakpoint: `*sm-*`,`*md-*`,`*lg-*`, `*xl-*`
+- Ends with one of the following class values: `*1`, `*2`, `*3`, `*4`, `*5`, `*hair`, `*line`, `*micro`, `*mini`, `*small-regular`, `*regular`, `*small-large`, `*large`, `*big`, `*bigger-big`, `*small-huge`, `*huge`, `*small-giant`, `*giant`, `*bigger-giant`, `*25`, `*50`, `*75`, `*100`
+
+**Note that the classes that have, as a value, `*small-large` and `*bigger-giant` don't have an exact matching in the new sizing utility classes. Therefore, they should be reviewed carefully.**
+
+## Rule Options
+
+This rule does not have any configuration options.
+
+## Example
+
+### ❌ Invalid Code
+
+```html
+
Content
+
Content
+
Content
+
Content
+```
+
+### ✅ Valid Code
+
+```html
+
Content
+
Content
+
Content
+
Content
+```
diff --git a/packages/eslint/src/rules/html/migrations/index.ts b/packages/eslint/src/rules/html/migrations/index.ts
index 764c19cec0..d442204731 100644
--- a/packages/eslint/src/rules/html/migrations/index.ts
+++ b/packages/eslint/src/rules/html/migrations/index.ts
@@ -9,9 +9,18 @@ import {
namePhase2 as noDeprecatedSpacingUtilitiesRulePhase2Name,
} from './no-deprecated-spacing-utilities';
+import {
+ rulePhase1 as noDeprecatedSizingUtilitiesRulePhase1,
+ rulePhase2 as noDeprecatedSizingUtilitiesRulePhase2,
+ namePhase1 as noDeprecatedSizingUtilitiesRulePhase1Name,
+ namePhase2 as noDeprecatedSizingUtilitiesRulePhase2Name,
+} from './no-deprecated-sizing-utilities';
+
export const htmlMigrationRules = {
[noDeprecatedBtnRgRuleName]: noDeprecatedBtnRgRule,
[noUnnumberedBorderRadiusRuleName]: noUnnumberedBorderRadiusRule,
[noDeprecatedSpacingUtilitiesRulePhase1Name]: noDeprecatedSpacingUtilitiesRulePhase1,
[noDeprecatedSpacingUtilitiesRulePhase2Name]: noDeprecatedSpacingUtilitiesRulePhase2,
+ [noDeprecatedSizingUtilitiesRulePhase1Name]: noDeprecatedSizingUtilitiesRulePhase1,
+ [noDeprecatedSizingUtilitiesRulePhase2Name]: noDeprecatedSizingUtilitiesRulePhase2,
};
diff --git a/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts b/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts
new file mode 100644
index 0000000000..afed11dbf6
--- /dev/null
+++ b/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts
@@ -0,0 +1,64 @@
+import { createClassUpdateRule } from '../../../utils/create-class-update-rule';
+import { setUpClassesMutations, TwoPhasesData } from '../../../utils/two-phases-classes-update';
+
+// Class names
+const classNamesMap: Record = {
+ 'w-': 'w-',
+ 'mw-': 'max-w-',
+ 'h-': 'w-',
+ 'mh-': 'max-h-',
+};
+
+// Previous values mapped to the new values
+const classValuesMap: Record = {
+ '1': 4,
+ '2': 8,
+ '4': 24,
+ '3': 16,
+ '5': 48,
+ 'hair': 1,
+ 'line': 2,
+ 'micro': 4,
+ 'mini': 8,
+ 'small-regular': 12,
+ 'regular': 16,
+ 'small-large': 24,
+ 'large': 24,
+ 'big': 32,
+ 'bigger-big': 40,
+ 'small-huge': 48,
+ 'huge': 56,
+ 'small-giant': 78,
+ 'giant': 80,
+ '25': 'quarter',
+ '50': 'half',
+ '75': 'three-quarters',
+ '100': 'full',
+};
+
+export const data: TwoPhasesData = setUpClassesMutations(
+ classNamesMap,
+ classValuesMap,
+ 'deprecatedSizingUtilities',
+);
+
+export const namePhase1 = 'no-deprecated-sizing-utilities-phase-1';
+export const namePhase2 = 'no-deprecated-sizing-utilities-phase-2';
+
+export const rulePhase1 = createClassUpdateRule({
+ name: namePhase1,
+ type: 'problem',
+ description:
+ 'Flags deprecated sizing utility classes and replaces them with the new ones with a temporary name (phase 1).',
+ messages: data.messagesPhase1,
+ mutations: data.mutationsPhase1,
+});
+
+export const rulePhase2 = createClassUpdateRule({
+ name: namePhase2,
+ type: 'problem',
+ description:
+ 'Flags deprecated sizing utility classes and replaces the temporary class names with the final ones.',
+ messages: data.messagesPhase2,
+ mutations: data.mutationsPhase2,
+});
diff --git a/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts b/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts
new file mode 100644
index 0000000000..325eb17c32
--- /dev/null
+++ b/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts
@@ -0,0 +1,41 @@
+import { RuleListener, RuleModule } from '@typescript-eslint/utils/ts-eslint';
+import {
+ rulePhase1,
+ namePhase1,
+ rulePhase2,
+ namePhase2,
+ data,
+} from '../../../../src/rules/html/migrations/no-deprecated-sizing-utilities';
+import { htmlRuleTester } from '../../../utils/html-rule-tester';
+import { RuleDocs } from '../../../../src/utils/create-rule';
+
+function runTests(
+ name: string,
+ rule: RuleModule,
+ data: Record,
+) {
+ // Generate all of the invalid use cases
+ const invalidData = Object.entries(data).map(([key, [oldClass, newClass]]) => ({
+ code: `
+```
From 2129142f7cbaa9983aff81d28192ffcf07234158 Mon Sep 17 00:00:00 2001
From: leagrdv
Date: Fri, 26 Sep 2025 17:44:53 +0200
Subject: [PATCH 12/12] feat(eslint): automigration rules for bootstrap
position helpers
---
.changeset/slow-breads-attack.md | 5 ++++
.../migrationv9-10.component.ts | 13 +++++++-
.../no-deprecated-position-helpers.md | 30 +++++++++++++++++++
.../eslint/src/rules/html/migrations/index.ts | 4 +++
.../no-deprecated-position-helpers.ts | 23 ++++++++++++++
.../no-deprecated-position-helpers.spec.ts | 14 +++++++++
6 files changed, 88 insertions(+), 1 deletion(-)
create mode 100644 .changeset/slow-breads-attack.md
create mode 100644 packages/eslint/docs/rules/html/migrations/no-deprecated-position-helpers.md
create mode 100644 packages/eslint/src/rules/html/migrations/no-deprecated-position-helpers.ts
create mode 100644 packages/eslint/test/rules/html/migrations/no-deprecated-position-helpers.spec.ts
diff --git a/.changeset/slow-breads-attack.md b/.changeset/slow-breads-attack.md
new file mode 100644
index 0000000000..3e45313be6
--- /dev/null
+++ b/.changeset/slow-breads-attack.md
@@ -0,0 +1,5 @@
+---
+'@swisspost/design-system-documentation': patch
+---
+
+Added information in the migration guide regarding the removal of position helpers.
diff --git a/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts b/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts
index 4ca9bd9172..f643bcf9b5 100644
--- a/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts
+++ b/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts
@@ -263,7 +263,6 @@ export class MigrationV99Component extends LitElement {
We recommend using the .elevation-* classes instead.
-
The following elevation utility classes have been renamed
@@ -350,6 +349,18 @@ export class MigrationV99Component extends LitElement {
.rounded-{top/bottom/start/end} are now .rounded-{top/bottom/start/end}-4
+
+
+ 🪄 migration rule
+ Removed some position helper classes that can be replaced with a combination of other utilities
+
+
+
fixed-top is now position-fixed top-0 start-0 end-0 z-fixed
+
fixed-bottom is now position-fixed bottom-0 start-0 end-0 z-fixed
+
sticky-top is now position-sticky top-0 z-header
+
sticky-bottom is now position-sticky bottom-0 z-header
+
+
Typography
diff --git a/packages/eslint/docs/rules/html/migrations/no-deprecated-position-helpers.md b/packages/eslint/docs/rules/html/migrations/no-deprecated-position-helpers.md
new file mode 100644
index 0000000000..bac615a579
--- /dev/null
+++ b/packages/eslint/docs/rules/html/migrations/no-deprecated-position-helpers.md
@@ -0,0 +1,30 @@
+# `no-deprecated-position-helpers`
+
+Flags deprecated Bootstrap position helpers `fixed-[top/bottom]` and `sticky-[top/bottom]` classes and replace them with a combination of other utility classes.
+
+- Type: problem
+- 🔧 Supports autofix (--fix)
+
+## Rule Options
+
+This rule does not have any configuration options.
+
+## Example
+
+### ❌ Invalid Code
+
+```html
+