Skip to content

Commit dd5c06f

Browse files
authored
Add skeleton component (#78)
1 parent fa3495f commit dd5c06f

10 files changed

Lines changed: 247 additions & 3 deletions

File tree

packages/docs/src/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const ComponentNav = () => {
215215
{ title: 'Input', path: 'Input' },
216216
{ title: 'Link', path: 'Link' },
217217
{ title: 'Select', path: 'Select' },
218+
{ title: 'Skeleton', path: 'Skeleton' },
218219
{ title: 'Spinner', path: 'Spinner' },
219220
{ title: 'Switch', path: 'Switch' },
220221
{ title: 'Text', path: 'Text' },

packages/docs/src/pages/components/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
Input,
1010
Link,
1111
Select,
12+
Skeleton,
1213
Spinner,
1314
Switch,
1415
Text,
@@ -165,6 +166,12 @@ const Documentation = () => {
165166
</Select>
166167
</Stack>
167168
</ComponentCard>
169+
<ComponentCard name="Skeleton">
170+
<Stack direction="vertical" gap={4}>
171+
<Skeleton />
172+
<Skeleton width={120} />
173+
</Stack>
174+
</ComponentCard>
168175
<ComponentCard name="Spinner" gap={4}>
169176
<Spinner size="small" />
170177
<Spinner size="medium" />
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import React from 'react'
2+
import { ThemeProvider, Stack, Skeleton, Link, Text } from 'react-ui'
3+
import { Page, Props, Example, Section, Table, Para } from '../../components'
4+
5+
const Documentation = () => {
6+
return (
7+
<Page
8+
title="Skeleton"
9+
tagline="Use a skeleton to hint that some content is on it's way"
10+
>
11+
<Example>
12+
<Example.Preview direction="vertical" gap={2}>
13+
<Skeleton />
14+
</Example.Preview>
15+
<Example.Code>
16+
{`
17+
<Skeleton />
18+
`}
19+
</Example.Code>
20+
</Example>
21+
22+
<Section title="Props">
23+
<Props
24+
props={[
25+
{
26+
name: 'width',
27+
type: 'number',
28+
description: 'width of skeleton',
29+
default: 200
30+
}
31+
]}
32+
/>
33+
</Section>
34+
35+
<Section title="Examples">
36+
<Example title="Width">
37+
<Example.Preview>
38+
<Stack direction="vertical" gap={2}>
39+
<Skeleton />
40+
<Skeleton width={120} />
41+
</Stack>
42+
</Example.Preview>
43+
<Example.Code>{`
44+
<Skeleton />
45+
<Skeleton width={120} />
46+
`}</Example.Code>
47+
</Example>
48+
</Section>
49+
50+
<Section title="Customisation">
51+
<Para>
52+
<Text variant="subtle" css={{ fontStyle: 'italic' }}>
53+
Please read the docs on{' '}
54+
<Link href="/core-concepts/customising-components">
55+
customising components
56+
</Link>{' '}
57+
first.
58+
</Text>
59+
</Para>
60+
61+
<Para>
62+
<Text>Skeleton uses the following properties from theme:</Text>
63+
</Para>
64+
65+
<Table>
66+
<Table.Header>
67+
<Table.Column span={6}>Property</Table.Column>
68+
<Table.Column span={6}>Theme key</Table.Column>
69+
</Table.Header>
70+
<Table.Row>
71+
<Table.Column span={6}>component name</Table.Column>
72+
<Table.Column span={6}>Skeleton</Table.Column>
73+
</Table.Row>
74+
<Table.Row>
75+
<Table.Column span={6}>colors</Table.Column>
76+
<Table.Column span={6}>
77+
backgroundColor + highlightColor
78+
</Table.Column>
79+
</Table.Row>
80+
</Table>
81+
82+
<Example>
83+
<Example.Code lang="js">{`
84+
import { tokens, components } from 'react-ui/themes/base'
85+
86+
// extend components
87+
components.Skeleton = {
88+
backgroundColor: 'lightgrey',
89+
highlightColor: 'white',
90+
height: 4,
91+
borderRadius: 2,
92+
animationDuration: 8
93+
}
94+
`}</Example.Code>
95+
<Example.Code lang="jsx">{`
96+
<ThemeProvider tokens={tokens} components={components}>
97+
<Skeleton />
98+
<Skeleton width={120} />
99+
</ThemeProvider>
100+
`}</Example.Code>
101+
<Example.Preview direction="vertical" gap={2}>
102+
<ThemeProvider
103+
components={{
104+
Skeleton: {
105+
backgroundColor: 'lightgrey',
106+
highlightColor: 'white',
107+
height: 4,
108+
borderRadius: 2,
109+
animationDuration: 8
110+
}
111+
}}
112+
>
113+
<Skeleton />
114+
<Skeleton width={120} />
115+
</ThemeProvider>
116+
</Example.Preview>
117+
</Example>
118+
</Section>
119+
</Page>
120+
)
121+
}
122+
123+
export default Documentation

packages/docs/src/pages/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export { default as Image } from './components/image'
77
export { default as Input } from './components/input'
88
export { default as Link } from './components/link'
99
export { default as Select } from './components/select'
10+
export { default as Skeleton } from './components/skeleton'
1011
export { default as Spinner } from './components/spinner'
1112
export { default as Switch } from './components/switch'
1213
export { default as Text } from './components/text'

packages/react-ui/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './src/components/image'
88
export * from './src/components/input'
99
export * from './src/components/link'
1010
export * from './src/components/select'
11+
export * from './src/components/skeleton'
1112
export * from './src/components/spinner'
1213
export * from './src/components/switch'
1314
export * from './src/components/text'
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React from 'react'
2+
import delve from 'dlv'
3+
import PropTypes from 'prop-types'
4+
import { Element } from '../../primitives'
5+
import { styles } from './skeleton.styles'
6+
import { merge } from '../../utils'
7+
8+
const getLinearBackground = theme => {
9+
/** Read special keys from theme but make sure
10+
* it doesn't break if the keys aren't present
11+
* */
12+
const backgroundColor = delve(theme.components, 'Skeleton.backgroundColor', '')
13+
const highlightColor = delve(theme.components, 'Skeleton.highlightColor', '')
14+
15+
const background = delve(theme.colors, backgroundColor, backgroundColor)
16+
const highlight = delve(theme.colors, highlightColor, highlightColor)
17+
18+
return `linear-gradient(90deg,
19+
${background} 0%,
20+
${background} 20%,
21+
${highlight} 50%,
22+
${background} 80%,
23+
${background} 100%
24+
)`
25+
}
26+
27+
export const Skeleton = React.forwardRef(function Skeleton(
28+
{ css, width, ...props },
29+
ref
30+
) {
31+
return (
32+
<Element
33+
ref={ref}
34+
as="span"
35+
component="Skeleton"
36+
css={merge(
37+
{ width, background: getLinearBackground },
38+
styles.Skeleton,
39+
css
40+
)}
41+
{...props}
42+
/>
43+
)
44+
})
45+
46+
Skeleton.propTypes = {
47+
width: PropTypes.number
48+
}
49+
50+
Skeleton.defaultProps = {
51+
width: 200
52+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { keyframes } from '@emotion/core'
2+
3+
const shine = keyframes`
4+
0% { background-position: 100% 50%; }
5+
100% { background-position: -100% 50%; }
6+
`
7+
8+
export const styles = {
9+
Skeleton: {
10+
backgroundSize: '200% 200%',
11+
animationName: `${shine}`,
12+
animationIterationCount: 'infinite',
13+
animationTimingFunction: 'linear'
14+
}
15+
}

packages/react-ui/src/themes/base.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,17 @@ const tokens = {
5959
2: '992px'
6060
},
6161

62-
durations: [0, '75ms', '100ms', '150ms', '200ms', '300ms', '500ms', '1000ms'],
62+
durations: {
63+
0: 0,
64+
1: '75ms',
65+
2: '100ms',
66+
3: '150ms',
67+
4: '200ms',
68+
5: '300ms',
69+
6: '500ms',
70+
7: '1000ms',
71+
8: '2500ms'
72+
},
6373

6474
// based on elevation levels
6575
shadows: {
@@ -180,6 +190,13 @@ const components = {
180190
borderColor: 'red'
181191
}
182192
},
193+
Skeleton: {
194+
backgroundColor: 'lightgrey',
195+
highlightColor: 'white',
196+
height: 4,
197+
borderRadius: 2,
198+
animationDuration: 8
199+
},
183200
Spinner: {
184201
sizes: { small: 2, medium: 4, large: 6 },
185202
borderColor: 'white',

packages/react-ui/src/themes/dark.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ const tokens = {
6262
4: '200ms',
6363
5: '300ms',
6464
6: '500ms',
65-
7: '1000ms'
65+
7: '1000ms',
66+
8: '2500ms'
6667
},
6768

6869
// based on elevation levels
@@ -380,6 +381,14 @@ const components = {
380381
}
381382
},
382383

384+
Skeleton: {
385+
backgroundColor: 'grays.800',
386+
highlightColor: 'grays.900',
387+
height: 4,
388+
borderRadius: 2,
389+
animationDuration: 8
390+
},
391+
383392
Spinner: {
384393
sizes: { small: 4, medium: 6, large: 8 },
385394
borderColor: 'grays.700',

packages/react-ui/src/themes/light.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,17 @@ const tokens = {
5959
2: '992px'
6060
},
6161

62-
durations: [0, '75ms', '100ms', '150ms', '200ms', '300ms', '500ms', '1000ms'],
62+
durations: {
63+
0: 0,
64+
1: '75ms',
65+
2: '100ms',
66+
3: '150ms',
67+
4: '200ms',
68+
5: '300ms',
69+
6: '500ms',
70+
7: '1000ms',
71+
8: '2500ms'
72+
},
6373

6474
// based on elevation levels
6575
shadows: {
@@ -367,6 +377,14 @@ const components = {
367377
}
368378
},
369379

380+
Skeleton: {
381+
backgroundColor: 'grays.300',
382+
highlightColor: 'grays.100',
383+
height: 4,
384+
borderRadius: 2,
385+
animationDuration: 8
386+
},
387+
370388
Spinner: {
371389
sizes: { small: 4, medium: 6, large: 8 },
372390
borderColor: 'grays.200',

0 commit comments

Comments
 (0)