+
+
+
+
+ Card with only text content
+
+
+
+
+ Card with only <mat-card-content> and text content.
+
+
+
+
+ Subtitle
+ Card with title and footer
+
+
This is supporting text.
+
{{longText}}
+
+
+
+
+
+
+
+
+ Subtitle
+ Card with title, footer, and inset-divider
+
+
diff --git a/src/dev-app/mdc-card/mdc-card-demo.scss b/src/dev-app/mdc-card/mdc-card-demo.scss
index f5ff0fdbee98..6038ba0385ae 100644
--- a/src/dev-app/mdc-card/mdc-card-demo.scss
+++ b/src/dev-app/mdc-card/mdc-card-demo.scss
@@ -1 +1,18 @@
-// TODO: copy in demo styles from existing mat-card demo.
+.demo-card-container {
+ display: flex;
+ flex-flow: column nowrap;
+
+ .mat-mdc-card {
+ margin: 0 16px 16px 0;
+ width: 350px;
+ }
+
+ // Use a gray background instead of real images for that "mock" feel.
+ img {
+ background-color: gray;
+ }
+
+ .mdc-button {
+ text-transform: uppercase;
+ }
+}
diff --git a/src/dev-app/mdc-card/mdc-card-demo.ts b/src/dev-app/mdc-card/mdc-card-demo.ts
index 7771d73cec90..f1a23af1c895 100644
--- a/src/dev-app/mdc-card/mdc-card-demo.ts
+++ b/src/dev-app/mdc-card/mdc-card-demo.ts
@@ -6,13 +6,20 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Component} from '@angular/core';
+import {Component, ViewEncapsulation} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'mdc-card-demo',
templateUrl: 'mdc-card-demo.html',
styleUrls: ['mdc-card-demo.css'],
+ encapsulation: ViewEncapsulation.None,
})
export class MdcCardDemo {
+ longText = `Once upon a midnight dreary, while I pondered, weak and weary,
+ Over many a quaint and curious volume of forgotten lore—
+ While I nodded, nearly napping, suddenly there came a tapping,
+ 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.”`;
}
diff --git a/src/material-experimental/mdc-card/BUILD.bazel b/src/material-experimental/mdc-card/BUILD.bazel
index 2188e04a5fc6..5472d60d14bc 100644
--- a/src/material-experimental/mdc-card/BUILD.bazel
+++ b/src/material-experimental/mdc-card/BUILD.bazel
@@ -22,12 +22,21 @@ sass_library(
srcs = glob(["**/_*.scss"]),
deps = [
"//src/material-experimental/mdc-helpers:mdc_helpers_scss_lib",
+ "//src/material-experimental/mdc-helpers:mdc_scss_deps_lib",
+ "//src/material/core:core_scss_lib",
],
)
sass_binary(
name = "card_scss",
src = "card.scss",
+ include_paths = [
+ "external/npm/node_modules",
+ ],
+ deps = [
+ "//src/material-experimental/mdc-helpers:mdc_helpers_scss_lib",
+ "//src/material-experimental/mdc-helpers:mdc_scss_deps_lib",
+ ],
)
ng_e2e_test_library(
diff --git a/src/material-experimental/mdc-card/_mdc-card.scss b/src/material-experimental/mdc-card/_mdc-card.scss
index ed41c765eee5..37c329db2945 100644
--- a/src/material-experimental/mdc-card/_mdc-card.scss
+++ b/src/material-experimental/mdc-card/_mdc-card.scss
@@ -1,13 +1,34 @@
+@import '@material/card/mixins';
+@import '@material/typography/mixins';
@import '../mdc-helpers/mdc-helpers';
@mixin mat-card-theme-mdc($theme) {
+ $foreground: map-get($theme, foreground);
+ $is-dark-theme: map-get($theme, is-dark);
+
@include mat-using-mdc-theme($theme) {
- // TODO: MDC theme styles here.
+ @include mdc-card-without-ripple($query: $mat-theme-styles-query);
+
+ // Card subtitles are an Angular Material construct (not MDC), so we explicitly set their
+ // color to secondary text here.
+ .mat-mdc-card-subtitle {
+ color: mat-color($foreground, secondary-text);
+ }
}
}
@mixin mat-card-typography-mdc($config) {
@include mat-using-mdc-typography($config) {
- // TODO: MDC typography styles here.
+ @include mdc-card-without-ripple($query: $mat-typography-styles-query);
+
+ // Card subtitles and titles are an Angular Material construct (not MDC), so we explicitly
+ // set their typographic styles here.
+ .mat-mdc-card-title {
+ @include mdc-typography(headline6);
+ }
+
+ .mat-mdc-card-subtitle {
+ @include mdc-typography(subtitle2);
+ }
}
}
diff --git a/src/material-experimental/mdc-card/card-header.html b/src/material-experimental/mdc-card/card-header.html
new file mode 100644
index 000000000000..b476f9327c98
--- /dev/null
+++ b/src/material-experimental/mdc-card/card-header.html
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/src/material-experimental/mdc-card/card-title-group.html b/src/material-experimental/mdc-card/card-title-group.html
new file mode 100644
index 000000000000..67c0eba6f6e9
--- /dev/null
+++ b/src/material-experimental/mdc-card/card-title-group.html
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/src/material-experimental/mdc-card/card.html b/src/material-experimental/mdc-card/card.html
index 9c75ddb6f477..6dbc74306383 100644
--- a/src/material-experimental/mdc-card/card.html
+++ b/src/material-experimental/mdc-card/card.html
@@ -1 +1 @@
-
+
diff --git a/src/material-experimental/mdc-card/card.scss b/src/material-experimental/mdc-card/card.scss
index f61450904c9a..cf9922f1e0a9 100644
--- a/src/material-experimental/mdc-card/card.scss
+++ b/src/material-experimental/mdc-card/card.scss
@@ -1 +1,149 @@
-// TODO: MDC core styles here.
+@import '@material/card/mixins';
+@import '../mdc-helpers/mdc-helpers';
+@import '../../cdk/a11y/a11y';
+
+// TODO(jelbourn): move header and title-group styles to their own files.
+
+// Size of the `mat-card-header` region custom to Angular Material.
+$mat-card-header-size: 40px !default;
+
+// Default padding for text content within a card.
+$mat-card-default-padding: 16px !default;
+
+// Include all MDC card styles except for color and typography.
+@include mdc-card-without-ripple($query: $mat-base-styles-query);
+
+// Add styles to the root card to have an outline in high-contrast mode.
+// TODO(jelbourn): file bug for MDC supporting high-contrast mode on `.mdc-card`.
+.mat-mdc-card {
+ @include cdk-high-contrast {
+ outline: solid 1px;
+ }
+}
+
+// Title text and subtitles text within a card. MDC doesn't have pre-made title sections for cards.
+// Maintained here for backwards compatibility with the previous generation MatCard.
+.mat-mdc-card-title,
+.mat-mdc-card-subtitle {
+ // Custom elements default to `display: inline`. Reset to 'block'.
+ display: block;
+
+ // Apply default padding for a text content region. Omit any bottom padding because we assume
+ // this region will be followed by another region that includes top padding.
+ padding: $mat-card-default-padding $mat-card-default-padding 0;
+}
+
+// Header section at the top of a card. MDC does not have a pre-made header section for cards.
+// Maintained here for backwards compatibility with the previous generation MatCard.
+.mat-mdc-card-header {
+ // The primary purpose of the card header is to lay out the title, subtitle, and image in the
+ // correct order. The image will come first, followed by a single div that will contain (via
+ // content projection) both the title and the subtitle.
+ display: flex;
+
+ // Apply default padding for the header region. Omit any bottom padding because we assume
+ // this region will be followed by another region that includes top padding.
+ padding: $mat-card-default-padding $mat-card-default-padding 0;
+
+ // When a subtitle is inside of a header, we want to move it up slightly to reduce the space with
+ // the title, and add a margin bottom to create space underneath the header.
+ .mat-mdc-card-subtitle {
+ margin-top: -($mat-card-default-padding / 2);
+ margin-bottom: $mat-card-default-padding;
+ }
+}
+
+// Primary card content. MDC does not have a pre-made section for primary content.
+// Adds the default 16px padding to the content. Maintained here for backwards compatibility
+// with the previous generation MatCard.
+.mat-mdc-card-content {
+ // Custom elements default to `display: inline`. Reset to 'block'.
+ display: block;
+
+ // Apply default horizontal padding for a content region.
+ padding: 0 $mat-card-default-padding;
+
+ // Only add vertical padding to the main content area if it's not preceeded/followed by another
+ // element within the card.
+ &:first-child {
+ padding-top: $mat-card-default-padding;
+ }
+
+ &:last-child {
+ padding-bottom: $mat-card-default-padding;
+ }
+}
+
+// Title group within a card. MDC does not have a pre-made section for anything title-related.
+// Maintained here for backwards compatibility with the previous generation MatCard.
+.mat-mdc-card-title-group {
+ // This element exists primary to lay out its children (title, subtitle, media). The title
+ // and subtitle go first (projected into a single div), followed by the media.
+ display: flex;
+ justify-content: space-between;
+
+ // Apply default padding for the title-group region. Omit any bottom padding because we assume
+ // this region will be followed by another region that includes top padding.
+ padding: $mat-card-default-padding $mat-card-default-padding 0;
+}
+
+// Avatar image for a card. MDC does not specifically have a card avatar or a "common" avatar.
+// They *do* have a list avatar, but it uses a different size than we do here, which we preserve
+// to reduce breaking changes. Ultimately, the avatar styles just consist of a size and a
+// border-radius.
+.mat-mdc-card-avatar {
+ height: $mat-card-header-size;
+ width: $mat-card-header-size;
+ border-radius: 50%;
+ flex-shrink: 0;
+
+ // Makes `` tags behave like `background-size: cover`. Not supported
+ // in IE, but we're using it as a progressive enhancement.
+ object-fit: cover;
+}
+
+// Specifically sized small image, specific to Angular Material.
+.mat-mdc-card-sm-image {
+ width: 80px;
+ height: 80px;
+}
+
+// Specifically sized medium image, specific to Angular Material.
+.mat-mdc-card-md-image {
+ width: 112px;
+ height: 112px;
+}
+
+// Specifically sized large image, specific to Angular Material.
+.mat-mdc-card-lg-image {
+ width: 152px;
+ height: 152px;
+}
+
+// Specifically sized extra-large image, specific to Angular Material.
+.mat-mdc-card-xl-image {
+ width: 240px;
+ height: 240px;
+}
+
+// When both a title and a subtitle are used, eliminate the top padding of whichever comes second
+// because the first already covers the padding.
+//
+// Additionally, reset the padding for title and subtitle when used within `mat-card-header` or
+// `mat-card-title-group` since the padding is captured by parent container already.
+.mat-mdc-card-subtitle ~ .mat-mdc-card-title,
+.mat-mdc-card-title ~ .mat-mdc-card-subtitle,
+.mat-mdc-card-header .mat-mdc-card-title,
+.mat-mdc-card-header .mat-mdc-card-subtitle,
+.mat-mdc-card-title-group .mat-mdc-card-title,
+.mat-mdc-card-title-group .mat-mdc-card-subtitle {
+ padding-top: 0;
+}
+
+// Remove the bottom margin from the last child of the card content. We intended to remove
+// margin from elements that have it built-in, such as `
`. We do this to avoid having too much
+// space between card content regions, as the space is already captured in the content region
+// element.
+.mat-mdc-card-content > :last-child:not(.mat-mdc-card-footer) {
+ margin-bottom: 0;
+}
diff --git a/src/material-experimental/mdc-card/card.ts b/src/material-experimental/mdc-card/card.ts
index 729f21ae3211..999297b87485 100644
--- a/src/material-experimental/mdc-card/card.ts
+++ b/src/material-experimental/mdc-card/card.ts
@@ -6,20 +6,226 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ Directive,
+ Input,
+ ViewEncapsulation,
+} from '@angular/core';
+
+/**
+ * Material Design card component. Cards contain content and actions about a single subject.
+ * See https://material.io/design/components/cards.html
+ *
+ * MatCard provides no behaviors, instead serving as a purely visual treatment.
+ */
@Component({
moduleId: module.id,
selector: 'mat-card',
templateUrl: 'card.html',
styleUrls: ['card.css'],
- host: {
- 'class': 'mat-mdc-card',
- },
+ host: {'class': 'mat-mdc-card mdc-card'},
exportAs: 'matCard',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatCard {
- // TODO: set up MDC foundation class.
+ // TODO(jelbourn): add `outline` option to card (supported by MDC)
+}
+
+// TODO(jelbourn): add `MatActionCard`, which is a card that acts like a button (and has a ripple).
+// Supported in MDC with `.mdc-card__primary-action`. Will require additional a11y docs for users.
+
+
+/**
+ * Title of a card, intended for use within ``. This component is an optional
+ * convenience for one variety of card title; any custom title element may be used in its place.
+ *
+ * MatCardTitle provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: `mat-card-title, [mat-card-title], [matCardTitle]`,
+ host: {'class': 'mat-mdc-card-title'}
+})
+export class MatCardTitle {}
+
+
+/**
+ * Container intended to be used within the `` component. Can contain exactly one
+ * ``, one `` and one content image of any size
+ * (e.g. ``).
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-card-title-group',
+ templateUrl: 'card-title-group.html',
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ host: {'class': 'mat-mdc-card-title-group'}
+})
+export class MatCardTitleGroup {}
+
+
+/**
+ * Content of a card, intended for use within ``. This component is an optional
+ * convenience for use with other convenience elements, such as ``; any custom
+ * content block element may be used in its place.
+ *
+ * MatCardContent provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: 'mat-card-content',
+ host: {'class': 'mat-mdc-card-content'}
+})
+export class MatCardContent {}
+
+
+/**
+ * Sub-title of a card, intended for use within `` beneath a ``. This
+ * component is an optional convenience for use with other convenience elements, such as
+ * ``.
+ *
+ * MatCardSubtitle provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: `mat-card-subtitle, [mat-card-subtitle], [matCardSubtitle]`,
+ host: {'class': 'mat-mdc-card-subtitle'}
+})
+export class MatCardSubtitle {}
+
+
+/**
+ * Bottom area of a card that contains action buttons, intended for use within ``.
+ * This component is an optional convenience for use with other convenience elements, such as
+ * ``; any custom action block element may be used in its place.
+ *
+ * MatCardActions provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: 'mat-card-actions',
+ exportAs: 'matCardActions',
+ host: {
+ 'class': 'mat-mdc-card-actions mdc-card__actions',
+ '[class.mat-mdc-card-actions-align-end]': 'align === "end"',
+ }
+})
+export class MatCardActions {
+ // TODO(jelbourn): deprecate `align` in favor of `actionPositon` or `actionAlignment`
+ // as to not conflict with the native `align` attribute.
+
+ /** Position of the actions inside the card. */
+ @Input() align: 'start' | 'end' = 'start';
+
+ // TODO(jelbourn): support `.mdc-card__actions--full-bleed`.
+
+ // TODO(jelbourn): support `.mdc-card__action-buttons` and `.mdc-card__action-icons`.
+
+ // TODO(jelbourn): figure out how to use `.mdc-card__action`, `.mdc-card__action--button`, and
+ // `mdc-card__action--icon`. They're used primarily for positioning, which we might be able to
+ // do implicitly.
}
+
+
+/**
+ * Header region of a card, intended for use within ``. This header captures
+ * a card title, subtitle, and avatar. This component is an optional convenience for use with
+ * other convenience elements, such as ``; any custom header block element may be
+ * used in its place.
+ *
+ * MatCardHeader provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-card-header',
+ templateUrl: 'card-header.html',
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ host: {'class': 'mat-mdc-card-header'}
+})
+export class MatCardHeader {}
+
+
+/**
+ * Footer area a card, intended for use within ``.
+ * This component is an optional convenience for use with other convenience elements, such as
+ * ``; any custom footer block element may be used in its place.
+ *
+ * MatCardFooter provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: 'mat-card-footer',
+ host: {'class': 'mat-mdc-card-footer'}
+})
+export class MatCardFooter {}
+
+// TODO(jelbourn): deprecate the "image" selectors to replace with "media".
+
+// TODO(jelbourn): support `.mdc-card__media-content`.
+
+/**
+ * Primary image content for a card, intended for use within ``. Can be applied to
+ * any media element, such as `` or ``.
+ *
+ * This component is an optional convenience for use with other convenience elements, such as
+ * ``; any custom media element may be used in its place.
+ *
+ * MatCardImage provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: '[mat-card-image], [matCardImage]',
+ host: {'class': 'mat-mdc-card-image mdc-card__media'}
+})
+export class MatCardImage {
+ // TODO(jelbourn): support `.mdc-card__media--square` and `.mdc-card__media--16-9`.
+}
+
+
+/** Same as `MatCardImage`, but small. */
+@Directive({
+ selector: '[mat-card-sm-image], [matCardImageSmall]',
+ host: {'class': 'mat-mdc-card-sm-image mdc-card__media'}
+})
+export class MatCardSmImage {}
+
+
+/** Same as `MatCardImage`, but medium. */
+@Directive({
+ selector: '[mat-card-md-image], [matCardImageMedium]',
+ host: {'class': 'mat-mdc-card-md-image mdc-card__media'}
+})
+export class MatCardMdImage {}
+
+
+/** Same as `MatCardImage`, but large. */
+@Directive({
+ selector: '[mat-card-lg-image], [matCardImageLarge]',
+ host: {'class': 'mat-mdc-card-lg-image mdc-card__media'}
+})
+export class MatCardLgImage {}
+
+
+/** Same as `MatCardImage`, but extra-large. */
+@Directive({
+ selector: '[mat-card-xl-image], [matCardImageXLarge]',
+ host: {'class': 'mat-mdc-card-xl-image mdc-card__media'}
+})
+export class MatCardXlImage {}
+
+
+/**
+ * Avatar image content for a card, intended for use within ``. Can be applied to
+ * any media element, such as `` or ``.
+ *
+ * This component is an optional convenience for use with other convenience elements, such as
+ * ``; any custom media element may be used in its place.
+ *
+ * MatCardAvatar provides no behaviors, instead serving as a purely visual treatment.
+ */
+@Directive({
+ selector: '[mat-card-avatar], [matCardAvatar]',
+ host: {'class': 'mat-mdc-card-avatar'}
+})
+export class MatCardAvatar {}
+
diff --git a/src/material-experimental/mdc-card/migration.md b/src/material-experimental/mdc-card/migration.md
new file mode 100644
index 000000000000..c1d7c311f15b
--- /dev/null
+++ b/src/material-experimental/mdc-card/migration.md
@@ -0,0 +1,2 @@
+# Migration notes for MDC-based `MatCard`
+* Previous `MatCard` always set 16px padding, which the MDC card sets no padding.
diff --git a/src/material-experimental/mdc-card/module.ts b/src/material-experimental/mdc-card/module.ts
index 70f61b5a7b84..09c06531da79 100644
--- a/src/material-experimental/mdc-card/module.ts
+++ b/src/material-experimental/mdc-card/module.ts
@@ -9,12 +9,45 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {MatCommonModule} from '@angular/material/core';
-import {MatCard} from './card';
+import {
+ MatCard,
+ MatCardActions,
+ MatCardAvatar,
+ MatCardContent,
+ MatCardFooter,
+ MatCardHeader,
+ MatCardImage,
+ MatCardLgImage,
+ MatCardMdImage,
+ MatCardSmImage,
+ MatCardSubtitle,
+ MatCardTitle,
+ MatCardTitleGroup,
+ MatCardXlImage,
+} from './card';
+
+
+const CARD_DIRECTIVES = [
+ MatCard,
+ MatCardActions,
+ MatCardAvatar,
+ MatCardContent,
+ MatCardFooter,
+ MatCardHeader,
+ MatCardImage,
+ MatCardLgImage,
+ MatCardMdImage,
+ MatCardSmImage,
+ MatCardSubtitle,
+ MatCardTitle,
+ MatCardTitleGroup,
+ MatCardXlImage
+];
@NgModule({
imports: [MatCommonModule, CommonModule],
- exports: [MatCard, MatCommonModule],
- declarations: [MatCard],
+ exports: [CARD_DIRECTIVES, MatCommonModule],
+ declarations: CARD_DIRECTIVES,
})
export class MatCardModule {
}
diff --git a/src/material-experimental/mdc-helpers/_mdc-helpers.scss b/src/material-experimental/mdc-helpers/_mdc-helpers.scss
index f3d7db088988..7ec68c9fe6fe 100644
--- a/src/material-experimental/mdc-helpers/_mdc-helpers.scss
+++ b/src/material-experimental/mdc-helpers/_mdc-helpers.scss
@@ -114,7 +114,7 @@ $mat-typography-level-mappings: (
$primary: mat-color(map-get($theme, primary));
$accent: mat-color(map-get($theme, accent));
$warn: mat-color(map-get($theme, warn));
- $background: mat-color(map-get($theme, background), background);
+ $background-palette: map-get($theme, background);
// Save the original values.
$orig-mdc-theme-primary: $mdc-theme-primary;
@@ -135,8 +135,8 @@ $mat-typography-level-mappings: (
$mdc-theme-secondary: $accent !global;
$mdc-theme-on-secondary:
if(mdc-theme-contrast-tone($mdc-theme-secondary) == 'dark', #000, #fff) !global;
- $mdc-theme-background: $background !global;
- $mdc-theme-surface: $background !global;
+ $mdc-theme-background: mat-color($background-palette, background) !global;
+ $mdc-theme-surface: mat-color($background-palette, card) !global;
$mdc-theme-on-surface:
if(mdc-theme-contrast-tone($mdc-theme-surface) == 'dark', #000, #fff) !global;
$mdc-theme-error: $warn !global;