Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/card/card-content-meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class CardContentMeta extends LitElement {
return css`
:host {
box-sizing: border-box;
color: var(--d2l-color-tungsten);
color: var(--d2l-theme-text-color-static-subtle);
display: inline-block;
font-size: 0.7rem;
font-weight: 400;
Expand Down
41 changes: 18 additions & 23 deletions components/card/card-loading-shimmer.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import '../colors/colors.js';
import { css, html, LitElement } from 'lit';
import { SkeletonMixin, skeletonStyles } from '../skeleton/skeleton-mixin.js';

/**
* A card layout component for when the card header is loading.
* @slot - Slot for header content being loaded
*/
class CardLoadingShimmer extends LitElement {
class CardLoadingShimmer extends SkeletonMixin(LitElement) {

static get properties() {
return {
Expand All @@ -18,37 +19,25 @@ class CardLoadingShimmer extends LitElement {
}

static get styles() {
return css`
return [skeletonStyles, css`
:host([hidden]) {
display: none;
}

.d2l-card-loading-indicator {
background-color: var(--d2l-color-regolith);
.d2l-skeletize {
border-radius: 7px 7px 0 0;
box-shadow: inset 0 -1px 0 0 var(--d2l-color-gypsum);
height: inherit;
overflow: hidden;
position: relative;
}

.d2l-card-loading-indicator::after {
animation: loadingShimmer 1.5s ease-in-out infinite;
background: linear-gradient(90deg, rgba(249, 250, 251, 0.1), rgba(114, 119, 122, 0.1), rgba(249, 250, 251, 0.1));
background-color: var(--d2l-color-regolith);
content: "";
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
:host([skeleton]) .d2l-skeletize::before {
box-shadow: inset 0 -1px 0 0 var(--d2l-color-gypsum);
border-radius: 0;
}

@keyframes loadingShimmer {
0% { transform: translate3d(-100%, 0, 0); }
100% { transform: translate3d(100%, 0, 0); }
:host([skeleton]) slot {
display: none;
}
`;
`];
}

constructor() {
Expand All @@ -58,11 +47,17 @@ class CardLoadingShimmer extends LitElement {

render() {
return html`
<div ?hidden="${!this.loading}" class="d2l-card-loading-indicator"></div>
<div ?hidden="${this.loading}"><slot></slot></div>
<div class="d2l-skeletize"><slot></slot></div>
`;
}

willUpdate(changedProperties) {
super.willUpdate(changedProperties);
if (changedProperties.has('loading')) {
this.skeleton = this.loading;
}
}

}

customElements.define('d2l-card-loading-shimmer', CardLoadingShimmer);
16 changes: 7 additions & 9 deletions components/card/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ class Card extends LitElement {
static get styles() {
return [offscreenStyles, css`
:host {
background-color: #ffffff;
border: 1px solid var(--d2l-color-gypsum);
background-color: var(--d2l-theme-background-color-base);
border: 1px solid var(--d2l-theme-border-color-subtle);
border-radius: 6px;
box-sizing: border-box;
display: inline-block;
Expand Down Expand Up @@ -191,20 +191,18 @@ class Card extends LitElement {
border: none;
}
:host([subtle][href]) {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.03);
}
:host([href]:not([_active]):hover) {
box-shadow: 0 2px 14px 1px rgba(0, 0, 0, 0.06);
box-shadow: var(--d2l-theme-shadow-attached);

@dbatiste dbatiste May 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that "attached" is probably what we want here, at least based on how the shadows are currently defined.

We have subtle card shadow...
from: 0 4px 8px 0 rgba(0, 0, 0, 0.03)
to: 0 2px 4px 0 rgba(0, 0, 0, 0.03) (attached)

hover from: 0 4px 18px 2px rgba(0, 0, 0, 0.06)
to: 0 2px 12px 0 rgba(0, 0, 0, 0.15) (floating)

We have normal card shadow...
hover from: 0 2px 14px 1px rgba(0, 0, 0, 0.06)
to: 0 2px 12px 0 rgba(0, 0, 0, 0.15) (floating)

Do I have that right?

Those seem pretty similar, but the floating semantic doesn't seem right to me, and I'm not sure this is being considered in the shadow design revisions.

}
:host([href]:not([_active]):hover),
:host([subtle][href]:not([_active]):hover) {
box-shadow: 0 4px 18px 2px rgba(0, 0, 0, 0.06);
box-shadow: var(--d2l-theme-shadow-floating);
}
${getFocusRingStyles(() => ':host([_active])', { 'extraStyles': css`border-color: transparent;` })}
/* .d2l-card-link-container-hover is used to only color/underline when
hovering the anchor; these styles are not applied when hovering actions */
:host([href]) .d2l-card-link-container-hover,
:host([href][_active]) .d2l-card-content {
color: var(--d2l-color-celestine);
color: var(--d2l-theme-text-color-interactive-default);
text-decoration: underline;
}
/* this is needed to ensure tooltip is not be clipped by adjacent cards */
Expand All @@ -222,7 +220,7 @@ class Card extends LitElement {
}
@media (prefers-contrast: more) {
:host([subtle]) {
border: 1px solid var(--d2l-color-gypsum);
border: 1px solid var(--d2l-theme-border-color-subtle);
}
}
`];
Expand Down
4 changes: 3 additions & 1 deletion components/card/demo/card.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import '../card-content-meta.js';
import '../card-content-title.js';
import '../card-footer-link.js';
import { registerCustomSemanticVariableValue } from '../../colors/colors.js';
registerCustomSemanticVariableValue('--d2l-subtle-cards-background', 'var(--d2l-theme-background-color-sunken)', 'var(--d2l-color-ferrite)');
</script>
<style>
.cards {
Expand All @@ -26,7 +28,7 @@
gap: 0.6rem;
}
.cards-subtle {
background-color: #f6f7f8;
background-color: var(--d2l-subtle-cards-background);
padding: 20px;
}
d2l-card {
Expand Down
24 changes: 13 additions & 11 deletions components/card/test/card-content.vdiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import '../card-content-title.js';
import { expect, fixture, html } from '@brightspace-ui/testing';

describe('card-content-meta', () => {
it('default', async() => {
const elem = await fixture(html`
<d2l-card-content-meta>
Card Meta
<ul>
<li>meta-data 1</li>
<li>meta-data 2</li>
</ul>
</d2l-card-content-meta>
`);
await expect(elem).to.be.golden();
['light', 'dark'].forEach(colorMode => {
it(`default-${colorMode}`, async() => {
const elem = await fixture(html`
<d2l-card-content-meta>
Card Meta
<ul>
<li>meta-data 1</li>
<li>meta-data 2</li>
</ul>
</d2l-card-content-meta>
`, { colorMode });
await expect(elem).to.be.golden();
});
});
});

Expand Down
33 changes: 22 additions & 11 deletions components/card/test/card.vdiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import '../../tooltip/tooltip.js';
import '../card.js';
import '../card-loading-shimmer.js';
import { clickElem, expect, fixture, focusElem, hoverElem, html, oneEvent } from '@brightspace-ui/testing';

import { registerCustomSemanticVariableValue } from '../../colors/colors.js';
registerCustomSemanticVariableValue('--d2l-subtle-cards-background', 'var(--d2l-theme-background-color-subtle)', 'var(--d2l-color-ferrite)');
registerCustomSemanticVariableValue('--d2l-subtle-cards-header-background', 'orange', 'darkblue');
function createCardTemplate(opts) {
const { alignCenter, content, subtle } = { alignCenter: false, subtle: false, ...opts };
return html`
Expand All @@ -24,7 +26,7 @@ function createLinkCardTemplate(opts) {
}
function createHeader(text) {
return html`
<div slot="header" style="background-color: orange; height: 95px;">${text}</div>
<div slot="header" style="background-color: var(--d2l-subtle-cards-header-background); height: 95px;">${text}</div>
`;
}
function createMultiLinkCardTemplate(opts) {
Expand Down Expand Up @@ -99,15 +101,15 @@ const badgeSlotContent = html`
</div>
`;

const subtleLinkCardTemplate = html`<div style="background-color: #f6f7f8; padding: 20px; width: 300px">
const subtleLinkCardTemplate = html`<div style="background-color: var(--d2l-subtle-cards-background); padding: 20px; width: 300px">
${createLinkCardTemplate({ content: simpleContent, subtle: true })}
</div>`;

describe('card', () => {
[
{ name: 'header-content', template: createCardTemplate({ content: simpleContent }) },
{ name: 'hover', template: createLinkCardTemplate({ content: simpleContent }), action: elem => hoverElem(elem) },
{ name: 'focus', template: createLinkCardTemplate({ content: simpleContent }), action: elem => focusElem(elem) },
{ name: 'hover', template: createLinkCardTemplate({ content: simpleContent }), action: elem => hoverElem(elem), testDarkMode: true },
{ name: 'focus', template: createLinkCardTemplate({ content: simpleContent }), action: elem => focusElem(elem), testDarkMode: true },
{ name: 'footer', template: createCardTemplate({ content: simpleContentWithFooter }) },
{ name: 'align-center', template: createCardTemplate({ content: simpleContentWithFooter, alignCenter: true }) },
{ name: 'badge', template: createCardTemplate({ content: html`${simpleContent}${badgeSlotContent}` }) },
Expand All @@ -133,18 +135,27 @@ describe('card', () => {
await oneEvent(elem, 'd2l-tooltip-show');
} },
{ name: 'loading', template: createCardTemplate({ content: html`<d2l-card-loading-shimmer slot="header" loading style="display: block; height: 103.5px; width: 100%;"></d2l-card-loading-shimmer>` }) },
{ name: 'subtle', template: html`<div style="background-color: #f6f7f8; padding: 20px; width: 300px">
{ name: 'subtle', template: html`<div style="background-color: var(--d2l-subtle-cards-background); padding: 20px; width: 300px">
${createCardTemplate({ content: simpleContent, subtle: true })}
</div>`, cardOnly: true },
{ name: 'subtle-link', template: subtleLinkCardTemplate, cardOnly: true },
{ name: 'subtle-link-hover', template: subtleLinkCardTemplate, action: elem => hoverElem(elem), cardOnly: true },
{ name: 'subtle-link-focus', template: subtleLinkCardTemplate, action: elem => focusElem(elem), cardOnly: true },
].forEach(({ name, template, action, rtl, cardOnly }) => {
</div>`, cardOnly: true, testDarkMode: true },
{ name: 'subtle-link', template: subtleLinkCardTemplate, cardOnly: true, testDarkMode: true },
{ name: 'subtle-link-hover', template: subtleLinkCardTemplate, action: elem => hoverElem(elem), cardOnly: true, testDarkMode: true },
{ name: 'subtle-link-focus', template: subtleLinkCardTemplate, action: elem => focusElem(elem), cardOnly: true, testDarkMode: true },
].forEach(({ name, template, action, rtl, cardOnly, testDarkMode }) => {
it(name, async() => {
let elem = await fixture(template, { rtl });
if (cardOnly) elem = elem.querySelector('d2l-card');
if (action) await action(elem);
await expect(elem).to.be.golden();
});

if (testDarkMode) {
it(`${name}-dark`, async() => {
let elem = await fixture(template, { rtl, colorMode: 'dark' });
if (cardOnly) elem = elem.querySelector('d2l-card');
if (action) await action(elem);
await expect(elem).to.be.golden();
});
}
});
});
18 changes: 13 additions & 5 deletions components/colors/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,19 @@ export function registerSemanticVariableForSvgImageUrl(name, value) {
}

const replacedLightValue = svgToCSS(replaceSemanticVariables(value, lightVariables));
style.sheet.insertRule(`html { ${ name }: ${ replacedLightValue } }`, 0);

const replacedDarkValue = svgToCSS(replaceSemanticVariables(value, darkVariables));
style.sheet.insertRule(`html[data-color-mode="dark"] { ${ name }: ${ replacedDarkValue } }`, 1);
registerCustomSemanticVariableValue(name, replacedLightValue, replacedDarkValue);

}

export function registerCustomSemanticVariableValue(name, lightValue, darkValue) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a double-edged sword, but unless we define all colors inside core I see consumers needing custom colors(e.g. lumi)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is a really slippery slope eh? If they can define there own colors, then it makes it really hard for us to make palette changes with confidence. Also, this requires that the consumer know the different color modes.

We've been having some discussion about the Lumi stuff. It still might be an issue, but I think the "AI colors" might get added to the semantic palette. There's also been some discussion of possibly adding a special button to core, given that AI is becoming more ubiquitous. But that's just the AI case. We also want to avoid multiple people each wanting some unique styles to highlight a new flashy feature, so there is also some discussion about generalizing that.

The Figma extract has a color for FACE background, but I did not include it because Jeff said it was somewhat experimental and we need to find a more general semantic for it. I think we should add this to the Confluence doc (as well as for d2l-collapsible-panel). I think there will be a color for it, it's just undefined atm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we would need a way to programmatically set a custom variable. Not sure what we ended up deciding for color input but if we want to support users being able to set colors for both palettes we would need something like this helper

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the helper internal, I would hope consumers can see red flags when trying to import it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is just for demo at the moment, what do you think about just adding the variables that we anticipate, knowing that the name will likely change, and keeping this helper in our back pocket until we need it in non-demo/test code? The colors (from Figma) are currently defined as:

// light
--d2l-theme-background-color-face: var(--d2l-color-gypsum);
// dark
--d2l-theme-background-color-face: #303335;

I'd only excluded it earlier because we think it is going to be redefined, but it's really no different than the other variables we've added that we expect to change.

if (!name || typeof lightValue !== 'string' || typeof darkValue !== 'string') {
throw new TypeError('registerCustomSemanticVariableValue requires a name, lightValue, and darkValue');
}

style.sheet.insertRule(`html { ${ name }: ${ lightValue } }`, 0);
style.sheet.insertRule(`html[data-color-mode="dark"] { ${ name }: ${ darkValue } }`, 1);
style.sheet.insertRule(`@media (prefers-color-scheme: dark) {
html[data-color-mode="os"] { ${ name }: ${ replacedDarkValue } }
html[data-color-mode="os"] { ${ name }: ${ darkValue } }
}`, 2);
};
}
Loading