diff --git a/src/components-examples/material/card/card-overview/card-overview-example.html b/src/components-examples/material/card/card-overview/card-overview-example.html index 6805d4fb8214..e91eecb2db93 100644 --- a/src/components-examples/material/card/card-overview/card-overview-example.html +++ b/src/components-examples/material/card/card-overview/card-overview-example.html @@ -1,3 +1,11 @@ + + Elevated + + - Simple card + Outlined + + + Filled + \ No newline at end of file diff --git a/src/dev-app/card/BUILD.bazel b/src/dev-app/card/BUILD.bazel index 28a377317c5d..b013cb719a60 100644 --- a/src/dev-app/card/BUILD.bazel +++ b/src/dev-app/card/BUILD.bazel @@ -12,7 +12,7 @@ ng_module( deps = [ "//src/material/button", "//src/material/card", - "//src/material/checkbox", + "//src/material/radio", ], ) diff --git a/src/dev-app/card/card-demo.html b/src/dev-app/card/card-demo.html index 3ad6e6dd97a9..89f66ceca18b 100644 --- a/src/dev-app/card/card-demo.html +++ b/src/dev-app/card/card-demo.html @@ -1,5 +1,9 @@
- Use outlined cards + + Raised + Outlined + Filled + diff --git a/src/dev-app/card/card-demo.scss b/src/dev-app/card/card-demo.scss index 6038ba0385ae..945be0598362 100644 --- a/src/dev-app/card/card-demo.scss +++ b/src/dev-app/card/card-demo.scss @@ -16,3 +16,7 @@ text-transform: uppercase; } } + +mat-radio-group { + margin-bottom: 10px; +} diff --git a/src/dev-app/card/card-demo.ts b/src/dev-app/card/card-demo.ts index 2dce2b2bb196..191463e12563 100644 --- a/src/dev-app/card/card-demo.ts +++ b/src/dev-app/card/card-demo.ts @@ -10,7 +10,7 @@ import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/co import {FormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; import {MatCardAppearance, MatCardModule} from '@angular/material/card'; -import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatRadioModule} from '@angular/material/radio'; @Component({ selector: 'card-demo', @@ -18,7 +18,7 @@ import {MatCheckboxModule} from '@angular/material/checkbox'; styleUrl: 'card-demo.css', encapsulation: ViewEncapsulation.None, standalone: true, - imports: [MatCardModule, MatButtonModule, MatCheckboxModule, FormsModule], + imports: [MatCardModule, MatButtonModule, MatRadioModule, FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, }) export class CardDemo { @@ -29,7 +29,4 @@ export class CardDemo { As of some one gently rapping, rapping at my chamber door. “’Tis some visitor,” I muttered, “tapping at my chamber door— Only this and nothing more.”`; - toggleAppearance() { - this.appearance = this.appearance == 'raised' ? 'outlined' : 'raised'; - } } diff --git a/src/material/card/_card-theme.scss b/src/material/card/_card-theme.scss index c8c4a7f59311..3fca8c52a651 100644 --- a/src/material/card/_card-theme.scss +++ b/src/material/card/_card-theme.scss @@ -8,6 +8,7 @@ @use '../core/tokens/m2/mat/card' as tokens-mat-card; @use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; @use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; +@use '../core/tokens/m2/mdc/filled-card' as tokens-mdc-filled-card; @mixin base($theme) { @if inspection.get-theme-version($theme) == 1 { @@ -22,6 +23,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-unthemable-tokens() ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-unthemable-tokens() + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-unthemable-tokens() @@ -43,6 +48,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-color-tokens($theme) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-color-tokens($theme) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-color-tokens($theme) @@ -64,6 +73,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-typography-tokens($theme) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-typography-tokens($theme) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-typography-tokens($theme) @@ -85,6 +98,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-density-tokens($theme) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-density-tokens($theme) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-density-tokens($theme) @@ -109,6 +126,11 @@ namespace: tokens-mdc-outlined-card.$prefix, tokens: tokens-mdc-outlined-card.get-token-slots(), prefix: 'outlined-', + ), + ( + namespace: tokens-mdc-filled-card.$prefix, + tokens: tokens-mdc-filled-card.get-token-slots(), + prefix: 'filled-', ) ); } @@ -145,6 +167,10 @@ tokens-mdc-outlined-card.$prefix, map.get($tokens, tokens-mdc-outlined-card.$prefix) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + map.get($tokens, tokens-mdc-filled-card.$prefix) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, map.get($tokens, tokens-mat-card.$prefix) diff --git a/src/material/card/card.scss b/src/material/card/card.scss index 21ae1ee38b42..032373d74a33 100644 --- a/src/material/card/card.scss +++ b/src/material/card/card.scss @@ -2,6 +2,7 @@ @use '../core/tokens/m2/mat/card' as tokens-mat-card; @use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; @use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; +@use '../core/tokens/m2/mdc/filled-card' as tokens-mdc-filled-card; // Size of the `mat-card-header` region custom to Angular Material. $mat-card-header-size: 40px !default; @@ -68,6 +69,17 @@ $mat-card-default-padding: 16px !default; } } +.mat-mdc-card-filled { + @include token-utils.use-tokens( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-token-slots() + ) { + @include token-utils.create-token-slot(background-color, container-color); + @include token-utils.create-token-slot(border-radius, container-shape); + @include token-utils.create-token-slot(box-shadow, container-elevation); + } +} + .mdc-card__media { position: relative; box-sizing: border-box; diff --git a/src/material/card/card.ts b/src/material/card/card.ts index 2a9b89fb4765..7f03f437a333 100644 --- a/src/material/card/card.ts +++ b/src/material/card/card.ts @@ -16,7 +16,7 @@ import { inject, } from '@angular/core'; -export type MatCardAppearance = 'outlined' | 'raised'; +export type MatCardAppearance = 'outlined' | 'raised' | 'filled'; /** Object that can be used to configure the default options for the card module. */ export interface MatCardConfig { @@ -41,6 +41,8 @@ export const MAT_CARD_CONFIG = new InjectionToken('MAT_CARD_CONFI 'class': 'mat-mdc-card mdc-card', '[class.mat-mdc-card-outlined]': 'appearance === "outlined"', '[class.mdc-card--outlined]': 'appearance === "outlined"', + '[class.mat-mdc-card-filled]': 'appearance === "filled"', + '[class.mdc-card--filled]': 'appearance === "filled"', }, exportAs: 'matCard', encapsulation: ViewEncapsulation.None, diff --git a/src/material/core/tokens/m2/_index.scss b/src/material/core/tokens/m2/_index.scss index 85c8c7195b50..9875b56cd44c 100644 --- a/src/material/core/tokens/m2/_index.scss +++ b/src/material/core/tokens/m2/_index.scss @@ -70,6 +70,7 @@ @use './mdc/switch' as tokens-mdc-switch; @use './mdc/secondary-navigation-tab' as tokens-mdc-secondary-navigation-tab; @use './mdc/tab-indicator' as tokens-mdc-tab-indicator; +@use './mdc/filled-card' as tokens-mdc-filled-card; @use '../../theming/inspection'; /// Gets the tokens for the given theme, m2 tokens module, and theming system. @@ -182,6 +183,7 @@ _get-tokens-for-module($theme, tokens-mdc-switch), _get-tokens-for-module($theme, tokens-mdc-tab-indicator), _get-tokens-for-module($theme, tokens-mdc-secondary-navigation-tab), - _get-tokens-for-module($theme, tokens-mdc-text-button) + _get-tokens-for-module($theme, tokens-mdc-text-button), + _get-tokens-for-module($theme, tokens-mdc-filled-card), ); } diff --git a/src/material/core/tokens/m2/mdc/_filled-card.scss b/src/material/core/tokens/m2/mdc/_filled-card.scss new file mode 100644 index 000000000000..61579153466f --- /dev/null +++ b/src/material/core/tokens/m2/mdc/_filled-card.scss @@ -0,0 +1,81 @@ +@use '../../../style/elevation'; +@use '../../../theming/inspection'; +@use '../../../style/sass-utils'; +@use '../../token-definition'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, filled-card); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +// +// Tokens that are available in MDC, but not used in Angular Material should be mapped to `null`. +// `null` indicates that we are intentionally choosing not to emit a slot or value for the token in +// our CSS. +@function get-unthemable-tokens() { + @return ( + // The border-radius of the card. + container-shape: 4px, + // ============================================================================================= + // = TOKENS NOT USED IN ANGULAR MATERIAL = + // ============================================================================================= + // Angular Material's card is not an interactive element, and therefore does not support states. + disabled-container-elevation: null, + disabled-outline-color: null, + disabled-outline-opacity: null, + dragged-container-elevation: null, + dragged-outline-color: null, + dragged-state-layer-color: null, + dragged-state-layer-opacity: null, + focus-container-elevation: null, + focus-outline-color: null, + focus-state-layer-color: null, + focus-state-layer-opacity: null, + hover-container-elevation: null, + hover-outline-color: null, + hover-state-layer-color: null, + hover-state-layer-opacity: null, + pressed-container-elevation: null, + pressed-outline-color: null, + pressed-state-layer-color: null, + pressed-state-layer-opacity: null, + container-shadow-color: null, + // Angular Material does not currently support surface tint. + container-surface-tint-layer-color: null, + // MDC does not seem to use these tokens. + icon-color: null, + icon-size: null, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($theme) { + $elevation: inspection.get-theme-color($theme, foreground, elevation); + + @return ( + // The background color of the card. + container-color: inspection.get-theme-color($theme, background, card), + container-elevation: elevation.get-box-shadow(0), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($theme) { + @return (); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($theme) { + @return (); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return sass-utils.deep-merge-all( + get-unthemable-tokens(), + get-color-tokens(token-definition.$placeholder-color-config), + get-typography-tokens(token-definition.$placeholder-typography-config), + get-density-tokens(token-definition.$placeholder-density-config) + ); +} diff --git a/src/material/core/tokens/m3/_index.scss b/src/material/core/tokens/m3/_index.scss index 4d85b20f002a..4763e83b9467 100644 --- a/src/material/core/tokens/m3/_index.scss +++ b/src/material/core/tokens/m3/_index.scss @@ -68,6 +68,7 @@ @use './mdc/switch' as tokens-mdc-switch; @use './mdc/secondary-navigation-tab' as tokens-mdc-secondary-navigation-tab; @use './mdc/tab-indicator' as tokens-mdc-tab-indicator; +@use './mdc/filled-card' as tokens-mdc-filled-card; $_module-names: ( // Custom tokens @@ -140,7 +141,8 @@ $_module-names: ( tokens-mdc-snack-bar, tokens-mdc-switch, tokens-mdc-secondary-navigation-tab, - tokens-mdc-tab-indicator + tokens-mdc-tab-indicator, + tokens-mdc-filled-card, ); /// Gets the full set of M3 tokens for the given theme object. diff --git a/src/material/core/tokens/m3/mdc/_filled-card.scss b/src/material/core/tokens/m3/mdc/_filled-card.scss new file mode 100644 index 000000000000..888b06d69d28 --- /dev/null +++ b/src/material/core/tokens/m3/mdc/_filled-card.scss @@ -0,0 +1,22 @@ +@use 'sass:map'; +@use '../../../style/elevation'; +@use '../../token-definition'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, filled-card); + +/// Generates the tokens for MDC filled-card +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @param {Map} $token-slots Possible token slots +/// @return {Map} A set of tokens for the MDC filled-card +@function get-tokens($systems, $exclude-hardcoded, $token-slots) { + $tokens: token-definition.get-mdc-tokens('filled-card', $systems, $exclude-hardcoded); + $elevation: map.get($tokens, container-elevation); + + @if ($elevation != null) { + $tokens: map.set($tokens, container-elevation, elevation.get-box-shadow($elevation)); + } + + @return token-definition.namespace-tokens($prefix, $tokens, $token-slots); +} diff --git a/tools/public_api_guard/material/card.md b/tools/public_api_guard/material/card.md index 576c88722df2..4938d3c17587 100644 --- a/tools/public_api_guard/material/card.md +++ b/tools/public_api_guard/material/card.md @@ -33,7 +33,7 @@ export class MatCardActions { } // @public (undocumented) -export type MatCardAppearance = 'outlined' | 'raised'; +export type MatCardAppearance = 'outlined' | 'raised' | 'filled'; // @public export class MatCardAvatar {