1+ @use ' sass:map' ;
12@use ' ./button-base' ;
23@use ' ../core/tokens/token-utils' ;
34@use ' ../core/style/private' as style-private ;
45@use ' ../core/style/vendor-prefixes' ;
56@use ' ../core/focus-indicators/private' as focus-indicators-private ;
7+ @use ' ../core/tokens/m3/md-sys-motion' as m3-motion ;
68@use ' ./m3-fab' ;
79
10+ $m3-motion-tokens : m3-motion .md-sys-motion-values ();
11+ $easing-emphasized : map .get ($m3-motion-tokens , easing-emphasized );
12+ $easing-emphasized-decelerate : map .get ($m3-motion-tokens , easing-emphasized-decelerate );
13+ $easing-emphasized-accelerate : map .get ($m3-motion-tokens , easing-emphasized-accelerate );
14+ $easing-standard : map .get ($m3-motion-tokens , easing-standard );
15+ $easing-standard-decelerate : map .get ($m3-motion-tokens , easing-standard-decelerate );
16+ $easing-standard-accelerate : map .get ($m3-motion-tokens , easing-standard-accelerate );
17+ $easing-legacy : map .get ($m3-motion-tokens , easing-legacy );
18+ $easing-legacy-decelerate : map .get ($m3-motion-tokens , easing-legacy-decelerate );
19+ $easing-legacy-accelerate : map .get ($m3-motion-tokens , easing-legacy-accelerate );
20+
821$fallbacks : m3-fab .get-tokens ();
922
1023.mat-mdc-fab-base {
@@ -24,8 +37,8 @@ $fallbacks: m3-fab.get-tokens();
2437 -moz-appearance : none ;
2538 -webkit-appearance : none ;
2639 overflow : visible ;
27- transition : box-shadow 280ms cubic-bezier ( 0.4 , 0 , 0.2 , 1 ) , opacity 15ms linear 30ms ,
28- transform 270ms 0ms cubic-bezier ( 0 , 0 , 0.2 , 1 ) ;
40+ transition : box-shadow 280ms $easing-legacy , opacity 15ms linear 30ms ,
41+ transform 270ms 0ms $easing-legacy-decelerate ;
2942 flex-shrink : 0 ; // Prevent the button from shrinking since it's always supposed to be a circle.
3043
3144 // Due to the shape of the FAB, inheriting the shape looks off. Disable it explicitly.
@@ -75,7 +88,7 @@ $fallbacks: m3-fab.get-tokens();
7588 // mixin will style the icons appropriately.
7689 // stylelint-disable-next-line selector-class-pattern
7790 .mat-icon , .material-icons {
78- transition : transform 180ms 90ms cubic-bezier ( 0 , 0 , 0.2 , 1 ) ;
91+ transition : transform 180ms 90ms $easing-legacy-decelerate ;
7992 fill : currentColor ;
8093 will-change : transform ;
8194 }
@@ -167,11 +180,8 @@ $fallbacks: m3-fab.get-tokens();
167180 // tokens it doesn't. We add it since it can cause tiny differences in
168181 // screenshot tests and it generally looks better.
169182 @include vendor-prefixes .smooth-font ();
170- padding-left : 20px ;
171- padding-right : 20px ;
183+
172184 width : auto ;
173- max-width : 100% ;
174- line-height : normal ;
175185 box-shadow : token-utils .slot (fab-extended-container-elevation-shadow , $fallbacks );
176186 height : token-utils .slot (fab-extended-container-height , $fallbacks );
177187 border-radius : token-utils .slot (fab-extended-container-shape , $fallbacks );
@@ -180,6 +190,20 @@ $fallbacks: m3-fab.get-tokens();
180190 font-weight : token-utils .slot (fab-extended-label-text-weight , $fallbacks );
181191 letter-spacing : token-utils .slot (fab-extended-label-text-tracking , $fallbacks );
182192
193+ // Outer container padding: expanded state (asymmetrical layout)
194+ // LTR: 16px at start (close to icon), 20px at end (close to text)
195+ padding-left : 16px ;
196+ padding-right : 20px ;
197+ transition : padding 270ms $easing-standard ,
198+ margin 270ms $easing-standard ,
199+ border-radius 270ms $easing-standard ;
200+
201+ // RTL: start moves to right (16px), end moves to left (20px)
202+ [dir = ' rtl' ] & {
203+ padding-left : 20px ;
204+ padding-right : 16px ;
205+ }
206+
183207 @media (hover : hover) {
184208 & :hover {
185209 box-shadow : token-utils .slot (fab-extended-hover-container-elevation-shadow , $fallbacks );
@@ -203,22 +227,74 @@ $fallbacks: m3-fab.get-tokens();
203227
204228 // stylelint-disable selector-class-pattern
205229 // For Extended FAB with text label followed by icon.
206- // We are checking for the a button class because white this is a FAB it
230+ // We are checking for the a button class because while this is a FAB it
207231 // uses the same template as button.
208- [dir = ' rtl' ] & .mdc-button__label + .mat-icon ,
209- [dir = ' rtl' ] & .mdc-button__label + .material-icons ,
210- > .mat-icon ,
211- > .material-icons {
212- margin-left : -8px ;
232+ .mdc-button__label {
233+ white-space : nowrap ;
234+ opacity : 1 ;
235+ display : inline-grid ;
236+ // Explicitly break the auto constraint, allowing tracks to be squeezed to 0
237+ // to achieve "label width" set to 0.
238+ grid-template-columns : minmax (0 , 1fr );
239+ overflow : hidden ;
240+
241+ transition : opacity 150ms 100ms linear ,
242+ grid-template-columns 270ms $easing-standard ;
243+ }
244+
245+ // Icon visually positioned on the LEFT (first icon in LTR, or last icon in RTL)
246+ // No margin on the left, rely on parent container padding to create space.
247+ // Use 12px margin on the right to push text.
248+ [dir = ' rtl' ] & .mdc-button__label + .mat-icon ,
249+ [dir = ' rtl' ] & .mdc-button__label + .material-icons ,
250+ > .mat-icon ,
251+ > .material-icons {
252+ margin-left : 0 ;
213253 margin-right : 12px ;
214- }
254+ transition : margin 270ms $easing-standard ;
255+ }
215256
216- .mdc-button__label + .mat-icon ,
217- .mdc-button__label + .material-icons ,
218- [dir = ' rtl' ] & > .mat-icon ,
219- [dir = ' rtl' ] & > .material-icons {
257+ // Icon visually positioned on the RIGHT (last icon in LTR, or first icon in RTL)
258+ // 12px margin on the left to push text; no margin on the right,
259+ // rely on parent container padding.
260+ .mdc-button__label + .mat-icon ,
261+ .mdc-button__label + .material-icons ,
262+ [dir = ' rtl' ] & > .mat-icon ,
263+ [dir = ' rtl' ] & > .material-icons {
220264 margin-left : 12px ;
221- margin-right : -8px ;
265+ margin-right : 0 ;
266+ transition : margin 270ms $easing-standard ;
267+ }
268+
269+ & .mat-mdc-extended-fab-collapsed {
270+ // Force symmetric 16px padding.
271+ // 16px (spacing) + 24px (icon) + 16px (spacing) = 56px perfect circle.
272+ padding-left : 16px ;
273+ padding-right : 16px ;
274+
275+ [dir = ' rtl' ] & {
276+ padding-left : 16px ;
277+ padding-right : 16px ;
278+ }
279+
280+ // Collapse text and make it transparent when collapsed
281+ .mdc-button__label {
282+ grid-template-columns : minmax (0 , 0fr );
283+
284+ opacity : 0 ;
285+ transition : grid-template-columns 270ms $easing-standard ,
286+ opacity 100ms 0ms linear ;
287+ }
288+
289+ // When collapsed, remove all extra icon margins to allow flexbox to center it perfectly
290+ > .mat-icon ,
291+ > .material-icons ,
292+ .mdc-button__label + .mat-icon ,
293+ .mdc-button__label + .material-icons {
294+ margin-left : 0 ;
295+ margin-right : 0 ;
296+ transition : margin 270ms $easing-standard ;
297+ }
222298 }
223299 // stylelint-enable selector-class-pattern
224300
0 commit comments