maverick-wave 4.27.0 → 5.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 (140) hide show
  1. package/.claude/settings.local.json +29 -1
  2. package/.claude/skills/mw-maverick-wave/SKILL.md +42 -25
  3. package/.claude/skills/mw-maverick-wave/examples/angular-services.md +16 -4
  4. package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +13 -4
  5. package/.claude/skills/mw-maverick-wave/references/components.md +90 -5
  6. package/.claude/skills/mw-maverick-wave/references/forms.md +14 -2
  7. package/.claude/skills/mw-maverick-wave/references/javascript.md +18 -4
  8. package/.claude/skills/mw-maverick-wave/references/layout.md +53 -19
  9. package/.claude/skills/mw-maverick-wave/references/theming.md +38 -1
  10. package/.impeccable/config.local.json +5 -0
  11. package/.impeccable/hook.cache.json +1 -0
  12. package/CHANGELOG.md +17 -0
  13. package/CLAUDE.md +5 -5
  14. package/README.md +48 -4
  15. package/index.html +19 -38
  16. package/maverick-wave.min.css +14 -12
  17. package/maverick-wave.min.js +1 -1
  18. package/package.json +2 -2
  19. package/scripts/verify.js +27 -2
  20. package/src/js/main.js +266 -75
  21. package/src/partials/accordions-container.html +6 -10
  22. package/src/partials/alerts-container.html +6 -6
  23. package/src/partials/announcement-container.html +5 -5
  24. package/src/partials/avatars-container.html +85 -17
  25. package/src/partials/badges-container.html +6 -6
  26. package/src/partials/blog-posts-container.html +11 -12
  27. package/src/partials/buttons-container.html +3 -3
  28. package/src/partials/cards-container.html +26 -9
  29. package/src/partials/code-container.html +5 -5
  30. package/src/partials/content-slider-container.html +5 -4
  31. package/src/partials/divider-container.html +7 -7
  32. package/src/partials/dropdown-container.html +2 -2
  33. package/src/partials/empty-state-container.html +5 -5
  34. package/src/partials/footer-container.html +21 -18
  35. package/src/partials/form-elements-container.html +14 -14
  36. package/src/partials/gallery-container.html +16 -8
  37. package/src/partials/get-started-container.html +4 -8
  38. package/src/partials/header-container.html +18 -7
  39. package/src/partials/header-utilities-container.html +192 -5
  40. package/src/partials/home-container.html +10 -20
  41. package/src/partials/input-group-container.html +11 -2
  42. package/src/partials/kbd-container.html +1 -1
  43. package/src/partials/login-container.html +5 -1
  44. package/src/partials/media-container.html +2 -2
  45. package/src/partials/modals-container.html +22 -32
  46. package/src/partials/pricing-container.html +9 -9
  47. package/src/partials/skeleton-container.html +6 -6
  48. package/src/partials/stepper-container.html +2 -2
  49. package/src/partials/tables-container.html +12 -12
  50. package/src/partials/tabs-container.html +17 -27
  51. package/src/partials/tags-container.html +23 -17
  52. package/src/partials/techstack-bucket-container.html +13 -0
  53. package/src/partials/tiles-container.html +26 -9
  54. package/src/partials/typography-container.html +7 -6
  55. package/src/partials/utilities-container.html +35 -15
  56. package/src/scss/_layers.scss +10 -0
  57. package/src/scss/abstracts/_index.scss +6 -0
  58. package/src/scss/abstracts/_mixins.scss +61 -153
  59. package/src/scss/abstracts/_variables.scss +123 -319
  60. package/src/scss/base/_base.scss +170 -190
  61. package/src/scss/base/_reset.scss +106 -104
  62. package/src/scss/base/_typography.scss +153 -151
  63. package/src/scss/components/_accordions.scss +97 -97
  64. package/src/scss/components/_alerts.scss +71 -70
  65. package/src/scss/components/_announcement.scss +71 -70
  66. package/src/scss/components/_avatars.scss +130 -142
  67. package/src/scss/components/_badge.scss +131 -132
  68. package/src/scss/components/_blog-post.scss +223 -220
  69. package/src/scss/components/_breadcrumbs.scss +93 -92
  70. package/src/scss/components/_button-bar.scss +102 -110
  71. package/src/scss/components/_buttons.scss +319 -324
  72. package/src/scss/components/_calendar.scss +251 -252
  73. package/src/scss/components/_cards.scss +359 -386
  74. package/src/scss/components/_code.scss +190 -194
  75. package/src/scss/components/_coming-soon.scss +77 -75
  76. package/src/scss/components/_content-slider.scss +90 -89
  77. package/src/scss/components/_divider.scss +55 -53
  78. package/src/scss/components/_dropdown.scss +179 -185
  79. package/src/scss/components/_empty-state.scss +57 -57
  80. package/src/scss/components/_flag.scss +325 -0
  81. package/src/scss/components/_gallery.scss +131 -133
  82. package/src/scss/components/_index.scss +2 -0
  83. package/src/scss/components/_info.scss +151 -167
  84. package/src/scss/components/_kanban.scss +332 -341
  85. package/src/scss/components/_kbd.scss +40 -38
  86. package/src/scss/components/_lang-switch.scss +108 -0
  87. package/src/scss/components/_links.scss +21 -28
  88. package/src/scss/components/_lists.scss +510 -514
  89. package/src/scss/components/_localhost-indicator.scss +26 -24
  90. package/src/scss/components/_media.scss +22 -20
  91. package/src/scss/components/_meta-info.scss +35 -33
  92. package/src/scss/components/_modals.scss +277 -186
  93. package/src/scss/components/_pagination.scss +81 -87
  94. package/src/scss/components/_panels.scss +99 -97
  95. package/src/scss/components/_pricing.scss +183 -241
  96. package/src/scss/components/_progress.scss +112 -89
  97. package/src/scss/components/_prose.scss +35 -33
  98. package/src/scss/components/_ratings.scss +40 -38
  99. package/src/scss/components/_segmented.scss +82 -84
  100. package/src/scss/components/_skeleton.scss +66 -72
  101. package/src/scss/components/_spinners.scss +155 -159
  102. package/src/scss/components/_stepper.scss +116 -116
  103. package/src/scss/components/_tables.scss +155 -148
  104. package/src/scss/components/_tabs.scss +232 -141
  105. package/src/scss/components/_tags.scss +109 -107
  106. package/src/scss/components/_techstack-bucket.scss +123 -122
  107. package/src/scss/components/_testimonial.scss +72 -70
  108. package/src/scss/components/_theme-toggle.scss +53 -51
  109. package/src/scss/components/_tiles.scss +174 -174
  110. package/src/scss/components/_timelines.scss +282 -280
  111. package/src/scss/components/_toasts.scss +66 -72
  112. package/src/scss/form-elements/_checkbox.scss +117 -132
  113. package/src/scss/form-elements/_form.scss +149 -115
  114. package/src/scss/form-elements/_input-group.scss +99 -103
  115. package/src/scss/form-elements/_input.scss +74 -74
  116. package/src/scss/form-elements/_login.scss +48 -48
  117. package/src/scss/form-elements/_prefilled.scss +53 -51
  118. package/src/scss/form-elements/_radio.scss +95 -107
  119. package/src/scss/form-elements/_select.scss +42 -42
  120. package/src/scss/form-elements/_slider.scss +117 -132
  121. package/src/scss/form-elements/_textarea.scss +68 -54
  122. package/src/scss/form-elements/_toggle.scss +127 -140
  123. package/src/scss/layout/_footer.scss +172 -171
  124. package/src/scss/layout/_grid.scss +268 -302
  125. package/src/scss/layout/_header.scss +426 -427
  126. package/src/scss/layout/_home.scss +28 -27
  127. package/src/scss/layout/_main.scss +275 -238
  128. package/src/scss/layout/_page-header.scss +38 -36
  129. package/src/scss/layout/_section.scss +150 -124
  130. package/src/scss/main.scss +1 -1
  131. package/src/scss/utilities/_accessibility.scss +46 -45
  132. package/src/scss/utilities/_aspect.scss +22 -20
  133. package/src/scss/utilities/_corner.scss +23 -5
  134. package/src/scss/utilities/_display.scss +76 -70
  135. package/src/scss/utilities/_elevation.scss +13 -11
  136. package/src/scss/utilities/_flex.scss +83 -85
  137. package/src/scss/utilities/_reveal.scss +40 -63
  138. package/src/scss/utilities/_spacing.scss +63 -64
  139. package/src/scss/utilities/_text.scss +139 -141
  140. package/src/scss/utilities/_touch-targets.scss +130 -147
@@ -1,48 +1,50 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
- .mw-page-header {
4
- display: flex;
5
- flex-wrap: wrap;
6
- align-items: flex-start;
7
- justify-content: space-between;
8
- gap: spacing('4');
9
- margin-bottom: spacing('6');
10
- padding-bottom: spacing('4');
11
- border-bottom: 1px solid var(--mw-border);
12
-
13
- h1,
14
- h2,
15
- h3 {
16
- margin: spacing('0');
17
- font-size: font-size('2xl');
18
- line-height: 1.2;
3
+ @layer mw.layout {
4
+ .mw-page-header {
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ align-items: flex-start;
8
+ justify-content: space-between;
9
+ gap: spacing('4');
10
+ margin-bottom: spacing('6');
11
+ padding-bottom: spacing('4');
12
+ border-bottom: 1px solid var(--mw-border);
19
13
 
20
- @include media-down('sm') {
14
+ h1,
15
+ h2,
16
+ h3 {
17
+ margin: spacing('0');
21
18
  font-size: font-size('xl');
19
+ line-height: 1.2;
20
+
21
+ @include media-up('sm') {
22
+ font-size: font-size('2xl');
23
+ }
22
24
  }
23
- }
24
25
 
25
- p {
26
- margin: spacing('1') spacing('0') spacing('0') spacing('0');
27
- color: var(--mw-text-muted-color);
28
- font-size: font-size('sm');
29
- }
26
+ p {
27
+ margin: spacing('1') spacing('0') spacing('0') spacing('0');
28
+ color: var(--mw-text-muted-color);
29
+ font-size: font-size('sm');
30
+ }
30
31
 
31
- &-actions {
32
- display: flex;
33
- flex-wrap: wrap;
34
- align-items: center;
35
- gap: spacing('3');
36
- margin-left: auto;
32
+ &-actions {
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ align-items: center;
36
+ gap: spacing('3');
37
+ margin-left: auto;
37
38
 
38
- @include media-down('sm') {
39
- width: 100%;
40
- margin-left: spacing('0');
39
+ @include media-down('sm') {
40
+ width: 100%;
41
+ margin-left: spacing('0');
42
+ }
41
43
  }
42
- }
43
44
 
44
- &-plain {
45
- padding-bottom: spacing('0');
46
- border-bottom: none;
45
+ &-plain {
46
+ padding-bottom: spacing('0');
47
+ border-bottom: none;
48
+ }
47
49
  }
48
50
  }
@@ -1,150 +1,176 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
- .mw-section-title {
4
- font-size: font-size('3xl');
5
- margin-bottom: var(--mw-section-title-gap);
6
- text-align: center;
7
- position: relative;
8
-
9
- &::after {
10
- content: '';
11
- position: absolute;
12
- bottom: spacing('-3');
13
- left: 50%;
14
- transform: translateX(-50%);
15
- width: 80px;
16
- height: 5px;
17
- background: var(--mw-primary-text-color);
3
+ @layer mw.layout {
4
+ // The rule under a section heading. Shared, so the two levels cannot drift
5
+ // apart in width and weight - the subtitle carried the wider mark, which reads
6
+ // as the louder one under the smaller heading. Colour is where they do part.
7
+ @mixin _section-rule(
8
+ $width,
9
+ $height,
10
+ $offset,
11
+ $color: var(--mw-primary-text-color)
12
+ ) {
13
+ &::after {
14
+ content: '';
15
+ position: absolute;
16
+ bottom: $offset;
17
+ left: 50%;
18
+ transform: translateX(-50%);
19
+ width: $width;
20
+ height: $height;
21
+ border-radius: radius('xs');
22
+ background: $color;
23
+ }
18
24
  }
19
25
 
20
- @include media-down('sm') {
26
+ .mw-section-title {
21
27
  font-size: font-size('2xl');
22
- }
23
- }
28
+ margin-bottom: var(--mw-section-title-gap);
29
+ text-align: center;
30
+ position: relative;
24
31
 
25
- // The lead line under a section title. Every landing page writes this
26
- // paragraph, and it always wants the same four things: centred, muted,
27
- // narrower than the container so it reads as a line rather than a block, and
28
- // pulled up toward the title it belongs to - the title's gap is sized for the
29
- // content below, not for its own subtitle.
30
- .mw-section-intro {
31
- max-width: 46rem;
32
- margin: spacing('-3') auto spacing('9');
33
- font-size: font-size('md');
34
- color: var(--mw-text-muted-color);
35
- text-align: center;
36
- text-wrap: balance;
37
- }
32
+ @include _section-rule(56px, 3px, spacing('-3'));
38
33
 
39
- .mw-section-subtitle {
40
- font-size: font-size('2xl');
41
- margin: spacing('8') spacing('0') spacing('6') spacing('0');
42
- text-align: center;
43
- position: relative;
44
-
45
- &::after {
46
- content: '';
47
- position: absolute;
48
- bottom: spacing('-2');
49
- left: 50%;
50
- transform: translateX(-50%);
51
- width: 100px;
52
- height: 2px;
53
- background: var(--mw-secondary-text-color);
34
+ @include media-up('sm') {
35
+ font-size: font-size('3xl');
36
+ }
54
37
  }
55
38
 
56
- @include media-down('sm') {
57
- font-size: font-size('xl');
39
+ // The lead line under a section title. Every landing page writes this
40
+ // paragraph, and it always wants the same four things: centred, muted,
41
+ // narrower than the container so it reads as a line rather than a block, and
42
+ // pulled up toward the title it belongs to - the title's gap is sized for the
43
+ // content below, not for its own subtitle.
44
+ .mw-section-intro {
45
+ max-width: 46rem;
46
+ margin: spacing('-3') auto spacing('9');
47
+ font-size: font-size('md');
48
+ color: var(--mw-text-muted-color);
49
+ text-align: center;
50
+ text-wrap: balance;
58
51
  }
59
- }
60
52
 
61
- .mw-section-nav {
62
- max-width: 100%;
63
- margin: spacing('0') auto spacing('0') auto;
64
- display: flex;
65
- flex-wrap: wrap;
66
- justify-content: center;
67
- gap: spacing('3');
68
- }
53
+ .mw-section-subtitle {
54
+ font-size: font-size('xl');
55
+ // The step above a demo gap of spacing('6') - at spacing('8') the boundary
56
+ // between two components read like the one between two demos
57
+ margin: spacing('10') spacing('0') spacing('6') spacing('0');
58
+ text-align: center;
59
+ position: relative;
69
60
 
70
- .mw-section-btn {
71
- display: inline-flex;
72
- align-items: center;
73
- justify-content: center;
74
- gap: spacing('3');
75
- padding: spacing('3') spacing('4');
76
- border: 1px solid var(--mw-primary-text-color);
77
- border-radius: radius('sm');
78
- font-weight: font-weight('medium');
79
- font-size: inherit;
80
- font-family: inherit;
81
- text-decoration: none;
82
- cursor: pointer;
83
- transition: var(--mw-transition);
84
- box-sizing: border-box;
85
- background: transparent;
86
- color: var(--mw-primary-text-color);
87
-
88
- &:hover:not(:disabled) {
89
- background: var(--mw-primary-color);
90
- color: var(--mw-primary-accent-text-color);
91
- }
61
+ @include _section-rule(
62
+ 32px,
63
+ 2px,
64
+ spacing('-2'),
65
+ var(--mw-secondary-text-color)
66
+ );
92
67
 
93
- &:active:not(:disabled) {
94
- transform: translateY(1px);
95
- transition-duration: var(--mw-duration-instant);
68
+ @include media-up('sm') {
69
+ font-size: font-size('2xl');
70
+ }
96
71
  }
97
72
 
98
- @include focus-ring;
99
- @include tappable;
100
-
101
- &:disabled {
102
- cursor: not-allowed;
103
- opacity: 0.6;
104
- }
105
- }
106
-
107
- // Responsive adjustments
108
- @include media-down('sm') {
73
+ // Sticky, because the strip is the only way back: inside a long section the
74
+ // nav at the entrance can be twenty thousand pixels behind the reader
109
75
  .mw-section-nav {
110
- flex-direction: column;
76
+ position: sticky;
77
+ top: var(--mw-header-height);
78
+ z-index: z-index('menu');
111
79
  width: 100%;
112
- max-width: 300px;
113
- margin-left: auto;
114
- margin-right: auto;
80
+ padding-block: spacing('3');
81
+ display: flex;
82
+ flex-wrap: nowrap;
83
+ justify-content: center;
84
+ // A long strip aligns to its start instead of centring both ends out of reach
85
+ justify-content: safe center;
86
+ gap: spacing('3');
87
+ overflow-x: auto;
88
+ overscroll-behavior-x: contain;
89
+ // Opaque and unblurred - the strip repaints on every scroll frame, and the
90
+ // alternate sections build their pattern on this same colour anyway
91
+ background: var(--mw-page-background);
92
+
93
+ @include scrollbar;
94
+ }
95
+
96
+ // The strip parks under the header, so a jump to a component clears both
97
+ .mw-container > div:has(> .mw-section-subtitle) {
98
+ scroll-margin-top: calc(
99
+ var(--mw-header-height) + var(--mw-section-nav-height) + #{spacing('5')}
100
+ );
115
101
  }
116
102
 
117
103
  .mw-section-btn {
118
- width: 100%;
119
- text-align: center;
104
+ display: inline-flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ // No shrinking in a row that scrolls rather than wraps
108
+ flex: 0 0 auto;
109
+ gap: spacing('3');
110
+ padding: spacing('3') spacing('4');
111
+ border: 1px solid var(--mw-primary-text-color);
112
+ border-radius: radius('sm');
113
+ font-weight: font-weight('medium');
114
+ font-size: inherit;
115
+ font-family: inherit;
116
+ text-decoration: none;
117
+ cursor: pointer;
118
+ transition: var(--mw-transition);
119
+ box-sizing: border-box;
120
+ background: transparent;
121
+ color: var(--mw-primary-text-color);
122
+
123
+ &:hover:not(:disabled) {
124
+ background: var(--mw-primary-color);
125
+ color: var(--mw-primary-accent-text-color);
126
+ }
127
+
128
+ &:active:not(:disabled) {
129
+ transform: translateY(1px);
130
+ transition-duration: var(--mw-duration-instant);
131
+ }
132
+
133
+ @include focus-ring;
134
+ @include tappable;
135
+
136
+ &:disabled {
137
+ cursor: not-allowed;
138
+ opacity: 0.6;
139
+ }
120
140
  }
121
- }
122
141
 
123
- // Additional spacing for the whole section
124
- .mw-section {
125
- padding: var(--mw-section-padding-block) spacing('0');
126
- position: relative;
127
-
128
- // ...except around a hero. The hero container is already 100dvh, so the
129
- // padding pushes exactly that much of it past the bottom of the screen -
130
- // and with it the scroll cue sitting on its lower edge. A hero is the full
131
- // screen by definition and brings its own breathing room.
132
- @supports (selector(:has(.mw-hero))) {
133
- &:has(.mw-hero) {
134
- padding-block: spacing('0');
142
+ @include media-down('sm') {
143
+ // The section boundary steps down to 56px on a phone, so the component
144
+ // boundary steps with it or the two stop being distinguishable
145
+ .mw-section-subtitle {
146
+ margin-top: spacing('7');
135
147
  }
136
148
  }
137
- }
138
149
 
139
- // Pattern variant for sections
140
- .mw-section-alternate {
141
- position: relative;
142
- --s: 5px; // size
143
- --c1: var(--mw-page-background);
144
- --c2: var(--mw-section-alternate-pattern);
145
- --g: var(--c2) -5% 5%, var(--c1) 0 45%;
146
- background:
147
- linear-gradient(0deg, #0000 10%, var(--c1) 0 40%, #0000 0),
148
- repeating-linear-gradient(-45deg, var(--g)) var(--c1);
149
- background-size: var(--s) var(--s);
150
+ .mw-section {
151
+ padding: var(--mw-section-padding-block) spacing('0');
152
+ position: relative;
153
+
154
+ // ...except around a hero. The hero container is already 100dvh, so the
155
+ // padding pushes exactly that much of it past the bottom of the screen -
156
+ // and with it the scroll cue sitting on its lower edge. A hero is the full
157
+ // screen by definition and brings its own breathing room.
158
+ @supports (selector(:has(.mw-hero))) {
159
+ &:has(.mw-hero) {
160
+ padding-block: spacing('0');
161
+ }
162
+ }
163
+ }
164
+
165
+ .mw-section-alternate {
166
+ position: relative;
167
+ --s: 5px;
168
+ --c1: var(--mw-page-background);
169
+ --c2: var(--mw-section-alternate-pattern);
170
+ --g: var(--c2) -5% 5%, var(--c1) 0 45%;
171
+ background:
172
+ linear-gradient(0deg, #0000 10%, var(--c1) 0 40%, #0000 0),
173
+ repeating-linear-gradient(-45deg, var(--g)) var(--c1);
174
+ background-size: var(--s) var(--s);
175
+ }
150
176
  }
@@ -3,7 +3,7 @@
3
3
  @forward 'abstracts/variables';
4
4
 
5
5
  @use 'base';
6
+ @use 'layout';
6
7
  @use 'components';
7
8
  @use 'form-elements';
8
- @use 'layout';
9
9
  @use 'utilities';
@@ -1,52 +1,53 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
- // Visible to screen readers only
4
- .mw-sr-only {
5
- position: absolute;
6
- width: 1px;
7
- height: 1px;
8
- padding: 0;
9
- margin: -1px;
10
- overflow: hidden;
11
- clip-path: inset(50%);
12
- white-space: nowrap;
13
- border: 0;
14
- }
3
+ @layer mw.utilities {
4
+ .mw-sr-only {
5
+ position: absolute;
6
+ width: 1px;
7
+ height: 1px;
8
+ padding: 0;
9
+ margin: -1px;
10
+ overflow: hidden;
11
+ clip-path: inset(50%);
12
+ white-space: nowrap;
13
+ border: 0;
14
+ }
15
15
 
16
- // The first thing a keyboard user meets on the page: a link that jumps past the
17
- // header and the navigation straight to the content. Off-screen until focused,
18
- // which is why it can be the first element in the body without being seen.
19
- //
20
- // <a class="mw-skip-link" href="#main">Skip to content</a>
21
- .mw-skip-link {
22
- position: absolute;
23
- left: spacing('3');
24
- top: spacing('3');
25
- z-index: z-index('alert');
26
- padding: spacing('3') spacing('5');
27
- border-radius: radius('sm');
28
- background: var(--mw-primary-color);
29
- color: var(--mw-primary-accent-text-color);
30
- font-weight: font-weight('medium');
31
- box-shadow: var(--mw-elevation-3);
32
- transform: translateY(calc(-100% - #{spacing('5')}));
33
- transition: transform var(--mw-duration-fast) var(--mw-ease-out);
16
+ // The first thing a keyboard user meets on the page: a link that jumps past the
17
+ // header and the navigation straight to the content. Off-screen until focused,
18
+ // which is why it can be the first element in the body without being seen.
19
+ //
20
+ // <a class="mw-skip-link" href="#main">Skip to content</a>
21
+ .mw-skip-link {
22
+ position: absolute;
23
+ left: spacing('3');
24
+ top: spacing('3');
25
+ z-index: z-index('alert');
26
+ padding: spacing('3') spacing('5');
27
+ border-radius: radius('sm');
28
+ background: var(--mw-primary-color);
29
+ color: var(--mw-primary-accent-text-color);
30
+ font-weight: font-weight('medium');
31
+ box-shadow: var(--mw-elevation-3);
32
+ transform: translateY(calc(-100% - #{spacing('5')}));
33
+ transition: transform var(--mw-duration-fast) var(--mw-ease-out);
34
34
 
35
- &:focus-visible {
36
- transform: translateY(0);
35
+ &:focus-visible {
36
+ transform: translateY(0);
37
+ }
37
38
  }
38
- }
39
39
 
40
- // Content that is there for a screen reader but comes back on focus - the same
41
- // idea as sr-only, for something that has to be reachable by tab.
42
- .mw-sr-only-focusable:not(:focus):not(:focus-within) {
43
- position: absolute;
44
- width: 1px;
45
- height: 1px;
46
- padding: 0;
47
- margin: -1px;
48
- overflow: hidden;
49
- clip-path: inset(50%);
50
- white-space: nowrap;
51
- border: 0;
40
+ // Content that is there for a screen reader but comes back on focus - the same
41
+ // idea as sr-only, for something that has to be reachable by tab.
42
+ .mw-sr-only-focusable:not(:focus):not(:focus-within) {
43
+ position: absolute;
44
+ width: 1px;
45
+ height: 1px;
46
+ padding: 0;
47
+ margin: -1px;
48
+ overflow: hidden;
49
+ clip-path: inset(50%);
50
+ white-space: nowrap;
51
+ border: 0;
52
+ }
52
53
  }
@@ -1,27 +1,29 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
- // Reserves the right box before the image inside it has loaded, which is the
4
- // whole point: without it the page reflows when the picture arrives and
5
- // everything below it jumps. The child fills the box and crops rather than
6
- // stretching.
7
- $_mw-aspects: (
8
- 'square': '1 / 1',
9
- 'video': '16 / 9',
10
- 'wide': '21 / 9',
11
- 'portrait': '3 / 4',
12
- 'photo': '4 / 3',
13
- );
3
+ @layer mw.utilities {
4
+ // Reserves the right box before the image inside it has loaded, which is the
5
+ // whole point: without it the page reflows when the picture arrives and
6
+ // everything below it jumps. The child fills the box and crops rather than
7
+ // stretching.
8
+ $_mw-aspects: (
9
+ 'square': '1 / 1',
10
+ 'video': '16 / 9',
11
+ 'wide': '21 / 9',
12
+ 'portrait': '3 / 4',
13
+ 'photo': '4 / 3',
14
+ );
14
15
 
15
- @each $name, $ratio in $_mw-aspects {
16
- .mw-aspect-#{$name} {
17
- aspect-ratio: #{$ratio};
16
+ @each $name, $ratio in $_mw-aspects {
17
+ .mw-aspect-#{$name} {
18
+ aspect-ratio: #{$ratio};
18
19
 
19
- > img,
20
- > video,
21
- > iframe {
22
- width: 100%;
23
- height: 100%;
24
- object-fit: cover;
20
+ > img,
21
+ > video,
22
+ > iframe {
23
+ width: 100%;
24
+ height: 100%;
25
+ object-fit: cover;
26
+ }
25
27
  }
26
28
  }
27
29
  }
@@ -1,8 +1,26 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
- // Opt-out from the corner accent - the arc on the two round corners that every
4
- // surface in the framework carries, see the surface() mixin. Everything else
5
- // stays: the radii, the border, the shadow, the hover.
6
- .mw-corner-plain::after {
7
- content: none;
3
+ @layer mw.utilities {
4
+ // Opt-out from the corner accent - the arc on the two round corners that every
5
+ // surface in the framework carries, see the surface() mixin. Everything else
6
+ // stays: the radii, the border, the shadow, the hover.
7
+ .mw-corner-plain::after {
8
+ content: none;
9
+ }
10
+
11
+ // The framework silhouette drawn with a superellipse instead of a circular
12
+ // arc - the same four radii, a fuller curve. Opt-in on purpose: `corner-shape`
13
+ // only lands in Chromium so far, and switching it on by default would make the
14
+ // same card read differently in two browsers side by side without anyone
15
+ // having asked for it.
16
+ //
17
+ // The corner accent follows, or the arc would sit on a curve it no longer
18
+ // matches.
19
+ .mw-squircle {
20
+ corner-shape: squircle;
21
+
22
+ &::after {
23
+ corner-shape: squircle;
24
+ }
25
+ }
8
26
  }