Skip to content

Commit 7160709

Browse files
authored
Remove use of useResponsiveValue hook - PageHeader (#7112)
1 parent 9382e52 commit 7160709

File tree

4 files changed

+129
-9
lines changed

4 files changed

+129
-9
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
PageHeader: Remove useResponsiveValue hook from TitleArea variant prop
6+
7+
Migrates PageHeader.TitleArea's `variant` prop to use `getResponsiveAttributes` following ADR-018 for SSR-safe responsive values. This prevents layout shift during hydration when using responsive variants.

packages/react/src/PageHeader/PageHeader.module.css

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,106 @@
5858
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
5959
}
6060

61+
/* Responsive size variants */
62+
@media (--viewportRange-narrow) {
63+
&:has([data-component='TitleArea'][data-size-variant-narrow='large']) {
64+
font-size: var(--custom-font-size, var(--text-title-size-large, 2rem));
65+
font-weight: var(--custom-font-weight, var(--base-text-weight-normal, 400));
66+
line-height: var(--custom-line-height, var(--text-title-lineHeight-large, 1.5));
67+
68+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-large, 1.5));
69+
}
70+
71+
&:has([data-component='TitleArea'][data-size-variant-narrow='medium']) {
72+
font-size: var(--custom-font-size, var(--text-title-size-medium, 1.25rem));
73+
font-weight: var(--custom-font-weight, var(--base-text-weight-semibold, 600));
74+
line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
75+
76+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
77+
}
78+
79+
&:has([data-component='TitleArea'][data-size-variant-narrow='subtitle']) {
80+
font-size: var(--custom-font-size, var(--text-title-size-medium, 1.25rem));
81+
font-weight: var(--custom-font-weight, var(--base-text-weight-normal, 400));
82+
line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
83+
84+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
85+
}
86+
}
87+
88+
@media (--viewportRange-regular) {
89+
&:has([data-component='TitleArea'][data-size-variant-regular='large']) {
90+
font-size: var(--custom-font-size, var(--text-title-size-large, 2rem));
91+
font-weight: var(--custom-font-weight, var(--base-text-weight-normal, 400));
92+
line-height: var(--custom-line-height, var(--text-title-lineHeight-large, 1.5));
93+
94+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-large, 1.5));
95+
}
96+
97+
&:has([data-component='TitleArea'][data-size-variant-regular='medium']) {
98+
font-size: var(--custom-font-size, var(--text-title-size-medium, 1.25rem));
99+
font-weight: var(--custom-font-weight, var(--base-text-weight-semibold, 600));
100+
line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
101+
102+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
103+
}
104+
105+
&:has([data-component='TitleArea'][data-size-variant-regular='subtitle']) {
106+
font-size: var(--custom-font-size, var(--text-title-size-medium, 1.25rem));
107+
font-weight: var(--custom-font-weight, var(--base-text-weight-normal, 400));
108+
line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
109+
110+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
111+
}
112+
}
113+
114+
@media (--viewportRange-wide) {
115+
&:has([data-component='TitleArea'][data-size-variant-wide='large']) {
116+
font-size: var(--custom-font-size, var(--text-title-size-large, 2rem));
117+
font-weight: var(--custom-font-weight, var(--base-text-weight-normal, 400));
118+
line-height: var(--custom-line-height, var(--text-title-lineHeight-large, 1.5));
119+
120+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-large, 1.5));
121+
}
122+
123+
&:has([data-component='TitleArea'][data-size-variant-wide='medium']) {
124+
font-size: var(--custom-font-size, var(--text-title-size-medium, 1.25rem));
125+
font-weight: var(--custom-font-weight, var(--base-text-weight-semibold, 600));
126+
line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
127+
128+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
129+
}
130+
131+
&:has([data-component='TitleArea'][data-size-variant-wide='subtitle']) {
132+
font-size: var(--custom-font-size, var(--text-title-size-medium, 1.25rem));
133+
font-weight: var(--custom-font-weight, var(--base-text-weight-normal, 400));
134+
line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
135+
136+
--title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6));
137+
}
138+
}
139+
61140
&[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-all]),
62141
&[data-has-border='true']:not(:has([data-component='PH_Navigation'])) {
63142
border-block-end: var(--borderWidth-thin) solid var(--borderColor-default);
64143
padding-block-end: var(--base-size-8);
65144
}
66145

67-
@media screen and (max-width: 768px) {
146+
@media (--viewportRange-narrow) {
68147
&[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-narrow]) {
69148
border-block-end: var(--borderWidth-thin) solid var(--borderColor-default);
70149
padding-block-end: var(--base-size-8);
71150
}
72151
}
73152

74-
@media screen and (min-width: 768px) {
153+
@media (--viewportRange-regular) {
75154
&[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-regular]) {
76155
border-block-end: var(--borderWidth-thin) solid var(--borderColor-default);
77156
padding-block-end: var(--base-size-8);
78157
}
79158
}
80159

81-
@media screen and (min-width: 1440px) {
160+
@media (--viewportRange-wide) {
82161
&[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-wide]) {
83162
border-block-end: var(--borderWidth-thin) solid var(--borderColor-default);
84163
padding-block-end: var(--base-size-8);
@@ -98,19 +177,19 @@
98177
}
99178

100179
& [data-hidden-narrow] {
101-
@media screen and (max-width: 768px) {
180+
@media (--viewportRange-narrow) {
102181
display: none;
103182
}
104183
}
105184

106185
& [data-hidden-regular] {
107-
@media screen and (min-width: 768px) {
186+
@media (--viewportRange-regular) {
108187
display: none;
109188
}
110189
}
111190

112191
& [data-hidden-wide] {
113-
@media screen and (min-width: 1440px) {
192+
@media (--viewportRange-wide) {
114193
display: none;
115194
}
116195
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type {Meta, StoryFn} from '@storybook/react-vite'
2+
import React from 'react'
3+
import {PageHeader} from './PageHeader'
4+
5+
const meta: Meta = {
6+
title: 'Components/PageHeader/Responsive Variants',
7+
parameters: {
8+
layout: 'fullscreen',
9+
controls: {expanded: true},
10+
},
11+
}
12+
13+
export default meta
14+
15+
/**
16+
* Test responsive size variants on TitleArea.
17+
* Resize the viewport to see different title sizes at different breakpoints.
18+
*/
19+
export const TitleAreaSizeResponsive: StoryFn = () => (
20+
<PageHeader>
21+
<PageHeader.TitleArea variant={{narrow: 'subtitle', regular: 'medium', wide: 'large'}}>
22+
<PageHeader.Title>Title variant: subtitle (narrow) → medium (regular) → large (wide)</PageHeader.Title>
23+
</PageHeader.TitleArea>
24+
</PageHeader>
25+
)
26+
27+
TitleAreaSizeResponsive.parameters = {
28+
docs: {
29+
description: {
30+
story:
31+
'The title size changes based on viewport width: **subtitle** on narrow viewports, **medium** on regular viewports, and **large** on wide viewports.',
32+
},
33+
},
34+
}

packages/react/src/PageHeader/PageHeader.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React, {useEffect} from 'react'
22
import type {ResponsiveValue} from '../hooks/useResponsiveValue'
3-
import {isResponsiveValue, useResponsiveValue} from '../hooks/useResponsiveValue'
3+
import {isResponsiveValue} from '../hooks/useResponsiveValue'
44
import Heading from '../Heading'
55
import {ArrowLeftIcon} from '@primer/octicons-react'
66
import type {LinkProps as BaseLinkProps} from '../Link'
77
import Link from '../Link'
8+
import {getResponsiveAttributes} from '../internal/utils/getResponsiveAttributes'
89

910
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
1011
import {areAllValuesTheSame, haveRegularAndWideSameValue} from '../utils/getBreakpointDeclarations'
@@ -205,13 +206,12 @@ export type TitleAreaProps = {
205206
const TitleArea = React.forwardRef<HTMLDivElement, React.PropsWithChildren<TitleAreaProps>>(
206207
({children, className, hidden = false, variant = 'medium'}, forwardedRef) => {
207208
const titleAreaRef = useProvidedRefOrCreate<HTMLDivElement>(forwardedRef as React.RefObject<HTMLDivElement>)
208-
const currentVariant = useResponsiveValue(variant, 'medium')
209209
return (
210210
<div
211211
className={clsx(classes.TitleArea, className)}
212212
ref={titleAreaRef}
213213
data-component="TitleArea"
214-
data-size-variant={currentVariant}
214+
{...getResponsiveAttributes('size-variant', variant)}
215215
{...getHiddenDataAttributes(hidden)}
216216
>
217217
{children}

0 commit comments

Comments
 (0)