@orangesk/orange-design-system 3.12.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/build/components/Breadcrumbs/style.css +1 -1
  2. package/build/components/Breadcrumbs/style.css.map +1 -1
  3. package/build/components/Carousel/style.css +1 -1
  4. package/build/components/Carousel/style.css.map +1 -1
  5. package/build/components/Footer/style.css +1 -1
  6. package/build/components/Footer/style.css.map +1 -1
  7. package/build/components/Megamenu/style.css +1 -1
  8. package/build/components/Megamenu/style.css.map +1 -1
  9. package/build/components/index.js +1 -1
  10. package/build/components/index.js.map +1 -1
  11. package/build/lib/base.css +1 -1
  12. package/build/lib/base.css.map +1 -1
  13. package/build/lib/breadcrumbs.css +2 -2
  14. package/build/lib/breadcrumbs.css.map +1 -1
  15. package/build/lib/components.css +1 -1
  16. package/build/lib/components.css.map +1 -1
  17. package/build/lib/fonts.css +1 -1
  18. package/build/lib/fonts.css.map +1 -1
  19. package/build/lib/footer.css +1 -1
  20. package/build/lib/footer.css.map +1 -1
  21. package/build/lib/footer.iife.js +2 -0
  22. package/build/lib/footer.iife.js.map +1 -0
  23. package/build/lib/megamenu.css +1 -1
  24. package/build/lib/megamenu.css.map +1 -1
  25. package/build/lib/megamenu.iife.js +2 -0
  26. package/build/lib/megamenu.iife.js.map +1 -0
  27. package/build/lib/megamenu.js +1 -1
  28. package/build/lib/megamenu.js.map +1 -1
  29. package/build/lib/scripts.js +1 -1
  30. package/build/lib/scripts.js.map +1 -1
  31. package/build/lib/style.css +1 -1
  32. package/build/lib/style.css.map +1 -1
  33. package/package.json +10 -12
  34. package/src/components/AnchorNavigation/AnchorNavigation.static.ts +3 -4
  35. package/src/components/AnchorNavigation/tests/AnchorNavigation.megamenu.unit.test.ts +74 -0
  36. package/src/components/AnchorNavigation/tests/AnchorNavigation.unit.test.jsx +6 -0
  37. package/src/components/Breadcrumbs/styles/mixins.scss +6 -0
  38. package/src/components/Breadcrumbs/styles/rules.scss +30 -0
  39. package/src/components/Breadcrumbs/styles/style.scss +2 -26
  40. package/src/components/Carousel/Carousel.static.ts +0 -3
  41. package/src/components/Carousel/styles/mixins.scss +0 -4
  42. package/src/components/Carousel/tests/Carousel.interactions.test.ts +116 -85
  43. package/src/components/CarouselHero/CarouselHero.static.ts +0 -3
  44. package/src/components/CarouselPromotions/CarouselPromotions.static.ts +0 -3
  45. package/src/components/Footer/classic.ts +3 -0
  46. package/src/components/Footer/styles/mixins.scss +32 -1
  47. package/src/components/Footer/styles/style.scss +8 -5
  48. package/src/components/Footer/tests/Footer.styles.unit.test.jsx +7 -2
  49. package/src/components/Megamenu/Megamenu.static.ts +25 -6
  50. package/src/components/Megamenu/classic.ts +3 -0
  51. package/src/components/Megamenu/styles/mixins.scss +114 -52
  52. package/src/components/Megamenu/styles/style.scss +6 -7
  53. package/src/components/Megamenu/tests/Megamenu.scroll.unit.test.ts +185 -0
  54. package/src/styles/base/globals.scss +1 -37
  55. package/src/styles/base/header-height-values.scss +9 -0
  56. package/src/styles/base/header-height.scss +50 -0
  57. package/src/styles/base/styleguide.scss +0 -1
  58. package/build/files/tone-of-voice.pdf +0 -26572
  59. package/build/images/card-device-example.png +0 -0
  60. package/build/images/cta-device.png +0 -0
  61. package/build/images/cta-plans.png +0 -0
  62. package/build/images/cta-renewal.png +0 -0
  63. package/build/images/device-mobile.png +0 -0
  64. package/build/images/form-validation-format.png +0 -0
  65. package/build/images/form-validation-info-structure.png +0 -0
  66. package/build/images/form-validation-semantics.png +0 -0
  67. package/build/images/forms-labels-grouping.png +0 -0
  68. package/build/images/forms-labels-instruction.png +0 -0
  69. package/build/images/forms-labels-structure.png +0 -0
  70. package/build/images/hero/example-lg.jpg +0 -0
  71. package/build/images/hero/example-lg@2x.jpg +0 -0
  72. package/build/images/hero/example-xl.jpg +0 -0
  73. package/build/images/hero/example-xl@2x.jpg +0 -0
  74. package/build/images/hero/example-xxl.jpg +0 -0
  75. package/build/images/hero/example-xxl@2x.jpg +0 -0
  76. package/build/images/hero/example.jpg +0 -0
  77. package/build/images/hero/example@2x.jpg +0 -0
  78. package/build/images/hero/old-vendor-2x.jpg +0 -0
  79. package/build/images/hero/old-vendor-lg-2x.jpg +0 -0
  80. package/build/images/hero/old-vendor-lg.jpg +0 -0
  81. package/build/images/hero/old-vendor-xl-2x.jpg +0 -0
  82. package/build/images/hero/old-vendor-xl.jpg +0 -0
  83. package/build/images/hero/old-vendor-xxl-2x.jpg +0 -0
  84. package/build/images/hero/old-vendor-xxl.jpg +0 -0
  85. package/build/images/hero/old-vendor.jpg +0 -0
  86. package/build/images/hero/photo-2x.jpg +0 -0
  87. package/build/images/hero/photo-lg-2x.jpg +0 -0
  88. package/build/images/hero/photo-lg.jpg +0 -0
  89. package/build/images/hero/photo-xl-2x.jpg +0 -0
  90. package/build/images/hero/photo-xl.jpg +0 -0
  91. package/build/images/hero/photo-xxl-2x.jpg +0 -0
  92. package/build/images/hero/photo-xxl.jpg +0 -0
  93. package/build/images/hero/photo.jpg +0 -0
  94. package/build/images/hero/single.png +0 -0
  95. package/build/images/hero/single@2x.png +0 -0
  96. package/build/images/hero/vendor-lg.png +0 -0
  97. package/build/images/hero/vendor-lg@2x.png +0 -0
  98. package/build/images/hero/vendor-xl.png +0 -0
  99. package/build/images/hero/vendor-xl@2x.png +0 -0
  100. package/build/images/hero/vendor-xxl.png +0 -0
  101. package/build/images/hero/vendor-xxl@2x.png +0 -0
  102. package/build/images/hero/vendor.png +0 -0
  103. package/build/images/hero/vendor@2x.png +0 -0
  104. package/build/images/phones.png +0 -0
  105. package/build/images/product-1.svg +0 -10
  106. package/build/images/product-2.svg +0 -14
  107. package/build/images/product-3.svg +0 -9
  108. package/build/images/tone-of-voice.jpg +0 -0
  109. package/build/lib/base.js +0 -3
  110. package/build/lib/breadcrumbs.js +0 -3
  111. package/build/lib/components.js +0 -3
  112. package/build/lib/fonts.js +0 -3
  113. package/build/lib/reset.js +0 -3
  114. package/build/lib/style.js +0 -3
  115. package/build/lib/utilities.js +0 -3
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@orangesk/orange-design-system",
3
- "version": "3.12.0",
3
+ "version": "4.0.0",
4
4
  "private": false,
5
5
  "engines": {
6
6
  "node": ">=20.x"
7
7
  },
8
8
  "files": [
9
9
  "build/",
10
+ "!build/images/**",
11
+ "!build/files/**",
10
12
  "public/fonts/",
11
13
  "public/footer-sprite-*.svg",
12
14
  "src/components/",
@@ -35,7 +37,6 @@
35
37
  "a11y-dialog": "^8.1.5",
36
38
  "classnames": "^2.5.1",
37
39
  "daypickr": "^0.3.4",
38
- "diff2html": "^3.4.56",
39
40
  "dompurify": "^3.4.17",
40
41
  "html-react-parser": "6.1.9",
41
42
  "lorem-ipsum": "3.0.0",
@@ -58,7 +59,7 @@
58
59
  "wnumb": "^1.2.0"
59
60
  },
60
61
  "devDependencies": {
61
- "@babel/core": "8.0.9",
62
+ "@babel/core": "8.0.10",
62
63
  "@babel/preset-env": "8.0.7",
63
64
  "@babel/preset-react": "8.0.1",
64
65
  "@babel/preset-typescript": "8.0.7",
@@ -71,32 +72,28 @@
71
72
  "@rollup/plugin-terser": "1.0.2",
72
73
  "@rollup/plugin-typescript": "^12.3.1",
73
74
  "@rollup/plugin-url": "^8.0.2",
74
- "@rollup/rollup-darwin-arm64": "^4.65.0",
75
75
  "@testing-library/dom": "^10.4.2",
76
76
  "@testing-library/jest-dom": "7.0.1",
77
77
  "@testing-library/react": "^16.3.3",
78
78
  "@testing-library/user-event": "^14.6.8",
79
- "@types/node": "26.6.5",
79
+ "@types/node": "26.6.6",
80
80
  "@types/react": "19.3.0",
81
81
  "@types/react-dom": "19.3.0",
82
82
  "@types/wnumb": "^1.2.3",
83
- "@vitejs/plugin-react": "6.1.2",
84
83
  "@vitest/coverage-v8": "5.0.5",
85
84
  "@vitest/ui": "5.0.5",
86
85
  "canvas": "^3.2.3",
87
- "fs-extra": "^11.4.1",
88
- "glob": "13.0.6",
86
+ "glob": "14.0.3",
89
87
  "html-validate": "11.17.0",
90
88
  "husky": "^9.1.7",
91
- "identity-obj-proxy": "^3.0.0",
92
89
  "jsdom": "30.1.3",
93
90
  "lint-staged": "17.6.0",
94
91
  "prettier": "^3.9.10",
95
- "rollup": "^4.65.0",
92
+ "rollup": "^4.66.0",
96
93
  "rollup-plugin-copy": "^3.5.0",
97
94
  "rollup-plugin-dts": "^6.5.1",
98
95
  "rollup-plugin-postcss": "^4.0.2",
99
- "sass": "^1.106.1",
96
+ "sass": "^1.107.0",
100
97
  "svg-sprite": "^2.0.4",
101
98
  "typescript": "6.0.3",
102
99
  "vitest": "5.0.5",
@@ -109,11 +106,12 @@
109
106
  "scripts": {
110
107
  "dev": "npm run sprite && npm run build:search-index && npm run build:preview-iframe && next dev",
111
108
  "build": "npm run sprite && npm run build:search-index && npm run build:preview-iframe && next build",
112
- "build:bundle": "pnpm run sprite && pnpm exec rollup -c && pnpm exec rollup -c rollup.megamenu.config.mjs && pnpm exec rollup -c rollup.footer.config.mjs && pnpm run verify:production-bundle",
109
+ "build:bundle": "pnpm run sprite && pnpm exec rollup -c && pnpm exec rollup -c rollup.megamenu.config.mjs && pnpm exec rollup -c rollup.footer.config.mjs && pnpm run verify:production-bundle && pnpm run verify:standalone-bundles",
113
110
  "build:preview-iframe": "node scripts/build-preview-iframe-bundle.mjs",
114
111
  "build:megamenu": "pnpm run sprite && pnpm exec rollup -c rollup.megamenu.config.mjs",
115
112
  "build:footer": "pnpm run build:scss-exports && node scripts/build-footer-sprites.mjs && pnpm exec rollup -c rollup.footer.config.mjs",
116
113
  "verify:production-bundle": "node scripts/verify-production-bundle.mjs",
114
+ "verify:standalone-bundles": "node scripts/verify-standalone-bundles.mjs",
117
115
  "dev:footer": "npm run build:scss-exports && node scripts/build-footer-sprites.mjs && pnpm exec rollup -c rollup.footer.config.mjs --watch",
118
116
  "build:scss-exports": "node scripts/build-scss-exports.js",
119
117
  "build:search-index": "node scripts/mdx-search-index.js",
@@ -99,7 +99,8 @@ export default class AnchorNavigation {
99
99
  }
100
100
 
101
101
  private getMegamenuOffsetHeight(): number {
102
- return this.megamenuElement?.offsetHeight || 0;
102
+ // offsetHeight rounds fractional CSS pixels and can leave a visible seam.
103
+ return this.megamenuElement?.getBoundingClientRect().height || 0;
103
104
  }
104
105
 
105
106
  private updateStickyPosition(): void {
@@ -304,9 +305,7 @@ export default class AnchorNavigation {
304
305
  }
305
306
 
306
307
  private getTotalStickyOffset(): number {
307
- const scrollOffset = this.megamenuElement
308
- ? this.megamenuElement.offsetHeight
309
- : 0;
308
+ const scrollOffset = this.getMegamenuOffsetHeight();
310
309
  const anchorNavOffset = this.element.offsetHeight;
311
310
  return scrollOffset + anchorNavOffset;
312
311
  }
@@ -0,0 +1,74 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ import AnchorNavigation from "../AnchorNavigation.static";
4
+
5
+ describe("AnchorNavigation fractional Megamenu height", () => {
6
+ let instance: AnchorNavigation;
7
+ let menu: HTMLElement;
8
+ let anchor: HTMLElement;
9
+ let height: number;
10
+
11
+ beforeEach(() => {
12
+ height = 80.5;
13
+ menu = document.createElement("nav");
14
+ menu.setAttribute("data-megamenu", "");
15
+ // jsdom has no layout; represent the browser's fractional and rounded APIs.
16
+ vi.spyOn(menu, "getBoundingClientRect").mockImplementation(
17
+ () => ({ height }) as DOMRect,
18
+ );
19
+ Object.defineProperty(menu, "offsetHeight", {
20
+ get: () => Math.round(height),
21
+ });
22
+ anchor = document.createElement("nav");
23
+ anchor.innerHTML = '<ul class="anchor-navigation__content-left"></ul>';
24
+ document.body.append(menu, anchor);
25
+ instance = new AnchorNavigation(anchor);
26
+ });
27
+
28
+ afterEach(() => {
29
+ instance.destroy();
30
+ menu.remove();
31
+ anchor.remove();
32
+ vi.restoreAllMocks();
33
+ });
34
+
35
+ it("places the navigation at the fractional menu edge on initialization", () => {
36
+ expect(anchor.style.top).toBe("80.5px");
37
+ });
38
+
39
+ it("follows fractional animation heights through its ResizeObserver", () => {
40
+ height = 113.625;
41
+ const observer = (instance as any).resizeObserver;
42
+ observer.callback([], observer);
43
+ expect(anchor.style.top).toBe("113.625px");
44
+ });
45
+
46
+ it("keeps the fractional menu offset when scrolling to a section", () => {
47
+ const section = document.createElement("section");
48
+ section.id = "fractional-section";
49
+ vi.spyOn(section, "getBoundingClientRect").mockReturnValue({
50
+ top: 600,
51
+ } as DOMRect);
52
+ document.body.append(section);
53
+ Object.defineProperty(anchor, "offsetHeight", { value: 50 });
54
+ const previousScrollY = window.scrollY;
55
+ Object.defineProperty(window, "scrollY", { configurable: true, value: 0 });
56
+ const scroll = vi.spyOn(window, "scrollTo").mockImplementation(() => {});
57
+ const link = document.createElement("a");
58
+ link.className = "anchor-navigation__item";
59
+ link.href = "#fractional-section";
60
+ anchor.append(link);
61
+
62
+ try {
63
+ link.click();
64
+ expect(scroll).toHaveBeenCalledWith({ top: 469.5, behavior: "smooth" });
65
+ } finally {
66
+ Object.defineProperty(window, "scrollY", {
67
+ configurable: true,
68
+ value: previousScrollY,
69
+ writable: true,
70
+ });
71
+ section.remove();
72
+ }
73
+ });
74
+ });
@@ -628,6 +628,9 @@ describe("rendering AnchorNavigation", () => {
628
628
  configurable: true,
629
629
  value: 120,
630
630
  });
631
+ vi.spyOn(megamenu, "getBoundingClientRect").mockReturnValue({
632
+ height: 120,
633
+ });
631
634
  document.body.appendChild(megamenu);
632
635
 
633
636
  Object.defineProperty(window, "scrollY", {
@@ -703,6 +706,9 @@ describe("rendering AnchorNavigation", () => {
703
706
  configurable: true,
704
707
  get: () => megamenuHeight,
705
708
  });
709
+ vi.spyOn(megamenu, "getBoundingClientRect").mockImplementation(() => ({
710
+ height: megamenuHeight,
711
+ }));
706
712
  document.body.appendChild(megamenu);
707
713
 
708
714
  Object.defineProperty(window, "scrollY", {
@@ -24,6 +24,7 @@
24
24
  }
25
25
 
26
26
  @mixin container {
27
+ @include typoMixins.list-unstyled;
27
28
  @include container.base;
28
29
 
29
30
  font-size: 0;
@@ -111,6 +112,11 @@
111
112
  text-decoration: none;
112
113
  display: inline;
113
114
 
115
+ &:focus-visible {
116
+ outline: 2px solid currentColor;
117
+ outline-offset: 2px;
118
+ }
119
+
114
120
  &:hover {
115
121
  color: var(--color-text-default);
116
122
  text-decoration: underline;
@@ -0,0 +1,30 @@
1
+ @use "./mixins";
2
+
3
+ // Shared selectors for the layered ODS and unlayered standalone outputs.
4
+ @mixin styles {
5
+ .osk-breadcrumbs {
6
+ @include mixins.base;
7
+
8
+ &__container {
9
+ @include mixins.container;
10
+ }
11
+ }
12
+
13
+ .osk-breadcrumbs__item {
14
+ @include mixins.item;
15
+ }
16
+
17
+ .background-contrast .osk-breadcrumbs__item,
18
+ .surface-contrast .osk-breadcrumbs__item,
19
+ .is-dark .osk-breadcrumbs__item {
20
+ @include mixins.item-dark;
21
+ }
22
+
23
+ .osk-breadcrumbs__link {
24
+ @include mixins.link;
25
+
26
+ &[aria-current="page"] {
27
+ @include mixins.link-current;
28
+ }
29
+ }
30
+ }
@@ -1,29 +1,5 @@
1
- @use "./mixins";
1
+ @use "./rules";
2
2
 
3
3
  @layer components {
4
- .osk-breadcrumbs {
5
- @include mixins.base;
6
-
7
- &__container {
8
- @include mixins.container;
9
- }
10
- }
11
-
12
- .osk-breadcrumbs__item {
13
- @include mixins.item;
14
-
15
- .background-contrast &,
16
- .surface-contrast &,
17
- .is-dark & {
18
- @include mixins.item-dark;
19
- }
20
- }
21
-
22
- .osk-breadcrumbs__link {
23
- @include mixins.link;
24
-
25
- &[aria-current="page"] {
26
- @include mixins.link-current;
27
- }
28
- }
4
+ @include rules.styles;
29
5
  }
@@ -40,9 +40,6 @@ interface ExternalControlsState {
40
40
  }
41
41
 
42
42
  export const defaultConfig: SwiperOptions = {
43
- // Keep pointer selection and click handlers available to CMS editors and integrations.
44
- touchStartPreventDefault: false,
45
- preventClicksPropagation: false,
46
43
  navigation: {
47
44
  nextEl: SELECTOR_NEXT,
48
45
  prevEl: SELECTOR_PREV,
@@ -106,10 +106,6 @@
106
106
  top: 50%;
107
107
  transform: translate(50%, -50%);
108
108
  }
109
-
110
- @include breakpoint.get("md", "down") {
111
- width: var(--carousel-side-arrows-width, 100%);
112
- }
113
109
  }
114
110
 
115
111
  @mixin centered-mobile-variant {
@@ -3,100 +3,131 @@ import CarouselHero from "../../CarouselHero/CarouselHero.static";
3
3
  import CarouselPromotions from "../../CarouselPromotions/CarouselPromotions.static";
4
4
  import Carousel from "../Carousel.static";
5
5
 
6
- describe.each([
7
- { name: "Carousel", Module: Carousel, prefix: "carousel" },
8
- { name: "CarouselHero", Module: CarouselHero, prefix: "carousel-hero" },
9
- {
10
- name: "CarouselPromotions",
11
- Module: CarouselPromotions,
12
- prefix: "carousel-promotions",
13
- },
14
- ])("$name pointer interactions with real Swiper", ({ Module, prefix }) => {
15
- let carousel: Carousel | CarouselHero | CarouselPromotions;
16
- let icon: SVGElement;
6
+ describe.each(
7
+ [
8
+ { name: "Carousel", Module: Carousel, prefix: "carousel" },
9
+ { name: "CarouselHero", Module: CarouselHero, prefix: "carousel-hero" },
10
+ {
11
+ name: "CarouselPromotions",
12
+ Module: CarouselPromotions,
13
+ prefix: "carousel-promotions",
14
+ },
15
+ ].flatMap((component) =>
16
+ ["defaults", "data attribute", "constructor"].map((source) => ({
17
+ ...component,
18
+ source,
19
+ editorMode: source !== "defaults",
20
+ })),
21
+ ),
22
+ )(
23
+ "$name pointer interactions with real Swiper ($source)",
24
+ ({ Module, prefix, source, editorMode }) => {
25
+ let carousel: Carousel | CarouselHero | CarouselPromotions;
26
+ let icon: SVGElement;
17
27
 
18
- beforeEach(() => {
19
- vi.useFakeTimers();
20
- document.body.innerHTML = `<div class="${prefix}" data-${prefix}>
28
+ beforeEach(() => {
29
+ vi.useFakeTimers();
30
+ document.body.innerHTML = `<div class="${prefix}" data-${prefix}>
21
31
  <div class="${prefix}__viewport-wrapper"><div class="${prefix}__viewport"><div class="${prefix}__track">
22
32
  <div class="${prefix}__slide"><a href="#card"><svg><path d="M0 0h10v10z" /></svg></a></div>
23
33
  <div class="${prefix}__slide">Second</div><div class="${prefix}__slide">Third</div>
24
34
  </div></div></div>
25
35
  </div>`;
26
- const element = document.body.firstElementChild as HTMLElement;
27
- for (const node of [
28
- element,
29
- element.querySelector(`.${prefix}__viewport`)!,
30
- ]) {
31
- Object.defineProperty(node, "clientWidth", { value: 600 });
32
- Object.defineProperty(node, "clientHeight", { value: 200 });
33
- }
34
- carousel = new Module(element, { loop: false, breakpoints: {} });
35
- vi.advanceTimersByTime(100);
36
- icon = element.querySelector("path")!;
37
- expect(carousel.instance.enabled).toBe(true);
38
- });
39
-
40
- afterEach(() => {
41
- carousel?.destroy();
42
- vi.clearAllTimers();
43
- vi.useRealTimers();
44
- document.body.innerHTML = "";
45
- });
36
+ const element = document.body.firstElementChild as HTMLElement;
37
+ for (const node of [
38
+ element,
39
+ element.querySelector(`.${prefix}__viewport`)!,
40
+ ]) {
41
+ Object.defineProperty(node, "clientWidth", { value: 600 });
42
+ Object.defineProperty(node, "clientHeight", { value: 200 });
43
+ }
44
+ const editorOptions = {
45
+ touchStartPreventDefault: false,
46
+ preventClicksPropagation: false,
47
+ };
48
+ if (source === "data attribute") {
49
+ element.setAttribute(
50
+ "data-swiper-options",
51
+ JSON.stringify(editorOptions),
52
+ );
53
+ }
54
+ carousel = new Module(element, {
55
+ loop: false,
56
+ breakpoints: {},
57
+ ...(source === "constructor" ? editorOptions : {}),
58
+ });
59
+ vi.advanceTimersByTime(100);
60
+ icon = element.querySelector("path")!;
61
+ expect(carousel.instance.enabled).toBe(true);
62
+ });
46
63
 
47
- function pointer(type: string, x: number, pointerType = "mouse") {
48
- const event = new MouseEvent(type, {
49
- bubbles: true,
50
- cancelable: true,
51
- clientX: x,
52
- clientY: 20,
53
- button: 0,
64
+ afterEach(() => {
65
+ carousel?.destroy();
66
+ vi.clearAllTimers();
67
+ vi.useRealTimers();
68
+ document.body.innerHTML = "";
54
69
  });
55
- Object.assign(event, { pointerId: 1, pointerType });
56
- icon.dispatchEvent(event);
57
- return event;
58
- }
59
70
 
60
- it.each(["mouse", "touch", "pen"])(
61
- "allows SVG icon selection with %s without canceling pointerdown",
62
- (pointerType) => {
63
- const onPointerDown = vi.fn();
64
- carousel.element.addEventListener("pointerdown", onPointerDown);
65
- const event = pointer("pointerdown", 400, pointerType);
66
- expect(onPointerDown).toHaveBeenCalledOnce();
67
- expect(event.defaultPrevented).toBe(false);
68
- },
69
- );
71
+ function pointer(type: string, x: number, pointerType = "mouse") {
72
+ const event = new MouseEvent(type, {
73
+ bubbles: true,
74
+ cancelable: true,
75
+ clientX: x,
76
+ clientY: 20,
77
+ button: 0,
78
+ });
79
+ Object.assign(event, { pointerId: 1, pointerType });
80
+ icon.dispatchEvent(event);
81
+ return event;
82
+ }
83
+
84
+ it.each(["mouse", "touch", "pen"])(
85
+ "applies the configured pointerdown behavior for %s",
86
+ (pointerType) => {
87
+ const onPointerDown = vi.fn();
88
+ carousel.element.addEventListener("pointerdown", onPointerDown);
89
+ const event = pointer("pointerdown", 400, pointerType);
90
+ expect(onPointerDown).toHaveBeenCalledOnce();
91
+ expect(event.defaultPrevented).toBe(!editorMode);
92
+ },
93
+ );
70
94
 
71
- it("delivers an ordinary icon click without canceling it", () => {
72
- const onClick = vi.fn();
73
- carousel.element.addEventListener("click", onClick);
74
- pointer("pointerdown", 400);
75
- pointer("pointerup", 400);
76
- const click = new MouseEvent("click", { bubbles: true, cancelable: true });
77
- icon.dispatchEvent(click);
78
- expect(onClick).toHaveBeenCalledOnce();
79
- expect(click.defaultPrevented).toBe(false);
80
- });
95
+ it("delivers an ordinary icon click without canceling it", () => {
96
+ const onClick = vi.fn();
97
+ carousel.element.addEventListener("click", onClick);
98
+ pointer("pointerdown", 400);
99
+ pointer("pointerup", 400);
100
+ const click = new MouseEvent("click", {
101
+ bubbles: true,
102
+ cancelable: true,
103
+ });
104
+ icon.dispatchEvent(click);
105
+ expect(onClick).toHaveBeenCalledOnce();
106
+ expect(click.defaultPrevented).toBe(false);
107
+ });
81
108
 
82
- it("keeps swipe navigation and link protection while letting click handlers run", () => {
83
- const onIconClick = vi.fn();
84
- const onClick = vi.fn();
85
- icon.addEventListener("click", onIconClick);
86
- carousel.element.addEventListener("click", onClick);
87
- // The initial translation is zero; jsdom cannot parse CSS transforms with DOMMatrixReadOnly.
88
- carousel.instance.wrapperEl.style.transform = "none";
89
- pointer("pointerdown", 400);
90
- pointer("pointermove", 350);
91
- pointer("pointermove", 100);
92
- pointer("pointerup", 100);
93
- expect(carousel.instance.activeIndex).toBe(1);
94
- expect(carousel.instance.animating).toBe(true);
109
+ it("keeps swipe navigation and link protection with configured click propagation", () => {
110
+ const onIconClick = vi.fn();
111
+ const onClick = vi.fn();
112
+ icon.addEventListener("click", onIconClick);
113
+ carousel.element.addEventListener("click", onClick);
114
+ // The initial translation is zero; jsdom cannot parse CSS transforms with DOMMatrixReadOnly.
115
+ carousel.instance.wrapperEl.style.transform = "none";
116
+ pointer("pointerdown", 400);
117
+ pointer("pointermove", 350);
118
+ pointer("pointermove", 100);
119
+ pointer("pointerup", 100);
120
+ expect(carousel.instance.activeIndex).toBe(1);
121
+ expect(carousel.instance.animating).toBe(true);
95
122
 
96
- const click = new MouseEvent("click", { bubbles: true, cancelable: true });
97
- icon.dispatchEvent(click);
98
- expect(onIconClick).toHaveBeenCalledOnce();
99
- expect(onClick).toHaveBeenCalledOnce();
100
- expect(click.defaultPrevented).toBe(true);
101
- });
102
- });
123
+ const click = new MouseEvent("click", {
124
+ bubbles: true,
125
+ cancelable: true,
126
+ });
127
+ icon.dispatchEvent(click);
128
+ expect(onIconClick).toHaveBeenCalledTimes(editorMode ? 1 : 0);
129
+ expect(onClick).toHaveBeenCalledTimes(editorMode ? 1 : 0);
130
+ expect(click.defaultPrevented).toBe(true);
131
+ });
132
+ },
133
+ );
@@ -27,9 +27,6 @@ import {
27
27
  } from "./constants";
28
28
 
29
29
  export const defaultConfig: SwiperOptions = {
30
- // Keep pointer selection and click handlers available to CMS editors and integrations.
31
- touchStartPreventDefault: false,
32
- preventClicksPropagation: false,
33
30
  pagination: {
34
31
  clickable: true,
35
32
  bulletClass: CLASS_PAGINATION_ITEM,
@@ -22,9 +22,6 @@ import {
22
22
  } from "./constants";
23
23
 
24
24
  export const defaultConfig: SwiperOptions = {
25
- // Keep pointer selection and click handlers available to CMS editors and integrations.
26
- touchStartPreventDefault: false,
27
- preventClicksPropagation: false,
28
25
  pagination: {
29
26
  el: SELECTOR_DOTS,
30
27
  clickable: true,
@@ -0,0 +1,3 @@
1
+ // The shared entry owns window.ODSFooter and auto-initialization.
2
+ // No exports: Rollup emits a classic script without creating another global.
3
+ import "./static";
@@ -16,10 +16,33 @@
16
16
  @include visibility.visually-hidden;
17
17
  }
18
18
 
19
- a {
19
+ :where(button, input, select, textarea) {
20
+ font: inherit;
21
+ color: inherit;
22
+ }
23
+
24
+ :where(h3, strong, b) {
25
+ font-family: inherit;
26
+ }
27
+
28
+ :where(strong, b) {
29
+ font-weight: 700;
30
+ }
31
+
32
+ li {
33
+ font-size: inherit;
34
+ line-height: inherit;
35
+ font-weight: inherit;
36
+ }
37
+
38
+ :where(a) {
20
39
  color: var(--color-text-default);
21
40
  text-decoration: none;
41
+ font-family: inherit;
42
+ font-weight: inherit;
22
43
  font-size: inherit;
44
+ background-color: transparent;
45
+ box-shadow: none;
23
46
 
24
47
  &:hover,
25
48
  &:focus,
@@ -27,6 +50,8 @@
27
50
  text-decoration: underline;
28
51
  text-underline-offset: convert.to-rem(2px);
29
52
  text-decoration-thickness: auto;
53
+ background-color: transparent;
54
+ box-shadow: none;
30
55
  }
31
56
 
32
57
  &:focus-visible {
@@ -46,6 +71,11 @@
46
71
  [tabindex]:not([tabindex="-1"]),
47
72
  [contentEditable="true"] {
48
73
  @include base.focusring;
74
+
75
+ &:focus-visible {
76
+ // Legacy ODS explicitly suppresses outlines with !important.
77
+ outline-width: base.$focusring-width !important;
78
+ }
49
79
  }
50
80
 
51
81
  *,
@@ -74,6 +104,7 @@
74
104
 
75
105
  @mixin base {
76
106
  font-size: 1rem;
107
+ font-weight: 400;
77
108
  letter-spacing: convert.to-rem(-0.1px);
78
109
  line-height: convert.to-rem(18px);
79
110
  color: var(--color-text-default);
@@ -7,7 +7,8 @@
7
7
  @use "../../SocialButton/styles/mixins" as socialButtonMixins;
8
8
  @use "../../Icon/styles/mixins" as iconMixins;
9
9
 
10
- #{config.$class-root} {
10
+ // Keep specificity equal in document and shadow trees, above legacy link rules.
11
+ :is(:root, :host) #{config.$class-root} {
11
12
  @include mixins.globals;
12
13
  }
13
14
 
@@ -22,7 +23,7 @@
22
23
  @include megamenuMixins.dark-vars;
23
24
  }
24
25
 
25
- #{config.$class-root} {
26
+ :is(:root, :host) #{config.$class-root} {
26
27
  @include mixins.base;
27
28
 
28
29
  &__container {
@@ -62,11 +63,13 @@
62
63
  }
63
64
  }
64
65
 
65
- [data-footer-toggle][aria-expanded="false"] > &__accordion-icon {
66
+ [data-footer-toggle][aria-expanded="false"]
67
+ > #{config.$class-root}__accordion-icon {
66
68
  transform: rotate(0deg);
67
69
  }
68
70
 
69
- [data-footer-toggle][aria-expanded="true"] > &__accordion-icon {
71
+ [data-footer-toggle][aria-expanded="true"]
72
+ > #{config.$class-root}__accordion-icon {
70
73
  transform: rotate(180deg);
71
74
  }
72
75
 
@@ -91,7 +94,7 @@
91
94
  }
92
95
  }
93
96
 
94
- &__menu-section > &__list {
97
+ &__menu-section > #{config.$class-root}__list {
95
98
  @include mixins.accordion-list;
96
99
  }
97
100