From fd907a44dec874aa30cb415100fb7b4e1946b756 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Aug 2025 11:22:18 +0200 Subject: [PATCH 1/5] fix(material/button): allow touch target size to be customized Adds tokens to the various button appearances to allow the touch target to be customized. --- src/material/button/_button-base.scss | 10 +++++++--- src/material/button/_m2-button.scss | 6 ++++++ src/material/button/_m2-fab.scss | 3 +++ src/material/button/_m2-icon-button.scss | 1 + src/material/button/_m3-button.scss | 6 ++++++ src/material/button/_m3-fab.scss | 3 +++ src/material/button/_m3-icon-button.scss | 1 + src/material/button/button.scss | 10 +++++----- src/material/button/fab.scss | 5 +++-- src/material/button/icon-button.scss | 2 +- 10 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/material/button/_button-base.scss b/src/material/button/_button-base.scss index 6fb3787d304b..644e8b4af773 100644 --- a/src/material/button/_button-base.scss +++ b/src/material/button/_button-base.scss @@ -119,16 +119,20 @@ } } -@mixin mat-private-button-touch-target($is-square, $touch-target-display-token, $fallbacks) { +@mixin mat-private-button-touch-target( + $is-square, + $touch-target-size-token, + $touch-target-display-token, + $fallbacks) { .mat-mdc-button-touch-target { position: absolute; top: 50%; - height: 48px; + height: token-utils.slot($touch-target-size-token, $fallbacks); display: token-utils.slot($touch-target-display-token, $fallbacks); @if $is-square { left: 50%; - width: 48px; + width: token-utils.slot($touch-target-size-token, $fallbacks); transform: translate(-50%, -50%); } @else { left: 0; diff --git a/src/material/button/_m2-button.scss b/src/material/button/_m2-button.scss index af81b87e7785..5a792eb543b4 100644 --- a/src/material/button/_m2-button.scss +++ b/src/material/button/_m2-button.scss @@ -17,6 +17,7 @@ -3: 24px, ), $scale); $touch-target-display: if($scale < -1, none, block); + $touch-target-size: 48px; @return ( base: ( @@ -24,12 +25,14 @@ button-filled-horizontal-padding: 16px, button-filled-icon-offset: -4px, button-filled-icon-spacing: 8px, + button-filled-touch-target-size: $touch-target-size, button-outlined-container-shape: 4px, button-outlined-horizontal-padding: 15px, // Normally it's 16px, but -1px for the outline. button-outlined-icon-offset: -4px, button-outlined-icon-spacing: 8px, button-outlined-keep-touch-target: false, button-outlined-outline-width: 1px, + button-outlined-touch-target-size: $touch-target-size, button-protected-container-elevation-shadow: elevation.get-box-shadow(2), button-protected-container-shape: 4px, button-protected-disabled-container-elevation-shadow: elevation.get-box-shadow(0), @@ -39,15 +42,18 @@ button-protected-icon-offset: -4px, button-protected-icon-spacing: 8px, button-protected-pressed-container-elevation-shadow: elevation.get-box-shadow(8), + button-protected-touch-target-size: $touch-target-size, button-text-container-shape: 4px, button-text-horizontal-padding: 8px, button-text-icon-offset: 0, button-text-icon-spacing: 8px, button-text-with-icon-horizontal-padding: 8px, + button-text-touch-target-size: $touch-target-size, button-tonal-container-shape: 4px, button-tonal-horizontal-padding: 16px, button-tonal-icon-offset: -4px, button-tonal-icon-spacing: 8px, + button-tonal-touch-target-size: $touch-target-size, ), color: ( button-filled-container-color: map.get($system, surface), diff --git a/src/material/button/_m2-fab.scss b/src/material/button/_m2-fab.scss index a666c2a474e3..1653cf1f9c88 100644 --- a/src/material/button/_m2-fab.scss +++ b/src/material/button/_m2-fab.scss @@ -16,11 +16,13 @@ $disabled: m3-utils.color-with-opacity(map.get($system, on-surface), 38%); $disabled-container : m3-utils.color-with-opacity(map.get($system, on-surface), 12%); $density-scale: theming.clamp-density(map.get($system, density-scale), -3); + $touch-target-size: 48px; @return ( base: ( fab-container-elevation-shadow: elevation.get-box-shadow(6), fab-container-shape: 50%, + fab-touch-target-size: $touch-target-size, fab-extended-container-elevation-shadow: elevation.get-box-shadow(6), fab-extended-container-height: 48px, fab-extended-container-shape: 24px, @@ -32,6 +34,7 @@ fab-pressed-container-elevation-shadow: elevation.get-box-shadow(12), fab-small-container-elevation-shadow: elevation.get-box-shadow(6), fab-small-container-shape: 50%, + fab-small-touch-target-size: $touch-target-size, fab-small-focus-container-elevation-shadow: elevation.get-box-shadow(8), fab-small-hover-container-elevation-shadow: elevation.get-box-shadow(8), fab-small-pressed-container-elevation-shadow: elevation.get-box-shadow(12), diff --git a/src/material/button/_m2-icon-button.scss b/src/material/button/_m2-icon-button.scss index 0e0465c6eb34..22d6e3582846 100644 --- a/src/material/button/_m2-icon-button.scss +++ b/src/material/button/_m2-icon-button.scss @@ -11,6 +11,7 @@ base: ( icon-button-icon-size: 24px, icon-button-container-shape: 50%, + icon-button-touch-target-size: 48px, ), color: ( icon-button-disabled-icon-color: diff --git a/src/material/button/_m3-button.scss b/src/material/button/_m3-button.scss index a3dea1acc855..4d52673eb524 100644 --- a/src/material/button/_m3-button.scss +++ b/src/material/button/_m3-button.scss @@ -9,6 +9,7 @@ /// Generates custom tokens for the button. @function get-tokens($theme: m3.$sys-theme, $color-variant: null) { $system: m3-utils.get-system($theme); + $touch-target-size: 48px; @if $color-variant { $system: m3-utils.replace-colors-with-variant($system, primary, $color-variant); $system: m3-utils.replace-colors-with-variant($system, secondary, $color-variant); @@ -21,28 +22,33 @@ button-filled-icon-offset: -8px, button-filled-icon-spacing: 8px, button-filled-label-text-transform: null, + button-filled-touch-target-size: $touch-target-size, button-outlined-container-shape: map.get($system, corner-full), button-outlined-horizontal-padding: 24px, button-outlined-icon-offset: -8px, button-outlined-icon-spacing: 8px, button-outlined-outline-width: 1px, button-outlined-label-text-transform: null, + button-outlined-touch-target-size: $touch-target-size, button-protected-container-shape: map.get($system, corner-full), button-protected-horizontal-padding: 24px, button-protected-icon-offset: -8px, button-protected-icon-spacing: 8px, button-protected-label-text-transform: null, + button-protected-touch-target-size: $touch-target-size, button-text-container-shape: map.get($system, corner-full), button-text-horizontal-padding: 12px, button-text-icon-offset: -4px, button-text-icon-spacing: 8px, button-text-with-icon-horizontal-padding: 16px, button-text-label-text-transform: null, + button-text-touch-target-size: $touch-target-size, button-tonal-container-shape: map.get($system, corner-full), button-tonal-horizontal-padding: 24px, button-tonal-icon-offset: -8px, button-tonal-icon-spacing: 8px, button-tonal-label-text-transform: null, + button-tonal-touch-target-size: $touch-target-size, ), color: ( button-filled-container-color: map.get($system, primary), diff --git a/src/material/button/_m3-fab.scss b/src/material/button/_m3-fab.scss index 08f1f6b73668..29798b9071ed 100644 --- a/src/material/button/_m3-fab.scss +++ b/src/material/button/_m3-fab.scss @@ -8,6 +8,7 @@ /// Generates custom tokens for the mat-fab. @function get-tokens($theme: m3.$sys-theme, $color-variant: null) { $system: m3-utils.get-system($theme); + $touch-target-size: 48px; @if $color-variant { $system: m3-utils.replace-colors-with-variant($system, primary, $color-variant); } @@ -19,6 +20,8 @@ fab-extended-container-shape: map.get($system, corner-large), fab-small-container-shape: map.get($system, corner-medium), fab-touch-target-display: null, + fab-touch-target-size: $touch-target-size, + fab-small-touch-target-size: $touch-target-size, ), color: ( fab-container-color: map.get($system, primary-container), diff --git a/src/material/button/_m3-icon-button.scss b/src/material/button/_m3-icon-button.scss index 7767c2fb8d4c..5639ca2a227c 100644 --- a/src/material/button/_m3-icon-button.scss +++ b/src/material/button/_m3-icon-button.scss @@ -20,6 +20,7 @@ base: ( icon-button-icon-size: 24px, icon-button-container-shape: map.get($system, corner-full), + icon-button-touch-target-size: 48px, ), color: ( icon-button-disabled-icon-color: diff --git a/src/material/button/button.scss b/src/material/button/button.scss index d9c25a5a14f8..bda5d9394d2b 100644 --- a/src/material/button/button.scss +++ b/src/material/button/button.scss @@ -93,7 +93,7 @@ $fallbacks: m3-button.get-tokens(); button-text-hover-state-layer-opacity, button-text-focus-state-layer-opacity, button-text-pressed-state-layer-opacity, $fallbacks); @include button-base.mat-private-button-touch-target(false, - button-text-touch-target-display, $fallbacks); + button-text-touch-target-size, button-text-touch-target-display, $fallbacks); } .mat-mdc-unelevated-button { @@ -114,7 +114,7 @@ $fallbacks: m3-button.get-tokens(); button-filled-hover-state-layer-opacity, button-filled-focus-state-layer-opacity, button-filled-pressed-state-layer-opacity, $fallbacks); @include button-base.mat-private-button-touch-target(false, - button-filled-touch-target-display, $fallbacks); + button-filled-touch-target-size, button-filled-touch-target-display, $fallbacks); &:not(:disabled) { color: token-utils.slot(button-filled-label-text-color, $fallbacks); @@ -152,7 +152,7 @@ $fallbacks: m3-button.get-tokens(); button-protected-hover-state-layer-opacity, button-protected-focus-state-layer-opacity, button-protected-pressed-state-layer-opacity, $fallbacks); @include button-base.mat-private-button-touch-target(false, - button-protected-touch-target-display, $fallbacks); + button-protected-touch-target-size, button-protected-touch-target-display, $fallbacks); &:not(:disabled) { color: token-utils.slot(button-protected-label-text-color, $fallbacks); @@ -209,7 +209,7 @@ $fallbacks: m3-button.get-tokens(); button-outlined-hover-state-layer-opacity, button-outlined-focus-state-layer-opacity, button-outlined-pressed-state-layer-opacity, $fallbacks); @include button-base.mat-private-button-touch-target(false, - button-outlined-touch-target-display, $fallbacks); + button-outlined-touch-target-size, button-outlined-touch-target-display, $fallbacks); &:not(:disabled) { color: token-utils.slot(button-outlined-label-text-color, $fallbacks); @@ -258,7 +258,7 @@ $fallbacks: m3-button.get-tokens(); button-tonal-hover-state-layer-opacity, button-tonal-focus-state-layer-opacity, button-tonal-pressed-state-layer-opacity, $fallbacks); @include button-base.mat-private-button-touch-target(false, - button-tonal-touch-target-display, $fallbacks); + button-tonal-touch-target-size, button-tonal-touch-target-display, $fallbacks); } .mat-mdc-button, diff --git a/src/material/button/fab.scss b/src/material/button/fab.scss index a832883c62ba..72e0c8133db1 100644 --- a/src/material/button/fab.scss +++ b/src/material/button/fab.scss @@ -118,7 +118,8 @@ $fallbacks: m3-fab.get-tokens(); background-color: token-utils.slot(fab-disabled-state-container-color, $fallbacks); } - @include button-base.mat-private-button-touch-target(true, fab-touch-target-display, $fallbacks); + @include button-base.mat-private-button-touch-target(true, fab-touch-target-size, + fab-touch-target-display, $fallbacks); @include button-base.mat-private-button-ripple(fab-ripple-color, fab-state-layer-color, fab-disabled-state-layer-color, fab-hover-state-layer-opacity, fab-focus-state-layer-opacity, fab-pressed-state-layer-opacity, $fallbacks); @@ -150,7 +151,7 @@ $fallbacks: m3-fab.get-tokens(); } @include button-base.mat-private-button-touch-target(true, - fab-small-touch-target-display, $fallbacks); + fab-small-touch-target-size, fab-small-touch-target-display, $fallbacks); @include button-base.mat-private-button-ripple(fab-small-ripple-color, fab-small-state-layer-color, fab-small-disabled-state-layer-color, fab-small-hover-state-layer-opacity, diff --git a/src/material/button/icon-button.scss b/src/material/button/icon-button.scss index e49934bb8680..9bc5f212c2b0 100644 --- a/src/material/button/icon-button.scss +++ b/src/material/button/icon-button.scss @@ -53,7 +53,7 @@ $fallbacks: m3-icon-button.get-tokens(); icon-button-hover-state-layer-opacity, icon-button-focus-state-layer-opacity, icon-button-pressed-state-layer-opacity, $fallbacks); @include button-base.mat-private-button-touch-target(true, - icon-button-touch-target-display, $fallbacks); + icon-button-touch-target-size, icon-button-touch-target-display, $fallbacks); @include private.private-animation-noop(); @include button-base.mat-private-button-disabled { From cff701f8f8296dfd4b1227024382d7291917eb54 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Aug 2025 11:29:26 +0200 Subject: [PATCH 2/5] fix(material/checkbox): allow touch target size to be customized Adds a token that allows for the checkbox touch target to be customized. --- src/material/checkbox/_m2-checkbox.scss | 1 + src/material/checkbox/_m3-checkbox.scss | 1 + src/material/checkbox/checkbox.scss | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/material/checkbox/_m2-checkbox.scss b/src/material/checkbox/_m2-checkbox.scss index 92091e704141..18dd374d6f0d 100644 --- a/src/material/checkbox/_m2-checkbox.scss +++ b/src/material/checkbox/_m2-checkbox.scss @@ -17,6 +17,7 @@ checkbox-unselected-hover-state-layer-opacity: map.get($system, hover-state-layer-opacity), checkbox-unselected-pressed-state-layer-opacity: map.get($system, pressed-state-layer-opacity), + checkbox-touch-target-size: 48px, ), color: private-get-color-palette-color-tokens($theme, secondary), typography: ( diff --git a/src/material/checkbox/_m3-checkbox.scss b/src/material/checkbox/_m3-checkbox.scss index 9d5f4bbeb452..82a872148cf2 100644 --- a/src/material/checkbox/_m3-checkbox.scss +++ b/src/material/checkbox/_m3-checkbox.scss @@ -20,6 +20,7 @@ checkbox-unselected-hover-state-layer-opacity: map.get($system, hover-state-layer-opacity), checkbox-unselected-pressed-state-layer-opacity: map.get($system, pressed-state-layer-opacity), + checkbox-touch-target-size: 48px, ), color: ( checkbox-disabled-label-color: m3-utils.color-with-opacity(map.get($system, on-surface), 38%), diff --git a/src/material/checkbox/checkbox.scss b/src/material/checkbox/checkbox.scss index 220f823b21b1..a8f7a7105652 100644 --- a/src/material/checkbox/checkbox.scss +++ b/src/material/checkbox/checkbox.scss @@ -85,8 +85,8 @@ $fallbacks: m3-checkbox.get-tokens(); position: absolute; top: 50%; left: 50%; - height: 48px; - width: 48px; + height: token-utils.slot(checkbox-touch-target-size, $fallbacks); + width: token-utils.slot(checkbox-touch-target-size, $fallbacks); transform: translate(-50%, -50%); display: token-utils.slot(checkbox-touch-target-display, $fallbacks); } From f54c6d1267d4ac51c39b793eccac7d7cbe7b48de Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Aug 2025 11:31:13 +0200 Subject: [PATCH 3/5] fix(material/radio): allow touch target size to be customized Adds a token that allows for the radio button touch target to be customized. --- src/material/radio/_m2-radio.scss | 1 + src/material/radio/_m3-radio.scss | 1 + src/material/radio/radio.scss | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/material/radio/_m2-radio.scss b/src/material/radio/_m2-radio.scss index 807e55255f58..9e4ba08b2197 100644 --- a/src/material/radio/_m2-radio.scss +++ b/src/material/radio/_m2-radio.scss @@ -15,6 +15,7 @@ // determines the size of the radio button itself and there are internal // tests who don't configure the theme correctly. radio-state-layer-size: 40px, + radio-touch-target-size: 48px, ), color: private-get-color-palette-color-tokens($theme, secondary), typography: ( diff --git a/src/material/radio/_m3-radio.scss b/src/material/radio/_m3-radio.scss index ab4e3e607856..b133ecd23069 100644 --- a/src/material/radio/_m3-radio.scss +++ b/src/material/radio/_m3-radio.scss @@ -16,6 +16,7 @@ base: ( radio-disabled-unselected-icon-opacity: 0.38, radio-disabled-selected-icon-opacity: 0.38, + radio-touch-target-size: 48px, ), color: ( radio-checked-ripple-color: map.get($system, primary), diff --git a/src/material/radio/radio.scss b/src/material/radio/radio.scss index a13db4c9fee9..d92b39e23ba6 100644 --- a/src/material/radio/radio.scss +++ b/src/material/radio/radio.scss @@ -88,8 +88,8 @@ $fallbacks: m3-radio.get-tokens(); position: absolute; top: 50%; left: 50%; - height: 48px; - width: 48px; + height: token-utils.slot(radio-touch-target-size, $fallbacks); + width: token-utils.slot(radio-touch-target-size, $fallbacks); transform: translate(-50%, -50%); display: token-utils.slot(radio-touch-target-display, $fallbacks); From d3555798d18d835880f4a4cba44eb4708ed74671 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Aug 2025 11:32:52 +0200 Subject: [PATCH 4/5] fix(material/slide-toggle): allow touch target size to be customized Adds a token that allows for the slide toggle touch target to be customized. --- src/material/slide-toggle/_m2-slide-toggle.scss | 1 + src/material/slide-toggle/_m3-slide-toggle.scss | 1 + src/material/slide-toggle/slide-toggle.scss | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/material/slide-toggle/_m2-slide-toggle.scss b/src/material/slide-toggle/_m2-slide-toggle.scss index d7f8dc700732..2d8aa565c5db 100644 --- a/src/material/slide-toggle/_m2-slide-toggle.scss +++ b/src/material/slide-toggle/_m2-slide-toggle.scss @@ -60,6 +60,7 @@ slide-toggle-visible-track-opacity: 1, slide-toggle-visible-track-transition: transform 75ms 0ms cubic-bezier(0, 0, 0.2, 1), slide-toggle-with-icon-handle-size: 20px, + slide-toggle-touch-target-size: 48px, ), color: map.merge(private-get-color-palette-color-tokens($theme, primary), ( slide-toggle-disabled-label-text-color: diff --git a/src/material/slide-toggle/_m3-slide-toggle.scss b/src/material/slide-toggle/_m3-slide-toggle.scss index 4286dfdaf062..82133ebc26a3 100644 --- a/src/material/slide-toggle/_m3-slide-toggle.scss +++ b/src/material/slide-toggle/_m3-slide-toggle.scss @@ -60,6 +60,7 @@ slide-toggle-with-icon-handle-size: 24px, slide-toggle-handle-width: null, slide-toggle-handle-height: null, + slide-toggle-touch-target-size: 48px, ), color: ( slide-toggle-disabled-label-text-color: map.get($system, on-surface), diff --git a/src/material/slide-toggle/slide-toggle.scss b/src/material/slide-toggle/slide-toggle.scss index dd03b18b38e2..babf986b0f8d 100644 --- a/src/material/slide-toggle/slide-toggle.scss +++ b/src/material/slide-toggle/slide-toggle.scss @@ -548,7 +548,7 @@ $fallbacks: m3-slide-toggle.get-tokens(); position: absolute; top: 50%; left: 50%; - height: 48px; + height: token-utils.slot(slide-toggle-touch-target-size, $fallbacks); width: 100%; transform: translate(-50%, -50%); display: token-utils.slot(slide-toggle-touch-target-display, $fallbacks); From f26c03fe82ea38eccea9ce286d237fe3ec36626b Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 16 Aug 2025 09:20:05 +0200 Subject: [PATCH 5/5] fix(material/paginator): allow touch target size to be customized Adds a token that allows for the paginator touch target to be customized. Also adds a token to customize the width of the page size select. --- src/material/paginator/_m2-paginator.scss | 5 ++++- src/material/paginator/_m3-paginator.scss | 5 ++++- src/material/paginator/paginator.scss | 8 +++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/material/paginator/_m2-paginator.scss b/src/material/paginator/_m2-paginator.scss index 1223c3675f15..8af8916314d9 100644 --- a/src/material/paginator/_m2-paginator.scss +++ b/src/material/paginator/_m2-paginator.scss @@ -18,7 +18,10 @@ ), if($density-scale > -4, -4, $density-scale)); @return ( - base: (), + base: ( + paginator-page-size-select-width: 84px, + paginator-page-size-select-touch-target-height: 48px, + ), color: ( paginator-container-text-color: map.get($system, on-surface), paginator-container-background-color: map.get($system, surface), diff --git a/src/material/paginator/_m3-paginator.scss b/src/material/paginator/_m3-paginator.scss index 6d6660d5bd9a..d4a1a6b41e84 100644 --- a/src/material/paginator/_m3-paginator.scss +++ b/src/material/paginator/_m3-paginator.scss @@ -9,7 +9,10 @@ $system: m3-utils.get-system($theme); @return ( - base: (), + base: ( + paginator-page-size-select-width: 84px, + paginator-page-size-select-touch-target-height: 48px, + ), color: ( paginator-container-text-color: map.get($system, on-surface), paginator-container-background-color: map.get($system, surface), diff --git a/src/material/paginator/paginator.scss b/src/material/paginator/paginator.scss index 83777a5e4a61..0f7f17b6a89c 100644 --- a/src/material/paginator/paginator.scss +++ b/src/material/paginator/paginator.scss @@ -8,8 +8,6 @@ $page-size-margin-right: 8px; $items-per-page-label-margin: 0 4px; $selector-margin: 0 4px; -$selector-trigger-width: 84px; -$touch-target-height: 48px; $range-label-margin: 0 32px 0 24px; $button-icon-size: 28px; @@ -84,7 +82,7 @@ $fallbacks: m3-paginator.get-tokens(); .mat-mdc-paginator-page-size-select { margin: $selector-margin; - width: $selector-trigger-width; + width: token-utils.slot(paginator-page-size-select-width, $fallbacks); } .mat-mdc-paginator-range-label { @@ -131,8 +129,8 @@ $fallbacks: m3-paginator.get-tokens(); position: absolute; top: 50%; left: 50%; - width: $selector-trigger-width; - height: $touch-target-height; + width: token-utils.slot(paginator-page-size-select-width, $fallbacks); + height: token-utils.slot(paginator-page-size-select-touch-target-height, $fallbacks); background-color: transparent; transform: translate(-50%, -50%); cursor: pointer;