/
Heading.jsx
136 lines (128 loc) · 3.32 KB
/
Heading.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import React, { forwardRef } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import {
helveticaNeueLight45,
helveticaNeueThin35,
helveticaNeueMedium65,
wordBreak,
baseSupSubScripts,
} from '@tds/shared-typography'
import { colorWhite, colorText, colorSecondary } from '@tds/core-colours'
import { media } from '@tds/core-responsive'
import { safeRest } from '@tds/util-helpers'
import HeadingSup, { StyledHeadingSup } from './HeadingSup/HeadingSup'
const HeadingLevels = {
h1: {
...helveticaNeueLight45,
fontSize: '1.75rem',
lineHeight: '1.29', // 36px
letterSpacing: '-1.6px',
...media.from('md').css({
...helveticaNeueThin35,
fontSize: '2.75rem',
lineHeight: '1.18',
letterSpacing: '0',
}),
[`${StyledHeadingSup}, sup`]: {
...baseSupSubScripts,
fontSize: '1.25rem',
top: '-1em',
...media.from('md').css({ fontSize: '1.25rem', top: '-1.3em' }),
},
},
h2: {
...helveticaNeueLight45,
fontSize: '1.5rem',
lineHeight: '1.33', // 30px
letterSpacing: '-0.7px',
...media.from('md').css({
fontSize: '1.75rem',
lineHeight: '1.29',
letterSpacing: '-0.8px',
}),
[`${StyledHeadingSup}, sup`]: {
...baseSupSubScripts,
fontSize: '1rem',
top: '-0.8em',
...media.from('md').css({ fontSize: '1rem', top: '-0.7em' }),
},
},
h3: {
...helveticaNeueMedium65,
fontSize: '1.25rem',
lineHeight: '1.4', // 28px
letterSpacing: '-0.6px',
[`${StyledHeadingSup}, sup`]: {
...baseSupSubScripts,
fontSize: '0.875rem',
top: '-0.5em',
},
},
h4: {
...helveticaNeueMedium65,
fontSize: '1rem',
lineHeight: '1.25', // 20px
letterSpacing: '-0.6px',
[`${StyledHeadingSup}, sup`]: {
...baseSupSubScripts,
fontSize: '0.875rem',
top: '-0.5em',
},
},
}
export const StyledHeading = styled.h1(wordBreak, ({ level, invert }) => {
const baseColor = level === 'h1' || level === 'h2' ? colorSecondary : colorText
const color = invert ? colorWhite : baseColor
return {
color,
...HeadingLevels[`${level}`],
'& > span': {
letterSpacing: 'inherit',
},
}
})
/**
* Page headings. Renders an HTML `<h1-h4>` element.
*
* @version ./package.json
*/
const Heading = forwardRef(({ level, tag = level, invert, children, ...rest }, ref) => {
return (
<StyledHeading
{...safeRest(rest)}
ref={ref}
as={tag}
level={level}
invert={invert}
data-testid="heading"
>
{children}
</StyledHeading>
)
})
Heading.displayName = 'Heading'
Heading.propTypes = {
/**
* The visual level of the heading. If `tag` is not specified, then `level` determines what HTML element to render.
*/
level: PropTypes.oneOf(['h1', 'h2', 'h3', 'h4']).isRequired,
/**
* The semantic level of the heading. Renders the specified HTML element, otherwise it matches `level`.
*/
tag: PropTypes.oneOf(['h1', 'h2', 'h3', 'h4', 'div', 'span']),
/**
* Invert the text color to appear light on dark backgrounds.
*/
invert: PropTypes.bool,
/**
* The content. Can be text, other components, or HTML elements.
*/
children: PropTypes.node.isRequired,
}
Heading.defaultProps = {
invert: false,
tag: undefined,
}
Heading.Sup = HeadingSup
export default Heading