@native-mate/core 0.1.0 → 0.1.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.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @native-mate/core
2
+
3
+ Token system, theme provider, and primitive components for [native-mate](https://github.com/native-mate/native-mate).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @native-mate/core
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Wrap your app in `ThemeProvider`:
14
+
15
+ ```tsx
16
+ import { ThemeProvider } from '@native-mate/core'
17
+
18
+ export default function App() {
19
+ return (
20
+ <ThemeProvider preset="zinc">
21
+ <YourApp />
22
+ </ThemeProvider>
23
+ )
24
+ }
25
+ ```
26
+
27
+ ## Presets
28
+
29
+ Four built-in presets: `zinc`, `slate`, `rose`, `midnight`
30
+
31
+ ```tsx
32
+ <ThemeProvider preset="zinc" /> // neutral grays (default)
33
+ <ThemeProvider preset="slate" /> // cool blue-gray
34
+ <ThemeProvider preset="rose" /> // warm rose accents
35
+ <ThemeProvider preset="midnight" /> // deep dark with purple
36
+ ```
37
+
38
+ ## Hooks
39
+
40
+ ### `useTheme()`
41
+
42
+ Access resolved tokens inside any component:
43
+
44
+ ```tsx
45
+ import { useTheme } from '@native-mate/core'
46
+
47
+ function MyComponent() {
48
+ const theme = useTheme()
49
+ return <View style={{ backgroundColor: theme.colors.background }} />
50
+ }
51
+ ```
52
+
53
+ ### `makeStyles(factory)`
54
+
55
+ Create memoized, theme-aware StyleSheets:
56
+
57
+ ```tsx
58
+ import { makeStyles } from '@native-mate/core'
59
+
60
+ const useStyles = makeStyles((theme) => ({
61
+ container: {
62
+ backgroundColor: theme.colors.surface,
63
+ padding: theme.spacing.lg,
64
+ borderRadius: theme.radius.md,
65
+ },
66
+ }))
67
+ ```
68
+
69
+ ### `useBreakpoint()`
70
+
71
+ Responsive breakpoints: `sm` (<768), `md` (768–1023), `lg` (1024+)
72
+
73
+ ```tsx
74
+ import { useBreakpoint } from '@native-mate/core'
75
+
76
+ const bp = useBreakpoint() // 'sm' | 'md' | 'lg'
77
+ ```
78
+
79
+ ## Token Categories
80
+
81
+ | Category | Examples |
82
+ |---|---|
83
+ | Colors | `background`, `foreground`, `primary`, `destructive`, `muted` |
84
+ | Spacing | `xs` (4), `sm` (8), `md` (12), `lg` (16), `xl` (24) |
85
+ | Radius | `sm` (6), `md` (10), `lg` (16), `full` (9999) |
86
+ | Typography | `size`, `weight`, `lineHeight` |
87
+ | Animation | `speed.fast` (150ms), `speed.normal` (250ms) |
88
+
89
+ ## Overrides
90
+
91
+ ```tsx
92
+ <ThemeProvider
93
+ preset="zinc"
94
+ overrides={{
95
+ dark: { colors: { primary: '#6366f1' } },
96
+ }}
97
+ >
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT
@@ -14,7 +14,7 @@ export const Spinner = ({ size = 'md', color }) => {
14
14
  const animatedStyle = useAnimatedStyle(() => ({
15
15
  transform: [{ rotate: `${rotation.value}deg` }],
16
16
  }));
17
- return (<View style={{ width: px, height: px }}>
17
+ return (<View style={{ width: px, height: px }}>
18
18
  <Animated.View style={[
19
19
  animatedStyle,
20
20
  {
@@ -25,7 +25,7 @@ export const Spinner = ({ size = 'md', color }) => {
25
25
  borderColor: spinnerColor,
26
26
  borderTopColor: 'transparent',
27
27
  },
28
- ]}/>
28
+ ]}/>
29
29
  </View>);
30
30
  };
31
31
  //# sourceMappingURL=Spinner.js.map
@@ -20,8 +20,8 @@ export const Text = ({ variant = 'body', size, weight, color, muted = false, sty
20
20
  lineHeight: theme.typography.lineHeight.normal,
21
21
  },
22
22
  style,
23
- ]} {...rest}>
24
- {children}
23
+ ]} {...rest}>
24
+ {children}
25
25
  </RNText>);
26
26
  };
27
27
  //# sourceMappingURL=Text.js.map
package/package.json CHANGED
@@ -1,31 +1,31 @@
1
- {
2
- "name": "@native-mate/core",
3
- "version": "0.1.0",
4
- "description": "Token system, ThemeProvider, and primitive components for native-mate",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
7
- "files": ["dist", "src"],
8
- "license": "MIT",
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/native-mate/native-mate",
12
- "directory": "packages/core"
13
- },
14
- "keywords": ["react-native", "expo", "ui", "theme", "tokens", "native-mate"],
15
- "scripts": {
16
- "build": "tsc --project tsconfig.json",
17
- "test": "cd ../.. && npx vitest run --config vitest.config.ts packages/core",
18
- "lint": "tsc --noEmit"
19
- },
20
- "peerDependencies": {
21
- "react": ">=18.0.0",
22
- "react-native": ">=0.73.0",
23
- "react-native-reanimated": ">=3.0.0"
24
- },
25
- "devDependencies": {
26
- "react": "18.2.0",
27
- "react-native": "0.73.6",
28
- "react-native-reanimated": "^3.6.0",
29
- "typescript": "^5.4.0"
30
- }
31
- }
1
+ {
2
+ "name": "@native-mate/core",
3
+ "version": "0.1.1",
4
+ "description": "Token system, ThemeProvider, and primitive components for native-mate",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "files": ["dist", "src"],
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/native-mate/native-mate",
12
+ "directory": "packages/core"
13
+ },
14
+ "keywords": ["react-native", "expo", "ui", "theme", "tokens", "native-mate"],
15
+ "scripts": {
16
+ "build": "tsc --project tsconfig.json",
17
+ "test": "cd ../.. && npx vitest run --config vitest.config.ts packages/core",
18
+ "lint": "tsc --noEmit"
19
+ },
20
+ "peerDependencies": {
21
+ "react": ">=18.0.0",
22
+ "react-native": ">=0.73.0",
23
+ "react-native-reanimated": ">=3.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "react": "18.2.0",
27
+ "react-native": "0.73.6",
28
+ "react-native-reanimated": "^3.6.0",
29
+ "typescript": "^5.4.0"
30
+ }
31
+ }
@@ -1,56 +1,56 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { resolveTokens, zinc } from '../tokens'
3
- import { makeStyles } from '../theme/makeStyles'
4
-
5
- // makeStyles is a factory that returns a hook. We can't call hooks outside
6
- // React, but we CAN verify the factory itself and the styles it produces.
7
-
8
- describe('makeStyles', () => {
9
- it('returns a function (hook)', () => {
10
- const useStyles = makeStyles((theme) => ({
11
- container: { backgroundColor: theme.colors.background },
12
- }))
13
- expect(typeof useStyles).toBe('function')
14
- })
15
-
16
- it('factory receives a theme and returns style objects', () => {
17
- const theme = resolveTokens(zinc, 'dark')
18
- const factory = (t: typeof theme) => ({
19
- box: { padding: t.spacing.md, borderRadius: t.radius.md },
20
- })
21
- const styles = factory(theme)
22
- expect(styles.box.padding).toBe(12)
23
- expect(styles.box.borderRadius).toBe(10)
24
- })
25
-
26
- it('factory can access all token categories', () => {
27
- const theme = resolveTokens(zinc, 'dark')
28
- const factory = (t: typeof theme) => ({
29
- text: {
30
- color: t.colors.foreground,
31
- fontSize: t.typography.size.md,
32
- fontWeight: t.typography.weight.bold,
33
- lineHeight: t.typography.lineHeight.normal,
34
- },
35
- animated: {
36
- // animation tokens are accessible
37
- opacity: t.animation.speed.fast > 0 ? 1 : 0,
38
- },
39
- })
40
- const styles = factory(theme)
41
- expect(styles.text.color).toBe('#fafafa')
42
- expect(styles.text.fontSize).toBe(15)
43
- expect(styles.text.fontWeight).toBe('700')
44
- expect(styles.animated.opacity).toBe(1)
45
- })
46
-
47
- it('produces different styles for different presets', () => {
48
- const darkTheme = resolveTokens(zinc, 'dark')
49
- const lightTheme = resolveTokens(zinc, 'light')
50
- const factory = (t: typeof darkTheme) => ({
51
- bg: { backgroundColor: t.colors.background },
52
- })
53
- expect(factory(darkTheme).bg.backgroundColor).toBe('#070709')
54
- expect(factory(lightTheme).bg.backgroundColor).toBe('#ffffff')
55
- })
56
- })
1
+ import { describe, it, expect } from 'vitest'
2
+ import { resolveTokens, zinc } from '../tokens'
3
+ import { makeStyles } from '../theme/makeStyles'
4
+
5
+ // makeStyles is a factory that returns a hook. We can't call hooks outside
6
+ // React, but we CAN verify the factory itself and the styles it produces.
7
+
8
+ describe('makeStyles', () => {
9
+ it('returns a function (hook)', () => {
10
+ const useStyles = makeStyles((theme) => ({
11
+ container: { backgroundColor: theme.colors.background },
12
+ }))
13
+ expect(typeof useStyles).toBe('function')
14
+ })
15
+
16
+ it('factory receives a theme and returns style objects', () => {
17
+ const theme = resolveTokens(zinc, 'dark')
18
+ const factory = (t: typeof theme) => ({
19
+ box: { padding: t.spacing.md, borderRadius: t.radius.md },
20
+ })
21
+ const styles = factory(theme)
22
+ expect(styles.box.padding).toBe(12)
23
+ expect(styles.box.borderRadius).toBe(10)
24
+ })
25
+
26
+ it('factory can access all token categories', () => {
27
+ const theme = resolveTokens(zinc, 'dark')
28
+ const factory = (t: typeof theme) => ({
29
+ text: {
30
+ color: t.colors.foreground,
31
+ fontSize: t.typography.size.md,
32
+ fontWeight: t.typography.weight.bold,
33
+ lineHeight: t.typography.lineHeight.normal,
34
+ },
35
+ animated: {
36
+ // animation tokens are accessible
37
+ opacity: t.animation.speed.fast > 0 ? 1 : 0,
38
+ },
39
+ })
40
+ const styles = factory(theme)
41
+ expect(styles.text.color).toBe('#fafafa')
42
+ expect(styles.text.fontSize).toBe(15)
43
+ expect(styles.text.fontWeight).toBe('700')
44
+ expect(styles.animated.opacity).toBe(1)
45
+ })
46
+
47
+ it('produces different styles for different presets', () => {
48
+ const darkTheme = resolveTokens(zinc, 'dark')
49
+ const lightTheme = resolveTokens(zinc, 'light')
50
+ const factory = (t: typeof darkTheme) => ({
51
+ bg: { backgroundColor: t.colors.background },
52
+ })
53
+ expect(factory(darkTheme).bg.backgroundColor).toBe('#070709')
54
+ expect(factory(lightTheme).bg.backgroundColor).toBe('#ffffff')
55
+ })
56
+ })
@@ -1,46 +1,46 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { resolveTokens, zinc, slate, rose, midnight, presets } from '../tokens'
3
-
4
- describe('performance', () => {
5
- it('resolveTokens runs 10,000 times under 200ms', () => {
6
- const start = performance.now()
7
- for (let i = 0; i < 10_000; i++) {
8
- resolveTokens(zinc, i % 2 === 0 ? 'dark' : 'light')
9
- }
10
- const elapsed = performance.now() - start
11
- expect(elapsed).toBeLessThan(200)
12
- })
13
-
14
- it('resolveTokens with overrides runs 10,000 times under 300ms', () => {
15
- const overrides = { colors: { primary: '#6366f1' }, spacing: { lg: 20 } }
16
- const start = performance.now()
17
- for (let i = 0; i < 10_000; i++) {
18
- resolveTokens(zinc, 'dark', overrides)
19
- }
20
- const elapsed = performance.now() - start
21
- expect(elapsed).toBeLessThan(300)
22
- })
23
-
24
- it('all 4 presets × 2 modes (80,000 calls) under 1s', () => {
25
- const allPresets = [zinc, slate, rose, midnight]
26
- const start = performance.now()
27
- for (let i = 0; i < 10_000; i++) {
28
- for (const preset of allPresets) {
29
- resolveTokens(preset, 'dark')
30
- resolveTokens(preset, 'light')
31
- }
32
- }
33
- const elapsed = performance.now() - start
34
- expect(elapsed).toBeLessThan(1000)
35
- })
36
-
37
- it('resolved theme object is consistently shaped', () => {
38
- const theme = resolveTokens(zinc, 'dark')
39
- const colorKeys = Object.keys(theme.colors)
40
- // Verify across all presets
41
- for (const preset of Object.values(presets)) {
42
- const t = resolveTokens(preset, 'dark')
43
- expect(Object.keys(t.colors).sort()).toEqual(colorKeys.sort())
44
- }
45
- })
46
- })
1
+ import { describe, it, expect } from 'vitest'
2
+ import { resolveTokens, zinc, slate, rose, midnight, presets } from '../tokens'
3
+
4
+ describe('performance', () => {
5
+ it('resolveTokens runs 10,000 times under 200ms', () => {
6
+ const start = performance.now()
7
+ for (let i = 0; i < 10_000; i++) {
8
+ resolveTokens(zinc, i % 2 === 0 ? 'dark' : 'light')
9
+ }
10
+ const elapsed = performance.now() - start
11
+ expect(elapsed).toBeLessThan(200)
12
+ })
13
+
14
+ it('resolveTokens with overrides runs 10,000 times under 300ms', () => {
15
+ const overrides = { colors: { primary: '#6366f1' }, spacing: { lg: 20 } }
16
+ const start = performance.now()
17
+ for (let i = 0; i < 10_000; i++) {
18
+ resolveTokens(zinc, 'dark', overrides)
19
+ }
20
+ const elapsed = performance.now() - start
21
+ expect(elapsed).toBeLessThan(300)
22
+ })
23
+
24
+ it('all 4 presets × 2 modes (80,000 calls) under 1s', () => {
25
+ const allPresets = [zinc, slate, rose, midnight]
26
+ const start = performance.now()
27
+ for (let i = 0; i < 10_000; i++) {
28
+ for (const preset of allPresets) {
29
+ resolveTokens(preset, 'dark')
30
+ resolveTokens(preset, 'light')
31
+ }
32
+ }
33
+ const elapsed = performance.now() - start
34
+ expect(elapsed).toBeLessThan(1000)
35
+ })
36
+
37
+ it('resolved theme object is consistently shaped', () => {
38
+ const theme = resolveTokens(zinc, 'dark')
39
+ const colorKeys = Object.keys(theme.colors)
40
+ // Verify across all presets
41
+ for (const preset of Object.values(presets)) {
42
+ const t = resolveTokens(preset, 'dark')
43
+ expect(Object.keys(t.colors).sort()).toEqual(colorKeys.sort())
44
+ }
45
+ })
46
+ })
@@ -1,34 +1,34 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { Platform } from 'react-native'
3
- import { shadow } from '../utils/platform'
4
-
5
- describe('shadow()', () => {
6
- it('returns elevation on Android', () => {
7
- Object.defineProperty(Platform, 'OS', { get: () => 'android', configurable: true })
8
- const s = shadow(2)
9
- expect(s).toHaveProperty('elevation')
10
- expect((s as { elevation: number }).elevation).toBe(4)
11
- })
12
-
13
- it('returns shadowColor and shadowOffset on iOS', () => {
14
- Object.defineProperty(Platform, 'OS', { get: () => 'ios', configurable: true })
15
- const s = shadow(1)
16
- expect(s).toHaveProperty('shadowColor')
17
- expect(s).toHaveProperty('shadowOffset')
18
- expect(s).toHaveProperty('shadowOpacity')
19
- expect(s).toHaveProperty('shadowRadius')
20
- })
21
-
22
- it('level 1 has lower shadow than level 4', () => {
23
- Object.defineProperty(Platform, 'OS', { get: () => 'android', configurable: true })
24
- const s1 = shadow(1) as { elevation: number }
25
- const s4 = shadow(4) as { elevation: number }
26
- expect(s4.elevation).toBeGreaterThan(s1.elevation)
27
- })
28
-
29
- it('returns elevation object on non-ios (android fallback)', () => {
30
- Object.defineProperty(Platform, 'OS', { get: () => 'android', configurable: true })
31
- const s = shadow(3) as { elevation: number }
32
- expect(s.elevation).toBe(8)
33
- })
34
- })
1
+ import { describe, it, expect } from 'vitest'
2
+ import { Platform } from 'react-native'
3
+ import { shadow } from '../utils/platform'
4
+
5
+ describe('shadow()', () => {
6
+ it('returns elevation on Android', () => {
7
+ Object.defineProperty(Platform, 'OS', { get: () => 'android', configurable: true })
8
+ const s = shadow(2)
9
+ expect(s).toHaveProperty('elevation')
10
+ expect((s as { elevation: number }).elevation).toBe(4)
11
+ })
12
+
13
+ it('returns shadowColor and shadowOffset on iOS', () => {
14
+ Object.defineProperty(Platform, 'OS', { get: () => 'ios', configurable: true })
15
+ const s = shadow(1)
16
+ expect(s).toHaveProperty('shadowColor')
17
+ expect(s).toHaveProperty('shadowOffset')
18
+ expect(s).toHaveProperty('shadowOpacity')
19
+ expect(s).toHaveProperty('shadowRadius')
20
+ })
21
+
22
+ it('level 1 has lower shadow than level 4', () => {
23
+ Object.defineProperty(Platform, 'OS', { get: () => 'android', configurable: true })
24
+ const s1 = shadow(1) as { elevation: number }
25
+ const s4 = shadow(4) as { elevation: number }
26
+ expect(s4.elevation).toBeGreaterThan(s1.elevation)
27
+ })
28
+
29
+ it('returns elevation object on non-ios (android fallback)', () => {
30
+ Object.defineProperty(Platform, 'OS', { get: () => 'android', configurable: true })
31
+ const s = shadow(3) as { elevation: number }
32
+ expect(s.elevation).toBe(8)
33
+ })
34
+ })
@@ -1,58 +1,58 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { defaultTheme, ThemeContext } from '../theme/ThemeContext'
3
- import { resolveTokens, zinc, presets } from '../tokens'
4
-
5
- describe('ThemeContext', () => {
6
- it('defaultTheme is zinc light', () => {
7
- const expected = resolveTokens(zinc, 'light')
8
- expect(defaultTheme.colorScheme).toBe('light')
9
- expect(defaultTheme.colors.background).toBe(expected.colors.background)
10
- expect(defaultTheme.colors.foreground).toBe(expected.colors.foreground)
11
- })
12
-
13
- it('defaultTheme has all required token categories', () => {
14
- expect(defaultTheme).toHaveProperty('colors')
15
- expect(defaultTheme).toHaveProperty('spacing')
16
- expect(defaultTheme).toHaveProperty('radius')
17
- expect(defaultTheme).toHaveProperty('typography')
18
- expect(defaultTheme).toHaveProperty('animation')
19
- expect(defaultTheme).toHaveProperty('colorScheme')
20
- })
21
-
22
- it('ThemeContext is a valid React context', () => {
23
- expect(ThemeContext).toHaveProperty('Provider')
24
- expect(ThemeContext).toHaveProperty('Consumer')
25
- })
26
- })
27
-
28
- describe('ThemeProvider logic', () => {
29
- it('forcedColorScheme dark resolves dark tokens', () => {
30
- const theme = resolveTokens(presets.zinc, 'dark')
31
- expect(theme.colorScheme).toBe('dark')
32
- expect(theme.colors.background).toBe('#070709')
33
- })
34
-
35
- it('forcedColorScheme light resolves light tokens', () => {
36
- const theme = resolveTokens(presets.zinc, 'light')
37
- expect(theme.colorScheme).toBe('light')
38
- expect(theme.colors.background).toBe('#ffffff')
39
- })
40
-
41
- it('each preset resolves to different primary colors', () => {
42
- const themes = Object.entries(presets).map(([name, preset]) => ({
43
- name,
44
- primary: resolveTokens(preset, 'dark').colors.primary,
45
- }))
46
- const primaries = new Set(themes.map((t) => t.primary))
47
- // At least 3 unique primaries across 4 presets (zinc and slate may share)
48
- expect(primaries.size).toBeGreaterThanOrEqual(3)
49
- })
50
-
51
- it('overrides are applied per-mode', () => {
52
- const darkOverrides = { colors: { primary: '#ff0000' } }
53
- const darkTheme = resolveTokens(presets.zinc, 'dark', darkOverrides)
54
- const lightTheme = resolveTokens(presets.zinc, 'light')
55
- expect(darkTheme.colors.primary).toBe('#ff0000')
56
- expect(lightTheme.colors.primary).not.toBe('#ff0000')
57
- })
58
- })
1
+ import { describe, it, expect } from 'vitest'
2
+ import { defaultTheme, ThemeContext } from '../theme/ThemeContext'
3
+ import { resolveTokens, zinc, presets } from '../tokens'
4
+
5
+ describe('ThemeContext', () => {
6
+ it('defaultTheme is zinc light', () => {
7
+ const expected = resolveTokens(zinc, 'light')
8
+ expect(defaultTheme.colorScheme).toBe('light')
9
+ expect(defaultTheme.colors.background).toBe(expected.colors.background)
10
+ expect(defaultTheme.colors.foreground).toBe(expected.colors.foreground)
11
+ })
12
+
13
+ it('defaultTheme has all required token categories', () => {
14
+ expect(defaultTheme).toHaveProperty('colors')
15
+ expect(defaultTheme).toHaveProperty('spacing')
16
+ expect(defaultTheme).toHaveProperty('radius')
17
+ expect(defaultTheme).toHaveProperty('typography')
18
+ expect(defaultTheme).toHaveProperty('animation')
19
+ expect(defaultTheme).toHaveProperty('colorScheme')
20
+ })
21
+
22
+ it('ThemeContext is a valid React context', () => {
23
+ expect(ThemeContext).toHaveProperty('Provider')
24
+ expect(ThemeContext).toHaveProperty('Consumer')
25
+ })
26
+ })
27
+
28
+ describe('ThemeProvider logic', () => {
29
+ it('forcedColorScheme dark resolves dark tokens', () => {
30
+ const theme = resolveTokens(presets.zinc, 'dark')
31
+ expect(theme.colorScheme).toBe('dark')
32
+ expect(theme.colors.background).toBe('#070709')
33
+ })
34
+
35
+ it('forcedColorScheme light resolves light tokens', () => {
36
+ const theme = resolveTokens(presets.zinc, 'light')
37
+ expect(theme.colorScheme).toBe('light')
38
+ expect(theme.colors.background).toBe('#ffffff')
39
+ })
40
+
41
+ it('each preset resolves to different primary colors', () => {
42
+ const themes = Object.entries(presets).map(([name, preset]) => ({
43
+ name,
44
+ primary: resolveTokens(preset, 'dark').colors.primary,
45
+ }))
46
+ const primaries = new Set(themes.map((t) => t.primary))
47
+ // At least 3 unique primaries across 4 presets (zinc and slate may share)
48
+ expect(primaries.size).toBeGreaterThanOrEqual(3)
49
+ })
50
+
51
+ it('overrides are applied per-mode', () => {
52
+ const darkOverrides = { colors: { primary: '#ff0000' } }
53
+ const darkTheme = resolveTokens(presets.zinc, 'dark', darkOverrides)
54
+ const lightTheme = resolveTokens(presets.zinc, 'light')
55
+ expect(darkTheme.colors.primary).toBe('#ff0000')
56
+ expect(lightTheme.colors.primary).not.toBe('#ff0000')
57
+ })
58
+ })