diff --git a/dev/card.html b/dev/card.html index a8d3a9bee0..68a187c210 100644 --- a/dev/card.html +++ b/dev/card.html @@ -15,14 +15,129 @@ import '@vaadin/card'; import { badge } from '@vaadin/vaadin-lumo-styles/badge.js'; + import '@vaadin/avatar'; + import '@vaadin/button'; + import '@vaadin/checkbox'; + import '@vaadin/checkbox-group'; import '@vaadin/icon'; import '@vaadin/icons'; import '@vaadin/vaadin-lumo-styles/icons.js'; + import '@vaadin/select'; + import '@vaadin/scroller'; import { utility } from '@vaadin/vaadin-lumo-styles/utility.js'; import { addGlobalThemeStyles } from '@vaadin/vaadin-themable-mixin/register-styles.js'; addGlobalThemeStyles('utility', utility); addGlobalThemeStyles('badge', badge); + + const playground = document.querySelector('.playground'); + const codeExample = document.querySelector('.code-example'); + const mediaSelect = document.querySelector('#media'); + const contentSelect = document.querySelector('#content'); + const slotCheckboxGroup = document.querySelector('.slot-options'); + const resizeOptions = document.querySelector('.playground-resize'); + const themeVariant = document.querySelector('.theme-variant'); + + mediaSelect.items = [ + { label: 'None', value: 'none' }, + { label: 'Image', value: '0' }, + { label: 'Icon', value: '1' }, + { label: 'Avatar', value: '2' }, + { label: 'Custom SVG', value: '3' }, + ]; + mediaSelect.value = 'none'; + const mediaElements = Array.from(document.querySelector('#media-options').content.querySelectorAll('*')); + mediaSelect.addEventListener('value-changed', (e) => { + const existing = playground.querySelector('[slot="media"]'); + if (existing) { + playground.removeChild(existing); + } + if (e.detail.value != 'none') { + playground.prepend(mediaElements[e.detail.value]); + } + updateCodeExample(); + }); + + const slotElements = document.querySelector('#slot-options').content.children; + const slotItems = { + 'header': slotElements[0], + 'header-prefix': slotElements[1], + 'header-suffix': slotElements[2], + 'title': slotElements[3], + 'subtitle': slotElements[4], + 'footer': slotElements[5], + }; + + slotCheckboxGroup.addEventListener('value-changed', (e) => { + Object.entries(slotItems).forEach(([key, val]) => { + if (e.detail.value.includes(key)) { + playground.append(val); + } else if (val.parentNode === playground) { + playground.removeChild(val); + } + }); + updateCodeExample(); + }); + + contentSelect.items = [ + { label: 'None', value: '-1' }, + { label: 'Text', value: '0' }, + { label: 'Scroller', value: '1' }, + { label: 'Image & Text', value: '2' }, + ]; + const contentElements = document.querySelector('#content-options').content.children; + contentSelect.addEventListener('value-changed', (e) => { + const existing = playground.querySelector(':scope > :not([slot])'); + if (existing) { + playground.removeChild(existing); + } + if (parseInt(e.detail.value) >= 0) { + playground.append(contentElements[e.detail.value].cloneNode(true)); + } + updateCodeExample(); + }); + contentSelect.value = '0'; + + resizeOptions.addEventListener('value-changed', (e) => { + playground.classList.toggle('resize-width', e.detail.value.includes('width')); + playground.classList.toggle('resize-height', e.detail.value.includes('height')); + }); + + themeVariant.addEventListener('value-changed', (e) => { + playground.setAttribute('theme', e.detail.value.filter(v => v != 'dark').join(' ')); + document.documentElement.setAttribute('theme', e.detail.value.includes('dark') ? 'dark' : ''); + updateCodeExample(); + }); + + function updateCodeExample() { + codeExample.textContent = format(playground.outerHTML + .replaceAll(/ tabindex="0"| role="button"| class="playground(.*?)"| style="(.+?)"| theme=""| has-content=""| has-title=""| has-subtitle=""| has-header=""| has-header-prefix=""| has-header-suffix=""| has-footer=""| has-media=""/g, '') + .replaceAll(/src=".+?"/g, 'src="..."') + .replace(/Lapland is the[\s\S]+?./, '>...') + .replace(/ overflow=".*?"/, '') + ).replace('\n ', ''); + } + + function format(html) { + var tab = ' '; + var result = ''; + var indent = ''; + + html.split(/>\s*\r\n'; + + if (element.match(/^]*[^/]$/) && !element.startsWith("input")) { + indent += tab; + } + }); + + return result.substring(1, result.length - 3); + } - - - - -

Default

-
- - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit incididunt ut.

- Label -
-
-
- -
- - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
-
- - - - -
- -
-

Page Views

- -
-
-

12,450

- - 15.8% - - -
-
- -
-

Total Revenue

- -
-
-

$363.95

- - 34.0% - - -
-
- -
-

Bounce Rate

- -
-
-

86.5%

- - 24.3% - - -
-
-
- - - - -

Outlined

-
- - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut.

- Label -
-
-
- - -
- - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
-
- - - -
- -
-

Page Views

- -
-
-

12,450

- - 15.8% - - -
-
- -
-

Total Revenue

- -
-
-

$363.95

- - 34.0% - - -
-
- -
-

Bounce Rate

- -
-
-

86.5%

- - 24.3% - - -
-
-
- - - - -

Elevated

-
- - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut.

- Label -
-
-
- - -
- - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
-
- + .playground:not(.resize-height), + .playground.resize-height:not([style*="height"]) { + height: auto !important; + } -
- -
-

Page Views

- -
-
-

12,450

- - 15.8% - - -
-
- -
-

Total Revenue

- -
-
-

$363.95

- - 34.0% - - -
-
- -
-

Bounce Rate

- -
-
-

86.5%

- - 24.3% - - -
-
-
+ .playground.resize-width { + resize: horizontal; + overflow: hidden; + } + .playground.resize-height { + resize: vertical; + overflow: hidden; + } + .playground.resize-width.resize-height { + resize: both; + } + .options { + display: flex; + flex-wrap: wrap; + gap: 0 2rem; + margin-bottom: 3rem; + } -

Outlined & Elevated

-
- - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit tempor incididunt ut.

- Label -
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut.

- Label -
-
-
+ .code-example { + white-space: pre; + font-family: monospace; + font-size: 13px; + line-height: 1.4; + padding: 1rem; + border: 1px solid var(--lumo-contrast-10pct); + background-color: var(--lumo-shade); + color: var(--lumo-tint-90pct); + border-radius: var(--lumo-border-radius-l); + width: fit-content; + } + .output { + display: flex; + align-items: start; + justify-content: space-between; + gap: 2rem; + } + + -
- - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
- - - -
-

Card Title

-

Lorem ipsum dolor sit amet

-
-
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - -
- -
-

Page Views

- -
-
-

12,450

- - 15.8% - - -
-
- -
-

Total Revenue

- -
-
-

$363.95

- - 34.0% - - -
-
- -
-

Bounce Rate

- -
-
-

86.5%

- - 24.3% - - -
-
+
+ +
- - - - diff --git a/packages/card/src/vaadin-card.js b/packages/card/src/vaadin-card.js index d8b0694c8d..56e43b82b2 100644 --- a/packages/card/src/vaadin-card.js +++ b/packages/card/src/vaadin-card.js @@ -10,14 +10,40 @@ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js'; import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; /** - * `` is a visual content container. + * `` is a versatile container for grouping related content and actions. + * It presents information in a structured and visually appealing manner, with + * customization options to fit various design requirements. * * ```html - * - *
Card content
+ * + * + *
Lapland
+ *
The Exotic North
+ *
Lapland is the northern-most region of Finland and an active outdoor destination.
+ * Book Vacation *
* ``` * + * ### Styling + * + * The following shadow DOM parts are available for styling: + * + * Part name | Description + * ----------|------------- + * `media` | The container for the media element (e.g., image, video, icon). Shown above of before the card content. + * `header` | The container for title and subtitle - or for custom header content - and header prefix and suffix elements. + * `content` | The container for the card content (usually text content). + * `footer` | The container for footer elements. This part is always at the bottom of the card. + * + * The following custom properties are available for styling: + * + * Custom property | Description | Default + * ----------------|-------------|------------- + * `--vaadin-card-padding` | The space between the card edge and its content. Needs to a unified value for all edges, i.e., a single length value. | `1em` + * `--vaadin-card-gap` | The space between content elements within the card. | `1em` + * + * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation. + * * @customElement * @extends HTMLElement * @mixes ElementMixin @@ -31,12 +57,239 @@ class Card extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) { static get styles() { return css` :host { - display: block; + display: flex; + flex-direction: column; + box-sizing: border-box; + padding: var(--_padding); + gap: var(--_gap); + --_padding: var(--vaadin-card-padding, 1em); + --_gap: var(--vaadin-card-gap, 1em); + --_media: 0; + --_title: 0; + --_subtitle: 0; + --_header: max(var(--_header-prefix), var(--_title), var(--_subtitle), var(--_header-suffix)); + --_header-prefix: 0; + --_header-suffix: 0; + --_content: 0; + --_footer: 0; } :host([hidden]) { display: none !important; } + + :host(:not([theme~='horizontal'])) { + justify-content: space-between; + } + + :host(:is(:has([slot='media']), [has-media])) { + --_media: 1; + } + + :host(:is(:has([slot='title']), [has-title])) { + --_title: 1; + } + + :host(:is(:has([slot='subtitle']), [has-subtitle])) { + --_subtitle: 1; + } + + :host(:is(:has([slot='header']), [has-header])) { + --_header: 1; + --_title: 0; + --_subtitle: 0; + } + + :host(:is(:has([slot='header-prefix']), [has-header-prefix])) { + --_header-prefix: 1; + } + + :host(:is(:has([slot='header-suffix']), [has-header-suffix])) { + --_header-suffix: 1; + } + + :host(:is(:has(> :not([slot])), [has-content])) { + --_content: 1; + } + + :host(:is(:has([slot='footer']), [has-footer])) { + --_footer: 1; + } + + [part='media'], + [part='header'], + [part='content'], + [part='footer'] { + display: none; + } + + :host(:is(:has([slot='media']), [has-media])) [part='media'], + :host(:is(:has(> :not([slot])), [has-content])) [part='content'] { + display: block; + } + + :host(:is(:has([slot='footer']), [has-footer])) [part='footer'] { + display: flex; + gap: var(--_gap); + } + + :host( + :is( + :has([slot='header']), + [has-header], + :has([slot='title']), + [has-title], + :has([slot='subtitle']), + [has-subtitle], + :has([slot='header-prefix']), + [has-header-prefix], + :has([slot='header-suffix']), + [has-header-suffix] + ) + ) + [part='header'] { + display: grid; + align-items: center; + gap: var(--_gap); + row-gap: 0; + } + + [part='header'] { + margin-bottom: auto; + } + + :host(:is(:has([slot='header-suffix']), [has-header-suffix])) [part='header'] { + grid-template-columns: 1fr auto; + } + + :host(:is(:has([slot='header-prefix']), [has-header-prefix])) [part='header'] { + grid-template-columns: repeat(var(--_header-prefix), auto) 1fr; + } + + slot { + border-radius: inherit; + } + + ::slotted([slot='header-prefix']) { + grid-column: 1; + grid-row: 1 / span calc(var(--_title) + var(--_subtitle)); + } + + ::slotted([slot='header']), + ::slotted([slot='title']) { + grid-column: calc(1 + var(--_header-prefix)); + grid-row: 1; + } + + ::slotted([slot='subtitle']) { + grid-column: calc(1 + var(--_header-prefix)); + grid-row: calc(1 + var(--_title)); + } + + ::slotted([slot='header-suffix']) { + grid-column: calc(2 + var(--_header-prefix)); + grid-row: 1 / span calc(var(--_title) + var(--_subtitle)); + } + + /* Horizontal */ + :host([theme~='horizontal']) { + display: grid; + grid-template-columns: repeat(var(--_media), minmax(auto, max-content)) 1fr; + align-items: start; + } + + :host([theme~='horizontal']:is(:has([slot='footer']), [has-footer])) { + grid-template-rows: 1fr auto; + } + + :host([theme~='horizontal']:is(:has(> :not([slot])), [has-content])) { + grid-template-rows: repeat(var(--_header), auto) 1fr; + } + + [part='media'] { + grid-column: 1; + grid-row: 1 / span calc(var(--_header) + var(--_content) + var(--_footer)); + align-self: stretch; + border-radius: inherit; + } + + [part='header'] { + grid-column: calc(1 + var(--_media)); + grid-row: 1; + } + + [part='content'] { + grid-column: calc(1 + var(--_media)); + grid-row: calc(1 + var(--_header)); + flex: auto; + min-height: 0; + } + + [part='footer'] { + grid-column: calc(1 + var(--_media)); + grid-row: calc(1 + var(--_header) + var(--_content)); + border-radius: inherit; + } + + :host([theme~='horizontal']) [part='footer'] { + align-self: end; + } + + :host(:not([theme~='horizontal'])) ::slotted([slot='media']:is(img, video, svg)) { + max-width: 100%; + } + + ::slotted([slot='media']) { + vertical-align: middle; + } + + :host(:is([theme~='cover-media'], [theme~='stretch-media'])) + ::slotted([slot='media']:is(img, video, svg, vaadin-icon)) { + width: 100%; + height: auto; + aspect-ratio: var(--vaadin-card-media-aspect-ratio, 16/9); + object-fit: cover; + } + + :host([theme~='horizontal']:is([theme~='cover-media'], [theme~='stretch-media'])) { + grid-template-columns: repeat(var(--_media), minmax(auto, 0.5fr)) 1fr; + } + + :host([theme~='horizontal']:is([theme~='cover-media'], [theme~='stretch-media'])) + ::slotted([slot='media']:is(img, video, svg, vaadin-icon)) { + height: 100%; + aspect-ratio: auto; + } + + :host([theme~='cover-media']) ::slotted([slot='media']:is(img, video, svg, vaadin-icon)) { + margin-top: calc(var(--_padding) * -1); + margin-inline: calc(var(--_padding) * -1); + width: calc(100% + var(--_padding) * 2); + max-width: none; + border-radius: inherit; + border-end-end-radius: 0; + border-end-start-radius: 0; + } + + :host([theme~='horizontal'][theme~='cover-media']) ::slotted([slot='media']:is(img, video, svg, vaadin-icon)) { + margin-inline-end: 0; + width: calc(100% + var(--_padding)); + height: calc(100% + var(--_padding) * 2); + border-radius: inherit; + border-start-end-radius: 0; + border-end-end-radius: 0; + } + + /* Scroller in content */ + [part='content'] ::slotted(vaadin-scroller) { + margin-inline: calc(var(--_padding) * -1); + padding-inline: var(--_padding); + } + + [part='content'] ::slotted(vaadin-scroller)::before, + [part='content'] ::slotted(vaadin-scroller)::after { + margin-inline: calc(var(--_padding) * -1); + } `; } @@ -46,7 +299,64 @@ class Card extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) { /** @protected */ render() { - return html``; + return html` +
+ +
+
+ + + + + + +
+
+ +
+
+ +
+ `; + } + + /** @private */ + _onSlotChange() { + // Chrome doesn't support `:host(:has())`, so we'll recreate that with custom attributes + this.toggleAttribute('has-media', this.querySelector(':scope > [slot="media"]')); + this.toggleAttribute('has-header', this.querySelector(':scope > [slot="header"]')); + this.toggleAttribute( + 'has-title', + this.querySelector(':scope > [slot="title"]') && !this.querySelector(':scope > [slot="header"]'), + ); + this.toggleAttribute( + 'has-subtitle', + this.querySelector(':scope > [slot="subtitle"]') && !this.querySelector(':scope > [slot="header"]'), + ); + this.toggleAttribute('has-header-prefix', this.querySelector(':scope > [slot="header-prefix"]')); + this.toggleAttribute('has-header-suffix', this.querySelector(':scope > [slot="header-suffix"]')); + this.toggleAttribute('has-content', this.querySelector(':scope > :not([slot])')); + this.toggleAttribute('has-footer', this.querySelector(':scope > [slot="footer"]')); + } + + /** @protected */ + connectedCallback() { + super.connectedCallback(); + + if (!CSS.supports('selector(:host(:has([slot])))')) { + if (!this.__boundSlotChangeListener) { + this.__boundSlotChangeListener = this._onSlotChange.bind(this); + } + this.shadowRoot.addEventListener('slotchange', this.__boundSlotChangeListener); + } + } + + /** @protected */ + disconnectedCallback() { + if (this.__boundSlotChangeListener) { + this.shadowRoot.removeEventListener('slotchange', this.__boundSlotChangeListener); + } + super.disconnectedCallback(); } } diff --git a/packages/card/test/visual/card-image.avif b/packages/card/test/visual/card-image.avif new file mode 100644 index 0000000000..3c31250ca4 Binary files /dev/null and b/packages/card/test/visual/card-image.avif differ diff --git a/packages/card/test/visual/lumo/card.test.js b/packages/card/test/visual/lumo/card.test.js index 76faabfcd1..6b6f7f6fb0 100644 --- a/packages/card/test/visual/lumo/card.test.js +++ b/packages/card/test/visual/lumo/card.test.js @@ -1,9 +1,43 @@ import { fixtureSync } from '@vaadin/testing-helpers'; import { visualDiff } from '@web/test-runner-visual-regression'; import '../../../theme/lumo/vaadin-card.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; +import '@vaadin/vaadin-lumo-styles/test/autoload.js'; +import '@vaadin/vaadin-lumo-styles/badge-global.js'; +import '@vaadin/icon'; +import '@vaadin/icons'; +import '@vaadin/avatar/theme/lumo/vaadin-avatar.js'; +window.Vaadin ||= {}; +window.Vaadin.featureFlags ||= {}; window.Vaadin.featureFlags.cardComponent = true; +const content = '
Content
'; +const title = '
Title
'; +const subTitle = '
Subtitle
'; +const header = '
Header
'; +const avatar = ''; +const button = 'Button'; +const image = ''; + +const complexCard = ` + ${image} + ${title} + ${subTitle} +
Badge
+
Content lorem ipsum dolor sit amet.
+ ${button} +
`; + +const complexCardWithIcon = ` + + ${title} + ${subTitle} +
Badge
+
Content lorem ipsum dolor sit amet.
+ ${button} +
`; + describe('card', () => { let div, element; @@ -11,27 +45,163 @@ describe('card', () => { div = document.createElement('div'); div.style.display = 'inline-block'; div.style.padding = '20px'; - element = fixtureSync('
Card
', div); }); - describe('theme', () => { - it('default', async () => { - await visualDiff(div, 'theme-default'); + const cardFixture = (content) => fixtureSync(`${content}`, div); + + describe('slot', () => { + it('content', async () => { + element = cardFixture(content); + await visualDiff(div, 'slot-content'); + }); + + it('title', async () => { + element = cardFixture(title); + await visualDiff(div, 'slot-title'); + }); + + it('subtitle', async () => { + element = cardFixture(subTitle); + await visualDiff(div, 'slot-subtitle'); + }); + + it('title-subtitle', async () => { + element = cardFixture(`${avatar}${title}${subTitle}`); + await visualDiff(div, 'slot-title-subtitle'); + }); + + it('title-subtitle-header', async () => { + element = cardFixture(`${avatar}${header}${title}${subTitle}`); + await visualDiff(div, 'slot-title-subtitle-header'); + }); + + it('header', async () => { + element = cardFixture(header); + await visualDiff(div, 'slot-header'); }); + it('header-prefix', async () => { + element = cardFixture('
Prefix
'); + await visualDiff(div, 'slot-header-prefix'); + }); + + it('header-suffix', async () => { + element = cardFixture('
Suffix
'); + await visualDiff(div, 'slot-header-suffix'); + }); + + it('footer', async () => { + element = cardFixture('
Footer
'); + await visualDiff(div, 'slot-footer'); + }); + + it('media', async () => { + element = cardFixture(image); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'slot-media'); + resolve(); + }; + }); + }); + + it('multiple', async () => { + element = fixtureSync(complexCard, div); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'slot-multiple'); + resolve(); + }; + }); + }); + }); + + describe('theme', () => { it('outlined', async () => { + element = cardFixture(content); element.setAttribute('theme', 'outlined'); await visualDiff(div, 'theme-outlined'); }); it('elevated', async () => { + element = cardFixture(content); + div.style.setProperty('background-image', 'linear-gradient(var(--lumo-shade-5pct), var(--lumo-shade-5pct))'); element.setAttribute('theme', 'elevated'); await visualDiff(div, 'theme-elevated'); }); - it('outlined elevated', async () => { + it('outlined-elevated', async () => { + element = cardFixture(content); + div.style.setProperty('background-image', 'linear-gradient(var(--lumo-shade-5pct), var(--lumo-shade-5pct))'); element.setAttribute('theme', 'outlined elevated'); await visualDiff(div, 'theme-outlined-elevated'); }); + + it('stretch-media', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'stretch-media'); + element.style.setProperty('width', '300px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-media-stretch'); + resolve(); + }; + }); + }); + + it('cover-media-image', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'cover-media'); + element.style.setProperty('width', '300px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-media-cover-image'); + resolve(); + }; + }); + }); + + it('cover-media-icon', async () => { + element = fixtureSync(complexCardWithIcon, div); + element.setAttribute('theme', 'cover-media'); + element.style.setProperty('width', '300px'); + await visualDiff(div, 'theme-media-cover-icon'); + }); + + it('horizontal', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'horizontal'); + element.style.setProperty('width', '400px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-horizontal'); + resolve(); + }; + }); + }); + + it('horizontal-cover-media', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'horizontal cover-media'); + element.style.setProperty('width', '400px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-horizontal-media-cover'); + resolve(); + }; + }); + }); + + it('horizontal-stretch-media', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'horizontal stretch-media'); + element.style.setProperty('width', '400px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-horizontal-media-stretch'); + resolve(); + }; + }); + }); }); }); diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-content.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-content.png new file mode 100644 index 0000000000..4005bf4556 Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-content.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-footer.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-footer.png new file mode 100644 index 0000000000..0125926c8a Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-footer.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header-prefix.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header-prefix.png new file mode 100644 index 0000000000..4124b8da1c Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header-prefix.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header-suffix.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header-suffix.png new file mode 100644 index 0000000000..7f1be7e0a5 Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header-suffix.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header.png new file mode 100644 index 0000000000..b3fce1921c Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-header.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-media.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-media.png new file mode 100644 index 0000000000..131b6b2fc0 Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-media.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-multiple.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-multiple.png new file mode 100644 index 0000000000..3732014ec7 Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-multiple.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-subtitle.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-subtitle.png new file mode 100644 index 0000000000..f1e5c43acb Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-subtitle.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title-subtitle-header.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title-subtitle-header.png new file mode 100644 index 0000000000..429f6aa4ae Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title-subtitle-header.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title-subtitle.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title-subtitle.png new file mode 100644 index 0000000000..b0ec963da6 Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title-subtitle.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title.png b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title.png new file mode 100644 index 0000000000..21f7353f5a Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/slot-title.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-default.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-default.png deleted file mode 100644 index 3c0a7b5212..0000000000 Binary files a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-default.png and /dev/null differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-elevated.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-elevated.png index 190e6fb131..19f7c8fc73 100644 Binary files a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-elevated.png and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-elevated.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal-media-cover.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal-media-cover.png new file mode 100644 index 0000000000..4fdacafd28 Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal-media-cover.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal-media-stretch.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal-media-stretch.png new file mode 100644 index 0000000000..aacb7e8e4e Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal-media-stretch.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal.png new file mode 100644 index 0000000000..c401f5788e Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-horizontal.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-cover-icon.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-cover-icon.png new file mode 100644 index 0000000000..f18a0b927d Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-cover-icon.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-cover-image.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-cover-image.png new file mode 100644 index 0000000000..8b11d053df Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-cover-image.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-stretch.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-stretch.png new file mode 100644 index 0000000000..a4024d5d4e Binary files /dev/null and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-media-stretch.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined-elevated.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined-elevated.png index 6cf38ab4f6..43b0e02a1e 100644 Binary files a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined-elevated.png and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined-elevated.png differ diff --git a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined.png b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined.png index 959322ccf7..0ff86d099b 100644 Binary files a/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined.png and b/packages/card/test/visual/lumo/screenshots/card/baseline/theme-outlined.png differ diff --git a/packages/card/test/visual/material/card.test.js b/packages/card/test/visual/material/card.test.js index b049721903..52438c1d89 100644 --- a/packages/card/test/visual/material/card.test.js +++ b/packages/card/test/visual/material/card.test.js @@ -1,9 +1,44 @@ import { fixtureSync } from '@vaadin/testing-helpers'; import { visualDiff } from '@web/test-runner-visual-regression'; import '../../../theme/material/vaadin-card.js'; +import '@vaadin/button/theme/material/vaadin-button.js'; +import '@vaadin/vaadin-material-styles/color-global.js'; +import '@vaadin/vaadin-material-styles/typography-global.js'; +import '@vaadin/icon'; +import '@vaadin/icons'; +import '@vaadin/avatar/theme/material/vaadin-avatar.js'; +window.Vaadin ||= {}; +window.Vaadin.featureFlags ||= {}; window.Vaadin.featureFlags.cardComponent = true; +const content = '
Content
'; +const title = '
Title
'; +const subTitle = '
Subtitle
'; +const header = '
Header
'; +const avatar = ''; +const suffix = '
Suffix
'; +const button = 'Button'; +const image = ''; + +const complexCard = ` + ${image} + ${title} + ${subTitle} + ${suffix} +
Content lorem ipsum dolor sit amet.
+ ${button} +
`; + +const complexCardWithIcon = ` + + ${title} + ${subTitle} + ${suffix} +
Content lorem ipsum dolor sit amet.
+ ${button} +
`; + describe('card', () => { let div, element; @@ -11,27 +46,163 @@ describe('card', () => { div = document.createElement('div'); div.style.display = 'inline-block'; div.style.padding = '20px'; - element = fixtureSync('
Card
', div); }); - describe('theme', () => { - it('default', async () => { - await visualDiff(div, 'theme-default'); + const cardFixture = (content) => fixtureSync(`${content}`, div); + + describe('slot', () => { + it('content', async () => { + element = cardFixture(content); + await visualDiff(div, 'slot-content'); + }); + + it('title', async () => { + element = cardFixture(title); + await visualDiff(div, 'slot-title'); + }); + + it('subtitle', async () => { + element = cardFixture(subTitle); + await visualDiff(div, 'slot-subtitle'); + }); + + it('title-subtitle', async () => { + element = cardFixture(`${avatar}${title}${subTitle}`); + await visualDiff(div, 'slot-title-subtitle'); + }); + + it('title-subtitle-header', async () => { + element = cardFixture(`${avatar}${header}${title}${subTitle}`); + await visualDiff(div, 'slot-title-subtitle-header'); + }); + + it('header', async () => { + element = cardFixture(header); + await visualDiff(div, 'slot-header'); }); + it('header-prefix', async () => { + element = cardFixture('
Prefix
'); + await visualDiff(div, 'slot-header-prefix'); + }); + + it('header-suffix', async () => { + element = cardFixture(suffix); + await visualDiff(div, 'slot-header-suffix'); + }); + + it('footer', async () => { + element = cardFixture('
Footer
'); + await visualDiff(div, 'slot-footer'); + }); + + it('media', async () => { + element = cardFixture(image); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'slot-media'); + resolve(); + }; + }); + }); + + it('multiple', async () => { + element = fixtureSync(complexCard, div); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'slot-multiple'); + resolve(); + }; + }); + }); + }); + + describe('theme', () => { it('outlined', async () => { + element = cardFixture(content); element.setAttribute('theme', 'outlined'); await visualDiff(div, 'theme-outlined'); }); it('elevated', async () => { + element = cardFixture(content); + div.style.setProperty('background', 'var(--material-secondary-background-color)'); element.setAttribute('theme', 'elevated'); await visualDiff(div, 'theme-elevated'); }); - it('outlined elevated', async () => { + it('outlined-elevated', async () => { + element = cardFixture(content); + div.style.setProperty('background', 'var(--material-secondary-background-color)'); element.setAttribute('theme', 'outlined elevated'); await visualDiff(div, 'theme-outlined-elevated'); }); + + it('stretch-media', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'stretch-media'); + element.style.setProperty('width', '300px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-media-stretch'); + resolve(); + }; + }); + }); + + it('cover-media-image', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'cover-media'); + element.style.setProperty('width', '300px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-media-cover-image'); + resolve(); + }; + }); + }); + + it('cover-media-icon', async () => { + element = fixtureSync(complexCardWithIcon, div); + element.setAttribute('theme', 'cover-media'); + element.style.setProperty('width', '300px'); + await visualDiff(div, 'theme-media-cover-icon'); + }); + + it('horizontal', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'horizontal'); + element.style.setProperty('width', '400px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-horizontal'); + resolve(); + }; + }); + }); + + it('horizontal-cover-media', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'horizontal cover-media'); + element.style.setProperty('width', '400px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-horizontal-media-cover'); + resolve(); + }; + }); + }); + + it('horizontal-stretch-media', async () => { + element = fixtureSync(complexCard, div); + element.setAttribute('theme', 'horizontal stretch-media'); + element.style.setProperty('width', '400px'); + await new Promise((resolve) => { + element.querySelector('img').onload = async () => { + await visualDiff(div, 'theme-horizontal-media-stretch'); + resolve(); + }; + }); + }); }); }); diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-content.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-content.png new file mode 100644 index 0000000000..4d41e4e83f Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-content.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-footer.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-footer.png new file mode 100644 index 0000000000..92ba9b4435 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-footer.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-header-prefix.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-header-prefix.png new file mode 100644 index 0000000000..e5d05b7bf6 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-header-prefix.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-header-suffix.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-header-suffix.png new file mode 100644 index 0000000000..b0442813a7 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-header-suffix.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-header.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-header.png new file mode 100644 index 0000000000..416b99dde9 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-header.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-media.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-media.png new file mode 100644 index 0000000000..d7ffa5c308 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-media.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-multiple.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-multiple.png new file mode 100644 index 0000000000..3bc833d74b Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-multiple.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-subtitle.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-subtitle.png new file mode 100644 index 0000000000..b16c1d1b85 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-subtitle.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-title-subtitle-header.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-title-subtitle-header.png new file mode 100644 index 0000000000..192d28cebf Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-title-subtitle-header.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-title-subtitle.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-title-subtitle.png new file mode 100644 index 0000000000..49fc8a18a7 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-title-subtitle.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/slot-title.png b/packages/card/test/visual/material/screenshots/card/baseline/slot-title.png new file mode 100644 index 0000000000..6d192eb257 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/slot-title.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-default.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-default.png deleted file mode 100644 index 7b1d7bc9a3..0000000000 Binary files a/packages/card/test/visual/material/screenshots/card/baseline/theme-default.png and /dev/null differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-elevated.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-elevated.png index 6984b0ec90..abbec30db9 100644 Binary files a/packages/card/test/visual/material/screenshots/card/baseline/theme-elevated.png and b/packages/card/test/visual/material/screenshots/card/baseline/theme-elevated.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal-media-cover.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal-media-cover.png new file mode 100644 index 0000000000..af86171583 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal-media-cover.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal-media-stretch.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal-media-stretch.png new file mode 100644 index 0000000000..7210a210a9 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal-media-stretch.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal.png new file mode 100644 index 0000000000..2a59284ac4 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/theme-horizontal.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-media-cover-icon.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-media-cover-icon.png new file mode 100644 index 0000000000..c2f8d89f79 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/theme-media-cover-icon.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-media-cover-image.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-media-cover-image.png new file mode 100644 index 0000000000..8da845a89c Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/theme-media-cover-image.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-media-stretch.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-media-stretch.png new file mode 100644 index 0000000000..eaef339de0 Binary files /dev/null and b/packages/card/test/visual/material/screenshots/card/baseline/theme-media-stretch.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined-elevated.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined-elevated.png index 937d0a2d4c..7fe7d006c9 100644 Binary files a/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined-elevated.png and b/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined-elevated.png differ diff --git a/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined.png b/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined.png index e407d101a4..6256d1e3a7 100644 Binary files a/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined.png and b/packages/card/test/visual/material/screenshots/card/baseline/theme-outlined.png differ diff --git a/packages/card/theme/lumo/vaadin-card-styles.js b/packages/card/theme/lumo/vaadin-card-styles.js index da04ea6760..7137535f1a 100644 --- a/packages/card/theme/lumo/vaadin-card-styles.js +++ b/packages/card/theme/lumo/vaadin-card-styles.js @@ -1,5 +1,7 @@ import '@vaadin/vaadin-lumo-styles/color.js'; import '@vaadin/vaadin-lumo-styles/style.js'; +import '@vaadin/vaadin-lumo-styles/spacing.js'; +import '@vaadin/vaadin-lumo-styles/typography.js'; import { addGlobalThemeStyles, css, registerStyles } from '@vaadin/vaadin-themable-mixin/register-styles.js'; const cardProps = css` @@ -8,6 +10,8 @@ const cardProps = css` --vaadin-card-border-radius: var(--lumo-border-radius-l); --vaadin-card-border-width: 0; --vaadin-card-border-color: var(--lumo-contrast-20pct); + --vaadin-card-padding: var(--lumo-space-m); + --vaadin-card-gap: var(--lumo-space-m); } `; @@ -26,7 +30,6 @@ const card = css` content: ''; position: absolute; inset: 0; - z-index: 999; pointer-events: none; border-radius: inherit; border: var(--vaadin-card-border, var(--vaadin-card-border-width) solid var(--vaadin-card-border-color)); @@ -49,6 +52,23 @@ const card = css` inset 0 -1px 0 0 var(--lumo-shade-10pct), var(--vaadin-card-box-shadow); } + + :host(:where([theme~='stretch-media'])) ::slotted([slot='media']:is(img, video, svg, vaadin-icon)) { + border-radius: var(--lumo-border-radius-m); + } + + ::slotted([slot='title']) { + font-size: var(--lumo-font-size-l); + line-height: var(--lumo-line-height-xs); + font-weight: 600; + color: var(--lumo-header-text-color); + } + + ::slotted([slot='subtitle']) { + font-size: var(--lumo-font-size-m); + line-height: var(--lumo-line-height-xs); + color: var(--lumo-secondary-text-color); + } `; registerStyles('vaadin-card', card, { moduleId: 'lumo-card' }); diff --git a/packages/card/theme/material/vaadin-card-styles.js b/packages/card/theme/material/vaadin-card-styles.js index e4c01c7055..b036a5a055 100644 --- a/packages/card/theme/material/vaadin-card-styles.js +++ b/packages/card/theme/material/vaadin-card-styles.js @@ -1,5 +1,6 @@ import '@vaadin/vaadin-material-styles/color.js'; import '@vaadin/vaadin-material-styles/shadow.js'; +import '@vaadin/vaadin-material-styles/typography.js'; import { addGlobalThemeStyles, css, registerStyles } from '@vaadin/vaadin-themable-mixin/register-styles.js'; const cardProps = css` @@ -8,6 +9,8 @@ const cardProps = css` --vaadin-card-border-radius: 8px; --vaadin-card-border-width: 0; --vaadin-card-border-color: var(--material-divider-color); + --vaadin-card-padding: 16px; + --vaadin-card-gap: 16px; } `; @@ -26,7 +29,6 @@ const card = css` content: ''; position: absolute; inset: 0; - z-index: 999; pointer-events: none; border-radius: inherit; border: var(--vaadin-card-border, var(--vaadin-card-border-width) solid var(--vaadin-card-border-color)); @@ -41,6 +43,19 @@ const card = css` --vaadin-card-background: var(--material-background-color); --vaadin-card-box-shadow: var(--material-shadow-elevation-2dp); } + + :host(:where([theme~='stretch-media'])) ::slotted([slot='media']:is(img, video, svg, vaadin-icon)) { + border-radius: 4px; + } + + ::slotted([slot='title']) { + font-size: var(--material-h6-font-size); + font-weight: 700; + } + + ::slotted([slot='subtitle']) { + color: var(--material-secondary-text-color); + } `; registerStyles('vaadin-card', card, { moduleId: 'material-card' });