Skip to content

Add Card component#7723

Open
liuliu-dev wants to merge 20 commits intomainfrom
liuliu/add-card-component
Open

Add Card component#7723
liuliu-dev wants to merge 20 commits intomainfrom
liuliu/add-card-component

Conversation

@liuliu-dev
Copy link
Copy Markdown
Contributor

@liuliu-dev liuliu-dev commented Apr 1, 2026

Upstream the Card component from Pacer into @primer/react.

Issue: https://github.com/github/primer/issues/6467

Changelog

New

  • Card — A styled container component with border, box-shadow, and border-radius; also supports custom content without subcomponents (plain container mode)
  • Card.Icon — Renders a decorative icon in the card header (defaults to aria-hidden, supports aria-label for meaningful icons)
  • Card.Image — Renders an edge-to-edge image in the card header
  • Card.Heading — Renders a heading (defaults to h3, configurable via as prop)
  • Card.Description — Renders a description below the heading
  • Card.Menu — Slot for overlay menu (positioned absolute top-right)
  • Card.Metadata — Renders metadata content below the description

Changed

  • Playground Storybook story updated with boolean toggle controls for Card.Icon and Card.Metadata
  • CustomContent Storybook story added to demonstrate using Card as a plain styled container with arbitrary children (no subcomponents)

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 1, 2026

🦋 Changeset detected

Latest commit: 06edb74

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

This PR includes changesets to release 1 package
Name Type
@primer/react 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

@liuliu-dev liuliu-dev changed the title card from pacer Add Card component Apr 1, 2026
@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Apr 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

⚠️ Action required

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Check the integration testing docs for step-by-step instructions. Or, apply the integration-tests: skipped manually label to skip these checks.

To publish a canary release for integration testing, apply the Canary Release label to this PR.

@liuliu-dev liuliu-dev added the update snapshots 🤖 Command that updates VRT snapshots on the pull request label Apr 1, 2026
@github-actions github-actions bot temporarily deployed to storybook-preview-7723 April 1, 2026 21:41 Inactive
@github-actions github-actions bot removed the update snapshots 🤖 Command that updates VRT snapshots on the pull request label Apr 1, 2026
@github-actions github-actions bot temporarily deployed to storybook-preview-7723 April 1, 2026 21:54 Inactive
@github-actions github-actions bot temporarily deployed to storybook-preview-7723 April 1, 2026 22:05 Inactive
@primer
Copy link
Copy Markdown
Contributor

primer bot commented Apr 2, 2026

🤖 Lint issues have been automatically fixed and committed to this PR.

@primer
Copy link
Copy Markdown
Contributor

primer bot commented Apr 2, 2026

🤖 Lint issues have been automatically fixed and committed to this PR.

Copy link
Copy Markdown
Contributor

@lukasoppermann lukasoppermann left a comment

Choose a reason for hiding this comment

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

@liuliu-dev design wise:

I think we can remove the fallback values, they are not needed and don't exist anymore.

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 7, 2026

@copilot in the Playground storybook story, add toggles for the icon <Card.Icon> and the <Card.Metadata>.

Also add a story that does not use any of the internal components and just adds some cu...

Done in 375b519.

  • Playground: Added boolean toggle controls (showIcon and showMetadata) via argTypes, both defaulting to true. Toggling them off removes the respective Card.Icon / Card.Metadata from the rendered story.
  • CustomContent: Added a new story that places a custom <div> with a heading, paragraph and list directly inside <Card> — no Card.* subcomponents used at all.
  • Component: Updated Card to detect when no slot children are present and render children directly, so the CustomContent story (and any consumer using Card as a plain styled container) works correctly.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /home/REDACTED/.cache/ms-playwright/chromium_headless_shell-1194/chrome-linux/headless_shell /home/REDACTED/.cache/ms-playwright/chromium_headless_shell-1194/chrome-linux/headless_shell --disable-field-trial-config --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-extensions --disable-features=AcceptCHFrame,AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate,AutoDeElevate,RenderD (dns block)
    • Triggering command: /home/REDACTED/.cache/ms-playwright/chromium_headless_shell-1194/chrome-linux/headless_shell /home/REDACTED/.cache/ms-playwright/chromium_headless_shell-1194/chrome-linux/headless_shell --disable-field-trial-config --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-extensions --disable-features=AcceptCHFrame,AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate,AutoDeElevate,RenderD -main/dist/gh-gpgsign/gh-gpgsign-linux-x86_64 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@lukasoppermann
Copy link
Copy Markdown
Contributor

@liuliu-dev should we tie the padding to the content-top, instead of icon bottom?

CleanShot 2026-04-07 at 09 23 53@2x

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Uh oh! @lukasoppermann, at least one image you shared is missing helpful alt text. Check #7723 (comment) to fix the following violations:

  • Images should have meaningful alternative text (alt text) at line 3

Alt text is an invisible description that helps screen readers describe images to blind or low-vision users. If you are using markdown to display images, add your alt text inside the brackets of the markdown image.

Learn more about alt text at Basic writing and formatting syntax: images on GitHub Docs.

🤖 Beep boop! This comment was added automatically by github/accessibility-alt-text-bot.

@lukasoppermann
Copy link
Copy Markdown
Contributor

This is also a use case: https://primer-1b76de82d9-13348165.drafts.github.io/storybook/?path=/story/components-card--custom-content

CleanShot 2026-04-07 at 09 25 01@2x

I am wondering if we should expect users to "bring their own padding" or if we should have a padding inside the card by default and a prop or a way to easily remove the default padding? CC: @siddharthkp ?

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Uh oh! @lukasoppermann, at least one image you shared is missing helpful alt text. Check #7723 (comment) to fix the following violations:

  • Images should have meaningful alternative text (alt text) at line 3

Alt text is an invisible description that helps screen readers describe images to blind or low-vision users. If you are using markdown to display images, add your alt text inside the brackets of the markdown image.

Learn more about alt text at Basic writing and formatting syntax: images on GitHub Docs.

🤖 Beep boop! This comment was added automatically by github/accessibility-alt-text-bot.

@ericwbailey
Copy link
Copy Markdown
Contributor

Thanks for the review @ericwbailey! Addressed:

Amazing! Thank you so much!

@liuliu-dev
Copy link
Copy Markdown
Contributor Author

Thanks @lukasoppermann! Here's what I've updated based on your feedback:

  • Updated typography and spacing to use the recommended tokens
  • Padding: moved padding to the Card root and added a padding prop (normal | none), defaults to normal. Header/body no longer have their own padding, spacing between them is controlled by gap instead.
  • Moved the custom content one into features stories.

@lukasoppermann
Copy link
Copy Markdown
Contributor

@liuliu-dev perfect, thank you.

@siddharthkp
Copy link
Copy Markdown
Member

I am wondering if we should expect users to "bring their own padding" or if we should have a padding inside the card by default and a prop or a way to easily remove the default padding? CC: @siddharthkp ?

I think bring your padding is a pattern we don't follow anywhere right now because it can lead to variation that is visibly inconsistent. Something we can take from PageLayout is the idea of padding: 'none' | 'condensed' | 'spacious' and of course, you can always fine-tune it with css.

@lukasoppermann
Copy link
Copy Markdown
Contributor

lukasoppermann commented Apr 8, 2026

I think bring your padding is a pattern we don't follow anywhere right now because it can lead to variation that is visibly inconsistent. Something we can take from PageLayout is the idea of padding: 'none' | 'condensed' | 'spacious' and of course, you can always fine-tune it with css.

The prop is now implemented, this makes sense to me.

@siddharthkp the Pagelayout does not make total sense to me, because there seems to be no default or normal one. Does the current implementation for card make sense to you?

you can always fine-tune it with css.

This is important! 👍

@siddharthkp
Copy link
Copy Markdown
Member

@siddharthkp the Pagelayout does not make total sense to me, because there seems to be no default or normal one.

My bad, the values in the docs are none | condensed | normal

normal as a value is a bit weird to me. default is totally fine.

Does the current implementation for card make sense to you?

I've come into this blind, so I don't actually know what's going on or what we are trying to do 😅

I would say however, we should put this in experimental and not root index right away

CardDescriptionProps,
CardMenuProps,
CardMetadataProps,
} from './Card'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we export this from src/experimental/index until we build some confidence in the API with some usage?

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.

Whatever you folks feel is best. Do we have a clear path for how we move this out of experimental? It feels like stuff stays in experimental for way to long some times.

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.

Makes sense to keep it in experimental for now. It’s mostly upstreamed from Pacer so it’s not totally new, but the padding prop is new, if it looks good in usage we can move it out pretty quickly.

@lukasoppermann
Copy link
Copy Markdown
Contributor

Figma component: https://github.com/github/primer/issues/6537

@primer-integration
Copy link
Copy Markdown

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/18126

@primer-integration
Copy link
Copy Markdown

Integration test results from github/github-ui:

Running  CI   Running
Passed  VRT   Passed
Running  Projects   Running

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Canary Release Apply this label when you want CI to create a canary release of the current PR integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants