-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Card Component Padding System Enhancement #72511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ate size type definitions
f0895d6 to
35212ec
Compare
…d-support-more-padding-options
32b730e to
9351230
Compare
…d-support-more-padding-options
…d-support-more-padding-options
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
…d-support-more-padding-options
mirka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, thanks for the enhancement.
Could you also update the readme files? The Card component family isn't hooked up to our readme auto-generation system yet, sorry.
| } & SizeableProps; | ||
|
|
||
| export type MediaProps = { | ||
| /** | ||
| * The children elements. | ||
| */ | ||
| children: React.ReactNode; | ||
| }; | ||
|
|
||
| type MarginalSubComponentProps = BaseSubComponentProps & { | ||
| /** | ||
| * Renders without a border. | ||
| * | ||
| * @default false | ||
| */ | ||
| isBorderless?: boolean; | ||
| }; | ||
|
|
||
| export type HeaderProps = MarginalSubComponentProps; | ||
| export type HeaderProps = MarginalSubComponentProps & SizeableProps; | ||
|
|
||
| export type FooterProps = MarginalSubComponentProps & { | ||
| justify?: CSSProperties[ 'justifyContent' ]; | ||
| }; | ||
| } & SizeableProps; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these additions necessary? It looks like SizeableProps is already included through BaseSubComponentProps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed with 832e1b1
| * Internal dependencies | ||
| */ | ||
| import type { Props, SizeToken } from './types'; | ||
| import { cardPaddings } from './styles'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move the cardPaddings stuff directly into this file? They're not used anywhere else. While you're at it, could you also move all the card padding values out of the config-values file? We want to get rid of that file eventually. I think this get-padding-by-size.ts file can be the canonical source of card padding sizes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed with f5d8c47
| /** | ||
| * The Card component supports three approaches to padding: | ||
| * 1. Default padding (medium) - no size prop needed | ||
| * 2. Token-based padding - using size tokens: xSmall (8px), small (16px), medium (24px), large (32px) | ||
| * 3. Directional padding - customize each side independently | ||
| * | ||
| * Each component (Card, CardHeader, CardBody) can have its own padding configuration. | ||
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we remove the exact pixel values from this story description and from the code comments? We don't want to commit to pixel values in documentation, since they could be changed at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed with c4af856
Thanks for the review! I addressed your feedback. Also, I updated the readme too: 9768cee |
|
Flaky tests detected in a398629. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/18885528260
|
…d-support-more-padding-options
|
@mirka can you take another look at this PR? Thanks! |
mirka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be out next week, so I'll leave it to my pals on @WordPress/gutenberg-components for any follow up 🙏
| return css` | ||
| padding: ${ getSinglePaddingValue( top ) } | ||
| ${ getSinglePaddingValue( right ) } | ||
| ${ getSinglePaddingValue( bottom ) } | ||
| ${ getSinglePaddingValue( left ) }; | ||
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The padding shorthand property is not a logical one, so it's not going to respect our logical values. We have to set each property separately (padding-block-start, etc).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry! I completely forgot to update it! I fixed it!
| default: | ||
| return '0'; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what I think about the "fall back to 0" behavior, given that 0 is not an explicitly allowed value and the object shape marks all properties as optional. I can see it becoming a weird loophole with consumers who want to set a 0 padding.
| {
blockStart?: SizeToken;
blockEnd?: SizeToken;
inlineStart?: SizeToken;
inlineEnd?: SizeToken;
};I can think of two ways to address this:
- Mark all sides as required in the object case, and fall back to
mediumfor undefined or invalid sides. - Officially support
noneas a size token.
No strong option here — 2 is probably fine? I'll also note that there's already a CardMedia subcomponent that can be used for full-bleed content.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark all sides as required in the object case, and fall back to medium for undefined or invalid sides.
I took this approach given that it is simple.
Co-authored-by: Lena Morita <[email protected]>
…d-support-more-padding-options
I created an issue for this at #72784 . The theme tokens for spacing are very incomplete so I wouldn't recommend basing anything off that quite yet, but we'll want to be mindful to potential future compatibility to align the direction here with ideas we want to explore there, so that we could ideally refactor what we're introducing here to use those primitives. I think the current direction here of an object of logical properties is one approach we could consider for this kind of direction-specific padding customization on a box primitive. I think the size values might be something we'd want to standardize at a design systems level, but considering that the proposed changes builds on existing values for Card size, I don't think that's an issue to sort out here. |
…WordPress/gutenberg into 72175-card-support-more-padding-options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few minor comments, but this looks good 👍 Of my comments, the most impactful is the one about differences between horizontal and vertical spacing with values like 'medium'.
| case 'medium': | ||
| return space( 6 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One quirk of this approach is that if I specify size={ { blockStart: 'medium' } }, I'd get space( 6 ) for the top padding, but if I specify size="medium", I get space( 4 ) for the top padding.
We could maybe have some convoluted logic for mapping sizes based on vertical or horizontal properties, but I do quite like keeping this simpler with each step mapping to 2, 4, 6, and 8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify, do you expect some changes here, or is it just a note?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily expecting any changes, just flagging a potential point of confusion or inconsistency of expectations. Unless you have concerns about it, I'm personally happy to try this as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it could cause some confusion, but I’m not sure how to fix it without introducing breaking changes.
The idea is that with size={{ blockStart: 'medium', blockEnd: 'medium', inlineStart: 'medium', inlineEnd: 'medium' }}, we can define consistent padding across all properties.
| } | ||
|
|
||
| // Handle object-based sizes | ||
| if ( size && typeof size === 'object' ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the typeof here check redundant? I can appreciate the explicitness, and only mention it because checking object-ness is pretty fraught (see also isPlainObject in various utility libraries), e.g. typeof null === 'object'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed with 767da77
|
|
||
| // Handle object-based sizes | ||
| if ( size && typeof size === 'object' ) { | ||
| const top = size.blockStart; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I'd probably avoid the top, etc. naming because the idea of logical properties is to avoid associating with specific directions like this, even if it's more familiar due to historical usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed with 767da77
| const CONFIG = { | ||
| cardPaddingXSmall: `${ space( 2 ) }`, | ||
| cardPaddingSmall: `${ space( 4 ) }`, | ||
| cardPaddingMedium: `${ space( 4 ) } ${ space( 6 ) }`, | ||
| cardPaddingLarge: `${ space( 6 ) } ${ space( 8 ) }`, | ||
| }; | ||
|
|
||
| const xSmallCardPadding = css` | ||
| padding: ${ CONFIG.cardPaddingXSmall }; | ||
| `; | ||
|
|
||
| export const cardPaddings = { | ||
| none: css` | ||
| padding: 0; | ||
| `, | ||
| large: css` | ||
| padding: ${ CONFIG.cardPaddingLarge }; | ||
| `, | ||
| medium: css` | ||
| padding: ${ CONFIG.cardPaddingMedium }; | ||
| `, | ||
| small: css` | ||
| padding: ${ CONFIG.cardPaddingSmall }; | ||
| `, | ||
| xSmall: xSmallCardPadding, | ||
| // The `extraSmall` size is not officially documented, but the following styles | ||
| // are kept for legacy reasons to support older values of the `size` prop. | ||
| extraSmall: xSmallCardPadding, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cardPaddings is already essentially acting like a constant, so internally referencing CONFIG seems like unnecessary indirection over just inlining the values.
| const CONFIG = { | |
| cardPaddingXSmall: `${ space( 2 ) }`, | |
| cardPaddingSmall: `${ space( 4 ) }`, | |
| cardPaddingMedium: `${ space( 4 ) } ${ space( 6 ) }`, | |
| cardPaddingLarge: `${ space( 6 ) } ${ space( 8 ) }`, | |
| }; | |
| const xSmallCardPadding = css` | |
| padding: ${ CONFIG.cardPaddingXSmall }; | |
| `; | |
| export const cardPaddings = { | |
| none: css` | |
| padding: 0; | |
| `, | |
| large: css` | |
| padding: ${ CONFIG.cardPaddingLarge }; | |
| `, | |
| medium: css` | |
| padding: ${ CONFIG.cardPaddingMedium }; | |
| `, | |
| small: css` | |
| padding: ${ CONFIG.cardPaddingSmall }; | |
| `, | |
| xSmall: xSmallCardPadding, | |
| // The `extraSmall` size is not officially documented, but the following styles | |
| // are kept for legacy reasons to support older values of the `size` prop. | |
| extraSmall: xSmallCardPadding, | |
| }; | |
| const xSmallCardPadding = css` | |
| padding: ${ space( 2 ) }; | |
| `; | |
| export const cardPaddings = { | |
| none: css` | |
| padding: 0; | |
| `, | |
| large: css` | |
| padding: ${ space( 6 ) } ${ space( 8 ) }; | |
| `, | |
| medium: css` | |
| padding: ${ space( 4 ) } ${ space( 6 ) }; | |
| `, | |
| small: css` | |
| padding: ${ space( 4 ) } ${ space( 4 ) }; | |
| `, | |
| xSmall: xSmallCardPadding, | |
| // The `extraSmall` size is not officially documented, but the following styles | |
| // are kept for legacy reasons to support older values of the `size` prop. | |
| extraSmall: xSmallCardPadding, | |
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed with 5c873be
…d-support-more-padding-options
What?
Closes #72175
This PR enhances the Card component's padding system by introducing directional padding support and allowing independent padding configuration for Card, CardHeader, and CardBody components.
Why?
The current Card component's padding system only supports predefined token sizes applied uniformly to all sides. This limits the component's flexibility in complex layouts where different padding values are needed for different sides or subcomponents.
This enhancement provides more granular control over padding while maintaining the simplicity of the existing token-based system.
How?
sizeprop to accept either a token or a directional object.CardHeaderandCardBodycomponents have thesizeprop.Testing Instructions
Padding variationstory.