├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── cypress.json
├── cypress
├── fixtures
│ ├── cheeseburger.jpg
│ └── example.json
├── integration
│ ├── Playground.Colors.spec.js
│ ├── Playground.Edit.spec.js
│ ├── Playground.Fonts.spec.js
│ ├── Playground.Load.spec.js
│ ├── Playground.Nav.spec.js
│ ├── Playground.Random.spec.js
│ └── Playground.Save.spec.js
├── plugins
│ └── index.js
└── support
│ ├── commands.js
│ └── index.js
├── gatsby-browser.js
├── gatsby-config.js
├── package-lock.json
├── package.json
├── public
└── index.html
├── src
├── animate
│ ├── useTweenLite.js
│ └── useTweenMax.js
├── components
│ ├── Footer.js
│ ├── Header.js
│ ├── Main.js
│ ├── Nav.js
│ ├── Wrapper.js
│ ├── head
│ │ ├── GlobalCSS.js
│ │ └── SEO.js
│ ├── playground
│ │ ├── Choose.js
│ │ ├── ChooseColorScheme.js
│ │ ├── ChooseColors.js
│ │ ├── ChooseFonts.js
│ │ ├── ColorAdd.js
│ │ ├── ColorPicker.js
│ │ ├── ColorSchemeFromColor.js
│ │ ├── ColorSchemeFromImage.js
│ │ ├── ColorSchemeFromPreset.js
│ │ ├── ColorSchemeSwatch.js
│ │ ├── Container.js
│ │ ├── FontSelectBrowser.js
│ │ ├── FontSelectWeb.js
│ │ ├── Header.js
│ │ ├── LoadTheme.js
│ │ ├── Main.js
│ │ ├── NextButton.js
│ │ ├── SaveTheme.js
│ │ ├── Welcome.js
│ │ ├── WelcomeBack.js
│ │ ├── Wrapper.js
│ │ └── index.js
│ ├── sections
│ │ ├── Section.js
│ │ ├── SubSection.js
│ │ ├── color
│ │ │ ├── Color.js
│ │ │ ├── Palette.js
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── theme.js
│ │ ├── typography
│ │ │ ├── Font.js
│ │ │ ├── FontWeights.js
│ │ │ ├── TypeScale.js
│ │ │ └── index.js
│ │ └── ui
│ │ │ ├── Buttons.js
│ │ │ ├── Cards.js
│ │ │ ├── Intro.js
│ │ │ ├── MediaObject.js
│ │ │ ├── NavBar.js
│ │ │ └── index.js
│ └── ui
│ │ ├── Button.js
│ │ ├── Card.js
│ │ ├── Figure.js
│ │ ├── Flex.js
│ │ ├── GithubLink.js
│ │ ├── HeaderButton.js
│ │ ├── Heading.js
│ │ ├── Link.js
│ │ └── Logo.js
├── context
│ ├── NavContext.js
│ └── ThemeContext.js
├── images
│ └── icon.png
├── pages
│ └── index.js
└── themes
│ ├── code.js
│ ├── dark.js
│ ├── index.js
│ ├── indigo.js
│ ├── orchid.js
│ ├── peru.js
│ ├── roboto.js
│ ├── royal.js
│ ├── salmon.js
│ ├── sky.js
│ ├── slate.js
│ ├── swiss.js
│ ├── tailwind.js
│ └── tomato.js
└── static
└── img
├── chakra.png
├── design-system-playground.png
└── rebass.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # Cypress
72 | cypress/screenshots
73 | cypress/videos
74 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 | package.json
3 | package-lock.json
4 | public
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "singleQuote": false,
5 | "tabWidth": 2,
6 | "trailingComma": "es5"
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 John Polacek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Design System Playground
2 |
3 | #### Play with typography and colors to generate a design system theme you can use in your projects.
4 |
5 | [](https://app.netlify.com/sites/design-system-playground/deploys)
6 |
7 | ---
8 |
9 | With a [Theme Object](https://theme-ui.com/theme-spec) that follows the [System UI Theme Specification](https://system-ui.com/) for style values, scales, and/or design tokens, you can create [interoperable](https://jxnblk.com/blog/interoperability/) components that share common underlying style properties across projects, websites, applications and environments.
10 |
11 | ---
12 |
13 | #### Related Projects
14 |
15 | Check out these other cool projects:
16 |
17 | - [System UI](https://system-ui.com/)
18 | - [Theme UI](https://theme-ui.com/)
19 | - [Rebass](https://rebassjs.org/)
20 | - [Chakra UI](https://chakra-ui.com/)
21 | - [Components AI](https://components.ai/)
22 | - [ColorBox](https://www.colorbox.io/)
23 | - [Color Thief](https://lokeshdhakar.com/projects/color-thief/)
24 |
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:8000",
3 | "chromeWebSecurity": false,
4 | "video": false
5 | }
6 |
--------------------------------------------------------------------------------
/cypress/fixtures/cheeseburger.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnpolacek/design-system-playground/b46fc02f8c80a7f051c89e53e1014cef1934537c/cypress/fixtures/cheeseburger.jpg
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Colors.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | beforeEach(function() {
3 | cy.visit("/")
4 | cy.get("header").should("have.css", "background-color", "rgb(65, 105, 225)")
5 | cy.wait(10000)
6 | cy.contains("What body font do you like?").should("be.visible")
7 | cy.contains("Next").click()
8 | cy.wait(1000)
9 | cy.contains("What heading font do you like?").should("be.visible")
10 | cy.contains("Next").click()
11 | cy.wait(1000)
12 | cy.contains("Get a color scheme...").should("be.visible")
13 | })
14 |
15 | it("can set color scheme from preset, image, preset or go random", function() {
16 | const defaultPrimaryColor =
17 | Cypress.browser.name === "electron"
18 | ? "rgb(222, 162, 94)"
19 | : "rgb(222, 162, 95)"
20 |
21 | cy.get("#colorsPreset").select("tomato")
22 | cy.get("header").should("have.css", "background-color", "rgb(255, 99, 71)")
23 |
24 | cy.get("#colorsFromColor").type("abc123")
25 | cy.get("header").should("have.css", "background-color", "rgb(171, 193, 35)")
26 |
27 | // see uploadFile in support/commands, cheeseburger.js in fixtures
28 | cy.uploadFile("cheeseburger.jpg", "#colorsFromImage")
29 | cy.get("#colorsFromImage").trigger("change")
30 | cy.get("header").should("have.css", "background-color", defaultPrimaryColor)
31 |
32 | cy.contains("go random").click()
33 | cy.get("header").should(
34 | "not.have.css",
35 | "background-color",
36 | defaultPrimaryColor
37 | )
38 |
39 | cy.contains("Next").click()
40 | cy.wait(1000)
41 | cy.contains("Here are your colors...").should("be.visible")
42 | cy.contains("primary").should("be.visible")
43 | cy.contains("secondary").should("be.visible")
44 | cy.contains("background").should("be.visible")
45 | cy.contains("text").should("be.visible")
46 | })
47 |
48 | it("can change, add or delete colors", function() {
49 | cy.contains("Next").click()
50 | cy.wait(1000)
51 | cy.contains("Here are your colors...").should("be.visible")
52 | cy.contains("primary").should("be.visible")
53 | cy.contains("secondary").should("be.visible")
54 | cy.contains("background").should("be.visible")
55 | cy.contains("text").should("be.visible")
56 |
57 | // change primary color
58 | cy.get("input")
59 | .first()
60 | .clear()
61 | .type("#abc123")
62 | cy.get("header").should("have.css", "background-color", "rgb(171, 193, 35)")
63 |
64 | // add color
65 | cy.get("#addColorPreview").should(
66 | "have.css",
67 | "background-color",
68 | "rgba(0, 0, 0, 0)"
69 | )
70 | cy.get("input[name=newColorName]").type("forest")
71 | cy.get("input[name=newColorValue]").type("228b22")
72 | cy.get("#addColor").click()
73 |
74 | cy.wait(1000)
75 | cy.contains("forest").should("be.visible")
76 | cy.contains("#228b22").should("be.visible")
77 | cy.contains("#228b22")
78 | .parent()
79 | .find("div")
80 | .should("have.css", "background-color", "rgb(34, 139, 34)")
81 |
82 | cy.get("input[name=newColorName]").should("have.value", "")
83 | cy.get("input[name=newColorValue]").should("have.value", "#")
84 |
85 | // delete color
86 | cy.contains("forest")
87 | .parent()
88 | .contains("×")
89 | .click()
90 | cy.contains("forest").should("not.exist")
91 | cy.contains("#228b22").should("not.exist")
92 | })
93 |
94 | it("can detect dark mode", function() {
95 | cy.get("#colorsFromColor").type("#fafafa")
96 | cy.get("header").should(
97 | "have.css",
98 | "background-color",
99 | "rgb(250, 250, 250)"
100 | )
101 | cy.get("main").should("have.css", "background-color", "rgb(0, 0, 0)")
102 | cy.get("main").should("have.css", "color", "rgb(255, 255, 255)")
103 | cy.get("footer").should("have.css", "background-color", "rgb(26, 26, 26)")
104 | cy.get("nav li").should("have.css", "background-color", "rgb(0, 0, 0)")
105 | cy.get("nav li span").should("have.css", "color", "rgb(250, 250, 250)")
106 | cy.get("#playground").should(
107 | "have.css",
108 | "background-color",
109 | "rgb(26, 26, 26)"
110 | )
111 |
112 | cy.wait(1000)
113 | cy.get("h3")
114 | .first()
115 | .should("have.css", "color", "rgb(255, 255, 255)")
116 | cy.get("input").should("have.css", "color", "rgb(255, 255, 255)")
117 | cy.get("input").should("have.css", "background-color", "rgb(0, 0, 0)")
118 | cy.get("select").should("have.css", "color", "rgb(255, 255, 255)")
119 | cy.get("select").should("have.css", "background-color", "rgb(0, 0, 0)")
120 | })
121 | })
122 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Edit.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | it("can edit fonts and colors", function() {
3 | cy.visit("/")
4 | cy.wait(10000)
5 | cy.get("header").should("have.css", "background-color", "rgb(65, 105, 225)")
6 | cy.contains("What body font do you like?").should("be.visible")
7 | cy.contains("Next").click()
8 | cy.wait(1000)
9 | cy.contains("What heading font do you like?").should("be.visible")
10 | cy.contains("Next").click()
11 | cy.wait(1000)
12 | cy.contains("Get a color scheme...").should("be.visible")
13 | cy.contains("Next").click()
14 | cy.wait(1000)
15 | cy.contains("Here are your colors...").should("be.visible")
16 | cy.contains("Next").click()
17 | cy.wait(1000)
18 |
19 | cy.wait(1000)
20 | cy.get("#playground").should("have.css", "height", "0px")
21 | cy.get("header")
22 | .contains("play")
23 | .click()
24 |
25 | cy.contains("What body font do you like?").should("be.visible")
26 | cy.get("#selectBrowserFont").select("Georgia, serif")
27 | cy.contains("Next").click()
28 | cy.wait(1000)
29 |
30 | cy.contains("What heading font do you like?").should("be.visible")
31 | cy.get("#selectBrowserFont").select("Arial, sans-serif")
32 | cy.contains("Next").click()
33 | cy.wait(1000)
34 |
35 | cy.contains("Get a color scheme...").should("be.visible")
36 | cy.get("#colorsPreset").select("tomato")
37 | cy.contains("Next").click()
38 | cy.wait(1000)
39 |
40 | cy.contains("Here are your colors...").should("be.visible")
41 | cy.contains("Next").click()
42 | cy.wait(1000)
43 |
44 | cy.wait(1000)
45 | cy.get("#playground").should("have.css", "height", "0px")
46 | cy.get("p#bodyText").should("have.css", "font-family", "Georgia, serif")
47 | cy.get("main section h2").should(
48 | "have.css",
49 | "font-family",
50 | "Arial, sans-serif"
51 | )
52 | cy.get("header").should("have.css", "background-color", "rgb(255, 99, 71)")
53 | })
54 | })
55 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Fonts.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | beforeEach(function() {
3 | cy.visit("/")
4 | })
5 |
6 | it("can set browser fonts", function() {
7 | cy.wait(9000)
8 | cy.contains("What body font do you like?").should("be.visible")
9 | cy.get("#selectBrowserFont").select("Georgia, serif")
10 | cy.contains("Next").click()
11 | cy.wait(500)
12 |
13 | cy.contains("What heading font do you like?").should("be.visible")
14 | cy.get("#selectBrowserFont").select("Arial, sans-serif")
15 | cy.contains("Next").click()
16 | cy.wait(500)
17 |
18 | cy.contains("Get a color scheme...").should("be.visible")
19 | cy.contains("Next").click()
20 | cy.wait(500)
21 |
22 | cy.contains("Here are your colors...").should("be.visible")
23 | cy.contains("Next").click()
24 | cy.wait(500)
25 |
26 | cy.wait(1000)
27 | cy.get("main section h2").should(
28 | "have.css",
29 | "font-family",
30 | "Arial, sans-serif"
31 | )
32 | cy.get("main section p#bodyText").should(
33 | "have.css",
34 | "font-family",
35 | "Georgia, serif"
36 | )
37 | })
38 |
39 | it("can set web fonts", function() {
40 | cy.wait(9000)
41 | cy.contains("What body font do you like?").should("be.visible")
42 | cy.get("#selectWebFont").select("Vollkorn")
43 | cy.contains("Next").click()
44 | cy.wait(500)
45 |
46 | cy.contains("What heading font do you like?").should("be.visible")
47 | cy.get("#selectWebFont").select("Yantramanav")
48 | cy.contains("Next").click()
49 | cy.wait(500)
50 |
51 | cy.contains("Get a color scheme...").should("be.visible")
52 | cy.contains("Next").click()
53 | cy.wait(500)
54 |
55 | cy.contains("Here are your colors...").should("be.visible")
56 | cy.contains("Next").click()
57 | cy.wait(500)
58 |
59 | cy.wait(1000)
60 |
61 | cy.get("main section p#bodyText").should(
62 | "have.css",
63 | "font-family",
64 | "Vollkorn, serif"
65 | )
66 | cy.get("main section h2").should(
67 | "have.css",
68 | "font-family",
69 | "Yantramanav, sans-serif"
70 | )
71 | })
72 |
73 | it("can set random fonts", function() {
74 | cy.wait(9000)
75 | cy.get("header h1").should(
76 | "have.css",
77 | "font-family",
78 | '"avenir next", avenir, helvetica, arial, sans-serif'
79 | )
80 | cy.contains("What body font do you like?").should("be.visible")
81 | cy.contains("go random").click()
82 | cy.get("header h1").should(
83 | "not.have.css",
84 | "font-family",
85 | '"avenir next", avenir, helvetica, arial, sans-serif'
86 | )
87 | })
88 | })
89 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Load.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | beforeEach(function() {
3 | cy.visit("/")
4 | })
5 |
6 | it("can load preset theme", function() {
7 | cy.wait(10000)
8 | cy.get("header")
9 | .contains("load")
10 | .click()
11 | cy.contains("Load Theme").should("be.visible")
12 | cy.contains("salmon").click()
13 | cy.get("header").should(
14 | "have.css",
15 | "background-color",
16 | "rgb(255, 140, 105)"
17 | )
18 | cy.get("main section p#bodyText").should(
19 | "have.css",
20 | "font-family",
21 | "Lora, serif"
22 | )
23 | cy.get("main section h2").should(
24 | "have.css",
25 | "font-family",
26 | '"Istok Web", sans-serif'
27 | )
28 | })
29 |
30 | it("can save, load and delete themes", function() {
31 | cy.wait(10000)
32 | cy.contains("What body font do you like?").should("be.visible")
33 | cy.get("#selectWebFont").select("Arimo")
34 | cy.contains("Next").click()
35 | cy.wait(1000)
36 |
37 | cy.contains("What heading font do you like?").should("be.visible")
38 | cy.get("#selectWebFont").select("Tinos")
39 | cy.contains("Next").click()
40 | cy.wait(1000)
41 |
42 | cy.contains("Get a color scheme...").should("be.visible")
43 | cy.get("#colorsPreset").select("indigo")
44 | cy.contains("Next").click()
45 | cy.wait(1000)
46 |
47 | cy.contains("Here are your colors...").should("be.visible")
48 | cy.contains("Next").click()
49 | cy.wait(1000)
50 |
51 | cy.wait(1000)
52 |
53 | cy.get("header")
54 | .contains("save")
55 | .click()
56 |
57 | cy.wait(1000)
58 |
59 | cy.contains("Save Theme").should("be.visible")
60 | cy.get("#themeName").type("Beep Boop Bop")
61 | cy.get("button")
62 | .contains("Save")
63 | .click()
64 |
65 | cy.reload()
66 | cy.wait(10000)
67 |
68 | cy.get("header")
69 | .contains("load")
70 | .click()
71 |
72 | cy.contains("Beep Boop Bop").click()
73 | cy.get("header").should("have.css", "background-color", "rgb(75, 0, 130)")
74 | cy.get("main section p#bodyText").should(
75 | "have.css",
76 | "font-family",
77 | "Arimo, sans-serif"
78 | )
79 | cy.get("main section h2").should("have.css", "font-family", "Tinos, serif")
80 | })
81 |
82 | it("can cancel load theme", function() {
83 | cy.wait(10000)
84 | cy.get("header")
85 | .contains("load")
86 | .click()
87 | cy.contains("Load Theme").should("be.visible")
88 | cy.get("button")
89 | .contains("Cancel")
90 | .click()
91 | cy.contains("Load Theme").should("not.exist")
92 | })
93 | })
94 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Nav.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | beforeEach(function() {
3 | cy.visit("/")
4 | })
5 |
6 | it("can navigate to font, color, theme and ui", function() {
7 | cy.get("h2")
8 | .contains("Font Families")
9 | .should("be.visible")
10 | cy.get("h2")
11 | .contains("Type Scale")
12 | .should("be.visible")
13 | cy.get("nav")
14 | .contains("COLOR")
15 | .click()
16 | cy.get("h2")
17 | .contains("Font Families")
18 | .should("not.exist")
19 | cy.get("h2")
20 | .contains("Type Scale")
21 | .should("not.exist")
22 | cy.get("h2")
23 | .contains("Color Palette")
24 | .should("be.visible")
25 | cy.get("nav")
26 | .contains("THEME")
27 | .click()
28 | cy.get("h2")
29 | .contains("Font Families")
30 | .should("not.exist")
31 | cy.get("h2")
32 | .contains("Type Scale")
33 | .should("not.exist")
34 | cy.get("h2")
35 | .contains("Color Palette")
36 | .should("not.exist")
37 | cy.get("h2")
38 | .contains("Your Theme Object")
39 | .should("be.visible")
40 | cy.get("nav")
41 | .contains("UI")
42 | .click()
43 | cy.get("h2")
44 | .contains("Font Families")
45 | .should("not.exist")
46 | cy.get("h2")
47 | .contains("Type Scale")
48 | .should("not.exist")
49 | cy.get("h2")
50 | .contains("Color Palette")
51 | .should("not.exist")
52 | cy.get("h2")
53 | .contains("Your Theme Object")
54 | .should("not.exist")
55 | cy.get("a")
56 | .contains("Theme UI Docs")
57 | .should("be.visible")
58 | cy.get("nav")
59 | .contains("FONT")
60 | .click()
61 | cy.get("h2")
62 | .contains("Font Families")
63 | .should("be.visible")
64 | cy.get("h2")
65 | .contains("Type Scale")
66 | .should("be.visible")
67 | cy.get("h2")
68 | .contains("Color Palette")
69 | .should("not.exist")
70 | cy.get("h2")
71 | .contains("Your Theme Object")
72 | .should("not.exist")
73 | cy.get("a")
74 | .contains("Theme UI Docs")
75 | .should("not.exist")
76 | })
77 | })
78 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Random.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | beforeEach(function() {
3 | cy.visit("/")
4 | })
5 |
6 | it("can generate random theme", function() {
7 | cy.wait(10000)
8 | cy.get("header h1").should(
9 | "have.css",
10 | "font-family",
11 | '"avenir next", avenir, helvetica, arial, sans-serif'
12 | )
13 | cy.get("main p#bodyText").should(
14 | "have.css",
15 | "font-family",
16 | '"avenir next", avenir, helvetica, arial, sans-serif'
17 | )
18 | cy.get("header").should("have.css", "background-color", "rgb(65, 105, 225)")
19 | cy.get("header")
20 | .contains("random")
21 | .click()
22 | cy.get("header h1").should(
23 | "not.have.css",
24 | "font-family",
25 | '"avenir next", avenir, helvetica, arial, sans-serif'
26 | )
27 | cy.get("main p#bodyText").should(
28 | "not.have.css",
29 | "font-family",
30 | '"avenir next", avenir, helvetica, arial, sans-serif'
31 | )
32 | cy.get("header").should(
33 | "not.have.css",
34 | "background-color",
35 | "rgb(65, 105, 225)"
36 | )
37 | })
38 | })
39 |
--------------------------------------------------------------------------------
/cypress/integration/Playground.Save.spec.js:
--------------------------------------------------------------------------------
1 | describe("Playground", function() {
2 | beforeEach(function() {
3 | cy.visit("/")
4 | })
5 |
6 | it("requires name to save theme", function() {
7 | cy.wait(10000)
8 | cy.get("header")
9 | .contains("save")
10 | .click()
11 |
12 | cy.contains("Save Theme").should("be.visible")
13 | cy.get("button")
14 | .contains("Save")
15 | .should("be.disabled")
16 | cy.get("#themeName").type("a")
17 | cy.get("button")
18 | .contains("Save")
19 | .should("not.be.disabled")
20 | cy.get("button")
21 | .contains("Save")
22 | .click()
23 |
24 | cy.get("header")
25 | .contains("load")
26 | .click()
27 |
28 | cy.contains("Load Theme").should("be.visible")
29 | cy.get("button")
30 | .contains("a")
31 | .should("be.visible")
32 | })
33 |
34 | it("can cancel save theme", function() {
35 | cy.wait(10000)
36 | cy.get("header")
37 | .contains("save")
38 | .click()
39 |
40 | cy.contains("Save Theme").should("be.visible")
41 | cy.get("button")
42 | .contains("Cancel")
43 | .click()
44 | cy.contains("Save Theme").should("not.exist")
45 | })
46 |
47 | it("can suggest theme name", function() {
48 | cy.wait(10000)
49 | cy.get("header")
50 | .contains("save")
51 | .click()
52 |
53 | cy.contains("Save Theme").should("be.visible")
54 | cy.get("button")
55 | .contains("Yes")
56 | .click()
57 | cy.get("input#themeName").should("have.value", "Royal Blue")
58 | cy.get("button")
59 | .contains("Save")
60 | .click()
61 |
62 | cy.get("header")
63 | .contains("load")
64 | .click()
65 |
66 | cy.contains("Load Theme").should("be.visible")
67 | cy.get("button")
68 | .contains("Royal Blue")
69 | .should("be.visible")
70 | })
71 | })
72 |
--------------------------------------------------------------------------------
/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | module.exports = (on, config) => {
15 | // `on` is used to hook into various events Cypress emits
16 | // `config` is the resolved Cypress config
17 | }
18 |
--------------------------------------------------------------------------------
/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | Cypress.Commands.add("uploadFile", (fileName, selector) => {
2 | cy.get(selector).then(subject => {
3 | cy.fixture(fileName, "base64").then(content => {
4 | const el = subject[0]
5 | const blob = b64toBlob(content)
6 | const testFile = new File([blob], fileName)
7 | const dataTransfer = new DataTransfer()
8 |
9 | dataTransfer.items.add(testFile)
10 | el.files = dataTransfer.files
11 | })
12 | })
13 | })
14 |
15 | function b64toBlob(b64Data, contentType, sliceSize) {
16 | contentType = contentType || ""
17 | sliceSize = sliceSize || 512
18 |
19 | var byteCharacters = atob(b64Data)
20 | var byteArrays = []
21 |
22 | for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
23 | var slice = byteCharacters.slice(offset, offset + sliceSize)
24 |
25 | var byteNumbers = new Array(slice.length)
26 | for (var i = 0; i < slice.length; i++) {
27 | byteNumbers[i] = slice.charCodeAt(i)
28 | }
29 |
30 | var byteArray = new Uint8Array(byteNumbers)
31 |
32 | byteArrays.push(byteArray)
33 | }
34 |
35 | var blob = new Blob(byteArrays, { type: contentType })
36 | blob.lastModifiedDate = new Date()
37 | return blob
38 | }
39 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import "./commands"
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's Browser APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/browser-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
8 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: `Design System Playground`,
4 | description: `Play with typography, colors and themeable components in your design system.`,
5 | author: `@johnpolacek`,
6 | },
7 | plugins: [
8 | `gatsby-plugin-react-helmet`,
9 | {
10 | resolve: `gatsby-source-filesystem`,
11 | options: {
12 | name: `images`,
13 | path: `${__dirname}/src/images`,
14 | },
15 | },
16 | `gatsby-transformer-sharp`,
17 | `gatsby-plugin-sharp`,
18 | {
19 | resolve: `gatsby-plugin-manifest`,
20 | options: {
21 | name: `design-system-playground`,
22 | short_name: `designplay`,
23 | start_url: `/`,
24 | background_color: `#fff`,
25 | theme_color: `#663399`,
26 | display: `minimal-ui`,
27 | icon: `src/images/icon.png`, // This path is relative to the root of the site.
28 | },
29 | },
30 | // this (optional) plugin enables Progressive Web App + Offline functionality
31 | // To learn more, visit: https://gatsby.dev/offline
32 | // `gatsby-plugin-offline`,
33 | ],
34 | }
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "design-system-playground",
3 | "description": "Experiment with design systems by playing with typography, colors and components to create a theme.",
4 | "version": "1.0.0",
5 | "author": "John Polacek (https://johnpolacek.com/)",
6 | "dependencies": {
7 | "@emotion/core": "^10.1.1",
8 | "@emotion/styled": "^10.0.27",
9 | "@mdx-js/react": "^1.6.22",
10 | "@theme-ui/color": "^0.2.53",
11 | "@theme-ui/presets": "^0.2.44",
12 | "@theme-ui/prism": "^0.2.50",
13 | "color-scheme": "^1.0.1",
14 | "colorthief": "^2.3.2",
15 | "emotion-theming": "^10.0.27",
16 | "gatsby": "^2.31.1",
17 | "gatsby-image": "^2.10.0",
18 | "gatsby-plugin-manifest": "^2.11.0",
19 | "gatsby-plugin-offline": "^3.9.0",
20 | "gatsby-plugin-react-helmet": "^3.9.0",
21 | "gatsby-plugin-sharp": "^2.13.3",
22 | "gatsby-source-filesystem": "^2.10.0",
23 | "gatsby-transformer-sharp": "^2.11.0",
24 | "gsap": "^3.6.0",
25 | "js-file-download": "^0.4.12",
26 | "ntcjs": "^1.1.2",
27 | "polished": "^3.6.7",
28 | "prism-react-renderer": "^0.1.7",
29 | "prop-types": "^15.7.2",
30 | "react": "^16.14.0",
31 | "react-dom": "^16.14.0",
32 | "react-ga": "^2.7.0",
33 | "react-helmet": "^5.2.1",
34 | "react-icons": "^3.11.0",
35 | "react-syntax-highlighter": "^11.0.2",
36 | "rgb-hex": "^3.0.0",
37 | "theme-ui": "^0.2.52"
38 | },
39 | "devDependencies": {
40 | "chai-colors": "^1.0.1",
41 | "cypress": "^6.3.0",
42 | "prettier": "^1.19.1",
43 | "start-server-and-test": "^1.11.7"
44 | },
45 | "keywords": [
46 | "gatsby"
47 | ],
48 | "license": "MIT",
49 | "scripts": {
50 | "build": "gatsby build",
51 | "cypress": "cypress open",
52 | "develop": "gatsby develop",
53 | "dev": "gatsby develop -o",
54 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
55 | "format-watch": "onchange \"**/*.{js,jsx,json,md}\" -- prettier --write {{changed}}",
56 | "start": "npm run develop",
57 | "serve": "gatsby serve",
58 | "test": "cypress run --env mode=headless"
59 | },
60 | "repository": {
61 | "type": "git",
62 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
63 | },
64 | "bugs": {
65 | "url": "https://github.com/gatsbyjs/gatsby/issues"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/animate/useTweenLite.js:
--------------------------------------------------------------------------------
1 | // based on: https://github.com/johnlindquist/use-gsap
2 |
3 | import { useRef, useEffect, useCallback } from "react"
4 | import { TweenLite } from "gsap"
5 |
6 | export default (duration, options, onMount = true) => {
7 | let ref = useRef(null)
8 | let go = useCallback(() => {
9 | TweenLite.to(ref.current, duration, options)
10 | }, [duration, options])
11 |
12 | useEffect(() => {
13 | if (onMount) go()
14 | }, [ref, go, options, onMount])
15 |
16 | return [ref, go]
17 | }
18 |
--------------------------------------------------------------------------------
/src/animate/useTweenMax.js:
--------------------------------------------------------------------------------
1 | // based on: https://github.com/johnlindquist/use-gsap
2 |
3 | import { useRef, useEffect, useCallback } from "react"
4 | import { TweenMax } from "gsap"
5 |
6 | export default (duration, options, onMount = true) => {
7 | let ref = useRef(null)
8 | let go = useCallback(() => {
9 | TweenMax.to(ref.current, duration, options)
10 | }, [duration, options])
11 |
12 | useEffect(() => {
13 | if (onMount) go()
14 | }, [ref, go, options, onMount])
15 |
16 | return [ref, go]
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { lightness } from "@theme-ui/color"
4 | // eslint-disable-next-line
5 | import React, { useContext } from "react"
6 | import Flex from "./ui/Flex"
7 | import { ThemeContext } from "../context/ThemeContext"
8 |
9 | export default props => {
10 | const { theme, isDarkMode } = useContext(ThemeContext)
11 |
12 | return (
13 | <>
14 |
22 |
23 |
95 |
139 |
140 |
141 | >
142 | )
143 | }
144 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { lightness } from "@theme-ui/color"
4 | // eslint-disable-next-line
5 | import React, { useContext, useState, useEffect } from "react"
6 | import fileDownload from "js-file-download"
7 | import {
8 | ThemeContext,
9 | getColorScheme,
10 | getRandomFont,
11 | } from "../context/ThemeContext"
12 | import { NavContext, PlaygroundViews } from "../context/NavContext"
13 | import Logo from "./ui/Logo"
14 | import GithubLink from "./ui/GithubLink"
15 | import HeaderButton from "./ui/HeaderButton"
16 | import Button from "./ui/Button"
17 |
18 | export default props => {
19 | const { playgroundView, setPlaygroundView, setPlaygroundHeight } = useContext(
20 | NavContext
21 | )
22 |
23 | const { theme, setTheme, isDarkMode } = useContext(ThemeContext)
24 |
25 | const editTheme = () => {
26 | setPlaygroundView(PlaygroundViews.FONT_BODY)
27 | setPlaygroundHeight("800px")
28 | }
29 |
30 | const downloadTheme = () => {
31 | fileDownload("export default " + JSON.stringify(theme, null, 2), "theme.js")
32 | }
33 |
34 | const randomTheme = () => {
35 | let newTheme = { ...theme }
36 | newTheme.colors = getColorScheme()
37 |
38 | const randomBodyFont = getRandomFont()
39 | newTheme.fonts.body = randomBodyFont.family
40 |
41 | const randomHeadingFont = getRandomFont()
42 | newTheme.fonts.heading = randomHeadingFont.family
43 | setTheme({ ...newTheme })
44 | }
45 |
46 | const loadTheme = () => {
47 | setPlaygroundView(PlaygroundViews.LOAD)
48 | setPlaygroundHeight("100vh")
49 | }
50 |
51 | const saveTheme = () => {
52 | setPlaygroundView(PlaygroundViews.SAVE)
53 | setPlaygroundHeight("800px")
54 | }
55 |
56 | return (
57 |
121 | )
122 | }
123 |
--------------------------------------------------------------------------------
/src/components/Main.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext } from "react"
4 | import useTweenMax from "../animate/useTweenMax"
5 | import { NavContext } from "../context/NavContext"
6 | import { ThemeContext } from "../context/ThemeContext"
7 | import { Typography, Color, UI, Theme } from "./sections"
8 |
9 | export default props => {
10 | const { currSection } = useContext(NavContext)
11 | const { theme } = useContext(ThemeContext)
12 |
13 | const [tweenRef] = useTweenMax(1, {
14 | opacity: 1,
15 | delay: 1,
16 | immediateRender: true,
17 | })
18 |
19 | return (
20 |
32 | {
33 | {
34 | FONT: ,
35 | COLOR: ,
36 | UI: ,
37 | THEME: ,
38 | }[currSection]
39 | }
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useState, useEffect } from "react"
4 | import { lightness } from "@theme-ui/color"
5 | import useTweenMax from "../animate/useTweenMax"
6 | import { Expo } from "gsap"
7 | import { useContext } from "react"
8 | import { NavContext, NavSections } from "../context/NavContext"
9 | import { ThemeContext } from "../context/ThemeContext"
10 |
11 | const NavButton = props => {
12 | const [tweenRef] = useTweenMax(
13 | 1,
14 | props.hasPlayed
15 | ? {}
16 | : {
17 | startAt: { y: 10, opacity: 0 },
18 | opacity: 1,
19 | y: 0,
20 | delay: 2 + props.index * 0.2,
21 | ease: Expo.easeOut,
22 | immediateRender: true,
23 | }
24 | )
25 |
26 | // do not need this once theme-ui bug is fixed - https://github.com/system-ui/theme-ui/issues/360
27 | const { theme, isDarkMode } = useContext(ThemeContext)
28 |
29 | const { currSection, setCurrSection } = useContext(NavContext)
30 | const isCurrent = currSection === props.section
31 |
32 | const styleOuter = {
33 | width: "25%",
34 | textAlign: "center",
35 | bg: isCurrent
36 | ? theme.colors.background
37 | : lightness(theme.colors.primary, isDarkMode() ? 0.1 : 0.95),
38 | }
39 |
40 | const styleInner = {
41 | display: "block",
42 | fontSize: ["4vw", "3vw", "2.5vw", "2vw"],
43 | textDecoration: "none",
44 | py: "4vh",
45 | opacity: props.hasPlayed ? 1 : 0,
46 | color: isCurrent ? "primary" : lightness(theme.colors.primary, 0.6),
47 | ":hover": {
48 | bg: isCurrent
49 | ? theme.colors.background
50 | : lightness(theme.colors.primary, isDarkMode() ? 0.08 : 0.925),
51 | color: isCurrent ? "primary" : lightness(theme.colors.primary, 0.4),
52 | },
53 | }
54 |
55 | return (
56 |
57 | {isCurrent ? (
58 |
59 | {props.section}
60 |
61 | ) : (
62 | {
67 | e.preventDefault()
68 | setCurrSection(e.target.textContent)
69 | }}
70 | >
71 | {props.section}
72 |
73 | )}
74 |
75 | )
76 | }
77 |
78 | export default props => {
79 | const [hasPlayed, setHasPlayed] = useState(false)
80 | useEffect(() => {
81 | setHasPlayed(true)
82 | }, [])
83 |
84 | return (
85 |
88 |
98 | {NavSections.map((section, index) => (
99 |
105 | ))}
106 |
107 |
108 | )
109 | }
110 |
--------------------------------------------------------------------------------
/src/components/Wrapper.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useContext } from "react"
5 | import { ThemeContext } from "../context/ThemeContext"
6 |
7 | export default props => {
8 | const { theme } = useContext(ThemeContext)
9 |
10 | const webfonts = {}
11 | Object.keys(theme.fonts)
12 | .map(font => {
13 | return theme.fonts[font]
14 | })
15 | .filter(font => {
16 | return (
17 | font.split(",").length === 2 && font.split(",")[0].indexOf("'") === 0
18 | )
19 | })
20 | .map(font => {
21 | return font.split(",")[0].split("'")[1]
22 | })
23 | .forEach(font => {
24 | webfonts[font] =
25 | "https://fonts.googleapis.com/css?family=" +
26 | font.replace(/ /g, "+") +
27 | ":100,200,300,400,500,600,700,800,900"
28 | })
29 |
30 | return (
31 | <>
32 | {props.children}
33 | {Object.keys(webfonts).map(webfont => {
34 | return
35 | })}
36 | >
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/head/GlobalCSS.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Global, css } from "@emotion/core"
3 |
4 | const GlobalCSS = props => (
5 |
225 | )
226 |
227 | export default GlobalCSS
228 |
--------------------------------------------------------------------------------
/src/components/head/SEO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * SEO component that queries for data with
3 | * Gatsby's useStaticQuery React hook
4 | *
5 | * See: https://www.gatsbyjs.org/docs/use-static-query/
6 | */
7 |
8 | import React from "react"
9 | import PropTypes from "prop-types"
10 | import Helmet from "react-helmet"
11 | import { useStaticQuery, graphql } from "gatsby"
12 |
13 | function SEO({ description, lang, meta, title }) {
14 | const { site } = useStaticQuery(
15 | graphql`
16 | query {
17 | site {
18 | siteMetadata {
19 | title
20 | description
21 | author
22 | }
23 | }
24 | }
25 | `
26 | )
27 |
28 | const metaDescription = description || site.siteMetadata.description
29 |
30 | return (
31 |
76 | )
77 | }
78 |
79 | SEO.defaultProps = {
80 | lang: `en`,
81 | meta: [],
82 | description: ``,
83 | }
84 |
85 | SEO.propTypes = {
86 | description: PropTypes.string,
87 | lang: PropTypes.string,
88 | meta: PropTypes.arrayOf(PropTypes.object),
89 | title: PropTypes.string.isRequired,
90 | }
91 |
92 | export default SEO
93 |
--------------------------------------------------------------------------------
/src/components/playground/Choose.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useState, useEffect } from "react"
4 | import useTweenMax from "../../animate/useTweenMax"
5 | import { Expo } from "gsap"
6 | import Container from "./Container"
7 | import Header from "./Header"
8 | import Main from "./Main"
9 | import NextButton from "./NextButton"
10 |
11 | export default props => {
12 | const [hasPlayed, setHasPlayed] = useState(false)
13 | useEffect(() => {
14 | setHasPlayed(true)
15 | }, [])
16 |
17 | const [tweenOutRef, tweenOut] = useTweenMax(
18 | 0.5,
19 | {
20 | opacity: 0,
21 | x: "-100vw",
22 | ease: Expo.easeInOut,
23 | onComplete: props.onComplete,
24 | },
25 | false
26 | )
27 |
28 | return (
29 |
30 |
33 |
34 | {props.children}
35 |
36 | tweenOut()} />
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/playground/ChooseColorScheme.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState, useContext } from "react"
5 | import { ThemeContext, getColorScheme } from "../../context/ThemeContext"
6 | import Choose from "./Choose"
7 | import Flex from "../ui/Flex"
8 | import Button from "../ui/Button"
9 | import ColorSchemeSwatch from "./ColorSchemeSwatch"
10 | import ColorSchemeFromColor from "./ColorSchemeFromColor"
11 | import ColorSchemeFromImage from "./ColorSchemeFromImage"
12 | import ColorSchemeFromPreset from "./ColorSchemeFromPreset"
13 |
14 | export default props => {
15 | const { theme, setTheme } = useContext(ThemeContext)
16 | const [colors, setColors] = useState(theme.colors)
17 | const [schemeMode, setSchemeMode] = useState(null)
18 |
19 | const FROM_PRESET = "FROM_PRESET"
20 | const FROM_COLOR = "FROM_COLOR"
21 | const FROM_IMAGE = "FROM_IMAGE"
22 |
23 | const onSelectColors = (colors, mode) => {
24 | let newTheme = { ...theme }
25 | newTheme.colors = colors
26 | setTheme({ ...newTheme })
27 | setColors(colors)
28 | setSchemeMode(mode)
29 | }
30 |
31 | const goRandom = () => {
32 | let newTheme = { ...theme }
33 | newTheme.colors = getColorScheme()
34 | setTheme({ ...newTheme })
35 | setColors(newTheme.colors)
36 | setSchemeMode(null)
37 | }
38 |
39 | return (
40 |
45 |
46 | {Object.values(colors)
47 | .filter(
48 | color =>
49 | typeof color === "string" &&
50 | color.toLowerCase() !== "#fff" &&
51 | color.toLowerCase() !== "#ffffff" &&
52 | color !== "#000" &&
53 | color !== "#000000" &&
54 | color !== "transparent"
55 | )
56 | .map((color, i) => (
57 |
58 | ))}
59 |
60 |
61 |
72 | go random
73 |
74 | or...
75 |
76 |
77 |
78 |
From Preset
79 | onSelectColors(colors, FROM_PRESET)}
82 | />
83 |
84 |
85 |
From Color
86 | onSelectColors(colors, FROM_COLOR)}
89 | />
90 |
91 |
92 |
From Image
93 | onSelectColors(colors, FROM_IMAGE)}
96 | />
97 |
98 |
99 |
100 | )
101 | }
102 |
--------------------------------------------------------------------------------
/src/components/playground/ChooseColors.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext } from "react"
4 | import {
5 | ThemeContext,
6 | getMainColors,
7 | getOtherColors,
8 | } from "../../context/ThemeContext"
9 | import Choose from "./Choose"
10 | import ColorAdd from "./ColorAdd"
11 | import Flex from "../ui/Flex"
12 | import ColorPicker from "./ColorPicker"
13 |
14 | export default props => {
15 | const { theme } = useContext(ThemeContext)
16 |
17 | return (
18 |
19 |
20 | {getMainColors().map((color, i) => (
21 |
22 | ))}
23 |
24 |
25 | {getOtherColors(theme).map((color, i) => (
26 |
32 | ))}
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/playground/ChooseFonts.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useContext, useState } from "react"
5 | import { ThemeContext, getRandomFont } from "../../context/ThemeContext"
6 | import FontSelectBrowser from "./FontSelectBrowser"
7 | import FontSelectWeb from "./FontSelectWeb"
8 | import FontWeights from "../sections/typography/FontWeights"
9 | import Choose from "./Choose"
10 | import Button from "../ui/Button"
11 |
12 | export default props => {
13 | const [currFont, setCurrFont] = useState(null)
14 | const { theme, setTheme } = useContext(ThemeContext)
15 | const onFontSelect = (fontName, fontFamily) => {
16 | let newTheme = { ...theme }
17 | newTheme.fonts[props.fontKey] = fontFamily
18 | setTheme({ ...newTheme })
19 | setCurrFont(fontName)
20 | }
21 |
22 | const onWebFontSelect = (fontName, fontType) => {
23 | let newTheme = { ...theme }
24 | newTheme.fonts[props.fontKey] = "'" + fontName + "', " + fontType
25 | setTheme({ ...newTheme })
26 | setCurrFont(fontName)
27 | }
28 |
29 | const goRandom = () => {
30 | const randomFont = getRandomFont()
31 | let newTheme = { ...theme }
32 | newTheme.fonts[props.fontKey] = randomFont.family
33 | setTheme({ ...newTheme })
34 | setCurrFont(randomFont.name)
35 | }
36 |
37 | const selectBody = () => {
38 | let newTheme = { ...theme }
39 | newTheme.fonts[props.fontKey] = theme.fonts.body
40 | setTheme({ ...newTheme })
41 | setCurrFont(theme.fonts.body.replace(/'/g, "").split(",")[0])
42 | }
43 |
44 | return (
45 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 | {" "}
64 | or{" "}
65 |
66 |
78 | go random
79 |
80 | {props.fontKey !== "body" && (
81 | <>
82 |
83 | {" "}
84 | or{" "}
85 |
86 |
97 | use body font
98 |
99 | >
100 | )}
101 |
102 |
103 | )
104 | }
105 |
--------------------------------------------------------------------------------
/src/components/playground/ColorAdd.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import { useContext, useState } from "react"
6 | import { ThemeContext } from "../../context/ThemeContext"
7 | import Button from "../ui/Button"
8 |
9 | export default props => {
10 | const [colorName, setColorName] = useState("")
11 | const [colorValue, setColorValue] = useState("#")
12 |
13 | const { theme, setTheme } = useContext(ThemeContext)
14 |
15 | const onNameChange = e => {
16 | setColorName(e.target.value)
17 | }
18 |
19 | const onColorChange = e => {
20 | let newColor = e.target.value.toUpperCase()
21 | if (newColor.indexOf("#") !== 0) {
22 | newColor = "#" + newColor
23 | }
24 | setColorValue(e.target.value)
25 | }
26 |
27 | const onColorAddConfirm = e => {
28 | let newTheme = { ...theme }
29 | newTheme.colors[colorName.toLowerCase().replace(/\s/g, "")] = colorValue
30 | setTheme({ ...newTheme })
31 | setColorName("")
32 | setColorValue("#")
33 | }
34 |
35 | return (
36 |
37 |
Add Color
38 |
50 |
59 |
60 | Name
61 |
62 |
76 |
77 |
86 |
87 | Value
88 |
89 |
97 |
98 |
119 | + Add Color
120 |
121 |
122 | )
123 | }
124 |
--------------------------------------------------------------------------------
/src/components/playground/ColorPicker.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState, useContext, useEffect } from "react"
5 | import { ThemeContext } from "../../context/ThemeContext"
6 | import useTweenMax from "../../animate/useTweenMax"
7 | import { Expo } from "gsap"
8 |
9 | export default props => {
10 | const { theme, setTheme } = useContext(ThemeContext)
11 |
12 | const [hasPlayed, setHasPlayed] = useState(false)
13 | useEffect(() => {
14 | setHasPlayed(true)
15 | }, [])
16 |
17 | const [tweenRef] = useTweenMax(
18 | 1,
19 | hasPlayed
20 | ? {}
21 | : {
22 | startAt: {
23 | scale: 0.5,
24 | opacity: 0,
25 | },
26 | opacity: 1,
27 | scale: 1,
28 | delay: 0.25 + Math.random(),
29 | ease: Expo.easeOut,
30 | }
31 | )
32 |
33 | const onColorChange = e => {
34 | const newColor = e.target.value
35 | if (/^#([0-9A-F]{3}){1,2}$/i.test(newColor)) {
36 | let newTheme = { ...theme }
37 | newTheme.colors[props.color] = e.target.value
38 | setTheme({ ...newTheme })
39 | }
40 | }
41 |
42 | const onDelete = e => {
43 | let newTheme = { ...theme }
44 | delete newTheme.colors[props.color]
45 | setTheme({ ...newTheme })
46 | }
47 |
48 | return (
49 |
54 |
64 | {props.canDelete && (
65 |
85 | ×
86 |
87 | )}
88 |
89 |
97 | {props.color}
98 |
99 | {props.canEdit ? (
100 |
106 | ) : (
107 |
108 | {theme.colors[props.color]}
109 |
110 | )}
111 |
112 | )
113 | }
114 |
--------------------------------------------------------------------------------
/src/components/playground/ColorSchemeFromColor.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState } from "react"
5 | import Flex from "../ui/Flex"
6 | import { getColorScheme } from "../../context/ThemeContext"
7 |
8 | export default props => {
9 | const [selected, setSelected] = useState(null)
10 |
11 | const onColorChange = e => {
12 | let newColor = e.target.value
13 | if (newColor.indexOf("#") !== 0) {
14 | newColor = "#" + newColor
15 | }
16 | if (newColor.length === 7 && /^#([0-9A-F]{3}){1,2}$/i.test(newColor)) {
17 | const newColors = getColorScheme(newColor)
18 | props.onSelect(newColors)
19 | setSelected(newColor)
20 | e.target.value = ""
21 | }
22 | }
23 |
24 | return (
25 |
26 |
34 | {props.isActive && (
35 |
46 | {selected}
47 |
48 | )}
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/playground/ColorSchemeFromImage.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState } from "react"
5 | import { isDark } from "../../context/ThemeContext"
6 | import ColorThief from "colorthief"
7 | import rgbHex from "rgb-hex"
8 | import ntc from "ntcjs"
9 |
10 | export default props => {
11 | const [image, setImage] = useState(null)
12 | const colorThief = new ColorThief()
13 |
14 | const onFileSelect = e => {
15 | const reader = new FileReader()
16 | reader.readAsDataURL(e.target.files[0])
17 | reader.onload = event => {
18 | setImage(event.target.result)
19 | }
20 | }
21 |
22 | const onImageLoad = e => {
23 | const colors = colorThief.getPalette(e.target)
24 | let newColors = {}
25 | colors.forEach((color, index) => {
26 | const colorVal = "#" + rgbHex("rgb(" + color.toString() + ")")
27 | if (index === 0) {
28 | newColors.primary = colorVal
29 | } else if (index === 1) {
30 | newColors.secondary = colorVal
31 | } else {
32 | const colorName = ntc
33 | .name(colorVal)[1]
34 | .toLowerCase()
35 | .split(" ")
36 | .pop()
37 | newColors[colorName] = colorVal
38 | }
39 | })
40 | newColors.background = isDark(newColors.primary) ? "#fff" : "#000"
41 | newColors.text = isDark(newColors.primary) ? "#000" : "#fff"
42 | props.onSelect(newColors)
43 | }
44 |
45 | return (
46 | <>
47 |
54 | {image ? (
55 |
66 | ) : (
67 |
68 | )}
69 | >
70 | )
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/playground/ColorSchemeFromPreset.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState } from "react"
5 | import { Themes } from "../../context/ThemeContext"
6 | import Flex from "../ui/Flex"
7 |
8 | export default props => {
9 | const [preset, setPreset] = useState(null)
10 |
11 | return (
12 | <>
13 | {
19 | if (e.target.value) {
20 | setPreset(e.target.value)
21 | props.onSelect(Themes[e.target.value].colors)
22 | e.target.value = ""
23 | }
24 | }}
25 | >
26 | Select a scheme...
27 | {Object.keys(Themes).map(theme => (
28 |
29 | {theme}
30 |
31 | ))}
32 |
33 | {props.isActive && (
34 |
45 | {preset}
46 |
47 | )}
48 | >
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/playground/ColorSchemeSwatch.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState, useEffect } from "react"
5 | import useTweenMax from "../../animate/useTweenMax"
6 | import { Expo } from "gsap"
7 |
8 | export default props => {
9 | const [hasPlayed, setHasPlayed] = useState(false)
10 | useEffect(() => {
11 | setHasPlayed(true)
12 | }, [])
13 |
14 | const [tweenRef] = useTweenMax(
15 | 1,
16 | hasPlayed
17 | ? {}
18 | : {
19 | startAt: {
20 | x: Math.random() * 40 - 20,
21 | y: Math.random() * 40 - 20,
22 | opacity: 0,
23 | },
24 | opacity: 1,
25 | x: 0,
26 | y: 0,
27 | delay: 0.25 + props.i * 0.05,
28 | ease: Expo.easeOut,
29 | }
30 | )
31 | return (
32 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/playground/Container.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import React, { useContext } from "react"
4 | import { NavContext } from "../../context/NavContext"
5 |
6 | export default React.forwardRef((props, ref) => {
7 | const { playgroundHeight } = useContext(NavContext)
8 |
9 | return (
10 |
23 | {props.children}
24 |
25 | )
26 | })
27 |
--------------------------------------------------------------------------------
/src/components/playground/FontSelectBrowser.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { BrowserFonts } from "../../context/ThemeContext"
4 |
5 | export default props => {
6 | const onFontSelect = e => {
7 | if (e.target.value !== "") {
8 | props.onSelect(
9 | e.target.options[e.target.selectedIndex].text,
10 | e.target.value
11 | )
12 | e.target.value = ""
13 | }
14 | }
15 |
16 | return (
17 |
23 | Choose a Browser Font...
24 |
25 | ────────── sans-serif
26 | {BrowserFonts.sans.map(font => {
27 | return (
28 |
29 | {font.name}
30 |
31 | )
32 | })}
33 |
34 | ────────── serif
35 | {BrowserFonts.serif.map(font => {
36 | return (
37 |
38 | {font.name}
39 |
40 | )
41 | })}
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/playground/FontSelectWeb.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { WebFonts } from "../../context/ThemeContext"
4 |
5 | export default props => {
6 | const onSelect = e => {
7 | if (e.target.value !== "") {
8 | const fontName = e.target.value.split(",")[0]
9 | const fontType = e.target.value.split(",")[1]
10 | props.onSelect(fontName, fontType)
11 | e.target.value = ""
12 | }
13 | }
14 |
15 | return (
16 |
22 | Choose a Web Font...
23 |
24 | ────────── sans-serif
25 | {WebFonts.sans.map(fontName => {
26 | return (
27 |
28 | {fontName}
29 |
30 | )
31 | })}
32 | ────────── serif
33 | {WebFonts.serif.map(fontName => {
34 | return (
35 |
36 | {fontName}
37 |
38 | )
39 | })}
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/playground/Header.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import Heading from "../ui/Heading"
6 | import useTweenMax from "../../animate/useTweenMax"
7 | import { Expo } from "gsap"
8 |
9 | export default props => {
10 | const [tweenDownRef] = useTweenMax(
11 | 1,
12 | props.animate
13 | ? {
14 | startAt: { y: "-25vh", opacity: 0 },
15 | opacity: 1,
16 | y: 0,
17 | ease: Expo.easeOut,
18 | immediateRender: true,
19 | }
20 | : {}
21 | )
22 |
23 | return (
24 |
29 |
40 | {props.children}
41 |
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/playground/LoadTheme.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import React, { useState, useEffect, useContext } from "react"
4 | import { ThemeContext } from "../../context/ThemeContext"
5 | import Container from "./Container"
6 | import Header from "./Header"
7 | import Main from "./Main"
8 | import Button from "../ui/Button"
9 | import { NavContext, PlaygroundViews } from "../../context/NavContext"
10 | import presets from "../../themes/index"
11 |
12 | export default props => {
13 | const [myThemes, setMyThemes] = React.useState(
14 | localStorage.getItem("themes")
15 | ? JSON.parse(localStorage.getItem("themes"))
16 | : null
17 | )
18 | const { setPlaygroundView, setPlaygroundHeight } = useContext(NavContext)
19 |
20 | const { setTheme } = useContext(ThemeContext)
21 |
22 | const modes = {
23 | LOAD: "LOAD",
24 | DELETE: "DELETE",
25 | }
26 |
27 | const [mode, setMode] = useState(modes.LOAD)
28 |
29 | const [hasPlayed, setHasPlayed] = useState(false)
30 | useEffect(() => {
31 | setHasPlayed(true)
32 | }, [])
33 |
34 | const cancel = () => {
35 | setPlaygroundView(PlaygroundViews.COMPLETE)
36 | setPlaygroundHeight(0)
37 | }
38 |
39 | const load = themeIndex => {
40 | setPlaygroundView(PlaygroundViews.COMPLETE)
41 | setPlaygroundHeight(0)
42 | setTheme(myThemes[themeIndex])
43 | }
44 |
45 | const remove = themeIndex => {
46 | console.log("remove")
47 | if (myThemes.length === 1) {
48 | localStorage.clear()
49 | setMyThemes(null)
50 | } else {
51 | const newMyThemes = myThemes.slice(0)
52 | newMyThemes.splice(themeIndex)
53 | localStorage.setItem("themes", JSON.stringify(newMyThemes))
54 | setMyThemes(newMyThemes)
55 | }
56 | }
57 |
58 | const loadPreset = preset => {
59 | setPlaygroundView(PlaygroundViews.COMPLETE)
60 | setPlaygroundHeight(0)
61 | setTheme(presets[preset])
62 | }
63 |
64 | return (
65 |
66 |
67 | {mode === modes.LOAD ? "Load" : "Delete"} Theme
68 |
69 |
70 | {mode === modes.LOAD && (
71 |
72 | {myThemes && myThemes.length > 0 ? (
73 | <>Select one of your themes to load...>
74 | ) : (
75 | <>
76 | You don’t have any themes saved yet. Click that save button up
77 | there ↑
78 | >
79 | )}
80 |
81 | )}
82 | {mode === modes.DELETE && (
83 |
84 | {myThemes && myThemes.length > 0
85 | ? "Choose themes you would like to remove."
86 | : "All saved themes have been removed."}
87 |
88 | )}
89 | {myThemes &&
90 | myThemes.length > 0 &&
91 | myThemes.map((myTheme, i) => (
92 | {
95 | mode === modes.LOAD ? load(i) : remove(i)
96 | }}
97 | sx={{ mx: 2, mb: 3, bg: myTheme.colors.primary }}
98 | >
99 | {myTheme.name}
100 |
101 | ))}
102 | {mode === modes.LOAD && (
103 | <>
104 | Or choose a preset theme...
105 |
106 | {Object.keys(presets).map((preset, i) => (
107 | {
110 | loadPreset(e.target.textContent)
111 | }}
112 | sx={{ mx: 2, mb: 3, bg: presets[preset].colors.primary }}
113 | >
114 | {preset}
115 |
116 | ))}
117 |
118 | >
119 | )}
120 |
121 |
125 | {mode === modes.LOAD ? "Cancel" : "Finish"}
126 |
127 | {myThemes && myThemes.length > 0 && mode === modes.LOAD && (
128 | {
130 | setMode(modes.DELETE)
131 | }}
132 | sx={{ bg: "rgba(0,0,0,0)", color: "red", mt: 4 }}
133 | >
134 | Delete
135 |
136 | )}
137 |
138 |
139 |
140 | )
141 | }
142 |
--------------------------------------------------------------------------------
/src/components/playground/Main.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import useTweenMax from "../../animate/useTweenMax"
4 |
5 | export default props => {
6 | const [tweenFadeInRef] = useTweenMax(
7 | 1,
8 | props.animate
9 | ? {
10 | opacity: 1,
11 | delay: 0.5,
12 | immediateRender: true,
13 | }
14 | : {}
15 | )
16 |
17 | return (
18 |
23 | {props.children}
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/playground/NextButton.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import useTweenMax from "../../animate/useTweenMax"
4 | import { Back } from "gsap"
5 | import Button from "../ui/Button"
6 |
7 | export default props => {
8 | const [tweenUpRef] = useTweenMax(
9 | 0.5,
10 | props.animate
11 | ? {
12 | startAt: { y: "10vh", opacity: 0 },
13 | opacity: 1,
14 | delay: 1,
15 | y: 0,
16 | ease: Back.easeOut,
17 | immediateRender: true,
18 | }
19 | : {}
20 | )
21 |
22 | return (
23 |
24 |
25 | Next »
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/playground/SaveTheme.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import React, { useState, useEffect, useContext } from "react"
4 | import { ThemeContext } from "../../context/ThemeContext"
5 | import Container from "./Container"
6 | import Header from "./Header"
7 | import Main from "./Main"
8 | import Flex from "../ui/Flex"
9 | import Button from "../ui/Button"
10 | import ntc from "ntcjs"
11 | import { NavContext, PlaygroundViews } from "../../context/NavContext"
12 |
13 | export default props => {
14 | const { setPlaygroundView, setPlaygroundHeight } = useContext(NavContext)
15 |
16 | const { theme } = useContext(ThemeContext)
17 | const [myThemes, setMyThemes] = React.useState(
18 | localStorage.getItem("themes")
19 | ? JSON.parse(localStorage.getItem("themes"))
20 | : []
21 | )
22 |
23 | const [hasPlayed, setHasPlayed] = useState(false)
24 | useEffect(() => {
25 | setHasPlayed(true)
26 | }, [])
27 |
28 | const [themeName, setThemeName] = useState("")
29 | const suggestedName = ntc.name(theme.colors.primary)[1]
30 |
31 | const cancel = () => {
32 | setPlaygroundView(PlaygroundViews.COMPLETE)
33 | setPlaygroundHeight(0)
34 | }
35 |
36 | const save = () => {
37 | if (themeName !== "") {
38 | const newMyThemes = myThemes.concat([{ name: themeName, ...theme }])
39 | localStorage.setItem("themes", JSON.stringify(newMyThemes))
40 | setMyThemes(newMyThemes)
41 | setPlaygroundView(PlaygroundViews.COMPLETE)
42 | setPlaygroundHeight(0)
43 | }
44 | }
45 |
46 | return (
47 |
48 |
49 |
50 | Save this theme by giving it a name.
51 |
55 | Theme Name
56 |
57 |
58 |
{
61 | setThemeName(e.target.value)
62 | }}
63 | maxLength="20"
64 | name="themeName"
65 | id="themeName"
66 | type="text"
67 | value={themeName}
68 | />
69 |
70 |
71 |
83 | Cancel
84 |
85 |
86 |
87 |
98 | Save
99 |
100 |
101 |
102 |
103 |
104 |
105 | How about {suggestedName}?{" "}
106 | {
108 | setThemeName(suggestedName)
109 | }}
110 | sx={{ ml: 1, fontSize: 0, px: 2, py: 1 }}
111 | >
112 | Yes
113 |
114 |
115 |
125 | Note: Themes are saved in your browser’s local storage and will not be
126 | available on a different browser or device. Make sure you download
127 | your themes for safe keeping.
128 |
129 |
130 |
131 | )
132 | }
133 |
--------------------------------------------------------------------------------
/src/components/playground/Welcome.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { PlaygroundViews, NavContext } from "../../context/NavContext"
4 | import { useState, useEffect, useContext } from "react"
5 | import useTweenMax from "../../animate/useTweenMax"
6 | import { Power4 } from "gsap"
7 |
8 | const getRandomPos = base => {
9 | return Math.random() * base - base / 2
10 | }
11 |
12 | export default props => {
13 | const { setPlaygroundView, setPlaygroundHeight } = useContext(NavContext)
14 |
15 | const [hasPlayed, setHasPlayed] = useState(false)
16 | useEffect(() => {
17 | setHasPlayed(true)
18 | }, [])
19 |
20 | const [tweenOutRef] = useTweenMax(
21 | 0.5,
22 | hasPlayed
23 | ? {}
24 | : {
25 | scale: 0.01,
26 | opacity: 0,
27 | delay: 4,
28 | ease: Power4.easeIn,
29 | onComplete: () => {
30 | setPlaygroundView(PlaygroundViews.FONT_BODY)
31 | setPlaygroundHeight("800px")
32 | },
33 | }
34 | )
35 |
36 | return (
37 |
41 | {"Welcome to the Playground"
42 | .replace(/ /g, "\u00a0")
43 | .split("")
44 | .map((letter, i) => {
45 | const [tweenRef] = useTweenMax(1.5, {
46 | startAt: {
47 | x: getRandomPos(2000),
48 | y: getRandomPos(1000),
49 | z: getRandomPos(100),
50 | opacity: 0,
51 | rotation: getRandomPos(720),
52 | rotationX: getRandomPos(360),
53 | rotationY: getRandomPos(360),
54 | },
55 | x: 0,
56 | y: 0,
57 | z: 0,
58 | rotation: 0,
59 | rotationX: 0,
60 | rotationY: 0,
61 | opacity: 1,
62 | delay: 0.5 + Math.random() * 0.5,
63 | immediateRender: true,
64 | ease: Power4.easeOut,
65 | })
66 |
67 | return (
68 |
74 | {letter}
75 |
76 | )
77 | })}
78 |
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/playground/WelcomeBack.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { PlaygroundViews, NavContext } from "../../context/NavContext"
4 | import { useState, useEffect, useContext } from "react"
5 | import useTweenMax from "../../animate/useTweenMax"
6 | import { Power4 } from "gsap"
7 |
8 | const getRandomPos = base => {
9 | return Math.random() * base - base / 2
10 | }
11 |
12 | export default props => {
13 | const { setPlaygroundView, setPlaygroundHeight } = useContext(NavContext)
14 |
15 | const [hasPlayed, setHasPlayed] = useState(false)
16 | useEffect(() => {
17 | setHasPlayed(true)
18 | }, [])
19 |
20 | const [tweenOutRef] = useTweenMax(
21 | 0.25,
22 | hasPlayed
23 | ? {}
24 | : {
25 | scale: 0.01,
26 | opacity: 0,
27 | delay: 3,
28 | ease: Power4.easeIn,
29 | onComplete: () => {
30 | setPlaygroundView(PlaygroundViews.COMPLETE)
31 | setPlaygroundHeight(0)
32 | },
33 | }
34 | )
35 |
36 | return (
37 |
41 | {"Welcome Back!"
42 | .replace(/ /g, "\u00a0")
43 | .split("")
44 | .map((letter, i) => {
45 | const [tweenRef] = useTweenMax(0.75, {
46 | startAt: {
47 | x: getRandomPos(2000),
48 | y: getRandomPos(1000),
49 | z: getRandomPos(100),
50 | opacity: 0,
51 | rotation: getRandomPos(720),
52 | rotationX: getRandomPos(360),
53 | rotationY: getRandomPos(360),
54 | },
55 | x: 0,
56 | y: 0,
57 | z: 0,
58 | rotation: 0,
59 | rotationX: 0,
60 | rotationY: 0,
61 | opacity: 1,
62 | delay: 1 + Math.random() * 0.25,
63 | immediateRender: true,
64 | ease: Power4.easeOut,
65 | })
66 |
67 | return (
68 |
74 | {letter}
75 |
76 | )
77 | })}
78 |
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/playground/Wrapper.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext, useState, useEffect } from "react"
4 | import { NavContext } from "../../context/NavContext"
5 | import { ThemeContext } from "../../context/ThemeContext"
6 | import { lightness } from "@theme-ui/color"
7 | import useTweenMax from "../../animate/useTweenMax"
8 | import { Expo } from "gsap"
9 |
10 | export default props => {
11 | const [hasPlayed, setHasPlayed] = useState(false)
12 | useEffect(() => {
13 | setHasPlayed(true)
14 | }, [])
15 |
16 | const { playgroundHeight } = useContext(NavContext)
17 |
18 | const { theme, isDarkMode } = useContext(ThemeContext)
19 | const [tweenRef] = useTweenMax(
20 | 1,
21 | hasPlayed
22 | ? {}
23 | : {
24 | startAt: { paddingBottom: "20vh" },
25 | paddingBottom: "0",
26 | delay: 2,
27 | ease: Expo.easeOut,
28 | immediateRender: true,
29 | }
30 | )
31 |
32 | return (
33 |
42 |
55 | {props.children}
56 |
57 |
58 | )
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/playground/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import React, { useContext } from "react"
4 | import { NavContext, PlaygroundViews } from "../../context/NavContext"
5 | import Wrapper from "./Wrapper"
6 | import Welcome from "./Welcome"
7 | import WelcomeBack from "./WelcomeBack"
8 | import ChooseFonts from "./ChooseFonts"
9 | import ChooseColors from "./ChooseColors"
10 | import ChooseColorScheme from "./ChooseColorScheme"
11 | import LoadTheme from "./LoadTheme"
12 | import SaveTheme from "./SaveTheme"
13 |
14 | export default props => {
15 | const { setPlaygroundHeight, playgroundView, setPlaygroundView } = useContext(
16 | NavContext
17 | )
18 |
19 | const [myThemes] = React.useState(
20 | typeof window !== "undefined" && window.localStorage.getItem("themes")
21 | ? window.localStorage.getItem("themes")
22 | : null
23 | )
24 |
25 | const onStepsComplete = () => {
26 | setPlaygroundHeight(0)
27 | setPlaygroundView(PlaygroundViews.COMPLETE)
28 | }
29 |
30 | return (
31 |
32 | {myThemes ? : }
33 | {playgroundView === PlaygroundViews.FONT_BODY && (
34 | {
37 | setPlaygroundView(PlaygroundViews.FONT_HEADING)
38 | setPlaygroundHeight(["1400px", "140vh"])
39 | }}
40 | />
41 | )}
42 | {playgroundView === PlaygroundViews.FONT_HEADING && (
43 | {
46 | setPlaygroundView(PlaygroundViews.COLOR_SCHEME)
47 | }}
48 | />
49 | )}
50 | {playgroundView === PlaygroundViews.COLOR_SCHEME && (
51 | {
53 | setPlaygroundView(PlaygroundViews.COLOR_REVIEW)
54 | }}
55 | />
56 | )}
57 | {playgroundView === PlaygroundViews.COLOR_REVIEW && (
58 |
59 | )}
60 | {playgroundView === PlaygroundViews.LOAD && }
61 | {playgroundView === PlaygroundViews.SAVE && }
62 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/sections/Section.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import useTweenMax from "../../animate/useTweenMax"
4 |
5 | export default props => {
6 | const [tweenRef] = useTweenMax(0.5, { opacity: 1 })
7 | return
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/sections/SubSection.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import Heading from "../ui/Heading"
4 |
5 | export default props => (
6 |
7 | {props.title}
8 | {props.children}
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/src/components/sections/color/Color.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { lighten, darken, shade, tint } from "@theme-ui/color"
4 | import Flex from "../../ui/Flex"
5 |
6 | export default props => (
7 |
8 | {props.colorName && (
9 |
{props.colorName}
10 | )}
11 |
{props.colorVal}
12 |
13 |
16 |
17 |
Lighten / Darken
18 |
19 | {[...Array(6).keys()]
20 | .splice(1)
21 | .reverse()
22 | .map(i => (
23 |
31 | ))}
32 | {[...Array(6).keys()].splice(1).map(i => (
33 |
41 | ))}
42 |
43 |
44 | Tint / Shade
45 |
46 |
47 | {[...Array(6).keys()]
48 | .reverse()
49 | .splice(1)
50 | .map(i => (
51 |
59 | ))}
60 | {[...Array(6).keys()].splice(1).map(i => (
61 |
69 | ))}
70 |
71 |
72 |
73 |
74 | )
75 |
--------------------------------------------------------------------------------
/src/components/sections/color/Palette.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import Color from "./Color"
6 |
7 | export default props => (
8 |
9 | {Object.entries(props.colors)
10 | .filter(
11 | color =>
12 | typeof color[1] === "string" && color[0].indexOf("primary") === 0
13 | )
14 | .map(color => (
15 |
16 | ))}
17 | {Object.entries(props.colors)
18 | .filter(
19 | color =>
20 | typeof color[1] === "string" && color[0].indexOf("secondary") === 0
21 | )
22 | .map(color => (
23 |
24 | ))}
25 | {Object.entries(props.colors)
26 | .filter(
27 | color =>
28 | typeof color[1] === "string" &&
29 | color[0].indexOf("text") === 0 &&
30 | color[1] !== "#fff" &&
31 | color[1] !== "#ffffff" &&
32 | color[1] !== "#000" &&
33 | color[1] !== "#000000"
34 | )
35 | .map(color => (
36 |
37 | ))}
38 | {Object.entries(props.colors)
39 | .filter(
40 | color =>
41 | typeof color[1] === "string" &&
42 | color[0].indexOf("background") === 0 &&
43 | color[1] !== "#fff" &&
44 | color[1] !== "#ffffff" &&
45 | color[1] !== "#000" &&
46 | color[1] !== "#000000"
47 | )
48 | .map(color => (
49 |
50 | ))}
51 | {Object.entries(props.colors)
52 | .filter(
53 | color =>
54 | typeof color[1] === "string" &&
55 | color[0].indexOf("primary") !== 0 &&
56 | color[0].indexOf("secondary") !== 0 &&
57 | color[0].indexOf("text") !== 0 &&
58 | color[0].indexOf("background") !== 0 &&
59 | color[0] !== "white" &&
60 | color[1] !== "transparent" &&
61 | color[1] !== "#fff" &&
62 | color[1] !== "#ffffff" &&
63 | color[1] !== "#000" &&
64 | color[1] !== "#000000"
65 | )
66 | .map(color => (
67 |
68 | ))}
69 | {Object.entries(props.colors)
70 | .filter(color => typeof color[1] === "object")
71 | .map(color => {
72 | const colorName = color[0]
73 | return (
74 | <>
75 |
{colorName}
76 |
77 | {color[1]
78 | .filter(color => color)
79 | .map((color, i) => (
80 |
84 | ))}
85 |
86 | >
87 | )
88 | })}
89 |
90 | )
91 |
--------------------------------------------------------------------------------
/src/components/sections/color/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext } from "react"
4 | import { ThemeContext } from "../../../context/ThemeContext"
5 | import Section from "../Section"
6 | import SubSection from "../SubSection"
7 | import Palette from "./Palette"
8 |
9 | export default props => {
10 | const { theme } = useContext(ThemeContext)
11 |
12 | return (
13 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/sections/index.js:
--------------------------------------------------------------------------------
1 | export { default as Typography } from "./typography"
2 | export { default as Color } from "./color"
3 | export { default as UI } from "./ui"
4 | export { default as Theme } from "./theme"
5 |
--------------------------------------------------------------------------------
/src/components/sections/theme.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext } from "react"
4 | import { ThemeContext } from "../../context/ThemeContext"
5 | import fileDownload from "js-file-download"
6 | import SyntaxHighlighter from "react-syntax-highlighter"
7 | import getCodeTheme from "../../themes/code"
8 | import Section from "./Section"
9 | import Heading from "../ui/Heading"
10 | import Button from "../ui/Button"
11 | import Link from "../ui/Link"
12 |
13 | export default props => {
14 | const { theme } = useContext(ThemeContext)
15 |
16 | const themeSource = "export default " + JSON.stringify(theme, null, 2)
17 |
18 | const downloadTheme = () => {
19 | fileDownload(themeSource, "theme.js")
20 | }
21 |
22 | return (
23 |
24 |
25 |
26 | Your Theme Object{" "}
27 |
40 | download
41 |
42 |
43 |
44 | The object below represents all the settings that have been applied to
45 | the active theme in this app. You can make adjustments to fonts and
46 | colors, then use the generated theme in your projects. For more info,
47 | check out System UI or{" "}
48 | Theme UI.
49 |
50 |
51 | {themeSource}
52 |
53 |
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/sections/typography/Font.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import FontWeights from "./FontWeights"
6 |
7 | export default props => (
8 | <>
9 | {Object.keys(props.fonts)
10 | .reverse()
11 | .filter(fontName => {
12 | return props.fonts[fontName] !== "inherit"
13 | })
14 | .map(fontName => (
15 |
19 |
20 |
28 | {props.fonts[fontName]}
29 |
30 |
31 | ))}
32 | >
33 | )
34 |
--------------------------------------------------------------------------------
/src/components/sections/typography/FontWeights.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useState, useEffect } from "react"
5 | import useTweenMax from "../../../animate/useTweenMax"
6 | import { Back } from "gsap"
7 |
8 | export default props => {
9 | const [hasPlayed, setHasPlayed] = useState(false)
10 | useEffect(() => {
11 | setHasPlayed(true)
12 | }, [])
13 |
14 | return (
15 | <>
16 |
17 | {props.fontName}
18 |
19 |
20 | The quick brown fox jumps over the lazy dog.
21 |
22 |
30 | {[100, 200, 300, 400, 500, 600, 700, 800, 900].map((weight, i) => {
31 | const [tweenRef] = useTweenMax(
32 | 0.5,
33 | hasPlayed
34 | ? {}
35 | : {
36 | startAt: { y: "20px", opacity: 0, scale: 0.5 },
37 | scale: 1,
38 | opacity: 1,
39 | y: 0,
40 | delay: 0.5 + i * 0.05,
41 | ease: Back.easeOut,
42 | immediateRender: true,
43 | }
44 | )
45 | return (
46 |
60 |
61 | Aa
62 |
63 |
74 | {weight}
75 |
76 |
77 | )
78 | })}
79 |
80 | >
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/src/components/sections/typography/TypeScale.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import { useContext } from "react"
6 | import { ThemeContext } from "../../../context/ThemeContext"
7 | import Heading from "../../ui/Heading"
8 |
9 | export default props => {
10 | const { theme } = useContext(ThemeContext)
11 | return (
12 | <>
13 |
14 | {props.fontSizes.map(size => (
15 |
53 | ))}
54 |
55 |
56 |
57 | Heading 1
58 | H1
59 |
60 |
61 | Heading 2
62 | H2
63 |
64 |
65 | Heading 3
66 | H3
67 |
68 |
69 | Heading 4
70 | H4
71 |
72 |
73 | Heading 5
74 | H5
75 |
76 |
77 | Heading 6
78 | H6
79 |
80 |
81 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
82 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas
83 | accumsan lacus vel facilisis. Massa tincidunt dui ut ornare. Amet nisl
84 | purus in mollis nunc sed id. Auctor augue mauris augue neque gravida
85 | in fermentum. Ut morbi tincidunt augue interdum velit euismod in
86 | pellentesque massa. Mattis molestie a iaculis at erat pellentesque
87 | adipiscing commodo.{" "}
88 |
89 |
90 | >
91 | )
92 | }
93 |
--------------------------------------------------------------------------------
/src/components/sections/typography/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext } from "react"
4 | import { ThemeContext } from "../../../context/ThemeContext"
5 | import Section from "../Section"
6 | import SubSection from "../SubSection"
7 | import Font from "./Font"
8 | import TypeScale from "./TypeScale"
9 |
10 | export default props => {
11 | const { theme } = useContext(ThemeContext)
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/sections/ui/Buttons.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import { useContext } from "react"
6 | import { ThemeContext } from "../../../context/ThemeContext"
7 | import Button from "../../ui/Button"
8 |
9 | export default props => {
10 | const { theme } = useContext(ThemeContext)
11 |
12 | const buttonColors = Object.keys(theme.colors).filter(
13 | color =>
14 | color !== "white" &&
15 | color !== "text" &&
16 | color !== "black" &&
17 | color !== "background"
18 | )
19 |
20 | return (
21 |
22 |
23 | {buttonColors.map(color => (
24 |
35 | {color}
36 |
37 | ))}
38 |
39 |
40 | {buttonColors.map(color => (
41 |
52 | {color}
53 |
54 | ))}
55 |
56 |
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/sections/ui/Cards.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import { useContext } from "react"
6 | import { ThemeContext } from "../../../context/ThemeContext"
7 | import Card from "../../ui/Card"
8 | import Flex from "../../ui/Flex"
9 |
10 | export default props => {
11 | const { theme } = useContext(ThemeContext)
12 |
13 | const cardColors = Object.keys(theme.colors).filter(
14 | color =>
15 | color !== "white" &&
16 | color !== "text" &&
17 | color !== "black" &&
18 | color !== "background"
19 | )
20 |
21 | return (
22 |
23 | {cardColors.slice(0, 6).map(color => (
24 |
25 |
32 | This is a card.
33 |
34 |
35 | ))}
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/sections/ui/Intro.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import Link from "../../ui/Link"
6 | import Figure from "../../ui/Figure"
7 |
8 | export default props => {
9 | return (
10 | <>
11 |
12 | Theme UI is a framework for
13 | building UI components with a{" "}
14 | style props API that
15 | follows the{" "}
16 |
17 | System UI Theme Specification
18 | {" "}
19 | for defining{" "}
20 | theme objects and{" "}
21 |
22 | design tokens
23 |
24 | .
25 |
26 |
35 | Looking for a good starting point? Check out the{" "}
36 | Theme UI Docs{" "}
37 | and try these compononent libraries that are compatible with
38 | Theme UI.
39 |
40 |
45 |
49 | Rebass
50 |
51 |
52 |
57 |
61 | Chakra UI
62 |
63 |
64 | >
65 | )
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/sections/ui/MediaObject.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import Flex from "../../ui/Flex"
6 | import Link from "../../ui/Link"
7 |
8 | export default props => (
9 |
10 |
17 |
18 | The{" "}
19 |
20 | media object
21 | {" "}
22 | is an image to the left, with descriptive content to the right
23 |
24 |
25 | )
26 |
--------------------------------------------------------------------------------
/src/components/sections/ui/NavBar.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 |
6 | export default props => (
7 |
8 |
18 | Left
19 |
20 |
30 | Mid
31 |
32 |
42 | Right
43 |
44 |
45 | )
46 |
--------------------------------------------------------------------------------
/src/components/sections/ui/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import Section from "../Section"
4 | import Intro from "./Intro"
5 | import Buttons from "./Buttons"
6 | import Cards from "./Cards"
7 | import MediaObject from "./MediaObject"
8 | import NavBar from "./NavBar"
9 | import Flex from "../../ui/Flex"
10 |
11 | export default props => (
12 |
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 |
--------------------------------------------------------------------------------
/src/components/ui/Button.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { useContext } from "react"
4 | import { ThemeContext } from "../../context/ThemeContext"
5 |
6 | export default props => {
7 | const { isDarkMode } = useContext(ThemeContext)
8 |
9 | return (
10 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/ui/Card.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 | import Button from "./Button"
6 |
7 | export default props => (
8 |
17 | {props.image && (
18 |
23 | )}
24 | {props.children}
25 | {props.buttonText && (
26 |
27 |
35 | {props.buttonText}
36 |
37 |
38 | )}
39 |
40 | )
41 |
--------------------------------------------------------------------------------
/src/components/ui/Figure.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 |
6 | export default props => (
7 |
8 |
9 |
19 |
20 | {props.children}
21 |
22 | )
23 |
--------------------------------------------------------------------------------
/src/components/ui/Flex.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 |
4 | export default props => (
5 |
9 | )
10 |
--------------------------------------------------------------------------------
/src/components/ui/GithubLink.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React from "react"
5 |
6 | export default props => (
7 |
19 |
27 |
31 |
32 |
33 | )
34 |
--------------------------------------------------------------------------------
/src/components/ui/HeaderButton.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | import { lightness } from "@theme-ui/color"
4 | // eslint-disable-next-line
5 | import React, { useContext, useEffect, useState } from "react"
6 | import useTweenMax from "../../animate/useTweenMax"
7 | import { Expo } from "gsap"
8 | import { ThemeContext } from "../../context/ThemeContext"
9 | import Button from "./Button"
10 |
11 | export default props => {
12 | const [hasPlayed, setHasPlayed] = useState(false)
13 | useEffect(() => {
14 | setHasPlayed(true)
15 | }, [])
16 |
17 | const [tweenRef] = useTweenMax(
18 | 0.5,
19 | hasPlayed
20 | ? {}
21 | : {
22 | startAt: { x: "50vw", opacity: 0 },
23 | opacity: 1,
24 | x: 0,
25 | delay: 0.5 + props.index / 10,
26 | ease: Expo.easeOut,
27 | immediateRender: true,
28 | onComplete: props.onCompleteAnimate || null,
29 | }
30 | )
31 |
32 | const { theme } = useContext(ThemeContext)
33 |
34 | return (
35 |
36 |
50 | {props.children}
51 |
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/ui/Heading.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx, Styled } from "theme-ui"
3 |
4 | export default props =>
5 |
--------------------------------------------------------------------------------
/src/components/ui/Link.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 |
4 | export default props => (
5 | // eslint-disable-next-line
6 |
7 | )
8 |
--------------------------------------------------------------------------------
/src/components/ui/Logo.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useContext } from "react"
5 | import { ThemeContext } from "../../context/ThemeContext"
6 | import Heading from "./Heading"
7 | import { lightness } from "@theme-ui/color"
8 |
9 | export default props => {
10 | const { theme, isDarkMode } = useContext(ThemeContext)
11 |
12 | return (
13 | <>
14 |
36 |
48 | Design System Playground{" "}
49 |
62 | ...powered by{" "}
63 |
71 | Theme UI
72 |
73 |
74 |
75 | >
76 | )
77 | }
78 |
--------------------------------------------------------------------------------
/src/context/NavContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, createContext } from "react"
2 |
3 | const NavContext = createContext({ currSection: "typography" })
4 | const NavSections = ["FONT", "COLOR", "THEME", "UI"]
5 | const PlaygroundViews = {
6 | WELCOME: "WELCOME",
7 | FONT_BODY: "FONT_BODY",
8 | FONT_HEADING: "FONT_HEADING",
9 | COLOR_SCHEME: "COLOR_SCHEME",
10 | COLOR_REVIEW: "COLOR_REVIEW",
11 | COMPLETE: "COMPLETE",
12 | SAVE: "SAVE",
13 | LOAD: "LOAD",
14 | }
15 |
16 | const NavProvider = ({ children }) => {
17 | const [currSection, setCurrSection] = useState(NavSections[0])
18 | const [playgroundHeight, setPlaygroundHeight] = useState(["60vh", "70vh"])
19 | const [playgroundView, setPlaygroundView] = useState(PlaygroundViews.WELCOME)
20 |
21 | return (
22 |
32 | {children}
33 |
34 | )
35 | }
36 |
37 | export { NavContext, NavProvider, NavSections, PlaygroundViews }
38 |
--------------------------------------------------------------------------------
/src/context/ThemeContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, createContext } from "react"
2 | import { ThemeProvider as ThemeUIProvider } from "theme-ui"
3 | import themes from "../themes"
4 | import ColorScheme from "color-scheme"
5 | import ntc from "ntcjs"
6 |
7 | const Themes = themes
8 | const ThemeContext = createContext({ theme: themes.royal })
9 |
10 | const ThemeProvider = ({ children }) => {
11 | const [theme, setTheme] = useState(themes.royal)
12 |
13 | const isDarkMode = () => {
14 | return !isDark(theme.colors.primary)
15 | }
16 |
17 | return (
18 |
19 | {children}
20 |
21 | )
22 | }
23 |
24 | const isDark = color => {
25 | return getLuminance(color) <= 0.7
26 | }
27 |
28 | const mainColors = ["primary", "secondary", "background", "text"]
29 |
30 | const getMainColors = () => {
31 | return mainColors
32 | }
33 |
34 | const getOtherColors = theme => {
35 | const otherColors = Object.keys(theme.colors).filter(color => {
36 | return (
37 | typeof theme.colors[color] === "string" &&
38 | mainColors.indexOf(color) === -1 &&
39 | color !== "transparent" &&
40 | color !== "white" &&
41 | theme.colors[color].toLowerCase() !== "#fff" &&
42 | theme.colors[color] !== "#000" &&
43 | theme.colors[color].toLowerCase() !== "#ffffff" &&
44 | theme.colors[color] !== "#000000"
45 | )
46 | })
47 | return otherColors
48 | }
49 |
50 | const BrowserFonts = {
51 | sans: [
52 | { family: "Arial, sans-serif", name: "Arial" },
53 | {
54 | family: "'avenir next', avenir, helvetica, arial, sans-serif",
55 | name: "Avenir",
56 | },
57 | {
58 | family: '"Helvetica Neue", Helvetica, Arial, sans-serif',
59 | name: "Helvetica",
60 | },
61 | {
62 | family:
63 | 'Frutiger, "Frutiger Linotype", Univers, Calibri, "Gill Sans", "Gill Sans MT", "Myriad Pro", Myriad, "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif',
64 | name: "Helvetica Based",
65 | },
66 | {
67 | family:
68 | "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji",
69 | name: "System",
70 | },
71 | {
72 | family: 'Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif',
73 | name: "Tahoma",
74 | },
75 | {
76 | family: '"Trebuchet MS", Verdana, "Verdana Ref", sans-serif',
77 | name: "Trebuchet",
78 | },
79 | {
80 | family:
81 | '"Segoe UI", Candara, "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, "Verdana Ref", sans-serif',
82 | name: "Trebuchet Based",
83 | },
84 | { family: 'Verdana, "Verdana Ref", sans-serif', name: "Verdana" },
85 | {
86 | family:
87 | 'Corbel, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", "DejaVu Sans", "Bitstream Vera Sans", "Liberation Sans", Verdana, "Verdana Ref", sans-serif',
88 | name: "Verdana Based",
89 | },
90 | ],
91 | serif: [
92 | {
93 | family:
94 | 'Baskerville, "Bookman Old Style", "Bitstream Charter", "Nimbus Roman No9 L", Garamond, "Apple Garamond", "ITC Garamond Narrow", "New Century Schoolbook", "Century Schoolbook", "Century Schoolbook L", Georgia, serif',
95 | name: "Baskerville",
96 | },
97 | {
98 | family:
99 | '"Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace',
100 | name: "Courier",
101 | },
102 | {
103 | family:
104 | 'Garamond, "Apple Garamond", "ITC Garamond Narrow", "New Century Schoolbook", "Century Schoolbook", "Century Schoolbook L", Georgia, serif',
105 | name: "Garamond",
106 | },
107 | { family: "Georgia, serif", name: "Georgia" },
108 | {
109 | family:
110 | 'Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif',
111 | name: "Georgia Based",
112 | },
113 | {
114 | family:
115 | '"Palatino Linotype", Palatino, Palladio, "URW Palladio L", "Book Antiqua", Baskerville, "Bookman Old Style", "Bitstream Charter", "Nimbus Roman No9 L", Garamond, "Apple Garamond", "ITC Garamond Narrow", "New Century Schoolbook", "Century Schoolbook", "Century Schoolbook L", Georgia, serif',
116 | name: "Palatino",
117 | },
118 | { family: 'Times, "Times New Roman", serif', name: "Times" },
119 | {
120 | family:
121 | 'Cambria, "Hoefler Text", Utopia, "Liberation Serif", "Nimbus Roman No9 L Regular", Times, "Times New Roman", serif',
122 | name: "Times Based",
123 | },
124 | ],
125 | }
126 |
127 | const WebFonts = {
128 | sans: [
129 | "ABeeZee",
130 | "Abel",
131 | "Alegreya Sans",
132 | "Amaranth",
133 | "Archivo Narrow",
134 | "Arimo",
135 | "Armata",
136 | "Asap",
137 | "Assistant",
138 | "Barlow Condensed",
139 | "Cabin",
140 | "Cairo",
141 | "Catamaran",
142 | "Cuprum",
143 | "Didact Gothic",
144 | "Dosis",
145 | "Economica",
146 | "Exo",
147 | "Fira Sans",
148 | "Fjalla One",
149 | "Francois One",
150 | "Gudea",
151 | "Heebo",
152 | "Hind Siliguri",
153 | "Istok Web",
154 | "Josefin Sans",
155 | "Karla",
156 | "Lato",
157 | "Libre Franklin",
158 | "Maven Pro",
159 | "Merriweather Sans",
160 | "Monda",
161 | "Montserrat",
162 | "Muli",
163 | "Nanum Gothic",
164 | "News Cycle",
165 | "Noto Sans",
166 | "Nunito",
167 | "Nunito Sans",
168 | "Open Sans",
169 | "Open Sans Condensed",
170 | "Orbitron",
171 | "Oswald",
172 | "Oxygen",
173 | "PT Sans",
174 | "Pathway Gothic One",
175 | "Philosopher",
176 | "Play",
177 | "Pontano Sans",
178 | "Poppins",
179 | "Quattrocento Sans",
180 | "Questrial",
181 | "Quicksand",
182 | "Rajdhani",
183 | "Raleway",
184 | "Roboto",
185 | "Roboto Condensed",
186 | "Ropa Sans",
187 | "Rubik",
188 | "Russo One",
189 | "Signika",
190 | "Source Sans Pro",
191 | "Teko",
192 | "Titillium Web",
193 | "Ubuntu",
194 | "Varela Round",
195 | "Work Sans",
196 | "Yanone Kaffeesatz",
197 | "Yantramanav",
198 | ],
199 | serif: [
200 | "Adamina",
201 | "Alegreya",
202 | "Amiri",
203 | "Arapey",
204 | "Arvo",
205 | "Bitter",
206 | "Bree Serif",
207 | "Cardo",
208 | "Cinzel",
209 | "Cormorant Garamond",
210 | "Crete Round",
211 | "Crimson Text",
212 | "Domine",
213 | "EB Garamond",
214 | "Josefin Slab",
215 | "Libre Baskerville",
216 | "Lora",
217 | "Merriweather",
218 | "Neuton",
219 | "Noticia Text",
220 | "Noto Serif",
221 | "Old Standard TT",
222 | "PT Serif",
223 | "Playfair Display",
224 | "Playfair Display SC",
225 | "Quattrocento",
226 | "Roboto Slab",
227 | "Rokkitt",
228 | "Sanchez",
229 | "Slabo 27px",
230 | "Source Serif Pro",
231 | "Tinos",
232 | "Vidaloka",
233 | "Volkhov",
234 | "Vollkorn",
235 | ],
236 | }
237 |
238 | const getRandomFont = () => {
239 | const allFonts = []
240 | .concat(BrowserFonts.sans)
241 | .concat(BrowserFonts.serif)
242 | .concat(
243 | WebFonts.sans.map(font => {
244 | return { name: font, family: "'" + font + "', sans-serif" }
245 | })
246 | )
247 | .concat(
248 | WebFonts.serif.map(font => {
249 | return { name: font, family: "'" + font + "', serif" }
250 | })
251 | )
252 | return allFonts[Math.floor(Math.random() * allFonts.length)]
253 | }
254 |
255 | const hexToH = H => {
256 | let r = 0,
257 | g = 0,
258 | b = 0
259 | if (H.length === 4) {
260 | r = "0x" + H[1] + H[1]
261 | g = "0x" + H[2] + H[2]
262 | b = "0x" + H[3] + H[3]
263 | } else if (H.length === 7) {
264 | r = "0x" + H[1] + H[2]
265 | g = "0x" + H[3] + H[4]
266 | b = "0x" + H[5] + H[6]
267 | }
268 | r /= 255
269 | g /= 255
270 | b /= 255
271 | let cmin = Math.min(r, g, b),
272 | cmax = Math.max(r, g, b),
273 | delta = cmax - cmin,
274 | h = 0
275 |
276 | if (delta === 0) h = 0
277 | else if (cmax === r) h = ((g - b) / delta) % 6
278 | else if (cmax === g) h = (b - r) / delta + 2
279 | else h = (r - g) / delta + 4
280 | h = Math.round(h * 60)
281 |
282 | if (h < 0) h += 360
283 |
284 | return h
285 | }
286 |
287 | const getLuminance = H => {
288 | let r = 0,
289 | g = 0,
290 | b = 0
291 | if (H.length === 4) {
292 | r = "0x" + H[1] + H[1]
293 | g = "0x" + H[2] + H[2]
294 | b = "0x" + H[3] + H[3]
295 | } else if (H.length === 7) {
296 | r = "0x" + H[1] + H[2]
297 | g = "0x" + H[3] + H[4]
298 | b = "0x" + H[5] + H[6]
299 | }
300 | r /= 255
301 | g /= 255
302 | b /= 255
303 |
304 | return r * 0.299 + g * 0.587 + b * 0.114
305 | }
306 |
307 | const getColorScheme = baseColor => {
308 | if (!baseColor) {
309 | baseColor = "#000000".replace(/0/g, function() {
310 | return (~~(Math.random() * 16)).toString(16)
311 | })
312 | }
313 |
314 | const scm = new ColorScheme()
315 |
316 | const isDarkMode = getLuminance(baseColor) > 0.7
317 |
318 | const colors = scm
319 | .from_hue(hexToH(baseColor))
320 | .scheme("triade")
321 | .colors()
322 |
323 | let colorPalette = {
324 | primary: baseColor,
325 | secondary: "#" + colors[0],
326 | text: isDarkMode ? "#FFFFFF" : "#000000",
327 | background: isDarkMode ? "#000000" : "#FFFFFF",
328 | }
329 |
330 | colors.splice(1).forEach(color => {
331 | const colorName = ntc
332 | .name(color)[1]
333 | .toLowerCase()
334 | .split(" ")
335 | .pop()
336 | colorPalette[colorName] = "#" + color
337 | })
338 |
339 | if (typeof colorPalette.white !== "undefined") {
340 | colorPalette.offwhite = colorPalette.white
341 | colorPalette.white = "#ffffff"
342 | }
343 |
344 | return colorPalette
345 | }
346 |
347 | export {
348 | ThemeContext,
349 | ThemeProvider,
350 | Themes,
351 | BrowserFonts,
352 | WebFonts,
353 | getRandomFont,
354 | getMainColors,
355 | getOtherColors,
356 | getColorScheme,
357 | isDark,
358 | }
359 |
--------------------------------------------------------------------------------
/src/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnpolacek/design-system-playground/b46fc02f8c80a7f051c89e53e1014cef1934537c/src/images/icon.png
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 | // eslint-disable-next-line
4 | import React, { useContext } from "react"
5 | import Header from "../components/Header"
6 | import Nav from "../components/Nav"
7 | import Main from "../components/Main"
8 | import Playground from "../components/playground"
9 | import Wrapper from "../components/Wrapper"
10 | import Footer from "../components/Footer"
11 | import { NavProvider } from "../context/NavContext"
12 | import { ThemeContext, ThemeProvider } from "../context/ThemeContext"
13 | import SEO from "../components/head/SEO"
14 | import GlobalCSS from "../components/head/GlobalCSS"
15 | import ReactGA from "react-ga"
16 |
17 | const IndexPage = () => {
18 | const { theme } = useContext(ThemeContext)
19 |
20 | ReactGA.initialize("UA-2821890-15")
21 | if (typeof window !== "undefined") {
22 | ReactGA.pageview(window.location.pathname + window.location.search)
23 | }
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | export default IndexPage
43 |
--------------------------------------------------------------------------------
/src/themes/code.js:
--------------------------------------------------------------------------------
1 | import { lighten } from "polished"
2 |
3 | const getCodeTheme = theme => {
4 | return {
5 | hljs: {
6 | display: "block",
7 | overflowX: "auto",
8 | padding: "2em",
9 | color: theme.colors.text || "#000", // text
10 | background: lighten(0.1, theme.colors.background || "#ddd") || "#f4f4f4", // background * lighter or darker
11 | },
12 | "hljs-comment": {
13 | color: theme.colors.primary, // primary - lighter
14 | fontStyle: "italic",
15 | },
16 | "hljs-quote": {
17 | color: theme.colors.primary, // primary - lighter
18 | fontStyle: "italic",
19 | },
20 | "hljs-keyword": {
21 | color: theme.colors.secondary || theme.colors.red, // secondary
22 | },
23 | "hljs-selector-tag": {
24 | color: theme.colors.secondary || theme.colors.red, // secondary
25 | },
26 | "hljs-literal": {
27 | color: theme.colors.secondary || theme.colors.red, // secondary
28 | },
29 | "hljs-subst": {
30 | color: theme.colors.secondary || theme.colors.red, // secondary
31 | },
32 | "hljs-number": {
33 | color: "#40a070", // primary
34 | },
35 | "hljs-string": {
36 | color: theme.colors.primary, // primary
37 | },
38 | "hljs-doctag": {
39 | color: theme.colors.primary, // primary
40 | },
41 | "hljs-selector-id": {
42 | color: "#19469d", // blue darker
43 | },
44 | "hljs-selector-class": {
45 | color: "#19469d", // blue darker
46 | },
47 | "hljs-section": {
48 | color: "#19469d", // blue darker
49 | },
50 | "hljs-type": {
51 | color: "#19469d", // blue darker
52 | },
53 | "hljs-params": {
54 | color: "#00f", // blue
55 | },
56 | "hljs-title": {
57 | color: "#458",
58 | fontWeight: "bold", // dk gray
59 | },
60 | "hljs-tag": {
61 | color: "#000080", // blue darker
62 | fontWeight: "normal",
63 | },
64 | "hljs-name": {
65 | color: "#000080", // blue darker
66 | fontWeight: "normal",
67 | },
68 | "hljs-attribute": {
69 | color: "#000080", // blue darker
70 | fontWeight: "normal",
71 | },
72 | "hljs-variable": {
73 | color: "#008080",
74 | },
75 | "hljs-template-variable": {
76 | color: "#008080", // primary
77 | },
78 | "hljs-regexp": {
79 | color: "#b68", // red
80 | },
81 | "hljs-link": {
82 | color: "#b68", // red
83 | },
84 | "hljs-symbol": {
85 | color: "#990073", // red darker
86 | },
87 | "hljs-bullet": {
88 | color: "#990073", // red darker
89 | },
90 | "hljs-built_in": {
91 | color: "#0086b3", // cyan
92 | },
93 | "hljs-builtin-name": {
94 | color: "#0086b3", // cyan
95 | },
96 | "hljs-meta": {
97 | color: "#999",
98 | fontWeight: "bold",
99 | },
100 | "hljs-deletion": {
101 | background: "#fdd", // light red
102 | },
103 | "hljs-addition": {
104 | background: "#dfd", // light green
105 | },
106 | "hljs-emphasis": {
107 | fontStyle: "italic",
108 | },
109 | "hljs-strong": {
110 | fontWeight: "bold",
111 | },
112 | }
113 | }
114 |
115 | export default getCodeTheme
116 |
--------------------------------------------------------------------------------
/src/themes/dark.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Orchid",
3 | useCustomProperties: true,
4 | fonts: {
5 | body:
6 | "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji",
7 | heading:
8 | "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji",
9 | },
10 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
11 | fontWeights: {
12 | lite: 200,
13 | body: 400,
14 | heading: 700,
15 | },
16 | lineHeights: {
17 | body: 1.5,
18 | heading: 1.25,
19 | },
20 | colors: {
21 | primary: "#33ccff",
22 | secondary: "#ee00ff",
23 | text: "#ffffff",
24 | background: "#000000",
25 | green: "#007d48",
26 | pink: "#ff80b8",
27 | orange: "#ff8000",
28 | red: "#e60066",
29 | },
30 | space: [0, 4, 8, 16, 32, 64, 128],
31 | breakpoints: ["32em", "48em", "64em", "80em"],
32 | radii: [0, 3, 6],
33 | shadows: {
34 | card: "0 0 4px rgba(0, 0, 0, .125)",
35 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
36 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
37 | lg:
38 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
39 | xl:
40 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
41 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
42 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
43 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
44 | none: "none",
45 | },
46 | }
47 |
--------------------------------------------------------------------------------
/src/themes/index.js:
--------------------------------------------------------------------------------
1 | import dark from "./dark"
2 | import indigo from "./indigo"
3 | import orchid from "./orchid"
4 | import peru from "./peru"
5 | import roboto from "./roboto"
6 | import royal from "./royal"
7 | import salmon from "./salmon"
8 | import sky from "./sky"
9 | import slate from "./slate"
10 | import swiss from "./swiss"
11 | import tailwind from "./tailwind"
12 | import tomato from "./tomato"
13 |
14 | export default {
15 | dark,
16 | indigo,
17 | orchid,
18 | peru,
19 | roboto,
20 | royal,
21 | salmon,
22 | sky,
23 | slate,
24 | swiss,
25 | tailwind,
26 | tomato,
27 | }
28 |
--------------------------------------------------------------------------------
/src/themes/indigo.js:
--------------------------------------------------------------------------------
1 | export default {
2 | useCustomProperties: true,
3 | fonts: {
4 | body: "'Raleway', sans-serif",
5 | heading: "'Raleway', sans-serif",
6 | },
7 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
8 | fontWeights: {
9 | lite: 200,
10 | body: 400,
11 | heading: 700,
12 | },
13 | lineHeights: {
14 | body: 1.5,
15 | heading: 1.25,
16 | },
17 | colors: {
18 | primary: "#4b0082",
19 | secondary: "#008209",
20 | text: "#000000",
21 | background: "#FFFFFF",
22 | white: "#ffffff",
23 | yellow: "#eeff00",
24 | blue: "#003882",
25 | red: "#820900",
26 | orange: "#ffa100",
27 | gray: "#b5b2b8",
28 | },
29 | space: [0, 4, 8, 16, 32, 64, 128],
30 | breakpoints: ["32em", "48em", "64em", "80em"],
31 | radii: [0, 3, 6],
32 | shadows: {
33 | card: "0 0 4px rgba(0, 0, 0, .125)",
34 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
35 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
36 | lg:
37 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
38 | xl:
39 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
40 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
41 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
42 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
43 | none: "none",
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/src/themes/orchid.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Orchid",
3 | useCustomProperties: true,
4 | fonts: {
5 | body: "'Nunito', sans-serif",
6 | heading: "'Arapey', serif",
7 | },
8 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
9 | fontWeights: {
10 | lite: 200,
11 | body: 400,
12 | heading: 700,
13 | },
14 | lineHeights: {
15 | body: 1.5,
16 | heading: 1.25,
17 | },
18 | colors: {
19 | primary: "#d488c8",
20 | secondary: "#850099",
21 | text: "#000000",
22 | background: "#FFFFFF",
23 | indigo: "#5d006b",
24 | mauve: "#f7bfff",
25 | heliotrope: "#ee80ff",
26 | yellow: "#ffdb00",
27 | gold: "#b39900",
28 | white: "#ffffff",
29 | kournikova: "#ffed80",
30 | green: "#aeff80",
31 | limeade: "#40b300",
32 | flurry: "#d6ffbf",
33 | offwhite: "#fff6bf",
34 | },
35 | space: [0, 4, 8, 16, 32, 64, 128],
36 | breakpoints: ["32em", "48em", "64em", "80em"],
37 | radii: [0, 3, 6],
38 | shadows: {
39 | card: "0 0 4px rgba(0, 0, 0, .125)",
40 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
41 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
42 | lg:
43 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
44 | xl:
45 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
46 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
47 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
48 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
49 | none: "none",
50 | },
51 | }
52 |
--------------------------------------------------------------------------------
/src/themes/peru.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Orchid",
3 | useCustomProperties: true,
4 | fonts: {
5 | body: "'Nunito', sans-serif",
6 | heading: "'Arapey', serif",
7 | },
8 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
9 | fontWeights: {
10 | lite: 200,
11 | body: 400,
12 | heading: 700,
13 | },
14 | lineHeights: {
15 | body: 1.5,
16 | heading: 1.25,
17 | },
18 | colors: {
19 | primary: "#CD853F",
20 | secondary: "#ff6600",
21 | text: "#000000",
22 | background: "#FFFFFF",
23 | green: "#41cd3f",
24 | blue: "#3f88cd",
25 | cyan: "#3fcdcb",
26 | yellow: "#cdcb3f",
27 | gray: "#bbb1a8",
28 | darkred: "#b34700",
29 | },
30 | space: [0, 4, 8, 16, 32, 64, 128],
31 | breakpoints: ["32em", "48em", "64em", "80em"],
32 | radii: [0, 3, 6],
33 | shadows: {
34 | card: "0 0 4px rgba(0, 0, 0, .125)",
35 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
36 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
37 | lg:
38 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
39 | xl:
40 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
41 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
42 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
43 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
44 | none: "none",
45 | },
46 | }
47 |
--------------------------------------------------------------------------------
/src/themes/roboto.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Orchid",
3 | useCustomProperties: true,
4 | fonts: {
5 | body: "'Roboto', sans-serif",
6 | heading: "'Roboto', sans-serif",
7 | },
8 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
9 | fontWeights: {
10 | lite: 200,
11 | body: 400,
12 | heading: 700,
13 | },
14 | lineHeights: {
15 | body: 1.5,
16 | heading: 1.25,
17 | },
18 | colors: {
19 | text: "#202124",
20 | background: "#fff",
21 | primary: "#1a73e8",
22 | secondary: "#9c27b0",
23 | muted: "#f1f3f4",
24 | highlight: "#efeffe",
25 | },
26 | space: [0, 4, 8, 16, 32, 64, 128],
27 | breakpoints: ["32em", "48em", "64em", "80em"],
28 | radii: [0, 3, 6],
29 | shadows: {
30 | card: "0 0 4px rgba(0, 0, 0, .125)",
31 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
32 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
33 | lg:
34 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
35 | xl:
36 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
37 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
38 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
39 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
40 | none: "none",
41 | },
42 | }
43 |
--------------------------------------------------------------------------------
/src/themes/royal.js:
--------------------------------------------------------------------------------
1 | export default {
2 | useCustomProperties: true,
3 | fonts: {
4 | body: `'avenir next', avenir, helvetica, arial, sans-serif`,
5 | heading: "inherit",
6 | },
7 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
8 | fontWeights: {
9 | lite: 200,
10 | body: 400,
11 | heading: 700,
12 | },
13 | lineHeights: {
14 | body: 1.5,
15 | heading: 1.25,
16 | },
17 | colors: {
18 | primary: "#4169e1",
19 | secondary: "#41e169",
20 | background: "#fff",
21 | text: "#1b1e21",
22 | blue: "#4169e1",
23 | cyan: "#41b9e1",
24 | gray: "#aeb3c0",
25 | green: "#41e169",
26 | purple: "#6941e1",
27 | orange: "#fba100",
28 | pink: "#e141b9",
29 | red: "#ee5555",
30 | white: "#fff",
31 | yellow: "#FFDD22",
32 | },
33 | space: [0, 4, 8, 16, 32, 64, 128],
34 | breakpoints: ["32em", "48em", "64em", "80em"],
35 | radii: [0, 3, 6],
36 | shadows: {
37 | card: "0 0 4px rgba(0, 0, 0, .125)",
38 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
39 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
40 | lg:
41 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
42 | xl:
43 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
44 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
45 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
46 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
47 | none: "none",
48 | },
49 | }
50 |
--------------------------------------------------------------------------------
/src/themes/salmon.js:
--------------------------------------------------------------------------------
1 | export default {
2 | useCustomProperties: true,
3 | fonts: {
4 | body: "'Lora', serif",
5 | heading: "'Istok Web', sans-serif",
6 | },
7 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
8 | fontWeights: {
9 | lite: 200,
10 | body: 400,
11 | heading: 700,
12 | },
13 | lineHeights: {
14 | body: 1.5,
15 | heading: 1.25,
16 | },
17 | colors: {
18 | primary: "#ff8c69",
19 | secondary: "#80ccff",
20 | text: "#111111",
21 | background: "#ffffff",
22 | red: "#b32200",
23 | pink: "#ffcbbf",
24 | blue: "#00497b",
25 | yellow: "#ecfa72",
26 | green: "#29b300",
27 | },
28 | space: [0, 4, 8, 16, 32, 64, 128],
29 | breakpoints: ["32em", "48em", "64em", "80em"],
30 | radii: [0, 3, 6],
31 | shadows: {
32 | card: "0 0 4px rgba(0, 0, 0, .125)",
33 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
34 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
35 | lg:
36 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
37 | xl:
38 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
39 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
40 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
41 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
42 | none: "none",
43 | },
44 | }
45 |
--------------------------------------------------------------------------------
/src/themes/sky.js:
--------------------------------------------------------------------------------
1 | export default {
2 | useCustomProperties: true,
3 | fonts: {
4 | body: "'Nunito', sans-serif",
5 | heading: "'Nunito', sans-serif",
6 | },
7 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
8 | fontWeights: {
9 | lite: 200,
10 | body: 400,
11 | heading: 700,
12 | },
13 | lineHeights: {
14 | body: 1.5,
15 | heading: 1.25,
16 | },
17 | colors: {
18 | primary: "#00bfff",
19 | secondary: "#00b366",
20 | text: "#000000",
21 | background: "#FFFFFF",
22 | green: "#007d48",
23 | pink: "#ff80b8",
24 | orange: "#ff8000",
25 | red: "#e60066",
26 | yellow: "#f8eb14",
27 | gray: "#a7b5ba",
28 | },
29 | space: [0, 4, 8, 16, 32, 64, 128],
30 | breakpoints: ["32em", "48em", "64em", "80em"],
31 | radii: [0, 3, 6],
32 | shadows: {
33 | card: "0 0 4px rgba(0, 0, 0, .125)",
34 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
35 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
36 | lg:
37 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
38 | xl:
39 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
40 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
41 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
42 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
43 | none: "none",
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/src/themes/slate.js:
--------------------------------------------------------------------------------
1 | export default {
2 | useCustomProperties: true,
3 | fonts: {
4 | body: "'Assistant', sans-serif",
5 | heading: "'Roboto Slab', serif",
6 | },
7 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
8 | fontWeights: {
9 | lite: 200,
10 | body: 400,
11 | heading: 700,
12 | },
13 | lineHeights: {
14 | body: 1.5,
15 | heading: 1.25,
16 | },
17 | colors: {
18 | primary: "#0077cc",
19 | secondary: "#00a188",
20 | text: "#111111",
21 | background: "#FFFFFF",
22 | forest: "#00705f",
23 | red: "#ad0018",
24 | pink: "#ff8091",
25 | orange: "#ff9100",
26 | cyan: "#00ddcc",
27 | gray: "#adb4b9",
28 | yellow: "#ffee33",
29 | },
30 | space: [0, 4, 8, 16, 32, 64, 128],
31 | breakpoints: ["32em", "48em", "64em", "80em"],
32 | radii: [0, 3, 6],
33 | shadows: {
34 | card: "0 0 4px rgba(0, 0, 0, .125)",
35 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
36 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
37 | lg:
38 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
39 | xl:
40 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
41 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
42 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
43 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
44 | none: "none",
45 | },
46 | }
47 |
--------------------------------------------------------------------------------
/src/themes/swiss.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Orchid",
3 | useCustomProperties: true,
4 | fonts: {
5 | body: '"Helvetica Neue", Helvetica, Arial, sans-serif',
6 | heading:
7 | 'Frutiger, "Frutiger Linotype", Univers, Calibri, "Gill Sans", "Gill Sans MT", "Myriad Pro", Myriad, "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif',
8 | },
9 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
10 | fontWeights: {
11 | lite: 200,
12 | body: 400,
13 | heading: 700,
14 | },
15 | lineHeights: {
16 | body: 1.5,
17 | heading: 1.25,
18 | },
19 | colors: {
20 | text: "#3d2c29",
21 | background: "#fafaf9",
22 | primary: "#e63b19",
23 | secondary: "#cc4c33",
24 | highlight: "#f0dfdb",
25 | purple: " #2e1f7a",
26 | muted: "#f3eeed",
27 | gray: "#996f66",
28 | },
29 | space: [0, 4, 8, 16, 32, 64, 128],
30 | breakpoints: ["32em", "48em", "64em", "80em"],
31 | radii: [0, 3, 6],
32 | shadows: {
33 | card: "0 0 4px rgba(0, 0, 0, .125)",
34 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
35 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
36 | lg:
37 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
38 | xl:
39 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
40 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
41 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
42 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
43 | none: "none",
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/src/themes/tailwind.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Orchid",
3 | useCustomProperties: true,
4 | fonts: {
5 | body:
6 | "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji",
7 | heading:
8 | "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji",
9 | },
10 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
11 | fontWeights: {
12 | lite: 200,
13 | body: 400,
14 | heading: 700,
15 | },
16 | lineHeights: {
17 | body: 1.5,
18 | heading: 1.25,
19 | },
20 | colors: {
21 | transparent: "transparent",
22 | black: "#000",
23 | white: "#fff",
24 | gray: [
25 | null,
26 | "#f7fafc",
27 | "#edf2f7",
28 | "#e2e8f0",
29 | "#cbd5e0",
30 | "#a0aec0",
31 | "#718096",
32 | "#4a5568",
33 | "#2d3748",
34 | "#1a202c",
35 | ],
36 | red: [
37 | null,
38 | "#fff5f5",
39 | "#fed7d7",
40 | "#feb2b2",
41 | "#fc8181",
42 | "#f56565",
43 | "#e53e3e",
44 | "#c53030",
45 | "#9b2c2c",
46 | "#742a2a",
47 | ],
48 | orange: [
49 | null,
50 | "#fffaf0",
51 | "#feebc8",
52 | "#fbd38d",
53 | "#f6ad55",
54 | "#ed8936",
55 | "#dd6b20",
56 | "#c05621",
57 | "#9c4221",
58 | "#7b341e",
59 | ],
60 | yellow: [
61 | null,
62 | "#fffff0",
63 | "#fefcbf",
64 | "#faf089",
65 | "#f6e05e",
66 | "#ecc94b",
67 | "#d69e2e",
68 | "#b7791f",
69 | "#975a16",
70 | "#744210",
71 | ],
72 | green: [
73 | null,
74 | "#f0fff4",
75 | "#c6f6d5",
76 | "#9ae6b4",
77 | "#68d391",
78 | "#48bb78",
79 | "#38a169",
80 | "#2f855a",
81 | "#276749",
82 | "#22543d",
83 | ],
84 | teal: [
85 | null,
86 | "#e6fffa",
87 | "#b2f5ea",
88 | "#81e6d9",
89 | "#4fd1c5",
90 | "#38b2ac",
91 | "#319795",
92 | "#2c7a7b",
93 | "#285e61",
94 | "#234e52",
95 | ],
96 | blue: [
97 | null,
98 | "#ebf8ff",
99 | "#bee3f8",
100 | "#90cdf4",
101 | "#63b3ed",
102 | "#4299e1",
103 | "#3182ce",
104 | "#2b6cb0",
105 | "#2c5282",
106 | "#2a4365",
107 | ],
108 | indigo: [
109 | null,
110 | "#ebf4ff",
111 | "#c3dafe",
112 | "#a3bffa",
113 | "#7f9cf5",
114 | "#667eea",
115 | "#5a67d8",
116 | "#4c51bf",
117 | "#434190",
118 | "#3c366b",
119 | ],
120 | purple: [
121 | null,
122 | "#faf5ff",
123 | "#e9d8fd",
124 | "#d6bcfa",
125 | "#b794f4",
126 | "#9f7aea",
127 | "#805ad5",
128 | "#6b46c1",
129 | "#553c9a",
130 | "#44337a",
131 | ],
132 | pink: [
133 | null,
134 | "#fff5f7",
135 | "#fed7e2",
136 | "#fbb6ce",
137 | "#f687b3",
138 | "#ed64a6",
139 | "#d53f8c",
140 | "#b83280",
141 | "#97266d",
142 | "#702459",
143 | ],
144 | grayDark: "#2d3748",
145 | text: "#2d3748",
146 | background: "#fff",
147 | primary: "#2b6cb0",
148 | primaryHover: "#2c5282",
149 | secondary: "#718096",
150 | muted: "#e2e8f0",
151 | success: "#9ae6b4",
152 | info: "#63b3ed",
153 | warning: "#faf089",
154 | danger: "#feb2b2",
155 | light: "#f7fafc",
156 | dark: "#2d3748",
157 | textMuted: "#718096",
158 | },
159 | space: [0, 4, 8, 16, 32, 64, 128],
160 | breakpoints: ["32em", "48em", "64em", "80em"],
161 | radii: [0, 3, 6],
162 | shadows: {
163 | card: "0 0 4px rgba(0, 0, 0, .125)",
164 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
165 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
166 | lg:
167 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
168 | xl:
169 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
170 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
171 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
172 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
173 | none: "none",
174 | },
175 | }
176 |
--------------------------------------------------------------------------------
/src/themes/tomato.js:
--------------------------------------------------------------------------------
1 | export default {
2 | useCustomProperties: true,
3 | fonts: {
4 | body: "'Dosis', sans-serif",
5 | heading: "'Dosis', sans-serif",
6 | },
7 | fontSizes: [16, 18, 20, 24, 30, 36, 40, 48, 64, 72, 96],
8 | fontWeights: {
9 | lite: 200,
10 | body: 400,
11 | heading: 700,
12 | },
13 | lineHeights: {
14 | body: 1.5,
15 | heading: 1.25,
16 | },
17 | colors: {
18 | primary: "#ff6347",
19 | secondary: "#41e169",
20 | text: "#1b1e21",
21 | background: "#ffffff",
22 | blue: "#4169e1",
23 | cyan: "#41b9e1",
24 | gray: "#aeb3c0",
25 | green: "#41e169",
26 | purple: "#6941e1",
27 | orange: "#ffa500",
28 | pink: "#e141b9",
29 | red: "#ff6347",
30 | white: "#fff",
31 | yellow: "#FFDD22",
32 | },
33 | space: [0, 4, 8, 16, 32, 64, 128],
34 | breakpoints: ["32em", "48em", "64em", "80em"],
35 | radii: [0, 3, 6],
36 | shadows: {
37 | card: "0 0 4px rgba(0, 0, 0, .125)",
38 | sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
39 | md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
40 | lg:
41 | "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
42 | xl:
43 | "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
44 | "2xl": "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
45 | outline: "0 0 0 3px rgba(66, 153, 225, 0.6)",
46 | inner: "inset 0 2px 4px 0 rgba(0,0,0,0.06)",
47 | none: "none",
48 | },
49 | }
50 |
--------------------------------------------------------------------------------
/static/img/chakra.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnpolacek/design-system-playground/b46fc02f8c80a7f051c89e53e1014cef1934537c/static/img/chakra.png
--------------------------------------------------------------------------------
/static/img/design-system-playground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnpolacek/design-system-playground/b46fc02f8c80a7f051c89e53e1014cef1934537c/static/img/design-system-playground.png
--------------------------------------------------------------------------------
/static/img/rebass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnpolacek/design-system-playground/b46fc02f8c80a7f051c89e53e1014cef1934537c/static/img/rebass.png
--------------------------------------------------------------------------------