feat(cards): swc-card files, styling#6515
Conversation
|
| } | ||
|
|
||
| /* Chromium :host(:has()) support */ | ||
| @scope { |
| expect(buildUnknownTokenSuggestions('a', store)).toEqual([]); | ||
| }); | ||
|
|
||
| it('prefers a true prefix match even when the missing suffix pushes the length delta past the fuzzy-match cutoff', () => { |
There was a problem hiding this comment.
Resolved a small token extension issue
There was a problem hiding this comment.
Do the changes need to be in this PR, or could they possibly be moved out (and potentially hit main sooner)?
📚 Branch Preview Links🔍 Gen1 Visual Regression Test ResultsWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
Deployed to Azure Blob Storage: If the changes are expected, update the |
rise-erpelding
left a comment
There was a problem hiding this comment.
This looks really beautiful 🔥
| --card-title-line-height: token("line-height-font-size-75"); | ||
| --card-description-font-size: token("font-size-50"); | ||
| --card-border-radius: token("corner-radius-500"); | ||
| --card-max-inline-size: 140px; |
There was a problem hiding this comment.
Out of curiosity, did design decide not to set max-inline-size tokens? What determines what px value we use for the max-inline size?
| --card-background-color: transparent; | ||
|
|
||
| .card { | ||
| box-shadow: none; |
There was a problem hiding this comment.
Minor, but is there a reason to not set a custom prop here? (--_card-box-shadow/--card-box-shadow)
| ───────────────────────────────────────────────────────────────────────────── */ | ||
|
|
||
| .card { | ||
| --_card-outline-width: var(--card-outline-width, 0px); |
There was a problem hiding this comment.
Custom props here all start with --card- or --_card-, is there a reason none of them start with --swc?
| - **`<a>` as the slotted element itself — full fidelity.** `::slotted()` only matches elements *directly* assigned to a slot, never their descendants, so the [pseudo-content stretched-link trick](https://inclusive-components.design/cards/#thepseudocontenttrick) (`::slotted(a)::after`, absolutely positioned to cover the card surface) is only structurally possible in this form — it's not a browser-support question, `::slotted(a)` cannot see a nested anchor at all, by spec. Browser support for `::slotted()::after` itself has been confirmed for this project's target matrix, so where it applies, this gives real native-anchor behavior across the *entire extended surface*: middle-click, cmd/ctrl-click to open in a new tab, and right-click's "open in new tab" context menu item all work, because the browser is hit-testing the actual anchor, not a JS approximation of it. The rule itself doesn't exist yet — it lands in `card-template.css` alongside the rest of the per-component styling work, not blocked on anything. | ||
| - **Anchor nested inside a wrapper — reduced fidelity.** The stretched-link CSS trick doesn't apply. The card falls back to the JS click-proxy (below) for the extended surface: plain click/tap anywhere on the card activates the link, but middle-click, modifier-clicks, and the right-click context menu only work if the user directly targets the visible anchor text itself — the JS proxy calls `.click()`, which cannot forward modifier-key or button state the way native hit-testing does. | ||
| - **Universal fallback / primary mechanism regardless of form:** a click-proxy on the card surface finds the title's linked anchor (`renderRoot.querySelector('slot[name="title"]').assignedElements()`, checking both forms above) and calls `.click()` on it, once filtering (below) rules out clicks that landed on a different interactive target. This is what's implemented today in `Card.base.ts`, and is sufficient on its own — the CSS trick is an enhancement layered on top for the direct-anchor form, not a requirement. | ||
| - **CSS hook, independent of fidelity tier:** `[title-as-link]` (and `[selectable]`) are useful as plain attribute selectors for hover/focus-visible/cursor-pointer styling on the card surface (e.g. `:host([title-as-link]) .card:hover`), giving a consistent visual affordance that the whole card is interactive regardless of which interaction tier actually applies underneath. |
There was a problem hiding this comment.
Just to clarify, when the migration plan says
[title-as-link](and[selectable]) are useful as plain attribute selectors for hover/focus-visible/cursor-pointer styling on the card surface
This doesn't mean that a selectable card gets cursor: pointer on it, does it? We would only do a cursor: pointer on the title-as-link card?
Asking for a friend (Claude) who got all tripped up on this.
| expect(buildUnknownTokenSuggestions('a', store)).toEqual([]); | ||
| }); | ||
|
|
||
| it('prefers a true prefix match even when the missing suffix pushes the length delta past the fuzzy-match cutoff', () => { |
There was a problem hiding this comment.
Do the changes need to be in this PR, or could they possibly be moved out (and potentially hit main sooner)?
Description
Implements the initial
swc-cardcomponent: file/API scaffold,CardBaseshared behaviors (already merged), and the sharedcard-template.cssstyling used by every card in the family.swc-cardscaffold:Card.ts,swc-card.ts,index.ts,card.css(placeholder for component-specific overrides)renderCardTemplate()(card-template.ts), reused by every concrete cardcard-template.css(_lit-styles/): base grid structure, size/density/visual (primary/secondary/tertiary/quiet) variants,title-as-linkstretched-link + nested-target elevation,actions-slot suppression atsize="xs", forced-colors overrideCardBase(core):size/variant/density,title-as-linkandselectablebehaviors (click-proxy, keyboard activation, interactive-target filtering),actions-slot size propagation and conditional support, dev-mode warningsCardBasetest-only fixtures, plus 7 per-story smoke testsMotivation and context
Establishes the shared foundation (
CardBase,renderCardTemplate(),card-template.css) thatswc-user-cardandswc-product-cardwill extend directly (sibling-inheritance, same pattern asButtonBase→Button/ActionButton).swc-cardis built first so its styling work also produces the reusable template both follow-up components depend on.Related issue(s)
Deferred to follow-up
swc-cardticketsThis PR covers structure, API, and shared styling only. Scoped out to keep the PR reviewable:
collectionslot (1–3 images, square aspect ratio). Tracked as its own follow-up ticket.previewis populated andtitle/description/actions/default/footerare all empty. Tracked as its own follow-up ticket.actions-slot reuse — still an open design question (Q5 in the card family plan), not implemented.selectableStorybook coverage — theSelectablestory is a placeholder TODO; full behaviors/accessibility story authoring lands in the documentation pass.swc-card, not yet started.CardBasebehavior level; real browser/Playwright verification of the extended click surface is still outstanding.Manual review test cases
Size and variant rendering
CardStorybook page,SizesandVariantsstoriesxs/s/m/l/xl) and variant (primary/secondary/tertiary/quiet) renders distinct, expected stylingActions slot at
xssize="xs"with content in theactionsslot (e.g. via the Playground story)Title as link
Title as linkstoryAccessibility testing checklist
Keyboard (required — document steps below)
CardPlayground story and enableselectablevia Controlsswc-card-clickevent to fire each time (check the Actions panel)Title as linkstory and press Tab — expect focus to land on the title's anchor directly (the card itself is not an extra tab stop unlessselectableis also set)title-as-linkandselectabletogether,Tabto the card, press Enter/Space — expect both the link activation and theswc-card-clickevent to fire from the same interactionsize="xs"andactionsslot content present, confirm the actions content cannot receive focus via TabScreen reader (required — document steps below)
selectablecard — note that no ARIA role is set yet (deferred pending theCardViewselection model; tracked in the card family plan), so it will announce as a generic focusable element rather than e.g. "button"title/description/actions/footercontent announces using its own native semantics — Card does not alter or relabel slotted contentactionscontent hidden atsize="xs"is not exposed to the accessibility tree (CSSdisplay: noneremoves it)Device review