@mitodl/smoot-design 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +142 -0
- package/.github/workflows/ci.yml +48 -0
- package/.github/workflows/publish-pages.yml +50 -0
- package/.github/workflows/release.yml +34 -0
- package/.github/workflows/validate-pr.yml +49 -0
- package/.pre-commit-config.yaml +90 -0
- package/.prettierignore +1 -0
- package/.prettierrc.json +4 -0
- package/.releaserc.json +40 -0
- package/.secrets.baseline +113 -0
- package/.storybook/main.ts +46 -0
- package/.storybook/manager-head.html +1 -0
- package/.storybook/preview-head.html +5 -0
- package/.storybook/preview.tsx +15 -0
- package/.storybook/public/pexels-photo-1851188.webp +0 -0
- package/.yarn/releases/yarn-4.5.1.cjs +934 -0
- package/.yarnrc.yml +23 -0
- package/LICENSE +28 -0
- package/README.md +13 -0
- package/jest.config.ts +22 -0
- package/package.json +110 -0
- package/src/components/Button/ActionButton.stories.tsx +186 -0
- package/src/components/Button/Button.stories.tsx +275 -0
- package/src/components/Button/Button.test.tsx +56 -0
- package/src/components/Button/Button.tsx +418 -0
- package/src/components/LinkAdapter/LinkAdapter.tsx +38 -0
- package/src/components/ThemeProvider/ThemeProvider.stories.tsx +94 -0
- package/src/components/ThemeProvider/ThemeProvider.tsx +127 -0
- package/src/components/ThemeProvider/Typography.stories.tsx +74 -0
- package/src/components/ThemeProvider/breakpoints.ts +20 -0
- package/src/components/ThemeProvider/buttons.ts +22 -0
- package/src/components/ThemeProvider/chips.tsx +167 -0
- package/src/components/ThemeProvider/colors.ts +33 -0
- package/src/components/ThemeProvider/typography.ts +174 -0
- package/src/index.ts +24 -0
- package/src/jest-setup.ts +0 -0
- package/src/story-utils/index.ts +28 -0
- package/src/types/theme.d.ts +106 -0
- package/src/types/typography.d.ts +54 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import {
|
|
3
|
+
createTheme as muiCreateTheme,
|
|
4
|
+
ThemeProvider as MuiThemeProvider,
|
|
5
|
+
} from "@mui/material/styles"
|
|
6
|
+
import type { ThemeOptions, Theme } from "@mui/material/styles"
|
|
7
|
+
import type {} from "@mui/lab/themeAugmentation"
|
|
8
|
+
import * as typography from "./typography"
|
|
9
|
+
import * as buttons from "./buttons"
|
|
10
|
+
import * as chips from "./chips"
|
|
11
|
+
import { colors } from "./colors"
|
|
12
|
+
import type { CustomTheme } from "../../types/theme"
|
|
13
|
+
|
|
14
|
+
const shadow = {
|
|
15
|
+
shadowOffsetX: 3,
|
|
16
|
+
shadowOffsetY: 4,
|
|
17
|
+
shadowColor: "rgb(0 0 0 / 36%)",
|
|
18
|
+
shadowBlurRadius: 12,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// To replace ../scss/theme.scss for #236 as we refactor it out
|
|
22
|
+
const custom: ThemeOptions["custom"] = {
|
|
23
|
+
transitionDuration: "300ms",
|
|
24
|
+
shadow: `${shadow.shadowOffsetX} ${shadow.shadowOffsetY} ${shadow.shadowBlurRadius} ${shadow.shadowColor}`,
|
|
25
|
+
colors,
|
|
26
|
+
dimensions: {
|
|
27
|
+
headerHeight: "72px",
|
|
28
|
+
headerHeightSm: "60px",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const BREAKPOINTS = {
|
|
33
|
+
values: {
|
|
34
|
+
xs: 0,
|
|
35
|
+
sm: 600,
|
|
36
|
+
md: 900,
|
|
37
|
+
lg: 1272 + 48,
|
|
38
|
+
xl: 1536,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const defaultThemeOptions: ThemeOptions = {
|
|
43
|
+
custom: custom,
|
|
44
|
+
palette: {
|
|
45
|
+
action: {
|
|
46
|
+
disabled: colors.lightGray2,
|
|
47
|
+
},
|
|
48
|
+
text: {
|
|
49
|
+
primary: "#000",
|
|
50
|
+
secondary: colors.silverGrayDark,
|
|
51
|
+
},
|
|
52
|
+
primary: {
|
|
53
|
+
main: colors.mitRed,
|
|
54
|
+
light: colors.lightRed,
|
|
55
|
+
active: colors.red,
|
|
56
|
+
contrastText: colors.white,
|
|
57
|
+
},
|
|
58
|
+
secondary: {
|
|
59
|
+
light: colors.darkGray2,
|
|
60
|
+
active: colors.silverGrayDark,
|
|
61
|
+
main: colors.black,
|
|
62
|
+
contrastText: colors.white,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
shape: {
|
|
66
|
+
borderRadius: 8,
|
|
67
|
+
},
|
|
68
|
+
spacing: 8,
|
|
69
|
+
typography: typography.globalSettings,
|
|
70
|
+
breakpoints: BREAKPOINTS,
|
|
71
|
+
components: {
|
|
72
|
+
MuiButtonBase: buttons.buttonBaseComponent,
|
|
73
|
+
MuiTypography: typography.component,
|
|
74
|
+
MuiTabPanel: {
|
|
75
|
+
styleOverrides: {
|
|
76
|
+
root: {
|
|
77
|
+
padding: "0px",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
MuiMenu: {
|
|
82
|
+
styleOverrides: { paper: { borderRadius: "4px" } },
|
|
83
|
+
},
|
|
84
|
+
MuiAutocomplete: {
|
|
85
|
+
styleOverrides: {
|
|
86
|
+
paper: { borderRadius: "4px" },
|
|
87
|
+
// Mui puts paddingRight: 2px, marginRight: -2px on the popupIndicator,
|
|
88
|
+
// which causes the browser to show a horizontal scrollbar on overflow
|
|
89
|
+
// containers when a scrollbar isn't really necessary.
|
|
90
|
+
popupIndicator: { paddingRight: 0, marginRight: 0 },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
MuiChip: chips.chipComponent,
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type ExtendedTheme = Theme & {
|
|
98
|
+
custom: CustomTheme
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const createTheme = (options?: {
|
|
102
|
+
custom: Partial<ThemeOptions["custom"]>
|
|
103
|
+
}): ExtendedTheme =>
|
|
104
|
+
muiCreateTheme({
|
|
105
|
+
...defaultThemeOptions,
|
|
106
|
+
custom: {
|
|
107
|
+
...defaultThemeOptions.custom,
|
|
108
|
+
...options?.custom,
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const defaultTheme = createTheme()
|
|
113
|
+
|
|
114
|
+
type ThemeProviderProps = {
|
|
115
|
+
children?: React.ReactNode
|
|
116
|
+
theme?: Theme
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
120
|
+
children,
|
|
121
|
+
theme = defaultTheme,
|
|
122
|
+
}) => {
|
|
123
|
+
return <MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { ThemeProvider, createTheme }
|
|
127
|
+
export type { ThemeProviderProps, Theme }
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import Stack from "@mui/material/Stack"
|
|
4
|
+
import Typography from "@mui/material/Typography"
|
|
5
|
+
import type { TypographyProps } from "@mui/material/Typography"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Typography styles can be controlled via the `theme.typography` object when
|
|
9
|
+
* using the `styled` helper or via the `<Tyopgraphy variant="..." />` component.
|
|
10
|
+
*
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const MyHeading = styled(({ theme }) => ({
|
|
13
|
+
* ...theme.typography.h2,
|
|
14
|
+
* [theme.breakpoints.down("sm")]: {
|
|
15
|
+
* ...theme.typography.h3,
|
|
16
|
+
* },
|
|
17
|
+
* }))
|
|
18
|
+
*
|
|
19
|
+
* // or:
|
|
20
|
+
* <Typography component="h1" typography={
|
|
21
|
+
* {
|
|
22
|
+
* xs: "h3", // above xs
|
|
23
|
+
* sm: "h2" // above sm
|
|
24
|
+
* }
|
|
25
|
+
* }>
|
|
26
|
+
* Hello, world!
|
|
27
|
+
* </Typography>
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
const meta: Meta<typeof Typography> = {
|
|
32
|
+
title: "smoot-design/Typography",
|
|
33
|
+
tags: ["autodocs"],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default meta
|
|
37
|
+
|
|
38
|
+
type Story = StoryObj<typeof Typography>
|
|
39
|
+
|
|
40
|
+
const text = "The quick brown fox jumps over the lazy dog. ".repeat(10)
|
|
41
|
+
const INSTANCES: TypographyProps[] = [
|
|
42
|
+
{ variant: "h1", children: "Heading level 1" },
|
|
43
|
+
{ variant: "h2", children: "Heading level 2" },
|
|
44
|
+
{ variant: "h3", children: "Heading level 3" },
|
|
45
|
+
{ variant: "h4", children: "Heading level 4" },
|
|
46
|
+
{ variant: "h5", children: "Heading level 5" },
|
|
47
|
+
{ variant: "subtitle1", children: "Subtitle level 1" },
|
|
48
|
+
{ variant: "subtitle2", children: "Subtitle level 2" },
|
|
49
|
+
{ variant: "subtitle3", children: "Subtitle level 3" },
|
|
50
|
+
{ variant: "subtitle4", children: "Subtitle level 4" },
|
|
51
|
+
{ variant: "body1", children: `body level 1... ${text}` },
|
|
52
|
+
{ variant: "body2", children: `body level 2... ${text}` },
|
|
53
|
+
{ variant: "body3", children: `body level 3... ${text}` },
|
|
54
|
+
{ variant: "body4", children: `body level 4... ${text}` },
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Typography variants are shown below.
|
|
59
|
+
*
|
|
60
|
+
* **Note:** The typography variant is not related to the HTML element used. A
|
|
61
|
+
* `variant="h1"` component does not automatically render an `<h1>` element.
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
export const Variants: Story = {
|
|
65
|
+
render: () => {
|
|
66
|
+
return (
|
|
67
|
+
<Stack gap="1rem">
|
|
68
|
+
{INSTANCES.map((props) => (
|
|
69
|
+
<Typography key={props.variant} {...props} />
|
|
70
|
+
))}
|
|
71
|
+
</Stack>
|
|
72
|
+
)
|
|
73
|
+
},
|
|
74
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ThemeOptions } from "@mui/material/styles"
|
|
2
|
+
import { createTheme } from "@mui/material/styles"
|
|
3
|
+
|
|
4
|
+
const BREAKPOINT_VALUES: ThemeOptions["breakpoints"] = {
|
|
5
|
+
values: {
|
|
6
|
+
xs: 0,
|
|
7
|
+
sm: 600,
|
|
8
|
+
md: 900,
|
|
9
|
+
lg: 1280,
|
|
10
|
+
xl: 1536,
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { breakpoints } = createTheme({
|
|
15
|
+
breakpoints: BREAKPOINT_VALUES,
|
|
16
|
+
// @ts-expect-error only using breakpoints
|
|
17
|
+
custom: {},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export { BREAKPOINT_VALUES, breakpoints }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ThemeOptions } from "@mui/material/styles"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* We don't use MUI's button directly, but ButtonBase does get used internally
|
|
5
|
+
* by some MUI components, so we override a few styles.
|
|
6
|
+
*/
|
|
7
|
+
const buttonBaseComponent: NonNullable<
|
|
8
|
+
ThemeOptions["components"]
|
|
9
|
+
>["MuiButtonBase"] = {
|
|
10
|
+
defaultProps: {
|
|
11
|
+
disableRipple: true,
|
|
12
|
+
},
|
|
13
|
+
styleOverrides: {
|
|
14
|
+
root: {
|
|
15
|
+
":focus-visible": {
|
|
16
|
+
outline: "revert",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { buttonBaseComponent }
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import type { ThemeOptions } from "@mui/material/styles"
|
|
3
|
+
import { colors } from "./colors"
|
|
4
|
+
import { RiCloseLine } from "@remixicon/react"
|
|
5
|
+
|
|
6
|
+
const chipComponent: NonNullable<ThemeOptions["components"]>["MuiChip"] = {
|
|
7
|
+
defaultProps: {
|
|
8
|
+
size: "medium",
|
|
9
|
+
color: "default",
|
|
10
|
+
variant: "outlined",
|
|
11
|
+
deleteIcon: <RiCloseLine aria-hidden="true" />,
|
|
12
|
+
},
|
|
13
|
+
styleOverrides: {
|
|
14
|
+
root: {
|
|
15
|
+
borderRadius: "100vh",
|
|
16
|
+
borderWidth: "1px",
|
|
17
|
+
},
|
|
18
|
+
deleteIcon: {
|
|
19
|
+
"&:hover": {
|
|
20
|
+
color: "inherit",
|
|
21
|
+
},
|
|
22
|
+
"&.MuiChip-deleteIconLarge": {
|
|
23
|
+
width: "16px",
|
|
24
|
+
height: "16px",
|
|
25
|
+
},
|
|
26
|
+
"&.MuiChip-deleteIconMedium": {
|
|
27
|
+
width: "14px",
|
|
28
|
+
height: "14px",
|
|
29
|
+
},
|
|
30
|
+
margin: "0 -2px 0 8px",
|
|
31
|
+
color: "inherit",
|
|
32
|
+
},
|
|
33
|
+
icon: {
|
|
34
|
+
margin: "0 8px 0 -2px",
|
|
35
|
+
color: "inherit",
|
|
36
|
+
"&.MuiChip-iconLarge": {
|
|
37
|
+
width: "16px",
|
|
38
|
+
height: "16px",
|
|
39
|
+
},
|
|
40
|
+
"&.MuiChip-iconMedium": {
|
|
41
|
+
width: "14px",
|
|
42
|
+
height: "14px",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
variants: [
|
|
47
|
+
{
|
|
48
|
+
props: { size: "medium" },
|
|
49
|
+
style: ({ theme }) => ({
|
|
50
|
+
...theme.typography.body3,
|
|
51
|
+
boxSizing: "border-box",
|
|
52
|
+
height: "24px",
|
|
53
|
+
paddingRight: "12px",
|
|
54
|
+
paddingLeft: "12px",
|
|
55
|
+
".MuiChip-label": {
|
|
56
|
+
paddingLeft: "0px",
|
|
57
|
+
paddingRight: "0px",
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
props: { size: "large" },
|
|
63
|
+
style: ({ theme }) => ({
|
|
64
|
+
...theme.typography.body2,
|
|
65
|
+
height: "32px",
|
|
66
|
+
paddingLeft: "16px",
|
|
67
|
+
paddingRight: "16px",
|
|
68
|
+
".MuiChip-label": {
|
|
69
|
+
paddingLeft: "0px",
|
|
70
|
+
paddingRight: "0px",
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
props: { variant: "outlined" },
|
|
76
|
+
style: {
|
|
77
|
+
borderColor: colors.silverGrayLight,
|
|
78
|
+
color: colors.darkGray1,
|
|
79
|
+
"&.Mui-focusVisible": {
|
|
80
|
+
backgroundColor: "transparent",
|
|
81
|
+
},
|
|
82
|
+
"&.MuiChip-clickable:hover, &.MuiChip-deletable:hover": {
|
|
83
|
+
color: colors.darkGray1,
|
|
84
|
+
borderColor: colors.silverGrayDark,
|
|
85
|
+
backgroundColor: "transparent", // mui has a default background color for hover
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
props: { variant: "outlinedWhite" },
|
|
91
|
+
style: {
|
|
92
|
+
backgroundColor: "white",
|
|
93
|
+
border: "1px solid",
|
|
94
|
+
borderColor: colors.silverGrayLight,
|
|
95
|
+
color: colors.darkGray1,
|
|
96
|
+
"&.Mui-focusVisible": {
|
|
97
|
+
backgroundColor: "white",
|
|
98
|
+
},
|
|
99
|
+
"&.MuiChip-clickable:hover, &.MuiChip-deletable:hover": {
|
|
100
|
+
color: colors.darkGray1,
|
|
101
|
+
borderColor: colors.silverGrayDark,
|
|
102
|
+
backgroundColor: "white", // mui has a default background color hover
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
props: { variant: "gray" },
|
|
108
|
+
style: {
|
|
109
|
+
backgroundColor: colors.lightGray2,
|
|
110
|
+
border: "none",
|
|
111
|
+
color: colors.darkGray2,
|
|
112
|
+
"&.Mui-focusVisible": {
|
|
113
|
+
backgroundColor: colors.lightGray2,
|
|
114
|
+
},
|
|
115
|
+
"&.MuiChip-clickable:hover, &.MuiChip-deletable:hover": {
|
|
116
|
+
color: colors.darkGray1,
|
|
117
|
+
backgroundColor: colors.silverGrayLight,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
props: { variant: "dark" },
|
|
123
|
+
style: {
|
|
124
|
+
backgroundColor: colors.silverGrayDark,
|
|
125
|
+
border: "none",
|
|
126
|
+
color: colors.white,
|
|
127
|
+
"&.Mui-focusVisible": {
|
|
128
|
+
backgroundColor: colors.silverGrayDark,
|
|
129
|
+
},
|
|
130
|
+
"&.MuiChip-clickable:hover, &.MuiChip-deletable:hover": {
|
|
131
|
+
backgroundColor: colors.darkGray1,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
props: { variant: "darker" },
|
|
137
|
+
style: {
|
|
138
|
+
backgroundColor: colors.darkGray2,
|
|
139
|
+
border: `1px solid ${colors.darkGray1}`,
|
|
140
|
+
color: colors.white,
|
|
141
|
+
"&.Mui-focusVisible": {
|
|
142
|
+
backgroundColor: colors.darkGray2,
|
|
143
|
+
},
|
|
144
|
+
"&.MuiChip-clickable:hover, &.MuiChip-deletable:hover": {
|
|
145
|
+
backgroundColor: colors.black,
|
|
146
|
+
border: `1px solid ${colors.silverGray}`,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
props: { variant: "filled" },
|
|
152
|
+
style: {
|
|
153
|
+
backgroundColor: colors.mitRed,
|
|
154
|
+
border: "none",
|
|
155
|
+
color: colors.white,
|
|
156
|
+
"&.Mui-focusVisible": {
|
|
157
|
+
backgroundColor: colors.mitRed,
|
|
158
|
+
},
|
|
159
|
+
"&.MuiChip-clickable:hover, &.MuiChip-deletable:hover": {
|
|
160
|
+
backgroundColor: colors.red,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { chipComponent }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const colors = {
|
|
2
|
+
mitRed: "#750014",
|
|
3
|
+
brightRed: "#FF1423",
|
|
4
|
+
black: "#000000",
|
|
5
|
+
white: "#FFFFFF",
|
|
6
|
+
darkGray2: "#212326",
|
|
7
|
+
darkGray1: "#40464C",
|
|
8
|
+
silverGrayDark: "#626A73",
|
|
9
|
+
silverGray: "#8B959E",
|
|
10
|
+
silverGrayLight: "#B8C2CC",
|
|
11
|
+
lightGray2: "#DDE1E6",
|
|
12
|
+
lightGray1: "#F3F4F8",
|
|
13
|
+
navGray: "#303337",
|
|
14
|
+
darkPink: "#750062",
|
|
15
|
+
pink: "#FF14F0",
|
|
16
|
+
lightPink: "#FFB3FF",
|
|
17
|
+
darkPurple: "#3E006B",
|
|
18
|
+
purple: "#93F",
|
|
19
|
+
lightPurple: "#BFB3FF",
|
|
20
|
+
darkBlue: "#002896",
|
|
21
|
+
blue: "#1966FF",
|
|
22
|
+
lightBlue: "#99EBFF",
|
|
23
|
+
darkGreen: "#004D1A",
|
|
24
|
+
green: "#00AD00",
|
|
25
|
+
lightGreen: "#AF3",
|
|
26
|
+
darkRed: "#83192A",
|
|
27
|
+
red: "#A31F34",
|
|
28
|
+
lightRed: "#D02E44",
|
|
29
|
+
orange: "#FAB005",
|
|
30
|
+
yellow: "#FFEB00",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { colors }
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { ThemeOptions } from "@mui/material/styles"
|
|
2
|
+
import { createTheme } from "@mui/material/styles"
|
|
3
|
+
|
|
4
|
+
const fontWeights = {
|
|
5
|
+
text: {
|
|
6
|
+
roman: 400,
|
|
7
|
+
medium: 500,
|
|
8
|
+
bold: 700,
|
|
9
|
+
},
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This function converts from pixels to rems, assuming a base font size of 16px
|
|
14
|
+
* (which is the default for most modern browsers).
|
|
15
|
+
*
|
|
16
|
+
* Using this function, we can:
|
|
17
|
+
* - match desgins that are in pixels for default font size
|
|
18
|
+
* - allow users to scale the font size up or down by chaning base font size.
|
|
19
|
+
*
|
|
20
|
+
* For example, a Chrome user might specify a base font size of 20px ("large")
|
|
21
|
+
* in their browser settings. Then, `pxToRem(32)` would actually be 40px for
|
|
22
|
+
* that user.
|
|
23
|
+
*/
|
|
24
|
+
const pxToRem = (px: number) => `${px / 16}rem`
|
|
25
|
+
|
|
26
|
+
const globalSettings: ThemeOptions["typography"] = {
|
|
27
|
+
// Note: Figma calls this "Neue Haas Grotesk Text", but that is incorrect based on Adobe's font family.
|
|
28
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
29
|
+
fontWeightLight: fontWeights.text.roman,
|
|
30
|
+
fontWeightRegular: fontWeights.text.roman,
|
|
31
|
+
fontWeightMedium: fontWeights.text.medium,
|
|
32
|
+
fontWeightBold: fontWeights.text.bold,
|
|
33
|
+
h1: {
|
|
34
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
35
|
+
fontWeight: fontWeights.text.bold,
|
|
36
|
+
fontStyle: "normal",
|
|
37
|
+
fontSize: pxToRem(52),
|
|
38
|
+
lineHeight: pxToRem(60),
|
|
39
|
+
},
|
|
40
|
+
h2: {
|
|
41
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
42
|
+
fontWeight: fontWeights.text.bold,
|
|
43
|
+
fontStyle: "normal",
|
|
44
|
+
fontSize: pxToRem(34),
|
|
45
|
+
lineHeight: pxToRem(40),
|
|
46
|
+
},
|
|
47
|
+
h3: {
|
|
48
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
49
|
+
fontWeight: fontWeights.text.bold,
|
|
50
|
+
fontStyle: "normal",
|
|
51
|
+
fontSize: pxToRem(28),
|
|
52
|
+
lineHeight: pxToRem(36),
|
|
53
|
+
},
|
|
54
|
+
h4: {
|
|
55
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
56
|
+
fontWeight: fontWeights.text.bold,
|
|
57
|
+
fontStyle: "normal",
|
|
58
|
+
fontSize: pxToRem(24),
|
|
59
|
+
lineHeight: pxToRem(30),
|
|
60
|
+
},
|
|
61
|
+
h5: {
|
|
62
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
63
|
+
fontWeight: fontWeights.text.medium,
|
|
64
|
+
fontStyle: "normal",
|
|
65
|
+
fontSize: pxToRem(18),
|
|
66
|
+
lineHeight: pxToRem(22),
|
|
67
|
+
},
|
|
68
|
+
subtitle1: {
|
|
69
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
70
|
+
fontWeight: fontWeights.text.medium,
|
|
71
|
+
fontStyle: "normal",
|
|
72
|
+
fontSize: pxToRem(16),
|
|
73
|
+
lineHeight: pxToRem(20),
|
|
74
|
+
},
|
|
75
|
+
subtitle2: {
|
|
76
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
77
|
+
fontWeight: fontWeights.text.medium,
|
|
78
|
+
fontStyle: "normal",
|
|
79
|
+
fontSize: pxToRem(14),
|
|
80
|
+
lineHeight: pxToRem(18),
|
|
81
|
+
},
|
|
82
|
+
subtitle3: {
|
|
83
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
84
|
+
fontWeight: fontWeights.text.medium,
|
|
85
|
+
fontStyle: "normal",
|
|
86
|
+
fontSize: pxToRem(12),
|
|
87
|
+
lineHeight: pxToRem(16),
|
|
88
|
+
},
|
|
89
|
+
subtitle4: {
|
|
90
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
91
|
+
fontWeight: fontWeights.text.medium,
|
|
92
|
+
fontStyle: "normal",
|
|
93
|
+
fontSize: pxToRem(10),
|
|
94
|
+
lineHeight: pxToRem(14),
|
|
95
|
+
},
|
|
96
|
+
body1: {
|
|
97
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
98
|
+
fontWeight: fontWeights.text.roman,
|
|
99
|
+
fontStyle: "normal",
|
|
100
|
+
fontSize: pxToRem(16),
|
|
101
|
+
lineHeight: pxToRem(20),
|
|
102
|
+
},
|
|
103
|
+
body2: {
|
|
104
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
105
|
+
fontWeight: fontWeights.text.roman,
|
|
106
|
+
fontStyle: "normal",
|
|
107
|
+
fontSize: pxToRem(14),
|
|
108
|
+
lineHeight: pxToRem(18),
|
|
109
|
+
},
|
|
110
|
+
body3: {
|
|
111
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
112
|
+
fontWeight: fontWeights.text.roman,
|
|
113
|
+
fontStyle: "normal",
|
|
114
|
+
fontSize: pxToRem(12),
|
|
115
|
+
lineHeight: pxToRem(16),
|
|
116
|
+
},
|
|
117
|
+
body4: {
|
|
118
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
119
|
+
fontWeight: fontWeights.text.roman,
|
|
120
|
+
fontStyle: "normal",
|
|
121
|
+
fontSize: pxToRem(10),
|
|
122
|
+
lineHeight: pxToRem(14),
|
|
123
|
+
},
|
|
124
|
+
buttonLarge: {
|
|
125
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
126
|
+
fontWeight: fontWeights.text.medium,
|
|
127
|
+
fontStyle: "normal",
|
|
128
|
+
fontSize: pxToRem(16),
|
|
129
|
+
lineHeight: pxToRem(20),
|
|
130
|
+
},
|
|
131
|
+
button: {
|
|
132
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
133
|
+
fontWeight: fontWeights.text.medium,
|
|
134
|
+
fontStyle: "normal",
|
|
135
|
+
fontSize: pxToRem(14),
|
|
136
|
+
lineHeight: pxToRem(18),
|
|
137
|
+
textTransform: "none",
|
|
138
|
+
},
|
|
139
|
+
buttonSmall: {
|
|
140
|
+
fontFamily: "neue-haas-grotesk-text, sans-serif",
|
|
141
|
+
fontWeight: fontWeights.text.medium,
|
|
142
|
+
fontStyle: "normal",
|
|
143
|
+
fontSize: pxToRem(12),
|
|
144
|
+
lineHeight: pxToRem(16),
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
const component: NonNullable<ThemeOptions["components"]>["MuiTypography"] = {
|
|
148
|
+
defaultProps: {
|
|
149
|
+
variantMapping: {
|
|
150
|
+
h1: "span",
|
|
151
|
+
h2: "span",
|
|
152
|
+
h3: "span",
|
|
153
|
+
h4: "span",
|
|
154
|
+
h5: "span",
|
|
155
|
+
body1: "p",
|
|
156
|
+
body2: "p",
|
|
157
|
+
body3: "p",
|
|
158
|
+
body4: "p",
|
|
159
|
+
subtitle1: "p",
|
|
160
|
+
subtitle2: "p",
|
|
161
|
+
subtitle3: "p",
|
|
162
|
+
subtitle4: "p",
|
|
163
|
+
button: "span",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const { typography } = createTheme({
|
|
169
|
+
typography: globalSettings,
|
|
170
|
+
// @ts-expect-error: we only care about typography from this theme
|
|
171
|
+
custom: {},
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
export { globalSettings, component, pxToRem, typography }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/// <reference types="./types/theme.d.ts" />
|
|
3
|
+
/// <reference types="./types/typography.d.ts" />
|
|
4
|
+
|
|
5
|
+
export { default as styled } from "@emotion/styled"
|
|
6
|
+
export { css, Global } from "@emotion/react"
|
|
7
|
+
|
|
8
|
+
export { AppRouterCacheProvider as NextJsAppRouterCacheProvider } from "@mui/material-nextjs/v15-appRouter"
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
ThemeProvider,
|
|
12
|
+
createTheme,
|
|
13
|
+
} from "./components/ThemeProvider/ThemeProvider"
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
Button,
|
|
17
|
+
ButtonLink,
|
|
18
|
+
ActionButton,
|
|
19
|
+
ActionButtonLink,
|
|
20
|
+
} from "./components/Button/Button"
|
|
21
|
+
|
|
22
|
+
export type { LinkAdapterPropsOverrides } from "./components/LinkAdapter/LinkAdapter"
|
|
23
|
+
|
|
24
|
+
export type { ButtonProps, ButtonLinkProps } from "./components/Button/Button"
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a string that represents an enum of the keys of the given object.
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* ```ts
|
|
6
|
+
* const SIZES = {
|
|
7
|
+
* small: "irrelevant-value",
|
|
8
|
+
* large: "irrelevant-value",
|
|
9
|
+
* }
|
|
10
|
+
* console.log(docsEnum(SIZES))
|
|
11
|
+
* // '"small" | "large"'
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Use case: Storybook docs are created with react-docgen, which fails to infer
|
|
16
|
+
* typescript enum types.
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
const docsEnum = <T extends string>(values: T[]) => {
|
|
21
|
+
return values.map((key) => `"${key}"`).join(" | ")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const enumValues = <T extends string>(obj: Record<T, unknown>): T[] => {
|
|
25
|
+
return Object.keys(obj) as T[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { docsEnum, enumValues }
|