Skip to content

Commit 23d77f8

Browse files
Revert "chore(Skeleton): Remove the CSS module feature flag from Skeleton (#5…" (#5496)
This reverts commit 9bd2f79.
1 parent 43afd36 commit 23d77f8

File tree

5 files changed

+184
-50
lines changed

5 files changed

+184
-50
lines changed

.changeset/thirty-wasps-sleep.md

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const CSS_MODULE_FLAG = 'primer_react_css_modules_ga'
Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
import React, {type CSSProperties} from 'react'
2+
import {getBreakpointDeclarations} from '../../utils/getBreakpointDeclarations'
3+
import {get} from '../../constants'
24
import {isResponsiveValue} from '../../hooks/useResponsiveValue'
35
import type {AvatarProps} from '../../Avatar'
46
import {DEFAULT_AVATAR_SIZE} from '../../Avatar/Avatar'
57
import {SkeletonBox} from './SkeletonBox'
68
import classes from './SkeletonAvatar.module.css'
79
import {clsx} from 'clsx'
10+
import {useFeatureFlag} from '../../FeatureFlags'
811
import {merge} from '../../sx'
12+
import {CSS_MODULE_FLAG} from './FeatureFlag'
913

1014
export type SkeletonAvatarProps = Pick<AvatarProps, 'size' | 'square'> & {
1115
/** Class name for custom styling */
1216
className?: string
1317
} & Omit<React.HTMLProps<HTMLDivElement>, 'size'>
1418

19+
const avatarSkeletonStyles = {
20+
'&[data-component="SkeletonAvatar"]': {
21+
borderRadius: '50%',
22+
boxShadow: `0 0 0 1px ${get('colors.avatar.border')}`,
23+
display: 'inline-block',
24+
lineHeight: get('lineHeights.condensedUltra'),
25+
height: 'var(--avatar-size)',
26+
width: 'var(--avatar-size)',
27+
},
28+
29+
'&[data-square]': {
30+
borderRadius: 'clamp(4px, var(--avatar-size) - 24px, 6px)',
31+
},
32+
}
33+
1534
export const SkeletonAvatar: React.FC<SkeletonAvatarProps> = ({
1635
size = DEFAULT_AVATAR_SIZE,
1736
square,
@@ -21,23 +40,40 @@ export const SkeletonAvatar: React.FC<SkeletonAvatarProps> = ({
2140
}) => {
2241
const responsive = isResponsiveValue(size)
2342
const cssSizeVars = {} as Record<string, string>
43+
const enabled = useFeatureFlag(CSS_MODULE_FLAG)
44+
const avatarSx = responsive
45+
? {
46+
...getBreakpointDeclarations(
47+
size,
48+
'--avatar-size' as keyof React.CSSProperties,
49+
value => `${value || DEFAULT_AVATAR_SIZE}px`,
50+
),
51+
...avatarSkeletonStyles,
52+
}
53+
: {
54+
'--avatar-size': `${size}px`,
55+
...avatarSkeletonStyles,
56+
}
2457

25-
if (responsive) {
26-
for (const [key, value] of Object.entries(size)) {
27-
cssSizeVars[`--avatarSize-${key}`] = `${value}px`
58+
if (enabled) {
59+
if (responsive) {
60+
for (const [key, value] of Object.entries(size)) {
61+
cssSizeVars[`--avatarSize-${key}`] = `${value}px`
62+
}
63+
} else {
64+
cssSizeVars['--avatarSize-regular'] = `${size}px`
2865
}
29-
} else {
30-
cssSizeVars['--avatarSize-regular'] = `${size}px`
3166
}
3267

3368
return (
3469
<SkeletonBox
35-
className={clsx(className, classes.SkeletonAvatar)}
70+
sx={enabled ? undefined : avatarSx}
71+
className={clsx(className, {[classes.SkeletonAvatar]: enabled})}
3672
{...rest}
3773
data-component="SkeletonAvatar"
3874
data-responsive={responsive ? '' : undefined}
3975
data-square={square ? '' : undefined}
40-
style={merge(style as CSSProperties, cssSizeVars)}
76+
style={merge(style as CSSProperties, enabled ? cssSizeVars : {})}
4177
/>
4278
)
4379
}

packages/react/src/experimental/Skeleton/SkeletonBox.tsx

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import React from 'react'
2-
import {merge, type SxProp} from '../../sx'
3-
import {type CSSProperties} from 'react'
2+
import styled, {keyframes} from 'styled-components'
3+
import sx, {merge, type SxProp} from '../../sx'
4+
import {get} from '../../constants'
5+
import {type CSSProperties, type HTMLProps} from 'react'
6+
import {toggleStyledComponent} from '../../internal/utils/toggleStyledComponent'
47
import {clsx} from 'clsx'
58
import classes from './SkeletonBox.module.css'
6-
import {defaultSxProp} from '../../utils/defaultSxProp'
7-
import Box from '../../Box'
9+
import {useFeatureFlag} from '../../FeatureFlags'
10+
import {CSS_MODULE_FLAG} from './FeatureFlag'
811

912
type SkeletonBoxProps = {
1013
/** Height of the skeleton "box". Accepts any valid CSS `height` value. */
@@ -14,39 +17,62 @@ type SkeletonBoxProps = {
1417
/** The className of the skeleton box */
1518
className?: string
1619
} & SxProp &
17-
React.ComponentPropsWithoutRef<'div'>
20+
HTMLProps<HTMLDivElement>
21+
22+
const shimmer = keyframes`
23+
from { mask-position: 200%; }
24+
to { mask-position: 0%; }
25+
`
26+
27+
const StyledSkeletonBox = toggleStyledComponent(
28+
CSS_MODULE_FLAG,
29+
'div',
30+
styled.div<SkeletonBoxProps>`
31+
animation: ${shimmer};
32+
display: block;
33+
background-color: var(--bgColor-muted, ${get('colors.canvas.subtle')});
34+
border-radius: 3px;
35+
height: ${props => props.height || '1rem'};
36+
width: ${props => props.width};
37+
38+
@media (prefers-reduced-motion: no-preference) {
39+
mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%);
40+
mask-size: 200%;
41+
animation: ${shimmer};
42+
animation-duration: 1s;
43+
animation-iteration-count: infinite;
44+
}
45+
46+
@media (forced-colors: active) {
47+
outline: 1px solid transparent;
48+
outline-offset: -1px;
49+
}
50+
51+
${sx};
52+
`,
53+
)
1854

1955
export const SkeletonBox = React.forwardRef<HTMLDivElement, SkeletonBoxProps>(function SkeletonBox(
20-
{height, width, className, sx: sxProp = defaultSxProp, style, ...props},
56+
{height, width, className, style, ...props},
2157
ref,
2258
) {
23-
if (sxProp !== defaultSxProp) {
24-
return (
25-
<Box
26-
className={clsx(className, classes.SkeletonBox)}
27-
style={merge(
28-
style as CSSProperties,
29-
{
30-
height,
31-
width,
32-
} as CSSProperties,
33-
)}
34-
{...props}
35-
ref={ref}
36-
/>
37-
)
38-
}
39-
59+
const enabled = useFeatureFlag(CSS_MODULE_FLAG)
4060
return (
41-
<div
42-
className={clsx(className, classes.SkeletonBox)}
43-
style={merge(
44-
style as CSSProperties,
45-
{
46-
height,
47-
width,
48-
} as CSSProperties,
49-
)}
61+
<StyledSkeletonBox
62+
height={enabled ? undefined : height}
63+
width={enabled ? undefined : width}
64+
className={clsx(className, {[classes.SkeletonBox]: enabled})}
65+
style={
66+
enabled
67+
? merge(
68+
style as CSSProperties,
69+
{
70+
height,
71+
width,
72+
} as CSSProperties,
73+
)
74+
: style
75+
}
5076
{...props}
5177
ref={ref}
5278
/>
Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React, {type CSSProperties, type HTMLProps} from 'react'
2+
import Box from '../../Box'
23
import {SkeletonBox} from './SkeletonBox'
34
import classes from './SkeletonText.module.css'
5+
import {useFeatureFlag} from '../../FeatureFlags'
46
import {clsx} from 'clsx'
57
import {merge} from '../../sx'
8+
import {CSS_MODULE_FLAG} from './FeatureFlag'
69

710
type SkeletonTextProps = {
811
/** Size of the text that the skeleton is replacing. */
@@ -15,6 +18,61 @@ type SkeletonTextProps = {
1518
className?: string
1619
} & Omit<HTMLProps<HTMLDivElement>, 'size'>
1720

21+
const skeletonTextStyles = {
22+
'&[data-component="SkeletonText"]': {
23+
'--font-size': 'var(--text-body-size-medium, 0.875rem)',
24+
'--line-height': 'var(--text-body-lineHeight-medium, 1.4285)',
25+
'--leading': 'calc(var(--font-size) * var(--line-height) - var(--font-size))',
26+
borderRadius: 'var(--borderRadius-small, 0.1875rem)',
27+
height: 'var(--font-size)',
28+
marginBlock: 'calc(var(--leading) / 2)',
29+
},
30+
'&[data-in-multiline="true"]': {
31+
marginBlockEnd: 'calc(var(--leading) * 2)',
32+
},
33+
'&[data-in-multiline="true"]:last-child': {
34+
maxWidth: '65%',
35+
minWidth: '50px',
36+
marginBottom: 0,
37+
},
38+
'@supports (margin-block: mod(1px, 1px))': {
39+
'&[data-component="SkeletonText"]': {
40+
'--leading': 'mod(var(--font-size) * var(--line-height), var(--font-size))',
41+
},
42+
},
43+
'&[data-text-skeleton-size="display"], &[data-text-skeleton-size="titleLarge"]': {
44+
borderRadius: 'var(--borderRadius-medium, 0.375rem)',
45+
},
46+
'&[data-text-skeleton-size="display"]': {
47+
'--font-size': 'var(--text-display-size, 2.5rem)',
48+
'--line-height': 'var(--text-display-lineHeight, 1.4)',
49+
},
50+
'&[data-text-skeleton-size="titleLarge"]': {
51+
'--font-size': 'var(--text-title-size-large, 2.5rem)',
52+
'--line-height': 'var(--text-title-lineHeight-large, 1.5)',
53+
},
54+
'&[data-text-skeleton-size="titleMedium"]': {
55+
'--font-size': 'var(--text-title-size-medium, 1.25rem)',
56+
'--line-height': 'var(--text-title-lineHeight-medium, 1.6)',
57+
},
58+
'&[data-text-skeleton-size="titleSmall"]': {
59+
'--font-size': 'var(--text-title-size-small, 1rem)',
60+
'--line-height': 'var(--text-title-lineHeight-small, 1.5)',
61+
},
62+
'&[data-text-skeleton-size="subtitle"]': {
63+
'--font-size': 'var(--text-subtitle-size, 1.25rem)',
64+
'--line-height': 'var(--text-subtitle-lineHeight, 1.6)',
65+
},
66+
'&[data-text-skeleton-size="bodyLarge"]': {
67+
'--font-size': 'var(--text-body-size-large, 1rem)',
68+
'--line-height': 'var(--text-body-lineHeight-large, 1.5)',
69+
},
70+
'&[data-text-skeleton-size="bodySmall"]': {
71+
'--font-size': 'var(--text-body-size-small, 0.75rem)',
72+
'--line-height': 'var(--text-body-lineHeight-small, 1.6666)',
73+
},
74+
}
75+
1876
export const SkeletonText: React.FC<SkeletonTextProps> = ({
1977
lines = 1,
2078
maxWidth,
@@ -23,34 +81,52 @@ export const SkeletonText: React.FC<SkeletonTextProps> = ({
2381
style,
2482
...rest
2583
}) => {
84+
const enabled = useFeatureFlag(CSS_MODULE_FLAG)
85+
2686
if (lines < 2) {
2787
return (
2888
<SkeletonBox
2989
data-component="SkeletonText"
3090
data-text-skeleton-size={size}
3191
width="100%"
32-
className={clsx(className, classes.SkeletonText)}
33-
style={merge(style as CSSProperties, {maxWidth} as CSSProperties)}
92+
className={clsx(className, {[classes.SkeletonText]: enabled})}
93+
sx={
94+
enabled
95+
? {}
96+
: {
97+
maxWidth,
98+
...skeletonTextStyles,
99+
}
100+
}
101+
style={enabled ? merge(style as CSSProperties, {maxWidth} as CSSProperties) : style}
34102
{...rest}
35103
/>
36104
)
37105
} else {
38106
return (
39-
<div
107+
<Box
40108
data-component="multilineContainer"
41-
style={merge(style as CSSProperties, {maxWidth, paddingBlock: '0.1px'} as CSSProperties)}
109+
sx={{
110+
maxWidth,
111+
/* The tiny `paddingBlock` prevents margin collapse between the first skeleton line
112+
* and a bottom margin above it.
113+
*/
114+
paddingBlock: '0.1px',
115+
}}
116+
style={enabled ? merge(style as CSSProperties, {maxWidth, paddingBlock: '0.1px'} as CSSProperties) : style}
42117
>
43118
{Array.from({length: lines}, (_, index) => (
44119
<SkeletonBox
45120
key={index}
46121
data-component="SkeletonText"
47122
data-in-multiline="true"
48123
data-text-skeleton-size={size}
49-
className={clsx(className, classes.SkeletonText)}
124+
sx={enabled ? {} : skeletonTextStyles}
125+
className={clsx(className, {[classes.SkeletonText]: enabled})}
50126
{...rest}
51127
/>
52128
))}
53-
</div>
129+
</Box>
54130
)
55131
}
56132
}

0 commit comments

Comments
 (0)