Skip to content

Commit 503a662

Browse files
committed
feat: add typography
1 parent ab121b1 commit 503a662

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed

src/typography/typography.tsx

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { css } from 'emotion';
2+
import * as React from 'react';
3+
import { merge, mergeProps } from '../util/hoc.util';
4+
import { ThemeContext } from '../util/theme';
5+
6+
interface TypographyProps extends React.HTMLAttributes<HTMLDivElement> {
7+
scale: keyof TypographyScale;
8+
emphase?: 'high' | 'medium' | 'low';
9+
baselineTop?: number;
10+
baselineBottom?: number;
11+
}
12+
13+
enum TypographyCase {
14+
Sentence,
15+
AllCase,
16+
}
17+
18+
interface TypographyAttribte {
19+
typeface: string;
20+
weight: 300 | 400 | 500;
21+
size: string;
22+
case: TypographyCase;
23+
LetterSpacing: string;
24+
}
25+
26+
interface TypographyScale {
27+
H1: TypographyAttribte;
28+
H2: TypographyAttribte;
29+
H3: TypographyAttribte;
30+
H4: TypographyAttribte;
31+
H5: TypographyAttribte;
32+
H6: TypographyAttribte;
33+
Subtitle1: TypographyAttribte;
34+
Subtitle2: TypographyAttribte;
35+
Body1: TypographyAttribte;
36+
Body2: TypographyAttribte;
37+
Button: TypographyAttribte;
38+
Caption: TypographyAttribte;
39+
Overline: TypographyAttribte;
40+
}
41+
42+
interface TypographyThemeColor {
43+
base: string;
44+
high: string;
45+
medium: string;
46+
low: string;
47+
}
48+
49+
interface TypographyTheme {
50+
typography: TypographyScale;
51+
color: TypographyThemeColor;
52+
}
53+
54+
const baseTheme = {
55+
palette: {
56+
surface: {
57+
on: '#FFFFFF',
58+
highEmphase: 'DD',
59+
mediumEmphase: '99',
60+
disabled: '61',
61+
},
62+
},
63+
};
64+
65+
const defaultTheme = (theme: any = baseTheme): TypographyTheme => {
66+
const base = {
67+
typeface: "'Dosis', sans-serif",
68+
weight: 400,
69+
case: TypographyCase.Sentence,
70+
};
71+
const title = { ...base, typeface: "'Pacifico', cursive" };
72+
return merge(
73+
{
74+
typography: {
75+
H1: { ...title, weight: 300, size: '6rem', LetterSpacing: '-0.09375rem' },
76+
H2: { ...title, weight: 300, size: '3.75rem', LetterSpacing: '-0.03125rem' },
77+
H3: { ...title, size: '3rem', LetterSpacing: '0rem' },
78+
H4: { ...title, size: '2.125rem', LetterSpacing: '0.015625rem' },
79+
H5: { ...title, size: '1.5rem', LetterSpacing: '0rem' },
80+
H6: { ...title, weight: 500, size: '1.25rem', LetterSpacing: '0.009375rem' },
81+
Subtitle1: { ...base, size: '1rem', LetterSpacing: '0.009375rem' },
82+
Subtitle2: { ...base, weight: 500, size: '0.875rem', LetterSpacing: '0.00625rem' },
83+
Body1: { ...base, size: '1rem', LetterSpacing: '0.03125rem' },
84+
Body2: { ...base, size: '0.875rem', LetterSpacing: '0.015625rem' },
85+
Button: {
86+
...base,
87+
case: TypographyCase.AllCase,
88+
weight: 500,
89+
size: '0.875rem',
90+
LetterSpacing: '0.046875rem',
91+
},
92+
Caption: { ...base, size: '0.75rem', LetterSpacing: '0.025rem' },
93+
Overline: {
94+
...base,
95+
case: TypographyCase.AllCase,
96+
size: '0.625rem',
97+
LetterSpacing: '0.09375rem',
98+
},
99+
} as TypographyScale,
100+
color: {
101+
base: theme.palette.surface.on,
102+
high: theme.palette.surface.highEmphase,
103+
medium: theme.palette.surface.mediumEmphase,
104+
low: theme.palette.surface.disabled,
105+
},
106+
} as TypographyTheme,
107+
theme
108+
);
109+
};
110+
111+
const baselineStrut = (distance: number | undefined) => `
112+
display: inline-block; width: 0; content: ''; height: ${distance}px
113+
`;
114+
115+
const typeScale = (
116+
theme: TypographyTheme,
117+
scale: keyof TypographyScale,
118+
baselineTop = 0,
119+
emphase: keyof TypographyThemeColor = 'high',
120+
baselineBottom?: number
121+
): string => {
122+
const attr = theme.typography[scale];
123+
const bottomPos =
124+
baselineBottom === undefined
125+
? ''
126+
: `
127+
position: absolute;
128+
bottom: ${baselineBottom}px;
129+
`;
130+
const color = `${theme.color.base}${theme.color[emphase]}`;
131+
return css`
132+
color: ${color};
133+
font-family: ${attr.typeface};
134+
font-weight: ${attr.weight};
135+
text-transform: ${attr.case === TypographyCase.Sentence ? 'initial' : 'uppercase'};
136+
letter-spacing: ${attr.LetterSpacing};
137+
font-size: ${attr.size};
138+
margin-bottom: ${-1 * (baselineBottom || 0)};
139+
&::before {
140+
${baselineStrut(baselineTop)};
141+
vertical-align: 0;
142+
}
143+
&::after {
144+
${baselineStrut(baselineBottom)};
145+
vertical-align: ${-1 * (baselineBottom || 0)};
146+
}
147+
${bottomPos};
148+
`;
149+
};
150+
151+
export const Typography: React.FunctionComponent<TypographyProps> = props => {
152+
const { children, scale, baselineTop, baselineBottom, emphase, ...defaultHostProps } = props;
153+
const theme = defaultTheme(React.useContext(ThemeContext));
154+
const hostProps = mergeProps(
155+
{ className: typeScale(theme, scale, baselineTop, emphase, baselineBottom) },
156+
defaultHostProps
157+
);
158+
return <div {...hostProps}>{children}</div>;
159+
};
160+
161+
export const TypographyProps = (options: TypographyProps & { theme: any }) => {
162+
const { theme: incomingTheme, scale, baselineTop, emphase, baselineBottom } = options;
163+
const theme = defaultTheme(incomingTheme);
164+
return { className: typeScale(theme, scale, baselineTop, emphase, baselineBottom) };
165+
};

0 commit comments

Comments
 (0)