Skip to content

feat: card primitives web app gaps#2769

Merged
mnoleto merged 7 commits into
mainfrom
feat/card-primitives-web-app-gaps
Jun 19, 2026
Merged

feat: card primitives web app gaps#2769
mnoleto merged 7 commits into
mainfrom
feat/card-primitives-web-app-gaps

Conversation

@mnoleto

@mnoleto mnoleto commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes API gaps in the Card.* primitives surfaced while web-app rebuilds its library asset/folder cards on Fondue. Five backward-compatible additions across Card.Banner, Card.BannerImage, Card.Root, and Card.Action — no breaking changes; every new prop defaults to today's behavior.

🎨 Design

Figma

What changed

Primitive Change
Card.BannerImage New padding prop (none | small | medium | large; none by default). Applies inner padding so logo/icon previews get breathing room; pairs with fit="contain".
Card.Banner New tone prop (dim | inverted). inverted = dark drop-target state (near-black bg + white icon); dim pins surface-dim and opts out of the implicit hover/active shift. Unset = previous behavior.
Card.Root Forwards className (merged after internal styles) — enables Tailwind group / group-hover: without an extra wrapper.
Card.Action Spreads arbitrary data-* (and other unknown) props onto the element — e.g. an Intercom/analytics selector — no wrapper needed.

Design decisions & trade-offs

  • Dark state lives on Card.Banner, not Card.BannerIcon. Background is a banner concern, so a single tone="inverted" sets the dark background and auto-flips the nested icon to on-primary (white). This also resolves the "don't auto-change background" friction: setting any tone pins the background and disables the implicit :has(> .bannerIcon) hover shift.
  • Padding tokens mapped by value, not name. --spacing-medium is 16px, but the spec wants medium = 24px, so small/medium/large--spacing-small/--spacing-large/--spacing-x-large (12/24/32px). Documented in the SCSS.

mnoleto and others added 6 commits June 16, 2026 13:54
Two related additions to the Card.Banner family, addressing gaps hit while
rebuilding web-app's library cards on the Card.* primitives:

- Card.BannerImage gains a `padding` prop ('none' | 'small' | 'medium' |
  'large'; none by default). Padding is applied inside the banner and pairs
  with `fit="contain"`, giving logo/icon previews 12/24/32px of breathing room
  without dropping out of the component. Tokens are mapped by value, not by
  same-named spacing tokens (--spacing-medium is 16px, not 24px).

- Card.Banner gains a `tone` prop ('dim' | 'active' | 'inverted'). 'inverted'
  renders the dark drop-target state (near-black background + white icon);
  'dim'/'active' pin the background and opt out of the implicit hover/active
  shift applied when a Card.BannerIcon is nested. Leaving `tone` unset preserves
  the previous behavior. This supersedes a separate BannerIcon dark variant:
  background is a banner concern, so one tone prop covers both the dark state
  and the "don't auto-change background" request.

Includes Storybook stories (padded image, inverted/dim tones), unit tests, and
JSDoc. Backward compatible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Card.Root now accepts a `className`, merged after the internal styles onto its
root element (same pattern as Button/Notice/Divider). This lets consumers apply
layout hooks such as Tailwind's `group` — e.g. to drive `group-hover:` styles on
descendants — without wrapping the card in an extra element that could break the
surrounding grid. Backward compatible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Card.Action now spreads unknown props onto its rendered <div> (className and the
internal data-test-id default are still owned by the component). This lets
consumers attach attributes such as an analytics/onboarding-tour selector
(e.g. data-intercom-tour-selector) directly on Card.Action instead of wrapping
its contents in an extra element. Backward compatible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The unit tests assert the data-* plumbing; these Playwright component tests
verify the actual computed CSS, which jsdom can't resolve:

- BannerImage `padding="medium"` applies --spacing-large (24px) inner padding
  and keeps object-fit: contain.
- Banner `tone="inverted"` paints --color-primary-default and flips the nested
  icon to --color-primary-on-primary.
- Banner `tone="dim"` stays dim on hover (opts out of the implicit shift), while
  an un-toned banner still shifts to --color-surface-hover (backward-compat).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Narrow CardBannerTone to 'dim' | 'inverted'. The 'active' value had no consuming
use case (the dark drop-target state is 'inverted'; 'dim' covers pinning the
resting background), so removing it keeps the public API focused. Updates the
type, JSDoc, SCSS, and changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- BannerImageWithPadding: add live `padding` and `fit` controls and use an
  inline SVG logo as the image, so the breathing room is adjustable and clear.
- BannerToneInverted: use a white arrow-align-down icon (the drop-target glyph),
  which inherits the inverted tone's on-primary color automatically.
- BannerToneDim: show a default vs tone="dim" pair, since the difference only
  appears on hover (default brightens, dim stays). Hover to compare.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mnoleto mnoleto requested a review from noahwaldner June 17, 2026 08:02
@mnoleto mnoleto requested a review from a team as a code owner June 17, 2026 08:02
@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 41d779d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@frontify/fondue-components Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jun 17, 2026

Copy link
Copy Markdown

Deploy Preview for fondue-components ready!

Name Link
🔨 Latest commit 41d779d
🔍 Latest deploy log https://app.netlify.com/projects/fondue-components/deploys/6a325fed01ebcf0008d1bfbe
😎 Deploy Preview https://deploy-preview-2769.components.fondue-components.frontify.com
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

SonarCloud flagged the `useState` calls in the inline `render` arrows of the
new banner stories (a lowercase `render` isn't recognized as a React component
or a hook). Move each stateful story body into an uppercase-named component so
the hooks run inside a real component, and drop the now-redundant inline
comments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mnoleto mnoleto requested review from SamuelAlev and removed request for noahwaldner June 18, 2026 11:56
@SamuelAlev SamuelAlev requested review from noahwaldner and syeo66 and removed request for SamuelAlev June 18, 2026 21:13

type BannerImagePaddingControls = { padding: CardBannerImagePadding; fit: CardBannerFit };

// Story bodies are extracted into named components so `useState` runs inside a React component.

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.

Is this comment needed?

@syeo66 syeo66 left a comment

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.

LGTM

@mnoleto mnoleto merged commit a75f700 into main Jun 19, 2026
18 checks passed
@mnoleto mnoleto deleted the feat/card-primitives-web-app-gaps branch June 19, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants