Skip to content

Commit b0a07a1

Browse files
committed
decouple the padding and position for hero media
1 parent 224484b commit b0a07a1

16 files changed

Lines changed: 377 additions & 96 deletions
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@primer/react-brand': minor
3+
'@primer/brand-css': minor
4+
---
5+
6+
⚠️ Breaking change to `Hero.Image` and `Hero.Video` ⚠️
7+
8+
Previously combined `padding` and `position` semantics have been separated.
9+
10+
The `position` prop will now only control layout and positioning relative to the content. It accepts `block-end`, `inline-start`, or `inline-end`.
11+
12+
Use `padding` to control the distance between the media container boundary and the media itself. `padding="all"` instead of the removed `*-padded` position values, or `padding="none"` to remove media padding and corner radius. The default padding remains position-specific.
13+
14+
```diff
15+
-<Hero.Image position="block-end-padded" src="image.jpg" alt="" />
16+
+<Hero.Image position="block-end" padding="all" src="image.jpg" alt="" />
17+
```
18+
19+
CSS-only consumers should replace the removed `*-padded` position modifiers with the corresponding position and padding modifiers, such as `.Hero-media--pos-block-end.Hero-media--padding-all`.

apps/next-docs/content/components/Hero/react.mdx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,21 +268,21 @@ Forwards all props from the [Button component](/components/Button).
268268

269269
### Hero.Image
270270

271-
| name | type | default | description |
272-
| ---------- | ------------------------- | ------------- | -------------------------------------------- |
273-
| `position` | <HeroImagePositionProp /> | `'block-end'` | Controls positioning of the foreground image |
271+
| name | type | default | description |
272+
| ---------- | ------------------------------ | ------------- | ------------------------------------------------- |
273+
| `position` | <HeroImagePositionProp /> | `'block-end'` | Controls positioning of the foreground image |
274+
| `padding` | `'default'`, `'none'`, `'all'` | `'default'` | Controls padding around media in gridline layouts |
274275

275276
Forwards all props from the [Image component](/components/Image), including `src`, `alt`, and `aspectRatio`.
276277

277-
### Hero.Video
278-
279-
| name | type | default | description |
280-
| --------------- | ------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------- |
281-
| `position` | <HeroImagePositionProp /> | `'block-end'` | Controls positioning of the child video element |
282-
| `poster` | `string` | | URL of the poster image to display before video plays. When set, `posterAltText` and `posterTitle` are required |
283-
| `posterAltText` | `string` | | Alt text for the poster image. Required when `poster` is set |
284-
| `posterTitle` | `string` | | Title text displayed on the poster overlay. Required when `poster` is set |
278+
`position` and `padding` props currently only apply to `gridline` variants. The `default` Hero variant ignores non-default media padding and logs a warning during development.
285279

286-
```
280+
### Hero.Video
287281

288-
```
282+
| name | type | default | description |
283+
| --------------- | ------------------------------ | ------------- | --------------------------------------------------------------------------------------------------------------- |
284+
| `position` | <HeroImagePositionProp /> | `'block-end'` | Controls positioning of the child video element |
285+
| `padding` | `'default'`, `'none'`, `'all'` | `'default'` | Controls padding around media in gridline layouts |
286+
| `poster` | `string` | | URL of the poster image to display before video plays. When set, `posterAltText` and `posterTitle` are required |
287+
| `posterAltText` | `string` | | Alt text for the poster image. Required when `poster` is set |
288+
| `posterTitle` | `string` | | Title text displayed on the poster overlay. Required when `poster` is set |

apps/next-docs/content/components/Hero/react.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ import {TextVariants, defaultTextVariant} from '@primer/react-brand'
55
export const HeroDescriptionVariantProp = () => <PropTableValues values={[...TextVariants]} addLineBreaks />
66
export const HeroDescriptionVariantPropDefault = () => <PropTableValues values={[defaultTextVariant]} />
77
export const HeroImagePositionProp = () => (
8-
<PropTableValues
9-
values={['block-end', 'block-end-padded', 'inline-end', 'inline-end-padded', 'inline-start', 'inline-start-padded']}
10-
addLineBreaks
11-
/>
8+
<PropTableValues values={['block-end', 'inline-end', 'inline-start']} addLineBreaks />
129
)
1310
export const HeroVariantProp = () => <PropTableValues values={['default', 'gridline']} addLineBreaks />

packages/react/src/Hero/Hero.examples.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const CustomBackgroundInlineEndPaddedImage: Story = {
124124
Secondary action
125125
</Button>
126126
</Hero.ButtonGroup>
127-
<Hero.Image position="inline-end-padded" src={renderUI3} alt="" enableBorder={false} />
127+
<Hero.Image position="inline-end" padding="all" src={renderUI3} alt="" enableBorder={false} />
128128
</Hero>
129129
)
130130
},
@@ -198,7 +198,7 @@ export const CustomBackgroundInlineEndPaddedVideo: Story = {
198198
Secondary action
199199
</Button>
200200
</Hero.ButtonGroup>
201-
<Hero.Video position="inline-end-padded" enableBorder={false}>
201+
<Hero.Video position="inline-end" padding="all" enableBorder={false}>
202202
<VideoPlayer title="GitHub media player" poster={posterImage}>
203203
<VideoPlayer.Source src="./example.mp4" type="video/mp4" />
204204
<VideoPlayer.Track src="./example.vtt" default />

packages/react/src/Hero/Hero.gridline.stories.tsx

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,31 @@ export const Gridline: Story = {
4242
),
4343
}
4444

45+
export const GridlineImageWithoutPadding: Story = {
46+
name: 'Gridline default variant: Image without padding',
47+
render: () => (
48+
<Hero variant="gridline" align="center">
49+
<Hero.Label>Label</Hero.Label>
50+
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
51+
<Hero.Description>
52+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
53+
felis nam pulvinar risus elementum.
54+
</Hero.Description>
55+
<Hero.ButtonGroup>
56+
<Button as="a" href="#">
57+
Primary action
58+
</Button>
59+
</Hero.ButtonGroup>
60+
<Hero.Image
61+
position="block-end"
62+
padding="none"
63+
src={placeholderImage}
64+
alt="placeholder image, blank with gray solid fill"
65+
/>
66+
</Hero>
67+
),
68+
}
69+
4570
export const GridlineCentered: Story = {
4671
name: 'Gridline default variant: Centered',
4772
render: () => (
@@ -118,7 +143,8 @@ export const GridlineImageInlineEndPadded: Story = {
118143
</Button>
119144
</Hero.ButtonGroup>
120145
<Hero.Image
121-
position="inline-end-padded"
146+
position="inline-end"
147+
padding="all"
122148
src={placeholderImage}
123149
alt="placeholder image, blank with gray solid fill"
124150
/>
@@ -142,7 +168,8 @@ export const GridlineImageInlineStartPadded: Story = {
142168
</Button>
143169
</Hero.ButtonGroup>
144170
<Hero.Image
145-
position="inline-start-padded"
171+
position="inline-start"
172+
padding="all"
146173
src={placeholderImage}
147174
alt="placeholder image, blank with gray solid fill"
148175
/>
@@ -166,7 +193,8 @@ export const GridlineImageBlockEndPadded: Story = {
166193
</Button>
167194
</Hero.ButtonGroup>
168195
<Hero.Image
169-
position="block-end-padded"
196+
position="block-end"
197+
padding="all"
170198
src={placeholderImage}
171199
alt="placeholder image, blank with gray solid fill"
172200
/>
@@ -208,6 +236,42 @@ export const GridlineYoutubeVideoBlockEnd: Story = {
208236
),
209237
}
210238

239+
export const GridlineYoutubeVideoWithoutPadding: Story = {
240+
name: 'Gridline default variant: YouTube video without padding',
241+
render: () => (
242+
<Hero variant="gridline" align="center">
243+
<Hero.Label>Label</Hero.Label>
244+
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
245+
<Hero.Description>
246+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
247+
felis nam pulvinar risus elementum.
248+
</Hero.Description>
249+
<Hero.ButtonGroup>
250+
<Button as="a" href="#">
251+
Primary action
252+
</Button>
253+
</Hero.ButtonGroup>
254+
<Hero.Video
255+
position="block-end"
256+
padding="none"
257+
poster={posterImage}
258+
posterAltText="Mona the Octocat"
259+
posterTitle="Introducing the GitHub Copilot coding agent "
260+
>
261+
<iframe
262+
src="https://www.youtube.com/embed/EPyyyB23NUU?autoplay=1"
263+
title="YouTube video player"
264+
frameBorder="0"
265+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
266+
referrerPolicy="strict-origin-when-cross-origin"
267+
allowFullScreen
268+
className={styles.customVideo}
269+
></iframe>
270+
</Hero.Video>
271+
</Hero>
272+
),
273+
}
274+
211275
export const GridlineYoutubeVideoInlineEnd: Story = {
212276
name: 'Gridline default variant: Inline-end YouTube video',
213277
render: () => (
@@ -293,7 +357,8 @@ export const GridlineYoutubeVideoBlockEndPadded: Story = {
293357
</Button>
294358
</Hero.ButtonGroup>
295359
<Hero.Video
296-
position="block-end-padded"
360+
position="block-end"
361+
padding="all"
297362
poster={posterImage}
298363
posterAltText="Mona the Octocat"
299364
posterTitle="Introducing the GitHub Copilot coding agent "
@@ -335,6 +400,34 @@ export const GridlineExpressive: Story = {
335400
),
336401
}
337402

403+
export const GridlineExpressiveImageWithoutPadding: Story = {
404+
name: 'Expressive variant: Image without padding',
405+
render: () => (
406+
<Hero variant="gridline-expressive">
407+
<Hero.Label>Eyebrow</Hero.Label>
408+
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
409+
<Hero.Description>
410+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem
411+
aperiam, quae inventore.
412+
</Hero.Description>
413+
<Hero.ButtonGroup>
414+
<Button as="a" href="#">
415+
Get started for free
416+
</Button>
417+
<Button as="a" href="#">
418+
See plans &amp; pricing
419+
</Button>
420+
</Hero.ButtonGroup>
421+
<Hero.Image
422+
position="block-end"
423+
padding="none"
424+
src={placeholderImage}
425+
alt="placeholder image, blank with gray solid fill"
426+
/>
427+
</Hero>
428+
),
429+
}
430+
338431
export const GridlineExpressiveNarrow: Story = {
339432
name: 'Expressive variant (narrow)',
340433
render: () => (
@@ -380,7 +473,8 @@ export const GridlineExpressiveBlockEndPadded: Story = {
380473
</Button>
381474
</Hero.ButtonGroup>
382475
<Hero.Image
383-
position="block-end-padded"
476+
position="block-end"
477+
padding="all"
384478
src={placeholderImage}
385479
alt="placeholder image, blank with gray solid fill"
386480
/>
@@ -411,7 +505,8 @@ export const GridlineExpressiveBlockEndPaddedTrailingComponent: Story = {
411505
</Button>
412506
</Hero.ButtonGroup>
413507
<Hero.Image
414-
position="block-end-padded"
508+
position="block-end"
509+
padding="all"
415510
src={placeholderImage}
416511
alt="placeholder image, blank with gray solid fill"
417512
/>

packages/react/src/Hero/Hero.module.css

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
padding: var(--base-size-32) var(--base-size-32) 0;
8484
}
8585

86-
.Hero-imageContainer--inline-bg-padded {
86+
.Hero-imageContainer--inline-padding-all {
8787
padding: var(--base-size-32);
8888
background-color: var(--brand-color-canvas-subtle);
8989
}
@@ -115,7 +115,7 @@
115115
padding: var(--base-size-64) var(--base-size-64) 0;
116116
}
117117

118-
.Hero-imageContainer--inline-bg-padded {
118+
.Hero-imageContainer--inline-padding-all {
119119
padding: var(--base-size-64);
120120
}
121121
}
@@ -133,10 +133,14 @@
133133
overflow: hidden;
134134
}
135135

136-
.Hero--variant-gridline .Hero-media--pos-block-end {
136+
.Hero--variant-gridline .Hero-media--pos-block-end:not(.Hero-media--padding-all):not(.Hero-media--padding-none) {
137137
border-radius: var(--brand-borderRadius-medium) var(--brand-borderRadius-medium) 0 0;
138138
}
139139

140+
.Hero-media--pos-block-end.Hero-media--padding-all {
141+
border-radius: var(--brand-borderRadius-medium);
142+
}
143+
140144
/* Applied directly to video/iframe elements for block-end position */
141145
.Hero-media-item--block-end {
142146
border-radius: var(--brand-borderRadius-medium) var(--brand-borderRadius-medium) 0 0;
@@ -161,24 +165,24 @@
161165
}
162166
}
163167

164-
.Hero--variant-gridline .Hero-imageWrapper-inner--padded {
168+
.Hero--variant-gridline .Hero-imageWrapper-inner--padding-all {
165169
padding: var(--base-size-32);
166170
}
167171

168-
.Hero-media--pos-block-end-padded {
169-
border-radius: var(--brand-borderRadius-medium);
170-
}
171-
172172
@media screen and (min-width: 63.25rem) {
173173
.Hero--variant-gridline .Hero-imageWrapper-inner {
174174
padding: var(--base-size-64) var(--base-size-64) 0;
175175
}
176176

177-
.Hero--variant-gridline .Hero-imageWrapper-inner--padded {
177+
.Hero--variant-gridline .Hero-imageWrapper-inner--padding-all {
178178
padding: var(--base-size-64);
179179
}
180180
}
181181

182+
.Hero--variant-gridline .Hero-imageWrapper-inner--padding-none {
183+
padding: 0;
184+
}
185+
182186
/* GridLine variant with inline (side-by-side) layout */
183187
.Hero-grid--bordered-inline {
184188
width: 100%;
@@ -219,7 +223,7 @@
219223
/* Media styling for gridline inline positions */
220224

221225
/* Edge-to-edge inline media (no padding) - remove border radius */
222-
.Hero--variant-gridline .Hero-media--pos-inline:not(.Hero-media--pos-inline-padded) {
226+
.Hero--variant-gridline .Hero-media--pos-inline:not(.Hero-media--padding-all) {
223227
border-radius: 0;
224228
}
225229

@@ -229,7 +233,7 @@
229233
}
230234

231235
/* Padded inline media - constrain size and center */
232-
.Hero--variant-gridline .Hero-media--pos-inline-padded {
236+
.Hero--variant-gridline .Hero-media--pos-inline.Hero-media--padding-all {
233237
max-width: 100%;
234238
height: auto;
235239
align-self: center;
@@ -461,7 +465,7 @@
461465
}
462466
}
463467

464-
.Hero--variant-gridline-expressive .Hero-imageWrapper-inner--padded {
468+
.Hero--variant-gridline-expressive .Hero-imageWrapper-inner--padding-all {
465469
padding: var(--base-size-32);
466470
}
467471

@@ -470,11 +474,15 @@
470474
padding: var(--base-size-64) var(--base-size-64) 0;
471475
}
472476

473-
.Hero--variant-gridline-expressive .Hero-imageWrapper-inner--padded {
477+
.Hero--variant-gridline-expressive .Hero-imageWrapper-inner--padding-all {
474478
padding: var(--base-size-64);
475479
}
476480
}
477481

482+
.Hero--variant-gridline-expressive .Hero-imageWrapper-inner--padding-none {
483+
padding: 0;
484+
}
485+
478486
/* Shared media styles for gridline-expressive */
479487
.Hero--variant-gridline-expressive .Hero-media {
480488
max-width: 1176px;
@@ -483,7 +491,13 @@
483491
overflow: hidden;
484492
}
485493

486-
.Hero--variant-gridline-expressive .Hero-media--pos-block-end,
487-
.Hero--variant-gridline-expressive .Hero-image:not(.Hero-media--pos-block-end-padded) {
494+
.Hero--variant-gridline-expressive
495+
.Hero-media--pos-block-end:not(.Hero-media--padding-all):not(.Hero-media--padding-none) {
488496
border-radius: var(--brand-borderRadius-medium) var(--brand-borderRadius-medium) 0 0;
489497
}
498+
499+
.Hero--variant-gridline .Hero-media--padding-none,
500+
.Hero--variant-gridline-expressive .Hero-media--padding-none {
501+
border-radius: 0;
502+
max-width: 100%;
503+
}

0 commit comments

Comments
 (0)