@marigold/system 0.0.3 → 0.3.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/Box.d.ts +14 -0
  3. package/dist/Global.d.ts +2 -0
  4. package/dist/SVG.d.ts +6 -0
  5. package/dist/SVG.stories.d.ts +5 -0
  6. package/dist/index.d.ts +6 -3
  7. package/dist/normalize.d.ts +144 -0
  8. package/dist/system.cjs.development.js +348 -132
  9. package/dist/system.cjs.development.js.map +1 -1
  10. package/dist/system.cjs.production.min.js +1 -1
  11. package/dist/system.cjs.production.min.js.map +1 -1
  12. package/dist/system.esm.js +341 -129
  13. package/dist/system.esm.js.map +1 -1
  14. package/dist/theme.d.ts +136 -0
  15. package/dist/types.d.ts +2 -3
  16. package/dist/useTheme.d.ts +15 -5
  17. package/dist/variant.d.ts +29 -0
  18. package/package.json +4 -6
  19. package/src/Box.test.tsx +308 -0
  20. package/src/Box.tsx +199 -0
  21. package/src/Global.test.tsx +57 -0
  22. package/src/Global.tsx +34 -0
  23. package/src/SVG.stories.tsx +48 -0
  24. package/src/SVG.test.tsx +82 -0
  25. package/src/SVG.tsx +24 -0
  26. package/src/index.ts +6 -3
  27. package/src/normalize.test.tsx +15 -0
  28. package/src/normalize.ts +100 -0
  29. package/src/theme.ts +157 -0
  30. package/src/types.ts +2 -3
  31. package/src/useTheme.test.tsx +43 -18
  32. package/src/useTheme.tsx +45 -7
  33. package/src/variant.test.ts +93 -0
  34. package/src/variant.ts +54 -0
  35. package/dist/cache.d.ts +0 -4
  36. package/dist/reset.d.ts +0 -24
  37. package/dist/useClassname.d.ts +0 -2
  38. package/dist/useStyles.d.ts +0 -15
  39. package/src/Colors.stories.mdx +0 -455
  40. package/src/cache.ts +0 -4
  41. package/src/concepts-principles.mdx +0 -84
  42. package/src/reset.ts +0 -106
  43. package/src/useClassname.test.tsx +0 -70
  44. package/src/useClassname.ts +0 -24
  45. package/src/useStyles.test.tsx +0 -286
  46. package/src/useStyles.ts +0 -55
  47. package/src/writeComponent.stories.mdx +0 -126
package/src/Box.tsx ADDED
@@ -0,0 +1,199 @@
1
+ import { jsx, Theme } from '@emotion/react';
2
+ import { css as transformStyleObject } from '@theme-ui/css';
3
+ import { forwardRef } from 'react';
4
+ import {
5
+ PolymorphicPropsWithRef,
6
+ PolymorphicComponentWithRef,
7
+ } from '@marigold/types';
8
+
9
+ import { getNormalizedStyles } from './normalize';
10
+ import { CSSObject } from './types';
11
+ import { ensureArrayVariant } from './variant';
12
+
13
+ export type StyleProps = Pick<
14
+ CSSObject,
15
+ | 'display'
16
+ | 'height'
17
+ | 'width'
18
+ | 'minWidth'
19
+ | 'maxWidth'
20
+ | 'position'
21
+ | 'top'
22
+ | 'bottom'
23
+ | 'right'
24
+ | 'left'
25
+ | 'zIndex'
26
+ | 'p'
27
+ | 'px'
28
+ | 'py'
29
+ | 'pt'
30
+ | 'pb'
31
+ | 'pl'
32
+ | 'pr'
33
+ | 'm'
34
+ | 'mx'
35
+ | 'my'
36
+ | 'mt'
37
+ | 'mb'
38
+ | 'ml'
39
+ | 'mr'
40
+ | 'flexDirection'
41
+ | 'flexWrap'
42
+ | 'flexShrink'
43
+ | 'flexGrow'
44
+ | 'alignItems'
45
+ | 'justifyContent'
46
+ | 'bg'
47
+ | 'border'
48
+ | 'borderRadius'
49
+ | 'boxShadow'
50
+ | 'opacity'
51
+ | 'overflow'
52
+ | 'transition'
53
+ >;
54
+
55
+ export type BoxOwnProps = {
56
+ css?: CSSObject;
57
+ variant?: string | string[];
58
+ /**
59
+ * Use to set base styles for the component
60
+ * @internal Used to set default styles for Marigold components
61
+ */
62
+ __baseCSS?: CSSObject;
63
+ } & StyleProps;
64
+
65
+ export type BoxProps = PolymorphicPropsWithRef<BoxOwnProps, 'div'>;
66
+
67
+ /**
68
+ * Check if there is any falsy value or empty object
69
+ */
70
+ const isNotEmpty = (val: any) =>
71
+ !(val && Object.keys(val).length === 0 && val.constructor === Object);
72
+
73
+ type CreateStyleProps = {
74
+ as?: BoxProps['as'];
75
+ __baseCSS?: BoxOwnProps['__baseCSS'];
76
+ variant?: BoxOwnProps['variant'];
77
+ css?: BoxOwnProps['css'];
78
+ styles?: StyleProps;
79
+ };
80
+
81
+ const createThemedStyle =
82
+ ({ as, __baseCSS, variant, styles, css }: CreateStyleProps) =>
83
+ (theme: Theme) => {
84
+ return [
85
+ getNormalizedStyles(as),
86
+ transformStyleObject(__baseCSS)(theme),
87
+ ...ensureArrayVariant(variant).map(v =>
88
+ transformStyleObject({ variant: v })(theme)
89
+ ),
90
+ transformStyleObject(styles)(theme),
91
+ transformStyleObject(css)(theme),
92
+ ].filter(isNotEmpty);
93
+ };
94
+
95
+ export const Box: PolymorphicComponentWithRef<BoxOwnProps, 'div'> = forwardRef(
96
+ (
97
+ {
98
+ as = 'div',
99
+ children,
100
+ __baseCSS,
101
+ variant,
102
+ css = {},
103
+ display,
104
+ height,
105
+ width,
106
+ minWidth,
107
+ maxWidth,
108
+ position,
109
+ top,
110
+ bottom,
111
+ right,
112
+ left,
113
+ zIndex,
114
+ p,
115
+ px,
116
+ py,
117
+ pt,
118
+ pb,
119
+ pl,
120
+ pr,
121
+ m,
122
+ mx,
123
+ my,
124
+ mt,
125
+ mb,
126
+ ml,
127
+ mr,
128
+ flexDirection,
129
+ flexWrap,
130
+ flexShrink,
131
+ flexGrow,
132
+ alignItems,
133
+ justifyContent,
134
+ bg,
135
+ border,
136
+ borderRadius,
137
+ boxShadow,
138
+ opacity,
139
+ overflow,
140
+ transition,
141
+ ...props
142
+ },
143
+ ref
144
+ ) =>
145
+ jsx(
146
+ as,
147
+ {
148
+ ...props,
149
+ css: createThemedStyle({
150
+ as,
151
+ __baseCSS,
152
+ variant,
153
+ css,
154
+ styles: {
155
+ display,
156
+ height,
157
+ width,
158
+ minWidth,
159
+ maxWidth,
160
+ position,
161
+ top,
162
+ bottom,
163
+ right,
164
+ left,
165
+ zIndex,
166
+ p,
167
+ px,
168
+ py,
169
+ pt,
170
+ pb,
171
+ pl,
172
+ pr,
173
+ m,
174
+ mx,
175
+ my,
176
+ mt,
177
+ mb,
178
+ ml,
179
+ mr,
180
+ flexDirection,
181
+ flexWrap,
182
+ flexShrink,
183
+ flexGrow,
184
+ alignItems,
185
+ justifyContent,
186
+ bg,
187
+ border,
188
+ borderRadius,
189
+ boxShadow,
190
+ opacity,
191
+ overflow,
192
+ transition,
193
+ },
194
+ }),
195
+ ref,
196
+ },
197
+ children
198
+ )
199
+ );
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { ThemeProvider } from './useTheme';
4
+
5
+ import { Global } from './Global';
6
+
7
+ test('applies normlaization to html and body', () => {
8
+ const root = render(<Global />);
9
+
10
+ const html = window.getComputedStyle(root.baseElement.parentElement!);
11
+ expect(html.height).toBe('100%');
12
+ const body = window.getComputedStyle(root.baseElement);
13
+ expect(body.height).toBe('100%');
14
+ expect(body.lineHeight).toBe('1.5');
15
+ });
16
+
17
+ test('applies global styles for body and html based on `theme.root`', () => {
18
+ const theme = {
19
+ colors: {
20
+ background: '#fff',
21
+ },
22
+ fonts: {
23
+ body: 'Inter',
24
+ },
25
+ lineHeights: {
26
+ body: 2.5,
27
+ },
28
+ fontWeights: {
29
+ body: 500,
30
+ html: 700,
31
+ },
32
+ root: {
33
+ body: {
34
+ fontFamily: 'body',
35
+ lineHeight: 'body',
36
+ fontWeight: 'body',
37
+ },
38
+ html: {
39
+ bg: 'background',
40
+ },
41
+ },
42
+ };
43
+
44
+ const root = render(
45
+ <ThemeProvider theme={theme}>
46
+ <Global />
47
+ </ThemeProvider>
48
+ );
49
+
50
+ const html = root.baseElement.parentElement;
51
+ expect(html).toHaveStyle(`background: ${theme.colors.background}`);
52
+
53
+ const body = root.baseElement;
54
+ expect(body).toHaveStyle(`font-family: ${theme.fonts.body}`);
55
+ expect(body).toHaveStyle(`line-height: ${theme.lineHeights.body}`);
56
+ expect(body).toHaveStyle(`font-weight: ${theme.fontWeights.body}`);
57
+ });
package/src/Global.tsx ADDED
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { Global as EmotionGlobal } from '@emotion/react';
3
+ import { useTheme } from './useTheme';
4
+
5
+ /**
6
+ * CSS snippet and idea from:
7
+ * https://css-tricks.com/revisiting-prefers-reduced-motion-the-reduced-motion-media-query/
8
+ */
9
+ const reduceMotionStyles = {
10
+ '@media screen and (prefers-reduced-motion: reduce), (update: slow)': {
11
+ '*': {
12
+ animationDuration: '0.001ms !important',
13
+ animationIterationCount: '1 !important',
14
+ transitionDuration: '0.001ms !important',
15
+ },
16
+ },
17
+ };
18
+
19
+ export const Global = () => {
20
+ const { css } = useTheme();
21
+ const styles = css({
22
+ html: {
23
+ height: '100%',
24
+ variant: 'root.html',
25
+ },
26
+ body: {
27
+ height: '100%',
28
+ lineHeight: 1.5,
29
+ WebkitFontSmoothing: 'antialiased',
30
+ variant: 'root.body',
31
+ },
32
+ });
33
+ return <EmotionGlobal styles={{ reduceMotionStyles, ...styles }} />;
34
+ };
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { SVG } from './SVG';
4
+
5
+ export default {
6
+ title: 'Components/SVG',
7
+ argTypes: {
8
+ variant: {
9
+ control: {
10
+ type: 'text',
11
+ },
12
+ table: {
13
+ defaultValue: {
14
+ summary: 'icon',
15
+ },
16
+ },
17
+ },
18
+ size: {
19
+ control: {
20
+ type: 'range',
21
+ min: 0,
22
+ max: 96,
23
+ step: 2,
24
+ },
25
+ table: {
26
+ defaultValue: {
27
+ summary: 24,
28
+ },
29
+ },
30
+ },
31
+ fill: {
32
+ control: {
33
+ type: 'text',
34
+ },
35
+ table: {
36
+ defaultValue: {
37
+ summary: 'currentColor',
38
+ },
39
+ },
40
+ },
41
+ },
42
+ } as Meta;
43
+
44
+ export const Basic: ComponentStory<typeof SVG> = args => (
45
+ <SVG {...args}>
46
+ <path d="M9.9 20.113V13.8415H14.1V20.113H19.35V11.751H22.5L12 2.34375L1.5 11.751H4.65V20.113H9.9Z" />
47
+ </SVG>
48
+ );
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { SVG } from './SVG';
4
+
5
+ test('renders svg', () => {
6
+ render(<SVG data-testid="svg" />);
7
+ const svg = screen.getByTestId('svg');
8
+ expect(svg instanceof SVGElement).toBeTruthy();
9
+ });
10
+
11
+ test('normalizes <svg>', () => {
12
+ render(<SVG data-testid="svg" />);
13
+ const svg = screen.getByTestId('svg');
14
+ expect(svg).toHaveStyle('display: block');
15
+ expect(svg).toHaveStyle('max-width: 100%');
16
+ });
17
+
18
+ test('supports default fill color', () => {
19
+ render(
20
+ <SVG data-testid="svg">
21
+ <path d="M9.9 20.113V13.8415H14" />
22
+ </SVG>
23
+ );
24
+ const svg = screen.getByTestId(/svg/);
25
+
26
+ expect(svg.getAttribute('fill')).toEqual('currentcolor');
27
+ });
28
+
29
+ test('supports default size', () => {
30
+ render(
31
+ <SVG data-testid="svg">
32
+ <path d="M9.9 20.113V13.8415H14" />
33
+ </SVG>
34
+ );
35
+ const svg = screen.getByTestId(/svg/);
36
+
37
+ expect(svg.getAttribute('width')).toEqual('24');
38
+ });
39
+
40
+ test('supports size prop', () => {
41
+ render(
42
+ <SVG data-testid="svg" size={30}>
43
+ <path d="M9.9 20.113V13.8415H14" />
44
+ </SVG>
45
+ );
46
+ const svg = screen.getByTestId(/svg/);
47
+
48
+ expect(svg.getAttribute('width')).toEqual('30');
49
+ });
50
+
51
+ test('supports fill prop', () => {
52
+ render(
53
+ <SVG data-testid="svg" fill="#fafafa">
54
+ <path d="M9.9 20.113V13.8415H14" />
55
+ </SVG>
56
+ );
57
+ const svg = screen.getByTestId(/svg/);
58
+
59
+ expect(svg.getAttribute('fill')).toEqual('#fafafa');
60
+ });
61
+
62
+ test('accepts custom styles prop className', () => {
63
+ render(
64
+ <SVG data-testid="svg" className="custom-class-name">
65
+ <path d="M9.9 20.113V13.8415H14" />
66
+ </SVG>
67
+ );
68
+ const svg = screen.getByTestId(/svg/);
69
+
70
+ expect(svg.getAttribute('class')).toMatch(/custom-class-name/);
71
+ });
72
+
73
+ test('renders <svg> element', () => {
74
+ render(
75
+ <SVG data-testid="svg">
76
+ <path d="M9.9 20.113V13.8415H14" />
77
+ </SVG>
78
+ );
79
+ const svg = screen.getByTestId(/svg/);
80
+
81
+ expect(svg instanceof SVGElement).toBeTruthy();
82
+ });
package/src/SVG.tsx ADDED
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { jsx } from '@emotion/react';
3
+ import { ComponentProps } from '@marigold/types';
4
+ import { getNormalizedStyles } from '@marigold/system';
5
+
6
+ const css = getNormalizedStyles('svg');
7
+
8
+ export type SVGProps = {
9
+ size?: number;
10
+ } & ComponentProps<'svg'>;
11
+
12
+ export const SVG: React.FC<SVGProps> = ({ size = 24, children, ...props }) =>
13
+ jsx(
14
+ 'svg',
15
+ {
16
+ width: size,
17
+ height: size,
18
+ viewBox: '0 0 24 24',
19
+ fill: 'currentcolor',
20
+ ...props,
21
+ css,
22
+ },
23
+ children
24
+ );
package/src/index.ts CHANGED
@@ -1,5 +1,8 @@
1
- export * from './cache';
1
+ export * from './Box';
2
+ export * from './Global';
3
+ export * from './normalize';
4
+ export * from './SVG';
5
+ export * from './theme';
2
6
  export * from './types';
3
- export * from './useClassname';
4
- export * from './useStyles';
5
7
  export * from './useTheme';
8
+ export * from './variant';
@@ -0,0 +1,15 @@
1
+ import { normalize, getNormalizedStyles } from './normalize';
2
+
3
+ test.each(Object.entries(normalize))('base is included in %p', (_, value) => {
4
+ expect(value).toMatchObject(normalize.base);
5
+ });
6
+
7
+ test('get normalized styles', () => {
8
+ expect(getNormalizedStyles('a')).toMatchObject(normalize.a);
9
+ expect(getNormalizedStyles('p')).toMatchObject(normalize.p);
10
+ });
11
+
12
+ test('return base normalzation for arbitrary components', () => {
13
+ const Component = () => null;
14
+ expect(getNormalizedStyles(Component)).toMatchObject(normalize.base);
15
+ });
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Normalize styling of certain elements between browsers.
3
+ * Based on https://www.joshwcomeau.com/css/custom-css-reset/
4
+ */
5
+ import { ElementType } from 'react';
6
+
7
+ const base = {
8
+ boxSizing: 'border-box',
9
+ margin: 0,
10
+ minWidth: 0,
11
+ } as const;
12
+
13
+ const a = {
14
+ ...base,
15
+ textDecoration: 'none',
16
+ } as const;
17
+
18
+ const text = {
19
+ ...base,
20
+ overflowWrap: 'break-word',
21
+ } as const;
22
+
23
+ const media = {
24
+ ...base,
25
+ display: 'block',
26
+ maxWidth: '100%',
27
+ } as const;
28
+
29
+ const button = {
30
+ ...base,
31
+ display: 'block',
32
+ appearance: 'none',
33
+ font: 'inherit',
34
+ background: 'transparent',
35
+ textAlign: 'center',
36
+ } as const;
37
+
38
+ const input = {
39
+ ...base,
40
+ display: 'block',
41
+ appearance: 'none',
42
+ font: 'inherit',
43
+ '&::-ms-clear': {
44
+ display: 'none',
45
+ },
46
+ '&::-webkit-search-cancel-button': {
47
+ WebkitAppearance: 'none',
48
+ },
49
+ } as const;
50
+
51
+ const select = {
52
+ ...base,
53
+ display: 'block',
54
+ appearance: 'none',
55
+ font: 'inherit',
56
+ '&::-ms-expand': {
57
+ display: 'none',
58
+ },
59
+ } as const;
60
+
61
+ const textarea = {
62
+ ...base,
63
+ display: 'block',
64
+ appearance: 'none',
65
+ font: 'inherit',
66
+ } as const;
67
+
68
+ // Normalize
69
+ // ---------------
70
+ export const normalize = {
71
+ base,
72
+ a,
73
+ p: text,
74
+ h1: text,
75
+ h2: text,
76
+ h3: text,
77
+ h4: text,
78
+ h5: text,
79
+ h6: text,
80
+ img: media,
81
+ picture: media,
82
+ video: media,
83
+ canvas: media,
84
+ svg: media,
85
+ select,
86
+ button,
87
+ textarea,
88
+ input,
89
+ } as const;
90
+
91
+ export type NormalizedElement = keyof typeof normalize;
92
+
93
+ /**
94
+ * Type-safe helper to get normalization. If no normalization is found,
95
+ * returns the *base* normalization.
96
+ */
97
+ export const getNormalizedStyles = (val?: ElementType) =>
98
+ typeof val === 'string' && val in normalize
99
+ ? normalize[val as NormalizedElement] // Typescript doesn't infer this correctly
100
+ : normalize.base;