material 3.4.3 → 3.5.1

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 (178) hide show
  1. package/README.md +2 -2
  2. package/dist/elements/css/badge.css +1 -1
  3. package/dist/elements/css/badge.js +1 -1
  4. package/dist/elements/css/bottom-app-bar.css +1 -1
  5. package/dist/elements/css/bottom-app-bar.js +1 -1
  6. package/dist/elements/css/card.css +1 -1
  7. package/dist/elements/css/card.js +1 -1
  8. package/dist/elements/css/datepicker.css +1 -1
  9. package/dist/elements/css/datepicker.js +1 -1
  10. package/dist/elements/css/dialog.css +1 -1
  11. package/dist/elements/css/dialog.js +1 -1
  12. package/dist/elements/css/drawer.css +1 -1
  13. package/dist/elements/css/drawer.js +1 -1
  14. package/dist/elements/css/extended-fab.css +1 -1
  15. package/dist/elements/css/extended-fab.js +1 -1
  16. package/dist/elements/css/fab-menu.css +1 -1
  17. package/dist/elements/css/fab-menu.js +1 -1
  18. package/dist/elements/css/fab.css +1 -1
  19. package/dist/elements/css/fab.js +1 -1
  20. package/dist/elements/css/navigation-bar.css +1 -1
  21. package/dist/elements/css/navigation-bar.js +1 -1
  22. package/dist/elements/css/navigation-rail.css +1 -1
  23. package/dist/elements/css/navigation-rail.js +1 -1
  24. package/dist/elements/css/side-sheet.css +1 -1
  25. package/dist/elements/css/side-sheet.js +1 -1
  26. package/dist/elements/css/split-button.css +1 -1
  27. package/dist/elements/css/split-button.js +1 -1
  28. package/dist/elements/css/tabs.css +1 -1
  29. package/dist/elements/css/tabs.js +1 -1
  30. package/dist/elements/css/toolbar.css +1 -1
  31. package/dist/elements/css/toolbar.js +1 -1
  32. package/dist/elements/css/top-app-bar.css +1 -1
  33. package/dist/elements/css/top-app-bar.js +1 -1
  34. package/dist/elements/preupgrade/badge.css +1 -1
  35. package/dist/elements/preupgrade/bottom-app-bar.css +1 -1
  36. package/dist/elements/preupgrade/bottom-sheet.css +1 -1
  37. package/dist/elements/preupgrade/button-group.css +1 -1
  38. package/dist/elements/preupgrade/button.css +1 -1
  39. package/dist/elements/preupgrade/card.css +1 -1
  40. package/dist/elements/preupgrade/carousel.css +1 -1
  41. package/dist/elements/preupgrade/checkbox.css +1 -1
  42. package/dist/elements/preupgrade/chips.css +1 -1
  43. package/dist/elements/preupgrade/datepicker.css +1 -1
  44. package/dist/elements/preupgrade/dialog.css +1 -1
  45. package/dist/elements/preupgrade/divider.css +1 -1
  46. package/dist/elements/preupgrade/drawer.css +1 -1
  47. package/dist/elements/preupgrade/extended-fab.css +1 -1
  48. package/dist/elements/preupgrade/fab-menu.css +1 -1
  49. package/dist/elements/preupgrade/fab.css +1 -1
  50. package/dist/elements/preupgrade/icon-button.css +1 -1
  51. package/dist/elements/preupgrade/list.css +1 -1
  52. package/dist/elements/preupgrade/loading-indicator.css +1 -1
  53. package/dist/elements/preupgrade/menu.css +1 -1
  54. package/dist/elements/preupgrade/navigation-bar.css +1 -1
  55. package/dist/elements/preupgrade/navigation-rail.css +1 -1
  56. package/dist/elements/preupgrade/progress.css +1 -1
  57. package/dist/elements/preupgrade/radios.css +1 -1
  58. package/dist/elements/preupgrade/search.css +1 -1
  59. package/dist/elements/preupgrade/select.css +1 -1
  60. package/dist/elements/preupgrade/side-sheet.css +1 -1
  61. package/dist/elements/preupgrade/slider.css +1 -1
  62. package/dist/elements/preupgrade/snackbar.css +1 -1
  63. package/dist/elements/preupgrade/split-button.css +1 -1
  64. package/dist/elements/preupgrade/switch.css +1 -1
  65. package/dist/elements/preupgrade/tabs.css +1 -1
  66. package/dist/elements/preupgrade/text-field.css +1 -1
  67. package/dist/elements/preupgrade/timepicker.css +1 -1
  68. package/dist/elements/preupgrade/toolbar.css +1 -1
  69. package/dist/elements/preupgrade/tooltip.css +1 -1
  70. package/dist/elements/preupgrade/top-app-bar.css +1 -1
  71. package/dist/elements/preupgrade.css +1 -1
  72. package/dist/package.json +1 -1
  73. package/dist/ssr/react.js +179 -33
  74. package/dist/styles/badge.css +2 -2
  75. package/dist/styles/base.css +1 -1
  76. package/dist/styles/bottom-app-bar.css +2 -2
  77. package/dist/styles/bottom-sheet.css +2 -2
  78. package/dist/styles/button-group.css +1 -1
  79. package/dist/styles/button.css +1 -1
  80. package/dist/styles/card.css +2 -2
  81. package/dist/styles/carousel.css +1 -1
  82. package/dist/styles/checkbox.css +1 -1
  83. package/dist/styles/chips.css +2 -2
  84. package/dist/styles/contrast.css +1 -1
  85. package/dist/styles/datepicker.css +2 -2
  86. package/dist/styles/dialog.css +2 -2
  87. package/dist/styles/divider.css +1 -1
  88. package/dist/styles/drawer.css +2 -2
  89. package/dist/styles/extended-fab.css +2 -2
  90. package/dist/styles/fab-menu.css +2 -2
  91. package/dist/styles/fab.css +2 -2
  92. package/dist/styles/icon-button.css +1 -1
  93. package/dist/styles/list.css +2 -2
  94. package/dist/styles/loading-indicator.css +1 -1
  95. package/dist/styles/menu.css +2 -2
  96. package/dist/styles/navigation-bar.css +2 -2
  97. package/dist/styles/navigation-rail.css +2 -2
  98. package/dist/styles/progress.css +2 -2
  99. package/dist/styles/radios.css +1 -1
  100. package/dist/styles/search.css +1 -1
  101. package/dist/styles/select.css +1 -1
  102. package/dist/styles/side-sheet.css +2 -2
  103. package/dist/styles/slider.css +1 -1
  104. package/dist/styles/snackbar.css +2 -2
  105. package/dist/styles/split-button.css +2 -2
  106. package/dist/styles/switch.css +2 -2
  107. package/dist/styles/tabs.css +2 -2
  108. package/dist/styles/text-field.css +1 -1
  109. package/dist/styles/timepicker.css +1 -1
  110. package/dist/styles/toolbar.css +2 -2
  111. package/dist/styles/tooltip.css +1 -1
  112. package/dist/styles/top-app-bar.css +2 -2
  113. package/dist/styles/typography.css +1 -1
  114. package/dist/styles/utilities.css +1 -1
  115. package/dist/styles.css +2 -2
  116. package/dist/themes/autumn-contrast.css +1 -1
  117. package/dist/themes/autumn.css +1 -1
  118. package/dist/themes/baseline-contrast.css +1 -1
  119. package/dist/themes/baseline.css +1 -1
  120. package/dist/themes/brownbeige-contrast.css +1 -1
  121. package/dist/themes/brownbeige.css +1 -1
  122. package/dist/themes/content-contrast.css +1 -1
  123. package/dist/themes/content.css +1 -1
  124. package/dist/themes/desert-contrast.css +1 -1
  125. package/dist/themes/desert.css +1 -1
  126. package/dist/themes/expressive-contrast.css +1 -1
  127. package/dist/themes/expressive.css +1 -1
  128. package/dist/themes/fidelity-contrast.css +1 -1
  129. package/dist/themes/fidelity.css +1 -1
  130. package/dist/themes/forest-contrast.css +1 -1
  131. package/dist/themes/forest.css +1 -1
  132. package/dist/themes/fruit-salad-contrast.css +1 -1
  133. package/dist/themes/fruit-salad.css +1 -1
  134. package/dist/themes/highcontrast-contrast.css +1 -1
  135. package/dist/themes/highcontrast.css +1 -1
  136. package/dist/themes/monochrome-contrast.css +1 -1
  137. package/dist/themes/monochrome.css +1 -1
  138. package/dist/themes/neutral-contrast.css +1 -1
  139. package/dist/themes/neutral.css +1 -1
  140. package/dist/themes/ocean-contrast.css +1 -1
  141. package/dist/themes/ocean.css +1 -1
  142. package/dist/themes/rainbow-contrast.css +1 -1
  143. package/dist/themes/rainbow.css +1 -1
  144. package/dist/themes/sageivory-contrast.css +1 -1
  145. package/dist/themes/sageivory.css +1 -1
  146. package/dist/themes/spring-contrast.css +1 -1
  147. package/dist/themes/spring.css +1 -1
  148. package/dist/themes/summer-contrast.css +1 -1
  149. package/dist/themes/summer.css +1 -1
  150. package/dist/themes/sunset-contrast.css +1 -1
  151. package/dist/themes/sunset.css +1 -1
  152. package/dist/themes/tealcaramel-contrast.css +1 -1
  153. package/dist/themes/tealcaramel.css +1 -1
  154. package/dist/themes/vibrant-contrast.css +1 -1
  155. package/dist/themes/vibrant.css +1 -1
  156. package/package.json +5 -2
  157. package/src/styles/abstract/_direction.scss +196 -0
  158. package/src/styles/abstract/_emit.scss +20 -0
  159. package/src/styles/abstract/_mixins.scss +27 -10
  160. package/src/styles/components/_bottom-sheet.scss +4 -4
  161. package/src/styles/components/_chips.scss +23 -3
  162. package/src/styles/components/_datepicker.scss +65 -11
  163. package/src/styles/components/_extended-fab.scss +8 -12
  164. package/src/styles/components/_fab-menu.scss +20 -48
  165. package/src/styles/components/_fab.scss +8 -12
  166. package/src/styles/components/_list.scss +13 -4
  167. package/src/styles/components/_menu.scss +8 -8
  168. package/src/styles/components/_navigation-bar.scss +34 -7
  169. package/src/styles/components/_navigation-rail.scss +45 -12
  170. package/src/styles/components/_progress.scss +15 -1
  171. package/src/styles/components/_side-sheet.scss +30 -4
  172. package/src/styles/components/_snackbar.scss +6 -2
  173. package/src/styles/components/_split-button.scss +74 -36
  174. package/src/styles/components/_switch.scss +27 -7
  175. package/src/styles/components/_toolbar.scss +6 -2
  176. package/src/styles/abstract/_config.scss +0 -28
  177. package/src/styles/base/_foundation.scss +0 -4
  178. package/src/styles/base/_typescale.scss +0 -9
@@ -4,6 +4,7 @@
4
4
  @use "../abstract/functions" as f;
5
5
  @use "../abstract/mixins" as m;
6
6
  @use "../abstract/theme" as t;
7
+ @use "../abstract/direction" as d;
7
8
  @use "../abstract/chips/assist";
8
9
  @use "../abstract/chips/filter";
9
10
  @use "../abstract/chips/input";
@@ -83,10 +84,25 @@ $container: "#{base.$prefix}-chips";
83
84
  padding: 0;
84
85
  padding-inline-end: 8px;
85
86
  justify-content: flex-start;
86
- border-start-start-radius: 0;
87
- border-end-start-radius: 0;
87
+ // The start corners only, without the arm: one aggregated arm below
88
+ // carries the mirrored form for both subjects at a shorter prelude
89
+ // than a per-subject arm (abstract/_direction.scss).
90
+ @include d.corners($start-start: 0, $end-start: 0, $arm: false);
88
91
  color: var(--#{base.$prefix}-chip-trailing-color);
89
92
  }
93
+ // Both mirrored forms in one arm at one prelude, after the rule whose
94
+ // corners it replaces; `inherit` restates the chip's radius before the
95
+ // end corners are zeroed. The subject list is bare, so a mark on the chip
96
+ // root reaches it. Nothing of this in the logical form, where the two
97
+ // start corners mirror natively.
98
+ @include d.arms-at-root(
99
+ ":where(.#{base.$prefix}-rtl, :host(.#{base.$prefix}-rtl), [dir='rtl'])" +
100
+ " :is(.#{$component}__remove, .#{$component}__trailing-action):not(.#{base.$prefix}-ltr *)"
101
+ ) {
102
+ border-radius: inherit;
103
+ border-top-right-radius: 0;
104
+ border-bottom-right-radius: 0;
105
+ }
90
106
  // m3.material.io, Chips: "Secondary actions (such as a trailing icon button
91
107
  // for Remove) must have a 48x48dp interaction target that doesn't interfere
92
108
  // with the chip's primary action (such as Edit or Drag). To achieve this,
@@ -218,7 +234,11 @@ $container: "#{base.$prefix}-chips";
218
234
  content: "";
219
235
  position: absolute;
220
236
  top: 50%;
221
- inset-inline-end: 0;
237
+ // The 48dp box runs back from the chip's end edge, as on main. The
238
+ // end inset goes through the direction switch, so the artefacts a
239
+ // consumer bundler lowers carry the physical side with its arm and
240
+ // no :lang() lowering can drag the box off the end edge.
241
+ @include d.end(0);
222
242
  width: 48px;
223
243
  height: 48px;
224
244
  transform: translateY(-50%);
@@ -1,9 +1,17 @@
1
1
  // Compose DatePickerModalTokens: 360px container, 120px header, 40px date state
2
2
  // layers, 36x72px year state layers, extra-large corners and elevation level 3.
3
+ //
4
+ // The single-side insets go through the direction switch
5
+ // (abstract/_direction.scss): the runtime-adopted sheets keep the logical
6
+ // `inset-inline-*` properties, and the artefacts a consumer bundler lowers
7
+ // carry the physical side with a direction arm, so the bundler's :lang()
8
+ // lowering cannot pin the trigger, the docked calendar or the full-screen
9
+ // buttons to the left-to-right side of a right-to-left page.
3
10
  @use '../abstract/base' as base;
4
11
  @use '../abstract/variables' as v;
5
12
  @use '../abstract/mixins' as m;
6
13
  @use '../abstract/theme' as t;
14
+ @use '../abstract/direction' as d;
7
15
 
8
16
  $component: '#{base.$prefix}-datepicker';
9
17
  .#{$component} {
@@ -17,16 +25,30 @@ $component: '#{base.$prefix}-datepicker';
17
25
  contain: inline-size;
18
26
  contain-intrinsic-inline-size: v.text-field('width');
19
27
  color: t.color('on-surface');
20
- // The physical range band and chevron flip: left-to-right at the root,
21
- // right-to-left under the direction arm. The defaults are declared here, not
22
- // only used as var() fallbacks, because a custom property inherits: a nested
23
- // LTR instance must not read its RTL parent's values.
28
+ // The physical range band, its bleed into the grid padding, and the chevron
29
+ // flip: left-to-right at the root, right-to-left under the direction arm.
30
+ // The defaults are declared here, not only used as var() fallbacks, because
31
+ // a custom property inherits: a nested LTR instance must not read its RTL
32
+ // parent's values. The bleed is a value here rather than a physical-pair arm
33
+ // on the ::before: the arm's `:not(.mtrl-ltr *)` cut sits after the
34
+ // pseudo-element, which no combinator can match, so an LTR instance in an
35
+ // RTL page would take the RTL band. These are flipped values, not properties
36
+ // a bundler lowers, so the arm is written directly and rides in both forms
37
+ // (abstract/_mixins.scss rtl).
24
38
  --#{$component}-start: 100% 0;
25
39
  --#{$component}-end: 0 0;
40
+ --#{$component}-bleed-first-left: auto;
41
+ --#{$component}-bleed-first-right: 100%;
42
+ --#{$component}-bleed-last-left: 100%;
43
+ --#{$component}-bleed-last-right: auto;
26
44
  --#{$component}-flip: none;
27
45
  @include m.rtl($self: true) {
28
46
  --#{$component}-start: 0 0;
29
47
  --#{$component}-end: 100% 0;
48
+ --#{$component}-bleed-first-left: 100%;
49
+ --#{$component}-bleed-first-right: auto;
50
+ --#{$component}-bleed-last-left: auto;
51
+ --#{$component}-bleed-last-right: 100%;
30
52
  --#{$component}-flip: scaleX(-1);
31
53
  }
32
54
  &__anchor { display: flex; position: relative; }
@@ -44,7 +66,7 @@ $component: '#{base.$prefix}-datepicker';
44
66
  &__anchor &__input { padding-inline-end: 56px; }
45
67
  // Normalize only the icon controls; their own font-size rules follow.
46
68
  &__trigger, &__mode-toggle, &__prev, &__next, &__close { font: inherit; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
47
- &__trigger { position: absolute; inset-inline-end: 4px; top: 4px; font-size: 24px; }
69
+ &__trigger { position: absolute; top: 4px; @include d.end(4px); font-size: 24px; }
48
70
  &__help, &__error { @include m.typography('body-small'); padding: 4px 16px 0; }
49
71
  &__help { color: t.color('on-surface-variant'); }
50
72
  &__error { color: t.color('error'); &:empty { display: none; } }
@@ -65,8 +87,12 @@ $component: '#{base.$prefix}-datepicker';
65
87
  border-radius: 0; box-shadow: none; overflow: hidden;
66
88
  .#{$component}__content { height: 100%; }
67
89
  .#{$component}__modal-header { min-height: 128px; padding: 64px 64px 12px; }
68
- .#{$component}__close { position: absolute; inset-inline-start: 4px; top: 4px; }
69
- .#{$component}__save { position: absolute; inset-inline-end: 12px; top: 8px; }
90
+ .#{$component}__close {
91
+ position: absolute; top: 4px; @include d.start(4px);
92
+ }
93
+ .#{$component}__save {
94
+ position: absolute; top: 8px; @include d.end(12px);
95
+ }
70
96
  .#{$component}__title { margin-bottom: 0; }
71
97
  .#{$component}__headline { @include m.typography('title-large'); }
72
98
  .#{$component}__weekdays { padding: 0 var(--#{$component}-grid-inset); }
@@ -75,7 +101,9 @@ $component: '#{base.$prefix}-datepicker';
75
101
  &__subhead { @include m.typography('title-small'); color: t.color('on-surface-variant'); padding: 20px 24px 8px; }
76
102
  // Docked: corner-large (16dp), where the dialogs are extra-large.
77
103
  &--docked { border-radius: v.shape('large'); }
78
- &--docked { position: absolute; inset: auto; top: 100%; inset-inline-start: 0; margin: 8px 0 0; z-index: v.z-index('dropdown'); }
104
+ &--docked {
105
+ position: absolute; inset: auto; top: 100%; @include d.start(0); margin: 8px 0 0; z-index: v.z-index('dropdown');
106
+ }
79
107
  &__content { display: flex; flex-direction: column; }
80
108
  &__modal-header {
81
109
  position: relative; box-sizing: border-box; min-height: 120px; padding: 16px 64px 16px 24px;
@@ -85,7 +113,27 @@ $component: '#{base.$prefix}-datepicker';
85
113
  &--range &__headline { @include m.typography('title-large'); }
86
114
  &__title { @include m.typography('label-large'); margin-bottom: 20px; }
87
115
  &__headline { @include m.typography('headline-large'); overflow-wrap: anywhere; }
88
- &__mode-toggle { position: absolute; inset-inline-end: 12px; bottom: 12px; font-size: 24px; }
116
+ &__mode-toggle {
117
+ position: absolute; @include d.end(12px); bottom: 12px; font-size: 24px;
118
+ }
119
+ // The arms above need the marker on an ancestor, and dir="rtl" may sit on
120
+ // this root instead — the runtime resolves the factory root from the
121
+ // nearest [dir], itself included (src/core/dom/direction.ts). These
122
+ // root-carried forms follow the inline arms, as the split-button's do.
123
+ // Nothing of them in the logical form.
124
+ @include d.arms-self-only {
125
+ .#{$component}__trigger { right: auto; left: 4px; }
126
+ .#{$component}__mode-toggle { right: auto; left: 12px; }
127
+ }
128
+ &--fullscreen {
129
+ @include d.arms-self-only {
130
+ .#{$component}__close { left: auto; right: 4px; }
131
+ .#{$component}__save { right: auto; left: 12px; }
132
+ }
133
+ }
134
+ &--docked {
135
+ @include d.arms-self-only { left: auto; right: 0; }
136
+ }
89
137
  // The month and year row is 56dp, as in Compose (48dp buttons, 4dp above and below).
90
138
  &__header { display: flex; align-items: center; padding: 4px 12px; min-height: 56px; box-sizing: border-box; }
91
139
  &__navigation { display: flex; flex: 1; }
@@ -133,8 +181,14 @@ $component: '#{base.$prefix}-datepicker';
133
181
  content: ''; position: absolute; top: 4px; width: var(--#{$component}-grid-inset); height: 40px;
134
182
  background: t.color('secondary-container');
135
183
  }
136
- &__cell--range:last-child::before { inset-inline-start: 100%; }
137
- &__cell--range:first-child::before { inset-inline-end: 100%; }
184
+ &__cell--range:last-child::before {
185
+ left: var(--#{$component}-bleed-last-left);
186
+ right: var(--#{$component}-bleed-last-right);
187
+ }
188
+ &__cell--range:first-child::before {
189
+ left: var(--#{$component}-bleed-first-left);
190
+ right: var(--#{$component}-bleed-first-right);
191
+ }
138
192
  &__day {
139
193
  position: relative; isolation: isolate; width: 100%; height: 48px; padding: 0; border: 0;
140
194
  background: transparent; color: t.color('on-surface'); cursor: pointer;
@@ -5,6 +5,7 @@
5
5
  @use '../../styles/abstract/functions' as f;
6
6
  @use '../../styles/abstract/mixins' as m;
7
7
  @use '../../styles/abstract/theme' as t;
8
+ @use '../../styles/abstract/direction' as d;
8
9
 
9
10
  $component: '#{base.$prefix}-extended-fab';
10
11
 
@@ -260,24 +261,19 @@ $sizes: (
260
261
  }
261
262
 
262
263
  // A logical corner follows the inline direction; the physical aliases stay
263
- // put. Bundlers lower inset-inline-start into :lang() arms that miss
264
- // dir="rtl" (src/styles/components/_text-field.scss), about 1.5 kB of the
265
- // built full sheet per declaration, so the flip is written as direction
266
- // arms instead.
264
+ // put. The flip goes through the direction switch
265
+ // (abstract/_direction.scss): the bundler-lowered artefacts carry it as a
266
+ // direction arm, because a bundler lowers inset-inline-start into :lang()
267
+ // arms that miss dir="rtl" (src/styles/components/_text-field.scss), and
268
+ // the adopted sheets carry the logical property itself.
267
269
  &--top-start,
268
270
  &--bottom-start {
269
- @include m.rtl(true) {
270
- left: auto;
271
- right: 16px;
272
- }
271
+ @include d.flip-start(16px);
273
272
  }
274
273
 
275
274
  &--top-end,
276
275
  &--bottom-end {
277
- @include m.rtl(true) {
278
- right: auto;
279
- left: 16px;
280
- }
276
+ @include d.flip-end(16px);
281
277
  }
282
278
 
283
279
  // Animation effects
@@ -13,6 +13,7 @@
13
13
  @use '../../styles/abstract/variables' as v;
14
14
  @use '../../styles/abstract/functions' as f;
15
15
  @use '../../styles/abstract/mixins' as m;
16
+ @use '../../styles/abstract/direction' as d;
16
17
  @use '../../styles/abstract/theme' as t;
17
18
 
18
19
  $component: '#{base.$prefix}-fab-menu';
@@ -66,43 +67,21 @@ $swap: 48ms;
66
67
  }
67
68
 
68
69
  &--bottom-end {
69
- right: var(--#{$component}-margin);
70
-
71
- @include m.rtl(true) {
72
- left: var(--#{$component}-margin);
73
- right: auto;
74
- }
70
+ @include d.end(var(--#{$component}-margin), $self: true);
75
71
  }
76
72
 
77
73
  &--bottom-start {
78
- left: var(--#{$component}-margin);
79
-
80
- @include m.rtl(true) {
81
- right: var(--#{$component}-margin);
82
- left: auto;
83
- }
74
+ @include d.start(var(--#{$component}-margin), $self: true);
84
75
 
85
76
  // Into the page from this edge. The shared rules stay on the inline end,
86
77
  // which is where bottom-end and an unplaced menu already open.
87
78
  .#{$fab}.#{$component}__fab {
88
- left: 0;
89
- right: auto;
90
-
91
- @include m.rtl {
92
- right: 0;
93
- left: auto;
94
- }
79
+ @include d.sides(0, auto);
95
80
  }
96
81
 
97
82
  .#{$component}__list {
98
- left: 0;
99
- right: auto;
83
+ @include d.sides(0, auto);
100
84
  align-items: flex-start;
101
-
102
- @include m.rtl {
103
- right: 0;
104
- left: auto;
105
- }
106
85
  }
107
86
 
108
87
  .#{$component}__item {
@@ -115,7 +94,7 @@ $swap: 48ms;
115
94
  @at-root .#{$fab}.#{$component}__fab {
116
95
  position: absolute;
117
96
  inset-block-start: 0;
118
- right: 0;
97
+ @include d.end(0);
119
98
  display: grid;
120
99
  place-items: center;
121
100
  transition:
@@ -126,12 +105,6 @@ $swap: 48ms;
126
105
  color $spatial-colour,
127
106
  box-shadow $spatial;
128
107
 
129
- // The corner follows the inline direction; the rule above is its
130
- // left-to-right form.
131
- @include m.rtl {
132
- left: 0;
133
- right: auto;
134
- }
135
108
 
136
109
  > * {
137
110
  grid-area: 1 / 1;
@@ -182,7 +155,7 @@ $swap: 48ms;
182
155
  &__list {
183
156
  position: absolute;
184
157
  bottom: calc(100% + 8px);
185
- right: 0;
158
+ @include d.end(0);
186
159
  display: flex;
187
160
  flex-direction: column;
188
161
  align-items: flex-end;
@@ -192,27 +165,26 @@ $swap: 48ms;
192
165
  // Hidden once the last item has left
193
166
  transition: visibility 0s linear var(--#{$component}-exit, 0ms);
194
167
 
195
- // The end edge follows the inline direction.
196
- @include m.rtl {
197
- left: 0;
198
- right: auto;
199
- }
200
168
  }
201
169
 
202
170
  // The placement class and the direction marker are the same element here:
203
171
  // the runtime writes mtrl-rtl on this root, and dir="rtl" may sit on it. The
204
172
  // arms above need a strict ancestor; a root-carried [dir] does reach the
205
173
  // inner parts through the at-root attribute arm, but that arm sets the
206
- // left-to-right edges, so this root-carried form is written after it.
207
- &--bottom-start:where(.#{base.$prefix}-rtl, [dir='rtl']) {
208
- .#{$fab}.#{$component}__fab {
209
- right: 0;
210
- left: auto;
211
- }
174
+ // left-to-right edges, so this root-carried form is written after it
175
+ // (abstract/_direction.scss: nothing in the logical form, where the edges
176
+ // mirror natively).
177
+ &--bottom-start {
178
+ @include d.arms-self-only {
179
+ .#{$fab}.#{$component}__fab {
180
+ right: 0;
181
+ left: auto;
182
+ }
212
183
 
213
- .#{$component}__list {
214
- right: 0;
215
- left: auto;
184
+ .#{$component}__list {
185
+ right: 0;
186
+ left: auto;
187
+ }
216
188
  }
217
189
  }
218
190
 
@@ -4,6 +4,7 @@
4
4
  @use '../../styles/abstract/functions' as f;
5
5
  @use '../../styles/abstract/mixins' as m;
6
6
  @use '../../styles/abstract/theme' as t;
7
+ @use '../../styles/abstract/direction' as d;
7
8
 
8
9
  $component: '#{base.$prefix}-fab';
9
10
 
@@ -195,24 +196,19 @@ $component: '#{base.$prefix}-fab';
195
196
  }
196
197
 
197
198
  // A logical corner follows the inline direction; the physical aliases stay
198
- // put. Bundlers lower inset-inline-start into :lang() arms that miss
199
- // dir="rtl" (src/styles/components/_text-field.scss), about 1.5 kB of the
200
- // built full sheet per declaration, so the flip is written as direction
201
- // arms instead.
199
+ // put. The flip goes through the direction switch
200
+ // (abstract/_direction.scss): the bundler-lowered artefacts carry it as a
201
+ // direction arm, because a bundler lowers inset-inline-start into :lang()
202
+ // arms that miss dir="rtl" (src/styles/components/_text-field.scss), and
203
+ // the adopted sheets carry the logical property itself.
202
204
  &--top-start,
203
205
  &--bottom-start {
204
- @include m.rtl(true) {
205
- left: auto;
206
- right: 16px;
207
- }
206
+ @include d.flip-start(16px);
208
207
  }
209
208
 
210
209
  &--top-end,
211
210
  &--bottom-end {
212
- @include m.rtl(true) {
213
- right: auto;
214
- left: 16px;
215
- }
211
+ @include d.flip-end(16px);
216
212
  }
217
213
 
218
214
  // Optional animations
@@ -18,6 +18,7 @@
18
18
  @use "../abstract/mixins" as m;
19
19
  @use "../abstract/theme" as t;
20
20
  @use "../abstract/variables" as v;
21
+ @use "../abstract/direction" as d;
21
22
 
22
23
  $component: "#{base.$prefix}-list";
23
24
  .#{$component} {
@@ -104,15 +105,23 @@ $component: "#{base.$prefix}-list";
104
105
  > .#{$component}__item:not(.#{$component}__item--selected) { background: var(--#{base.$prefix}-list-item-container-color, t.color("surface-container")); }
105
106
  // Outer corners of a group (the first row, the last, and a row beside a
106
107
  // divider or subheader) are large. Specs, 2026-10-02: 4dp inner, 16dp outer.
108
+ // Block-edge pairs, which mirror to themselves: the arms form carries the
109
+ // physical pair with no direction arm.
107
110
  > .#{$component}__item:first-child,
108
111
  > :not(.#{$component}__item) + .#{$component}__item {
109
- border-start-start-radius: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large'));
110
- border-start-end-radius: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large'));
112
+ @include d.corners(
113
+ $start-start: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large')),
114
+ $start-end: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large')),
115
+ $arm: false
116
+ );
111
117
  }
112
118
  > .#{$component}__item:last-child,
113
119
  > .#{$component}__item:has(+ :not(.#{$component}__item)) {
114
- border-end-start-radius: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large'));
115
- border-end-end-radius: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large'));
120
+ @include d.corners(
121
+ $end-start: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large')),
122
+ $end-end: var(--#{base.$prefix}-list-item-shape-outer, v.shape('large')),
123
+ $arm: false
124
+ );
116
125
  }
117
126
  // Hover, keyboard focus, press and selection replace that shape on every
118
127
  // corner. Focus is the row's own action under :focus-visible, so a click
@@ -4,6 +4,7 @@
4
4
  @use "../../styles/abstract/mixins" as m;
5
5
  @use "../../styles/abstract/functions" as f;
6
6
  @use "../../styles/abstract/theme" as t;
7
+ @use "../../styles/abstract/direction" as d;
7
8
 
8
9
  $prefix: base.$prefix;
9
10
 
@@ -475,14 +476,14 @@ $component: "#{$prefix}-menu";
475
476
  }
476
477
 
477
478
  // The ends of the column round outwards, so the items read as one
479
+ // (a block edge, not an inline one: the pair mirrors to itself,
480
+ // so the arms form needs no direction arm).
478
481
  &:first-child {
479
- border-start-start-radius: v.shape("medium");
480
- border-start-end-radius: v.shape("medium");
482
+ @include d.corners($start-start: v.shape("medium"), $start-end: v.shape("medium"), $arm: false);
481
483
  }
482
484
 
483
485
  &:last-child {
484
- border-end-start-radius: v.shape("medium");
485
- border-end-end-radius: v.shape("medium");
486
+ @include d.corners($end-start: v.shape("medium"), $end-end: v.shape("medium"), $arm: false);
486
487
  }
487
488
 
488
489
  &--selected {
@@ -638,14 +639,13 @@ $component: "#{$prefix}-menu";
638
639
  // one menu that has been cut, rather than as loose cards.
639
640
  border-radius: v.shape("small");
640
641
 
642
+ // Block edges again: the pairs mirror to themselves
641
643
  &:first-child {
642
- border-start-start-radius: v.shape("large"); // ContainerShape
643
- border-start-end-radius: v.shape("large");
644
+ @include d.corners($start-start: v.shape("large"), $start-end: v.shape("large"), $arm: false); // ContainerShape
644
645
  }
645
646
 
646
647
  &:last-child {
647
- border-end-start-radius: v.shape("large");
648
- border-end-end-radius: v.shape("large");
648
+ @include d.corners($end-start: v.shape("large"), $end-end: v.shape("large"), $arm: false);
649
649
  }
650
650
 
651
651
  // its own margin would add to the list gap
@@ -2,6 +2,7 @@
2
2
  @use '../abstract/theme' as t;
3
3
  @use '../abstract/mixins' as m;
4
4
  @use '../abstract/variables' as v;
5
+ @use '../abstract/direction' as d;
5
6
 
6
7
  // M3 Expressive navigation bar: Compose ShortNavigationBar with
7
8
  // NavigationBarTokens, NavigationBarVerticalItemTokens and
@@ -29,10 +30,15 @@ $spatial: v.motion('spring-default-spatial-duration') v.motion('spring-default-s
29
30
  }
30
31
  .#{$component}__indicator {
31
32
  inset-block-start: 6px;
32
- inset-inline-start: 50%;
33
+ // A centred symmetric box mirrors to itself: physical sides and a physical
34
+ // centring margin place the pill at the item's centre in every direction,
35
+ // with no arm at all. The horizontal form the container query switches
36
+ // `--auto` to at 600px lives at (0,2,0); a direction arm here would carry
37
+ // the [dir] prelude's extra class and outrank it, collapsing the pill.
38
+ left: 50%;
39
+ margin-left: -28px;
33
40
  inline-size: 56px;
34
41
  block-size: 32px;
35
- margin-inline-start: -28px;
36
42
  }
37
43
  // The icon is centred in the indicator: 4dp below its top edge
38
44
  .#{$component}__icon { margin-block-start: 4px; }
@@ -40,11 +46,14 @@ $spatial: v.motion('spring-default-spatial-duration') v.motion('spring-default-s
40
46
  .#{$component}__label { margin-block-start: 8px; }
41
47
  .#{$component}__item--active .#{$component}__label { color: t.color('secondary'); }
42
48
  // BadgedBox: a large badge's start at the icon's end less 12dp, its bottom 14dp
43
- // below the icon's top; the dot 6dp in from the icon's end, at its top
49
+ // below the icon's top; the dot 6dp in from the icon's end, at its top. The
50
+ // dot is the badge's anchor shifted by its own property, so the direction
51
+ // arms mirror the badge alone. The mirrored form is aggregated after the
52
+ // component block (d.arms), naming both variants this mixin serves.
44
53
  .#{$component}__badge {
45
- inset-inline-start: 50%;
54
+ @include d.start(calc(50% + var(--#{$component}-dot-shift, 0px)), $arm: false);
46
55
  inset-block-start: 8px;
47
- &--dot { inset-inline-start: calc(50% + 6px); inset-block-start: 10px; }
56
+ &--dot { --#{$component}-dot-shift: 6px; inset-block-start: 10px; }
48
57
  }
49
58
  }
50
59
 
@@ -75,9 +84,15 @@ $spatial: v.motion('spring-default-spatial-duration') v.motion('spring-default-s
75
84
  // The horizontal item's active label takes the icon's colour (Compose, pending its own token)
76
85
  .#{$component}__item--active .#{$component}__label { color: t.color('on-secondary-container'); }
77
86
  .#{$component}__badge {
78
- inset-inline-start: 28px;
87
+ @include d.start(calc(28px + var(--#{$component}-dot-shift, 0px)), $arm: false);
79
88
  inset-block-start: 6px;
80
- &--dot { inset-inline-start: 34px; inset-block-start: 8px; }
89
+ &--dot { --#{$component}-dot-shift: 6px; inset-block-start: 8px; }
90
+ }
91
+
92
+ // As the vertical form's: the start side, mirrored by one arm that also
93
+ // matches the marker on the root carrying this variant.
94
+ @include d.arms($self: true) {
95
+ .#{$component}__badge { right: calc(28px + var(--#{$component}-dot-shift, 0px)); left: auto; }
81
96
  }
82
97
  }
83
98
 
@@ -231,6 +246,18 @@ $spatial: v.motion('spring-default-spatial-duration') v.motion('spring-default-s
231
246
  }
232
247
  }
233
248
 
249
+ // The vertical items' mirrored form — the badge and its dot; the pill needs
250
+ // no arm (see the mixin: it is centred by physical sides alone). The :is()
251
+ // names both variants the mixin serves, halving the preludes every shipped
252
+ // copy of the sheet pays, and the self arm still matches a marker on the root
253
+ // that carries the variant. Nothing in the logical form, where the badge's
254
+ // inset mirrors natively.
255
+ :is(.#{$component}--vertical, .#{$component}--auto) {
256
+ @include d.arms($self: true) {
257
+ .#{$component}__badge { right: calc(50% + var(--#{$component}-dot-shift, 0px)); left: auto; }
258
+ }
259
+ }
260
+
234
261
  // 'auto': horizontal items once the bar itself is 600px wide (MDC's medium threshold)
235
262
  @container #{$component} (min-width: 600px) {
236
263
  .#{$component}--auto { @include horizontal; }