From 8dc4e1c69328c3639059fcfb85e73d55621df4db Mon Sep 17 00:00:00 2001 From: Andrew Seguin Date: Thu, 14 Aug 2025 09:33:42 -0600 Subject: [PATCH] feat(material/core): add experimental utility classes --- src/dev-app/BUILD.bazel | 1 + src/dev-app/dev-app/dev-app-layout.ts | 1 + src/dev-app/routes.ts | 5 + src/dev-app/theme-m3.scss | 2 + src/dev-app/theme.scss | 1 + src/dev-app/utility-classes-demo/BUILD.bazel | 20 + .../utility-classes-demo.html | 93 ++++ .../utility-classes-demo.scss | 18 + .../utility-classes-demo.ts | 9 + src/material/BUILD.bazel | 1 + src/material/_index.scss | 1 + src/material/core/tokens/BUILD.bazel | 5 + src/material/core/tokens/_classes.scss | 415 ++++++++++++++++++ 13 files changed, 572 insertions(+) create mode 100644 src/dev-app/utility-classes-demo/BUILD.bazel create mode 100644 src/dev-app/utility-classes-demo/utility-classes-demo.html create mode 100644 src/dev-app/utility-classes-demo/utility-classes-demo.scss create mode 100644 src/dev-app/utility-classes-demo/utility-classes-demo.ts create mode 100644 src/material/core/tokens/_classes.scss diff --git a/src/dev-app/BUILD.bazel b/src/dev-app/BUILD.bazel index ce195230e55d..07966decf366 100644 --- a/src/dev-app/BUILD.bazel +++ b/src/dev-app/BUILD.bazel @@ -94,6 +94,7 @@ ng_project( "//src/dev-app/tooltip", "//src/dev-app/tree", "//src/dev-app/typography", + "//src/dev-app/utility-classes-demo", "//src/dev-app/virtual-scroll", "//src/dev-app/youtube-player", "//src/material/core", diff --git a/src/dev-app/dev-app/dev-app-layout.ts b/src/dev-app/dev-app/dev-app-layout.ts index e48ec9c21309..983249d48d4c 100644 --- a/src/dev-app/dev-app/dev-app-layout.ts +++ b/src/dev-app/dev-app/dev-app-layout.ts @@ -122,6 +122,7 @@ export class DevAppLayout { {name: 'Tooltip', route: '/tooltip'}, {name: 'Tree', route: '/tree'}, {name: 'Typography', route: '/typography'}, + {name: 'Utility', route: '/utility'}, {name: 'Virtual Scrolling', route: '/virtual-scroll'}, {name: 'YouTube Player', route: '/youtube-player'}, ]; diff --git a/src/dev-app/routes.ts b/src/dev-app/routes.ts index 298a14cb59ba..161d22e63022 100644 --- a/src/dev-app/routes.ts +++ b/src/dev-app/routes.ts @@ -302,6 +302,11 @@ export const DEV_APP_ROUTES: Routes = [ loadComponent: () => import('./youtube-player/youtube-player-demo').then(m => m.YouTubePlayerDemo), }, + { + path: 'utility', + loadComponent: () => + import('./utility-classes-demo/utility-classes-demo').then(m => m.UtilityClassesDemo), + }, { path: 'selection', loadComponent: () => import('./selection/selection-demo').then(m => m.SelectionDemo), diff --git a/src/dev-app/theme-m3.scss b/src/dev-app/theme-m3.scss index 7beab1a85606..f475852ae3d5 100644 --- a/src/dev-app/theme-m3.scss +++ b/src/dev-app/theme-m3.scss @@ -52,6 +52,8 @@ html { density: 0, )); } + + @include mat.utility-classes(); } @include mat.typography-hierarchy($light-theme); diff --git a/src/dev-app/theme.scss b/src/dev-app/theme.scss index 0d3eace594b7..7e63395ad3fe 100644 --- a/src/dev-app/theme.scss +++ b/src/dev-app/theme.scss @@ -24,6 +24,7 @@ $candy-app-theme: mat.m2-define-light-theme(( @include mat.app-background(); @include mat.elevation-classes(); @include mat.m2-theme($candy-app-theme); +@include mat.utility-classes(); // Include the default theme styles. @include mat.all-component-themes($candy-app-theme); diff --git a/src/dev-app/utility-classes-demo/BUILD.bazel b/src/dev-app/utility-classes-demo/BUILD.bazel new file mode 100644 index 000000000000..d9343fe18b9c --- /dev/null +++ b/src/dev-app/utility-classes-demo/BUILD.bazel @@ -0,0 +1,20 @@ +load("//tools:defaults.bzl", "ng_project", "sass_binary") + +package(default_visibility = ["//visibility:public"]) + +ng_project( + name = "utility-classes-demo", + srcs = glob(["**/*.ts"]), + assets = [ + "utility-classes-demo.html", + ":utility_classes_demo_scss", + ], + deps = [ + "//:node_modules/@angular/core", + ], +) + +sass_binary( + name = "utility_classes_demo_scss", + src = "utility-classes-demo.scss", +) diff --git a/src/dev-app/utility-classes-demo/utility-classes-demo.html b/src/dev-app/utility-classes-demo/utility-classes-demo.html new file mode 100644 index 000000000000..dbdf7089882c --- /dev/null +++ b/src/dev-app/utility-classes-demo/utility-classes-demo.html @@ -0,0 +1,93 @@ +

Utility Classes

+ +
+

Color

+
+
mat-bg-primary
+
mat-bg-primary-container
+
mat-bg-secondary
+
mat-bg-secondary-container
+
mat-bg-error
+
mat-bg-error-container
+
mat-bg-surface
+
mat-bg-surface-variant
+
mat-bg-surface-container-highest
+
mat-bg-surface-container-high
+
mat-bg-surface-container
+
mat-bg-surface-container-low
+
mat-bg-surface-container-lowest
+
mat-bg-inverse-surface
+
mat-bg-disabled
+
+
+ +
+

Text Color

+
+
mat-text-primary
+
mat-text-secondary
+
mat-text-error
+
mat-text-disabled
+
+
+ +
+

Typography

+
+
Display Large
+
Display Medium
+
Display Small
+
Headline Large
+
Headline Medium
+
Headline Small
+
Title Large
+
Title Medium
+
Title Small
+
Body Large
+
Body Medium
+
Body Small
+
+
+ +
+

Shape

+
+
+ mat-corner-extra-small +
+
mat-corner-small
+
mat-corner-medium
+
mat-corner-large
+
+ mat-corner-extra-large +
+
mat-corner-full
+
+
+ +
+

Elevation

+
+
mat-shadow-level-1
+
mat-shadow-level-2
+
mat-shadow-level-3
+
mat-shadow-level-4
+
mat-shadow-level-5
+
+
+ +
+

Outline

+
+
mat-outline
+
mat-outline-variant
+
+
+ +
+

Stateful

+
+
mat-stateful
+
mat-stateful-primary
+
+
diff --git a/src/dev-app/utility-classes-demo/utility-classes-demo.scss b/src/dev-app/utility-classes-demo/utility-classes-demo.scss new file mode 100644 index 000000000000..8aef04fe6e8c --- /dev/null +++ b/src/dev-app/utility-classes-demo/utility-classes-demo.scss @@ -0,0 +1,18 @@ +.demo-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 16px; +} + +.demo-box { + padding: 16px; + display: flex; + align-items: center; + justify-content: center; + min-height: 100px; + text-align: center; +} + +.demo-typography > div { + margin-bottom: 16px; +} diff --git a/src/dev-app/utility-classes-demo/utility-classes-demo.ts b/src/dev-app/utility-classes-demo/utility-classes-demo.ts new file mode 100644 index 000000000000..761375caa71e --- /dev/null +++ b/src/dev-app/utility-classes-demo/utility-classes-demo.ts @@ -0,0 +1,9 @@ +import {ChangeDetectionStrategy, Component} from '@angular/core'; + +@Component({ + selector: 'app-utility-classes-demo', + templateUrl: './utility-classes-demo.html', + styleUrls: ['./utility-classes-demo.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UtilityClassesDemo {} diff --git a/src/material/BUILD.bazel b/src/material/BUILD.bazel index ce9d523f61c3..4640784669a3 100644 --- a/src/material/BUILD.bazel +++ b/src/material/BUILD.bazel @@ -65,6 +65,7 @@ sass_library( "//src/material/core/theming:_inspection", "//src/material/core/theming:_palettes", "//src/material/core/theming:core_all_theme", + "//src/material/core/tokens:classes", "//src/material/core/tokens:system", "//src/material/core/typography", "//src/material/core/typography:all_typography", diff --git a/src/material/_index.scss b/src/material/_index.scss index d26ade0240c4..b4c205d4cce5 100644 --- a/src/material/_index.scss +++ b/src/material/_index.scss @@ -20,6 +20,7 @@ @forward 'core/tokens/system' show system-level-colors, system-level-typography, system-level-elevation, system-level-shape, system-level-motion, system-level-state, theme, theme-overrides, m2-theme; +@forward 'core/tokens/classes' show utility-classes; // Private/Internal @forward './core/density/private/all-density' show all-component-densities; diff --git a/src/material/core/tokens/BUILD.bazel b/src/material/core/tokens/BUILD.bazel index b48c4a61bc1f..a9ef6c339235 100644 --- a/src/material/core/tokens/BUILD.bazel +++ b/src/material/core/tokens/BUILD.bazel @@ -86,3 +86,8 @@ sass_library( "//src/material/tree:m3", ], ) + +sass_library( + name = "classes", + srcs = ["_classes.scss"], +) diff --git a/src/material/core/tokens/_classes.scss b/src/material/core/tokens/_classes.scss new file mode 100644 index 000000000000..f7207271b02b --- /dev/null +++ b/src/material/core/tokens/_classes.scss @@ -0,0 +1,415 @@ +// Utility classes that can be used to style elements with the most commonly used tokens in +// Material Design. Includes color, typography, elevation, and shape. +// This API is experimental and may not release. Implemented for prototyping and testing. +// Use caution depending on this. +@mixin utility-classes() { + + // *********************************************************************************************** + // Background + // *********************************************************************************************** + + // Styles an element with a primary color background and applies an accessible contrasting + // color for text and icons. Use for key components across the UI, such as buttons that + // have greater importance on the page. In Angular Material, this is used for the selected + // date in a datepicker, the handle of a slider, and the background of a checkbox. + .mat-bg-primary { + background-color: var(--mat-sys-primary); + color: var(--mat-sys-on-primary); + } + + // Styles an element with a primary container color background and applies an accessible + // contrasting color for text and icons. Use for filling components that should stand out + // on a surface. In Angular Material, this is used for + // the container of a floating action button. + .mat-bg-primary-container { + background-color: var(--mat-sys-primary-container); + color: var(--mat-sys-on-primary-container); + } + + // Styles an element with a secondary color background and applies an accessible contrasting + // color for text and icons. Use for less prominent components in the UI that have a different + // color scheme than the primary. + .mat-bg-secondary { + background-color: var(--mat-sys-secondary); + color: var(--mat-sys-on-secondary); + } + + // Styles an element with a secondary container color background and applies an accessible + // contrasting color for text and icons. Use for components that need less emphasis than + // secondary, such as filter chips. In Angular Material, this is used for selected items + // in a list and the container of a tonal button. + .mat-bg-secondary-container { + background-color: var(--mat-sys-secondary-container); + color: var(--mat-sys-on-secondary-container); + } + + // Styles an element with an error color background and applies an accessible contrasting + // color for text and icons. Use for indicating an error state, such as an invalid text field, or + // for the background of an important notification. In Angular Material, this is used for the + // background of a badge. + .mat-bg-error { + background-color: var(--mat-sys-error); + color: var(--mat-sys-on-error); + } + + // Styles an element with an error container color background and applies an accessible + // contrasting color for text and icons. Use for components that need less emphasis than + // error, such as a container for error text. + .mat-bg-error-container { + background-color: var(--mat-sys-error-container); + color: var(--mat-sys-on-error-container); + } + + // Styles an element with a surface color background and applies an accessible contrasting + // color for text and icons. Use for general surfaces of components. In Angular Material, this is + // used for the background of many components, like tables, dialogs, menus, and toolbars. + .mat-bg-surface { + background-color: var(--mat-sys-surface); + color: var(--mat-sys-on-surface); + } + + // Styles an element with a surface variant color background and applies an accessible + // contrasting color for text and icons. Use for surfaces that need to stand out from the + // main surface color. In Angular Material, this is used for the background of a filled + // form field and the track of a progress bar. + .mat-bg-surface-variant { + background-color: var(--mat-sys-surface-variant); + color: var(--mat-sys-on-surface-variant); + } + + // Styles an element with the "highest" surface container color background and applies an + // accessible contrasting color for text and icons. Use for surfaces that need the most + // emphasis against the main surface color. In Angular Material, this is used for the + // background of a filled card. + .mat-bg-surface-container-highest { + background-color: var(--mat-sys-surface-container-highest); + color: var(--mat-sys-on-surface); + } + + // Styles an element with a "high" surface container color background and applies an accessible + // contrasting color for text and icons. Use for surfaces that need more emphasis against + // the main surface color. In Angular Material, this is used for the background of a datepicker. + .mat-bg-surface-container-high { + background-color: var(--mat-sys-surface-container-high); + color: var(--mat-sys-on-surface); + } + + // Styles an element with a surface container color background and applies an accessible + // contrasting color for text and icons. Use for surfaces that need to stand out from the + // main surface color. In Angular Material, this is used for the background of a menu. + .mat-bg-surface-container { + background-color: var(--mat-sys-surface-container); + color: var(--mat-sys-on-surface); + } + + // Styles an element with a "low" surface container color background and applies an accessible + // contrasting color for text and icons. Use for surfaces that need less emphasis against + // the main surface color. In Angular Material, this is used for the background of a bottom sheet. + .mat-bg-surface-container-low { + background-color: var(--mat-sys-surface-container-low); + color: var(--mat-sys-on-surface); + } + + // Styles an element with the "lowest" surface container color background and applies an + // accessible contrasting color for text and icons. Use for surfaces that need the least + // emphasis against the main surface color. + .mat-bg-surface-container-lowest { + background-color: var(--mat-sys-surface-container-lowest); + color: var(--mat-sys-on-surface); + } + + // Styles an element with an inverse surface color background and applies an accessible + // contrasting color for text and icons. Use for making elements stand out against the + // default color scheme. Good for temporary notifications. In Angular Material, this is used for + // the background of a snackbar and a tooltip. + .mat-bg-inverse-surface { + background-color: var(--mat-sys-inverse-surface); + color: var(--mat-sys-inverse-on-surface); + } + + // Styles an element with a disabled color background and applies an accessible contrasting + // color for text and icons. Use for disabled components. In Angular Material, this is used + // for components generally filled with the primary color but are currently disabled. + .mat-bg-disabled { + color: color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent); + background-color: color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent); + } + + + // *********************************************************************************************** + // Text + // *********************************************************************************************** + + // Styles the text of an element with the primary color. Use for text that needs to stand out. + // In Angular Material, this is used for the text of a text button and the selected tab label. + .mat-text-primary { + color: var(--mat-sys-primary); + } + + // Styles the text of an element with the secondary color. Use for text that needs less emphasis + // than primary text. + .mat-text-secondary { + color: var(--mat-sys-secondary); + } + + // Styles the text of an element with the error color. Use for text that indicates an error, such + // as validation messages. In Angular Material, this is used for the error text in a form field. + .mat-text-error { + color: var(--mat-sys-error); + } + + // Styles the text of an element with the disabled color. Use for text in disabled components. + // In Angular Material this is used to show text is disabled, generally on a "surface" background. + .mat-text-disabled { + color: color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent); + } + + + // *********************************************************************************************** + // Font + // *********************************************************************************************** + + // Sets the font to the body small typeface. Use for small body text, such as captions. In Angular + // Material, this is used for the subscript text in a form field and the text in a paginator. + .mat-font-body-small { + font: var(--mat-sys-body-small); + letter-spacing: var(--mat-sys-body-small-tracking); + } + + // Sets the font to the body medium typeface. Use for medium body text, this is the default + // body font. In Angular Material, this is used for the text in a table row and the supporting + // text in a dialog. + .mat-font-body-medium { + font: var(--mat-sys-body-medium); + letter-spacing: var(--mat-sys-body-medium-tracking); + } + + // Sets the font to the body large typeface. Use for large body text, such as an introductory + // paragraph. In Angular Material, this is used for the text in a list item and the text in a + // select trigger. + .mat-font-body-large { + font: var(--mat-sys-body-large); + letter-spacing: var(--mat-sys-body-large-tracking); + } + + // Sets the font to the display small typeface. Use for small display text, such as a date. + .mat-font-display-small { + font: var(--mat-sys-display-small); + letter-spacing: var(--mat-sys-display-small-tracking); + } + + // Sets the font to the display medium typeface. Use for medium display text, such as a hero + // title. + .mat-font-display-medium { + font: var(--mat-sys-display-medium); + letter-spacing: var(--mat-sys-display-medium-tracking); + } + + // Sets the font to the display large typeface. Use for large display text, such as a hero title. + .mat-font-display-large { + font: var(--mat-sys-display-large); + letter-spacing: var(--mat-sys-display-large-tracking); + } + + // Sets the font to the headline small typeface. Use for small headlines, such as a page title. In + // Angular Material, this is used for the headline in a dialog. + .mat-font-headline-small { + font: var(--mat-sys-headline-small); + letter-spacing: var(--mat-sys-headline-small-tracking); + } + + // Sets the font to the headline medium typeface. Use for medium headlines, such as a section + // title. + .mat-font-headline-medium { + font: var(--mat-sys-headline-medium); + letter-spacing: var(--mat-sys-headline-medium-tracking); + } + + // Sets the font to the headline large typeface. Use for large headlines, such as a page title on + // a large screen. + .mat-font-headline-large { + font: var(--mat-sys-headline-large); + letter-spacing: var(--mat-sys-headline-large-tracking); + } + + // Sets the font to the title small typeface. Use for small titles, such as a card title. In + // Angular Material, this is used for the header of a table and the label of an option group. + .mat-font-title-small { + font: var(--mat-sys-title-small); + letter-spacing: var(--mat-sys-title-small-tracking); + } + + // Sets the font to the title medium typeface. Use for medium titles, such as a dialog title + // or the primary text in a list item. In Angular Material, this is used for the subtitle + // of a card and the header of an expansion panel. + .mat-font-title-medium { + font: var(--mat-sys-title-medium); + letter-spacing: var(--mat-sys-title-medium-tracking); + } + + // Sets the font to the title large typeface. Use for large titles, such as a page title on a + // small screen. In Angular Material, this is used for the title of a card and the title of a + // toolbar. + .mat-font-title-large { + font: var(--mat-sys-title-large); + letter-spacing: var(--mat-sys-title-large-tracking); + } + + + // *********************************************************************************************** + // Corner + // *********************************************************************************************** + + // Sets the border radius to extra small. Use for components that need a small amount of rounding, + // such as a chip. In Angular Material, this is used for the shape of a snackbar and a tooltip. + .mat-corner-extra-small { + border-radius: var(--mat-sys-corner-extra-small); + } + + // Sets the border radius to small. Use for components that need a small amount of rounding, such + // as a text field. + .mat-corner-small { + border-radius: var(--mat-sys-corner-small); + } + + // Sets the border radius to medium. Use for components that need a medium amount of rounding, + // such as a button. In Angular Material, this is used for the shape of a card. + .mat-corner-medium { + border-radius: var(--mat-sys-corner-medium); + } + + // Sets the border radius to large. Use for components that need a large amount of rounding, such + // as a card. In Angular Material, this is used for the shape of a floating action button and a + // datepicker. + .mat-corner-large { + border-radius: var(--mat-sys-corner-large); + } + + // Sets the border radius to extra large. Use for components that need a large amount of + // rounding. In Angular Material, this is used for the shape of a button toggle and the shape of + // a dialog. + .mat-corner-extra-large { + border-radius: var(--mat-sys-corner-extra-large); + } + + // Sets the border radius to full. Use for components that are circular, such as a user avatar. + // In Angular Material, this is used for the shape of a badge and the shape of a button. + .mat-corner-full { + border-radius: var(--mat-sys-corner-full); + } + + + // *********************************************************************************************** + // Border + // *********************************************************************************************** + + // Adds an outline to an element. Use for components that need a visible boundary. In Angular + // Material, this is used for the outline of an outlined button. + .mat-outline { + border: 1px solid var(--mat-sys-outline); + } + + // Adds a less prominent outline to an element. Use for components that need a less obvious + // boundary. In Angular Material, this is used for the outline of an outlined card and the color + // of the divider + .mat-outline-variant { + border: 1px solid var(--mat-sys-outline-variant); + } + + + // *********************************************************************************************** + // State + // *********************************************************************************************** + + // Adds hover, focus, and active states to an element by applying varying shades of the surface + // color. Use for interactive components that are not based on a primary color. + .mat-stateful { + &:hover { + background: color-mix( + in srgb, + var(--mat-sys-on-surface) calc(var(--mat-sys-hover-state-layer-opacity) * 100%), + transparent + ); + } + + &:focus { + background: color-mix( + in srgb, + var(--mat-sys-on-surface) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), + transparent + ); + } + + &:active { + background: color-mix( + in srgb, + var(--mat-sys-on-surface) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), + transparent + ); + } + } + + // Adds hover, focus, and active states to an element by applying varying shades of the primary + // color. Use for interactive components that are not based on a primary color. + .mat-stateful-primary { + &:hover { + background: color-mix( + in srgb, + var(--mat-sys-primary) calc(var(--mat-sys-hover-state-layer-opacity) * 100%), + transparent + ); + } + + &:focus { + background: color-mix( + in srgb, + var(--mat-sys-primary) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), + transparent + ); + } + + &:active { + background: color-mix( + in srgb, + var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), + transparent + ); + } + } + + + // *********************************************************************************************** + // Elevation + // *********************************************************************************************** + + // Use to slightly raise the appearance of a surface. In Angular Material, this is used for the + // elevation of an elevated card and the handle of a slider. + .mat-shadow-level-1 { + box-shadow: var(--mat-sys-level1); + } + + // Use to raise the appearance of a surface. In Angular Material, this is used for the + // elevation of a menu and a select panel. + .mat-shadow-level-2 { + box-shadow: var(--mat-sys-level2); + } + + // Used to raise the appearance of a surface. In Angular Material, this is used for the elevation + // of a floating action button. + .mat-shadow-level-3 { + box-shadow: var(--mat-sys-level3); + } + + // Used to raise the appearance of a surface. This is generally reserved for elevation changes + // due to interaction like focus and hover. In Angular Material, this is used for the elevation + // of a hovered floating action button. + .mat-shadow-level-4 { + box-shadow: var(--mat-sys-level4); + } + + // Used to greatly raise the appearance of a surface. This is generally reserved for elevation + // changes due to interaction like focus and hover. + .mat-shadow-level-5 { + box-shadow: var(--mat-sys-level5); + } +}