├── src
├── components
│ ├── Box
│ │ ├── index.ts
│ │ ├── Box.tsx
│ │ └── Box.stories.tsx
│ ├── Flex
│ │ ├── index.ts
│ │ ├── Flex.stories.tsx
│ │ └── Flex.tsx
│ ├── Grid
│ │ ├── index.ts
│ │ ├── Grid.stories.tsx
│ │ └── Grid.tsx
│ ├── Link
│ │ ├── index.ts
│ │ ├── Link.stories.tsx
│ │ └── Link.tsx
│ ├── Text
│ │ ├── index.ts
│ │ ├── Text.tsx
│ │ └── Text.stories.tsx
│ ├── Avatar
│ │ ├── index.ts
│ │ ├── Avatar.tsx
│ │ └── Avatar.stories.tsx
│ ├── Button
│ │ ├── index.ts
│ │ ├── Button.stories.tsx
│ │ └── Button.tsx
│ ├── Input
│ │ ├── index.ts
│ │ ├── Input.stories.tsx
│ │ └── Input.tsx
│ ├── Heading
│ │ ├── index.ts
│ │ ├── Heading.tsx
│ │ └── Heading.stories.tsx
│ ├── Container
│ │ ├── index.ts
│ │ ├── Container.tsx
│ │ └── Container.stories.tsx
│ ├── Paragraph
│ │ ├── index.ts
│ │ ├── Paragraph.tsx
│ │ └── Paragraph.stories.tsx
│ ├── Textarea
│ │ ├── index.ts
│ │ ├── Textarea.tsx
│ │ └── Textarea.stories.tsx
│ └── index.ts
├── helper-components
│ └── Pre
│ │ ├── index.ts
│ │ └── Pre.tsx
├── main.tsx
├── theme
│ ├── focus.styles.ts
│ ├── tailwindColors.ts
│ └── stitches.config.ts
├── favicon.svg
└── App.tsx
├── .prettierignore
├── .prettierrc
├── vite.config.ts
├── .storybook
├── main.js
└── preview.tsx
├── index.html
├── tsconfig.json
├── .gitignore
├── package.json
└── README.md
/src/components/Box/index.ts:
--------------------------------------------------------------------------------
1 | export { Box } from './Box'
2 |
--------------------------------------------------------------------------------
/src/components/Flex/index.ts:
--------------------------------------------------------------------------------
1 | export { Flex } from './Flex'
2 |
--------------------------------------------------------------------------------
/src/components/Grid/index.ts:
--------------------------------------------------------------------------------
1 | export { Grid } from './Grid'
2 |
--------------------------------------------------------------------------------
/src/components/Link/index.ts:
--------------------------------------------------------------------------------
1 | export { Link } from './Link'
2 |
--------------------------------------------------------------------------------
/src/components/Text/index.ts:
--------------------------------------------------------------------------------
1 | export { Text } from './Text'
2 |
--------------------------------------------------------------------------------
/src/components/Avatar/index.ts:
--------------------------------------------------------------------------------
1 | export { Avatar } from './Avatar'
2 |
--------------------------------------------------------------------------------
/src/components/Button/index.ts:
--------------------------------------------------------------------------------
1 | export { Button } from './Button'
2 |
--------------------------------------------------------------------------------
/src/components/Input/index.ts:
--------------------------------------------------------------------------------
1 | export { Input } from './Input'
2 |
--------------------------------------------------------------------------------
/src/helper-components/Pre/index.ts:
--------------------------------------------------------------------------------
1 | export { Pre } from './Pre'
2 |
--------------------------------------------------------------------------------
/src/components/Heading/index.ts:
--------------------------------------------------------------------------------
1 | export { Heading } from './Heading'
2 |
--------------------------------------------------------------------------------
/src/components/Container/index.ts:
--------------------------------------------------------------------------------
1 | export { Container } from './Container'
2 |
--------------------------------------------------------------------------------
/src/components/Paragraph/index.ts:
--------------------------------------------------------------------------------
1 | export { Paragraph } from './Paragraph'
2 |
--------------------------------------------------------------------------------
/src/components/Textarea/index.ts:
--------------------------------------------------------------------------------
1 | export { Textarea } from './Textarea'
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | package-lock.json
3 | public
4 | .yalc
5 | dist
6 | dist-ssr
7 | storybook-static
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "trailingComma": "all",
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Box/Box.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Box = styled('div', {})
4 |
--------------------------------------------------------------------------------
/src/components/Paragraph/Paragraph.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Paragraph = styled('p', {
4 | boxSizing: 'border-box',
5 | color: '$coolGray600',
6 | })
7 |
--------------------------------------------------------------------------------
/src/components/Container/Container.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Container = styled('div', {
4 | width: '97%',
5 | maxWidth: '$container',
6 | margin: '0 auto',
7 | })
8 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import reactRefresh from '@vitejs/plugin-react-refresh'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [reactRefresh()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root'),
10 | )
11 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
3 | addons: [
4 | '@storybook/addon-essentials',
5 | '@storybook/addon-a11y',
6 | 'storybook-addon-jsx',
7 | ],
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Text/Text.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Text = styled('span', {
4 | boxSizing: 'border-box',
5 |
6 | variants: {
7 | variant: {
8 | caps: {
9 | textTransform: 'uppercase',
10 | letterSpacing: '0.2em',
11 | },
12 | },
13 | },
14 | })
15 |
--------------------------------------------------------------------------------
/src/helper-components/Pre/Pre.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Pre = styled('pre', {
4 | border: '1px solid',
5 | borderColor: '$coolGray300',
6 | borderRadius: '$default',
7 | wordBreak: 'break-all',
8 | whiteSpace: 'break-spaces',
9 | fontFamily: '$mono',
10 | fontWeight: 'normal',
11 | fontSize: '$2',
12 | p: '$2',
13 | })
14 |
--------------------------------------------------------------------------------
/src/components/Textarea/Textarea.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 | import { sharedInputStyles } from '../Input/Input'
3 |
4 | export const Textarea = styled('textarea', {
5 | display: 'block',
6 | width: '100%',
7 | p: '$2',
8 | appearance: 'none',
9 | fontSize: 'inherit',
10 | fontFamily: 'inherit',
11 | lineHeight: 'inherit',
12 |
13 | ...sharedInputStyles,
14 | })
15 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Stitches UI
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export { Avatar } from './Avatar'
2 | export { Box } from './Box'
3 | export { Button } from './Button'
4 | export { Container } from './Container'
5 | export { Flex } from './Flex'
6 | export { Grid } from './Grid'
7 | export { Link } from './Link'
8 | export { Text } from './Text'
9 | export { Heading } from './Heading'
10 | export { Paragraph } from './Paragraph'
11 | export { Input } from './Input'
12 | export { Textarea } from './Textarea'
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "target": "es5",
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "types": ["vite/client"],
7 | "allowJs": false,
8 | "skipLibCheck": false,
9 | "esModuleInterop": false,
10 | "allowSyntheticDefaultImports": true,
11 | "strict": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "module": "ESNext",
14 | "moduleResolution": "Node",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "noEmit": true,
18 | "jsx": "react",
19 | "noImplicitAny": true
20 | },
21 | "include": ["./src"]
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Text/Text.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Text } from './Text'
4 | // import docs from './text.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Text',
8 | component: Text,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const caps = Template.bind({})
21 | caps.args = {
22 | children: 'Lorem ipsum dolor sit amet',
23 | variant: 'caps',
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Textarea/Textarea.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Textarea } from './Textarea'
4 | // import docs from './textarea.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Textarea',
8 | component: Textarea,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Basic = Template.bind({})
21 | Basic.args = {
22 | defaultValue: 'A basic textarea',
23 | rows: 8,
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Avatar/Avatar.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Avatar = styled('div', {
4 | display: 'inline-flex',
5 | borderRadius: '$full',
6 | bg: '$coolGray800',
7 | color: '$coolGray50',
8 | fontWeight: 'bold',
9 | alignItems: 'center',
10 | justifyContent: 'center',
11 | backgroundSize: 'cover',
12 |
13 | variants: {
14 | variant: {
15 | small: {
16 | width: 35,
17 | height: 35,
18 | fontSize: '$2',
19 | },
20 | large: {
21 | width: 50,
22 | height: 50,
23 | fontSize: '$4',
24 | },
25 | },
26 | },
27 |
28 | defaultVariants: {
29 | variant: 'large',
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | *.pem
17 |
18 | # debug
19 | *-debug.log*
20 | yarn-error.log*
21 |
22 | # local env files
23 | .env.local
24 | .env.development.local
25 | .env.test.local
26 | .env.production.local
27 | *.local
28 |
29 | # yalc
30 | .yalc
31 | yalc.lock
32 |
33 | # cypress
34 | cypress/screenshots
35 | cypress/videos
36 |
37 | # intellij
38 | .idea
39 | *.iml
40 |
41 | # firebase
42 | /.firebase/
43 |
44 | # storybook
45 | storybook-static
46 |
47 | # vite
48 | dist
49 | dist-ssr
50 |
51 | # bundle visulizer
52 | stats.html
--------------------------------------------------------------------------------
/src/components/Box/Box.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Box } from './Box'
4 | // import docs from './box.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Box',
8 | component: Box,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Basic = Template.bind({})
21 | Basic.args = {
22 | children: 'A basic box',
23 | }
24 |
25 | export const WithCss = Template.bind({})
26 | WithCss.args = {
27 | children: 'A Box with custom CSS styles',
28 | css: {
29 | bg: '$indigo300',
30 | p: '$2',
31 | borderRadius: '$default',
32 | minHeight: '200px',
33 | },
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/Paragraph/Paragraph.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Paragraph } from './Paragraph'
4 | // import docs from './paragraph.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Paragraph',
8 | component: Paragraph,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Basic = Template.bind({})
21 | Basic.args = {
22 | children:
23 | 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sequi similique nulla repellendus repudiandae voluptas minus molestias impedit explicabo fugit, ipsum, amet harum nisi optio vero quidem obcaecati aperiam esse quo.',
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Link/Link.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Link } from './Link'
4 | // import docs from './link.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Link',
8 | component: Link,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Basic = Template.bind({})
21 | Basic.args = {
22 | children: 'A basic link',
23 | href: 'https://zander.wtf',
24 | tabIndex: 0,
25 | }
26 |
27 | export const Subtle = Template.bind({})
28 | Subtle.args = {
29 | ...Basic.args,
30 | children: 'A subtle link',
31 | variant: 'subtle',
32 | }
33 |
34 | export const Blue = Template.bind({})
35 | Blue.args = {
36 | ...Basic.args,
37 | children: 'A blue link',
38 | variant: 'blue',
39 | }
40 |
--------------------------------------------------------------------------------
/src/theme/focus.styles.ts:
--------------------------------------------------------------------------------
1 | export const focus = {
2 | outline: 0,
3 | boxShadow: `inset 0px 0px 0px 3px $colors$focus`,
4 | }
5 |
6 | export const focusOutset = {
7 | outline: 0,
8 | boxShadow: `0px 0px 0px 3px $colors$focus`,
9 | }
10 |
11 | // export const focusDarkBg = {
12 | // outline: 0,
13 | // boxShadow: `inset 0px 0px 0px 3px ${theme.colors?.focusDark}`,
14 | // }
15 |
16 | // export const focusDarkBgOutset = {
17 | // outline: 0,
18 | // boxShadow: `0px 0px 0px 3px ${theme.colors?.focusDark}`,
19 | // }
20 |
21 | export const focusNone = {
22 | outline: 0,
23 | }
24 |
25 | export const focusVisibleOutset = {
26 | '&:focus': {
27 | ...focusOutset,
28 | },
29 | '&:focus:not(:focus-visible)': {
30 | boxShadow: 'none',
31 | },
32 | '&:focus-visible': {
33 | ...focusOutset,
34 | },
35 | }
36 |
37 | export const focusVisibleInset = {
38 | '&:focus': {
39 | ...focus,
40 | },
41 | '&:focus:not(:focus-visible)': {
42 | boxShadow: 'none',
43 | },
44 | '&:focus-visible': { ...focus },
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Avatar/Avatar.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Avatar } from './Avatar'
4 | // import docs from './Avatar.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Avatar',
8 | component: Avatar,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const small = Template.bind({})
21 | small.args = {
22 | children: 'ZM',
23 | variant: 'small',
24 | }
25 |
26 | export const large = Template.bind({})
27 | large.args = {
28 | children: 'ZM',
29 | variant: 'large',
30 | }
31 |
32 | export const withImage = Template.bind({})
33 | withImage.args = {
34 | css: {
35 | backgroundImage:
36 | 'url(https://images-na.ssl-images-amazon.com/images/M/MV5BMjEzMjA0ODk1OF5BMl5BanBnXkFtZTcwMTA4ODM3OQ@@._V1_UY256_CR5,0,172,256_AL_.jpg)',
37 | },
38 | variant: 'large',
39 | }
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stitches-ui",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "yarn build-storybook",
7 | "serve": "serve storybook-static",
8 | "storybook": "start-storybook -p 6006",
9 | "build-storybook": "build-storybook",
10 | "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\""
11 | },
12 | "dependencies": {
13 | "@stitches/react": "^0.2.2",
14 | "react": "^17.0.0",
15 | "react-dom": "^17.0.0"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.14.6",
19 | "@storybook/addon-a11y": "^6.3.4",
20 | "@storybook/addon-actions": "^6.3.4",
21 | "@storybook/addon-essentials": "^6.3.4",
22 | "@storybook/addon-links": "^6.3.4",
23 | "@storybook/react": "^6.3.4",
24 | "@types/react": "^17.0.14",
25 | "@types/react-dom": "^17.0.9",
26 | "@vitejs/plugin-react-refresh": "^1.3.5",
27 | "babel-loader": "^8.2.2",
28 | "serve": "^12.0.0",
29 | "storybook-addon-jsx": "^7.3.12",
30 | "typescript": "^4.3.5",
31 | "vite": "^2.4.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/Container/Container.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Container } from './Container'
4 | // import docs from './container.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Container',
8 | component: Container,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Basic = Template.bind({})
21 | Basic.args = {
22 | children:
23 | 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sequi similique nulla repellendus repudiandae voluptas minus molestias impedit explicabo fugit, ipsum, amet harum nisi optio vero quidem obcaecati aperiam esse quo.Lorem ipsum dolor sit amet consectetur adipisicing elit. Sequi similique nulla repellendus repudiandae voluptas minus molestias impedit explicabo fugit, ipsum, amet harum nisi optio vero quidem obcaecati aperiam esse quo.',
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Input/Input.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Input } from './Input'
4 | // import docs from './input.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Input',
8 | component: Input,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Basic = Template.bind({})
21 | Basic.args = {
22 | value: 'Default',
23 | }
24 |
25 | export const BasicSize2 = Template.bind({})
26 | BasicSize2.args = {
27 | value: 'Size 2',
28 | size: '2',
29 | }
30 |
31 | export const WithPlaceholder = Template.bind({})
32 | WithPlaceholder.args = {
33 | placeholder: 'Some placeholder text',
34 | }
35 |
36 | export const Disabled = Template.bind({})
37 | Disabled.args = {
38 | ...Basic.args,
39 | disabled: true,
40 | }
41 |
42 | export const ReadOnly = Template.bind({})
43 | ReadOnly.args = {
44 | ...Basic.args,
45 | readOnly: true,
46 | }
47 |
48 | export const Email = Template.bind({})
49 | Email.args = {
50 | value: 'someone@foo.com',
51 | type: 'email',
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/Heading/Heading.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const heading = {
4 | fontFamily: '$sans',
5 | lineHeight: '$heading',
6 | fontWeight: '$light',
7 | mb: '$4',
8 | mt: '$6',
9 | color: '$text',
10 | }
11 |
12 | export const Heading = styled('h2', {
13 | boxSizing: 'border-box',
14 |
15 | variants: {
16 | variant: {
17 | h1: {
18 | ...heading,
19 | fontSize: '$11',
20 | mt: '$9',
21 | },
22 | h2: {
23 | ...heading,
24 | fontSize: '$9',
25 | mt: '$9',
26 | },
27 | h3: {
28 | ...heading,
29 | fontSize: '$7',
30 | },
31 | h4: {
32 | ...heading,
33 | fontSize: '$5',
34 | },
35 | h5: {
36 | ...heading,
37 | fontSize: '$3',
38 | },
39 | h6: {
40 | ...heading,
41 | fontSize: '$3',
42 | },
43 | sectionHeading: {
44 | ...heading,
45 | fontSize: '$9',
46 | m: 0,
47 | },
48 | sectionSubHeading: {
49 | ...heading,
50 | fontSize: '$7',
51 | m: 0,
52 | textTransform: 'uppercase',
53 | color: '$coolGray600',
54 | },
55 | },
56 | },
57 | defaultVariants: {
58 | variant: 'h1',
59 | },
60 | })
61 |
--------------------------------------------------------------------------------
/src/components/Link/Link.tsx:
--------------------------------------------------------------------------------
1 | import { focusVisibleOutset } from '../../theme/focus.styles'
2 | import { styled } from '../../theme/stitches.config'
3 |
4 | export const Link = styled('a', {
5 | display: 'inline-flex',
6 | flexShrink: 0,
7 | outline: 'none',
8 | textDecorationLine: 'none',
9 | textUnderlineOffset: '3px',
10 | textDecorationColor: '$slate300',
11 | WebkitTapHighlightColor: 'rgba(0,0,0,0)',
12 | lineHeight: 'inherit',
13 |
14 | '&:hover': {
15 | textDecorationLine: 'underline',
16 | },
17 |
18 | ...focusVisibleOutset,
19 |
20 | variants: {
21 | variant: {
22 | blue: {
23 | color: '$blue900',
24 | textDecorationColor: '$blue300',
25 |
26 | '&:focus-visible': {
27 | outlineColor: '$blue700',
28 | },
29 | },
30 | subtle: {
31 | color: '$coolGray900',
32 | textDecorationColor: '$coolGray300',
33 | '&:focus-visible': {
34 | outlineColor: '$coolGray700',
35 | },
36 | },
37 | contrast: {
38 | color: '$hiContrast',
39 | textDecoration: 'underline',
40 | textDecorationColor: '$coolGray300',
41 | '&:hover': {
42 | textDecorationColor: '$coolGray600',
43 | },
44 | '&:focus-visible': {
45 | outlineColor: '$coolGray700',
46 | },
47 | },
48 | },
49 | },
50 | defaultVariants: {
51 | variant: 'contrast',
52 | },
53 | })
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Stitches UI
2 |
3 | ## A minimal implementation of [Theme UI](https://theme-ui.com) using [Stitches](https://stitches.dev)
4 |
5 | As a long-time fan of [Theme UI](https://theme-ui.com) I wanted to see how I might replicate the same functionality using [Stitches](https://stitches.dev). I found that I not only could replicate the functionality, but also create a more robust design-system whilst having the flexibility of Theme UI's `sx` prop, but with Stitches' `css` prop.
6 |
7 | This experiment includes enough of Theme UI's components and functionality to enable me to properly evaluate Stitches.
8 |
9 | ℹ️ The styles do not match Theme UI's default styles
10 |
11 | ## Important/example files
12 |
13 | - [stitches.config.ts](./src/theme/stitches.config.ts)
14 | - [`` component](./src/components/Box/Box.tsx)
15 | - [`` component](./src/components/Flex/Flex.tsx)
16 | - [`` component](./src/components/Heading/Heading.tsx)
17 |
18 | ## Demo
19 |
20 | [stitches-ui.vercel.app](https://stitches-ui.vercel.app)
21 |
22 |
23 |
27 |
28 |
29 | ---
30 |
31 | ## License
32 |
33 | [MIT](https://choosealicense.com/licenses/mit/) © [Zander Martineau](https://zander.wtf)
34 |
35 | > Made by Zander • [zander.wtf](https://zander.wtf) • [GitHub](https://github.com/mrmartineau/) • [Twitter](https://twitter.com/mrmartineau/)
36 |
--------------------------------------------------------------------------------
/src/favicon.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/components/Grid/Grid.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Grid } from './Grid'
4 | import { Box } from '../Box'
5 | // import docs from './grid.docs.mdx'
6 |
7 | export default {
8 | title: 'Components/Grid',
9 | component: Grid,
10 | parameters: {
11 | // docs: { page: docs },
12 | // paddings: [],
13 | /* backgrounds: {
14 | default: 'white',
15 | },*/
16 | },
17 | } as Meta
18 |
19 | const Template: Story = (args) =>
20 |
21 | export const Basic = Template.bind({})
22 | Basic.args = {
23 | children: (
24 |
25 |
32 | 1
33 |
34 |
41 | 2
42 |
43 |
50 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cumque ipsa
51 | repudiandae ex voluptatum voluptas soluta, consequatur dicta quas porro
52 | qui itaque esse eum illum delectus. Illo tempore modi labore id!
53 |
54 |
55 | ),
56 | }
57 |
58 | export const WithCss = Template.bind({})
59 | WithCss.args = {
60 | ...Basic.args,
61 | css: {
62 | gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
63 | // alignItems: 'center',
64 | gap: '$2',
65 | },
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/Heading/Heading.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Heading } from './Heading'
4 | // import docs from './heading.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Heading',
8 | component: Heading,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | } as Meta
17 |
18 | const Template: Story = (args) =>
19 |
20 | export const Heading1 = Template.bind({})
21 | Heading1.args = {
22 | children: 'Heading 1',
23 | variant: 'h1',
24 | as: 'h1',
25 | }
26 |
27 | export const Heading2 = Template.bind({})
28 | Heading2.args = {
29 | children: 'Heading 2',
30 | variant: 'h2',
31 | as: 'h2',
32 | }
33 |
34 | export const Heading3 = Template.bind({})
35 | Heading3.args = {
36 | children: 'Heading 3',
37 | variant: 'h3',
38 | as: 'h3',
39 | }
40 |
41 | export const Heading4 = Template.bind({})
42 | Heading4.args = {
43 | children: 'Heading 4',
44 | variant: 'h4',
45 | as: 'h4',
46 | }
47 |
48 | export const Heading5 = Template.bind({})
49 | Heading5.args = {
50 | children: 'Heading 5',
51 | variant: 'h5',
52 | as: 'h5',
53 | }
54 |
55 | export const Heading6 = Template.bind({})
56 | Heading6.args = {
57 | children: 'Heading 6',
58 | variant: 'h6',
59 | as: 'h6',
60 | }
61 |
62 | export const sectionHeading = Template.bind({})
63 | sectionHeading.args = {
64 | children: 'Section heading',
65 | variant: 'sectionHeading',
66 | }
67 |
68 | export const sectionSubHeading = Template.bind({})
69 | sectionSubHeading.args = {
70 | children: 'Section sub-heading',
71 | variant: 'sectionSubHeading',
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/Input/Input.tsx:
--------------------------------------------------------------------------------
1 | import { focusVisibleOutset } from '../../theme/focus.styles'
2 | import { styled } from '../../theme/stitches.config'
3 |
4 | export const sharedInputStyles = {
5 | bg: '$background',
6 | border: '1px solid $colors$coolGray400',
7 | borderRadius: '$default',
8 | color: '$text',
9 | fontVariantNumeric: 'tabular-nums',
10 | ...focusVisibleOutset,
11 | '&:focus': {
12 | borderColor: '$primary',
13 | },
14 | '&:disabled,&:read-only': {
15 | backgroundColor: '$coolGray200',
16 | borderColor: '$coolGray400',
17 | },
18 | '&:disabled': {
19 | pointerEvents: 'none',
20 | color: '$coolGray500',
21 | cursor: 'not-allowed',
22 | '&::placeholder': {
23 | color: '$coolGray600',
24 | },
25 | },
26 | '&:read-only': {
27 | '&:focus': {
28 | borderColor: '$primary',
29 | },
30 | },
31 | }
32 |
33 | export const Input = styled('input', {
34 | // Reset
35 | appearance: 'none',
36 | display: 'block',
37 | fontFamily: 'inherit',
38 | fontSize: '$body',
39 | margin: 0,
40 | outline: 'none',
41 | padding: 0,
42 | width: '100%',
43 | WebkitTapHighlightColor: 'rgba(0,0,0,0)',
44 |
45 | ...sharedInputStyles,
46 |
47 | '&:-webkit-autofill': {
48 | // boxShadow:
49 | // 'inset 0 0 0 1px $colors$blue500, inset 0 0 0 100px $colors$blue200',
50 | },
51 |
52 | '&:-webkit-autofill::first-line': {
53 | fontFamily: '$sans',
54 | color: '$text',
55 | },
56 |
57 | '&::placeholder': {
58 | color: '$coolGray800',
59 | },
60 |
61 | variants: {
62 | size: {
63 | '1': {
64 | py: '$2',
65 | px: '$2',
66 | fontSize: '$body',
67 | },
68 | '2': {
69 | py: '$2',
70 | px: '$3',
71 | fontSize: '$5',
72 | },
73 | },
74 | },
75 | defaultVariants: {
76 | size: '1',
77 | },
78 | })
79 |
--------------------------------------------------------------------------------
/src/components/Button/Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Button } from './Button'
4 | // import docs from './button.docs.mdx'
5 |
6 | export default {
7 | title: 'Components/Button',
8 | component: Button,
9 | parameters: {
10 | // docs: { page: docs },
11 | // paddings: [],
12 | /* backgrounds: {
13 | default: 'white',
14 | },*/
15 | },
16 | /* argTypes: {
17 | // Select element
18 | variant: {
19 | name: 'Variant',
20 | defaultValue: 'large',
21 | control: {
22 | type: 'select',
23 | options: ['large', 'small']
24 | },
25 | },
26 | // Radio
27 | variant: {
28 | name: 'Variant',
29 | defaultValue: 'large',
30 | control: {
31 | type: 'radio',
32 | options: ['large', 'small']
33 | },
34 | },
35 | }, */
36 | } as Meta
37 |
38 | const Template: Story = (args) =>
39 |
40 | export const Primary = Template.bind({})
41 | Primary.args = {
42 | children: 'Click me',
43 | }
44 |
45 | export const PrimaryLarge = Template.bind({})
46 | PrimaryLarge.args = {
47 | ...Primary.args,
48 | size: 2,
49 | }
50 |
51 | export const Secondary = Template.bind({})
52 | Secondary.args = {
53 | ...Primary.args,
54 | variant: 'secondary',
55 | }
56 |
57 | export const SecondaryLarge = Template.bind({})
58 | SecondaryLarge.args = {
59 | ...Secondary.args,
60 | size: 2,
61 | }
62 |
63 | export const Ghost = Template.bind({})
64 | Ghost.args = {
65 | ...Primary.args,
66 | variant: 'ghost',
67 | }
68 |
69 | export const GhostLarge = Template.bind({})
70 | GhostLarge.args = {
71 | ...Ghost.args,
72 | size: 2,
73 | }
74 |
75 | /* Standard.decorators = [
76 | (Story) => (
77 |
78 |
79 |
80 | ),
81 | ]*/
82 | /* Standard.parameters = {
83 | backgrounds: {
84 | default: 'blue',
85 | },
86 | }*/
87 |
--------------------------------------------------------------------------------
/.storybook/preview.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
3 | import { globalStyles } from '../src/theme/stitches.config'
4 | import { jsxDecorator } from 'storybook-addon-jsx'
5 |
6 | export const parameters = {
7 | layout: 'padded',
8 | actions: { argTypesRegex: '^on[A-Z].*' },
9 | controls: {
10 | matchers: {
11 | color: /(background|color)$/i,
12 | date: /Date$/,
13 | },
14 | },
15 | colorMode: {
16 | defaultMode: 'default',
17 | modes: {
18 | light: {
19 | name: 'Light',
20 | },
21 | },
22 | },
23 | paddings: {
24 | values: [
25 | { name: 'Small', value: '16px' },
26 | { name: 'Medium', value: '32px' },
27 | { name: 'Large', value: '64px' },
28 | ],
29 | default: 'Medium',
30 | },
31 | backgrounds: {
32 | default: 'white',
33 | values: [
34 | { name: 'white', value: '#fff' },
35 | { name: 'peach', value: 'hsla(36, 100%, 92%, 1)' },
36 | { name: 'pink', value: 'hsla(0, 69%, 91%, 1)' },
37 | { name: 'green', value: 'hsla(114, 70%, 93%, 1)' },
38 | { name: 'light blue', value: 'hsla(199, 100%, 93%, 1)' },
39 | { name: 'blue', value: 'hsl(240, 100%, 22%)' },
40 | { name: 'dark', value: 'hsl(109, 0%, 16%)' },
41 | { name: 'grey', value: '#f2f2f2' },
42 | ],
43 | },
44 | viewport: {
45 | viewports: {
46 | iphoneSe: {
47 | name: 'iPhone SE',
48 | styles: {
49 | height: '667px',
50 | width: '375px',
51 | },
52 | type: 'mobile',
53 | },
54 | iphone12Mini: {
55 | name: 'iPhone 12 Mini',
56 | styles: {
57 | height: '812px',
58 | width: '375px',
59 | },
60 | type: 'mobile',
61 | },
62 | ...INITIAL_VIEWPORTS,
63 | },
64 | },
65 | }
66 |
67 | export const decorators = [
68 | jsxDecorator,
69 | (Story) => {
70 | globalStyles()
71 | return
72 | },
73 | ]
74 |
--------------------------------------------------------------------------------
/src/components/Flex/Flex.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react'
2 | import { Story, Meta } from '@storybook/react/types-6-0'
3 | import { Flex } from './Flex'
4 | import { Box } from '../Box'
5 | import { Button } from '../Button'
6 | // import docs from './flex.docs.mdx'
7 |
8 | export default {
9 | title: 'Components/Flex',
10 | component: Flex,
11 | parameters: {
12 | // docs: { page: docs },
13 | // paddings: [],
14 | /* backgrounds: {
15 | default: 'white',
16 | },*/
17 | },
18 | } as Meta
19 |
20 | const Template: Story = (args) =>
21 |
22 | export const Basic = Template.bind({})
23 | Basic.args = {
24 | children: (
25 |
26 |
33 | 1
34 |
35 |
42 | 2
43 |
44 |
51 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cumque ipsa
52 | repudiandae ex voluptatum voluptas soluta, consequatur dicta quas porro
53 | qui itaque esse eum illum delectus. Illo tempore modi labore id!
54 |
55 |
56 | ),
57 | }
58 |
59 | export const usingProps = Template.bind({})
60 | usingProps.args = {
61 | ...Basic.args,
62 | gap: 2,
63 | align: 'center',
64 | direction: 'row',
65 | }
66 | export const usingCss = Template.bind({})
67 | usingCss.args = {
68 | ...Basic.args,
69 | css: {
70 | alignItems: 'center',
71 | gap: '$2',
72 | },
73 | }
74 |
75 | export const AnotherExample = Template.bind({})
76 | AnotherExample.args = {
77 | children: (
78 |
79 |
80 | Some text
81 |
82 | ),
83 | css: {
84 | alignItems: 'center',
85 | gap: '$2',
86 | },
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/Flex/Flex.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Flex = styled('div', {
4 | boxSizing: 'border-box',
5 | display: 'flex',
6 |
7 | variants: {
8 | direction: {
9 | row: {
10 | flexDirection: 'row',
11 | },
12 | column: {
13 | flexDirection: 'column',
14 | },
15 | rowReverse: {
16 | flexDirection: 'row-reverse',
17 | },
18 | columnReverse: {
19 | flexDirection: 'column-reverse',
20 | },
21 | },
22 | align: {
23 | start: {
24 | alignItems: 'flex-start',
25 | },
26 | center: {
27 | alignItems: 'center',
28 | },
29 | end: {
30 | alignItems: 'flex-end',
31 | },
32 | stretch: {
33 | alignItems: 'stretch',
34 | },
35 | baseline: {
36 | alignItems: 'baseline',
37 | },
38 | },
39 | justify: {
40 | start: {
41 | justifyContent: 'flex-start',
42 | },
43 | center: {
44 | justifyContent: 'center',
45 | },
46 | end: {
47 | justifyContent: 'flex-end',
48 | },
49 | between: {
50 | justifyContent: 'space-between',
51 | },
52 | },
53 | wrap: {
54 | noWrap: {
55 | flexWrap: 'nowrap',
56 | },
57 | wrap: {
58 | flexWrap: 'wrap',
59 | },
60 | wrapReverse: {
61 | flexWrap: 'wrap-reverse',
62 | },
63 | },
64 | gap: {
65 | 1: {
66 | gap: '$1',
67 | },
68 | 2: {
69 | gap: '$2',
70 | },
71 | 3: {
72 | gap: '$3',
73 | },
74 | 4: {
75 | gap: '$4',
76 | },
77 | 5: {
78 | gap: '$5',
79 | },
80 | 6: {
81 | gap: '$6',
82 | },
83 | 7: {
84 | gap: '$7',
85 | },
86 | 8: {
87 | gap: '$8',
88 | },
89 | 9: {
90 | gap: '$9',
91 | },
92 | },
93 | },
94 | defaultVariants: {
95 | direction: 'row',
96 | align: 'stretch',
97 | justify: 'start',
98 | wrap: 'noWrap',
99 | },
100 | })
101 |
--------------------------------------------------------------------------------
/src/components/Grid/Grid.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Grid = styled('div', {
4 | boxSizing: 'border-box',
5 | display: 'grid',
6 |
7 | variants: {
8 | align: {
9 | start: {
10 | alignItems: 'start',
11 | },
12 | center: {
13 | alignItems: 'center',
14 | },
15 | end: {
16 | alignItems: 'end',
17 | },
18 | stretch: {
19 | alignItems: 'stretch',
20 | },
21 | baseline: {
22 | alignItems: 'baseline',
23 | },
24 | },
25 | justify: {
26 | start: {
27 | justifyContent: 'start',
28 | },
29 | center: {
30 | justifyContent: 'center',
31 | },
32 | end: {
33 | justifyContent: 'end',
34 | },
35 | between: {
36 | justifyContent: 'space-between',
37 | },
38 | },
39 | flow: {
40 | row: {
41 | gridAutoFlow: 'row',
42 | },
43 | column: {
44 | gridAutoFlow: 'column',
45 | },
46 | dense: {
47 | gridAutoFlow: 'dense',
48 | },
49 | rowDense: {
50 | gridAutoFlow: 'row dense',
51 | },
52 | columnDense: {
53 | gridAutoFlow: 'column dense',
54 | },
55 | },
56 | columns: {
57 | 1: {
58 | gridTemplateColumns: 'repeat(1, 1fr)',
59 | },
60 | 2: {
61 | gridTemplateColumns: 'repeat(2, 1fr)',
62 | },
63 | 3: {
64 | gridTemplateColumns: 'repeat(3, 1fr)',
65 | },
66 | 4: {
67 | gridTemplateColumns: 'repeat(4, 1fr)',
68 | },
69 | },
70 | gap: {
71 | 1: {
72 | gap: '$1',
73 | },
74 | 2: {
75 | gap: '$2',
76 | },
77 | 3: {
78 | gap: '$3',
79 | },
80 | 4: {
81 | gap: '$4',
82 | },
83 | 5: {
84 | gap: '$5',
85 | },
86 | 6: {
87 | gap: '$6',
88 | },
89 | 7: {
90 | gap: '$7',
91 | },
92 | 8: {
93 | gap: '$8',
94 | },
95 | 9: {
96 | gap: '$9',
97 | },
98 | },
99 | gapX: {
100 | 1: {
101 | columnGap: '$1',
102 | },
103 | 2: {
104 | columnGap: '$2',
105 | },
106 | 3: {
107 | columnGap: '$3',
108 | },
109 | 4: {
110 | columnGap: '$4',
111 | },
112 | 5: {
113 | columnGap: '$5',
114 | },
115 | 6: {
116 | columnGap: '$6',
117 | },
118 | 7: {
119 | columnGap: '$7',
120 | },
121 | 8: {
122 | columnGap: '$8',
123 | },
124 | 9: {
125 | columnGap: '$9',
126 | },
127 | },
128 | gapY: {
129 | 1: {
130 | rowGap: '$1',
131 | },
132 | 2: {
133 | rowGap: '$2',
134 | },
135 | 3: {
136 | rowGap: '$3',
137 | },
138 | 4: {
139 | rowGap: '$4',
140 | },
141 | 5: {
142 | rowGap: '$5',
143 | },
144 | 6: {
145 | rowGap: '$6',
146 | },
147 | 7: {
148 | rowGap: '$7',
149 | },
150 | 8: {
151 | rowGap: '$8',
152 | },
153 | 9: {
154 | rowGap: '$9',
155 | },
156 | },
157 | },
158 | defaultVariants: {
159 | direction: 'row',
160 | align: 'stretch',
161 | justify: 'start',
162 | wrap: 'noWrap',
163 | },
164 | })
165 |
--------------------------------------------------------------------------------
/src/components/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '../../theme/stitches.config'
2 |
3 | export const Button = styled('button', {
4 | // Reset
5 | alignItems: 'center',
6 | appearance: 'none',
7 | boxSizing: 'border-box',
8 | display: 'inline-flex',
9 | flexShrink: 0,
10 | justifyContent: 'center',
11 | lineHeight: '1',
12 | margin: '0',
13 | outline: 'none',
14 | padding: '0',
15 | textDecoration: 'none',
16 | userSelect: 'none',
17 | WebkitTapHighlightColor: 'rgba(0,0,0,0)',
18 | '&::before': {
19 | boxSizing: 'border-box',
20 | },
21 | '&::after': {
22 | boxSizing: 'border-box',
23 | },
24 |
25 | // Custom
26 | border: 'none',
27 | borderRadius: '$default',
28 | height: '$5',
29 | px: '$2',
30 | fontFamily: '$sans',
31 | fontSize: '$2',
32 | fontWeight: 500,
33 | fontVariantNumeric: 'tabular-nums',
34 |
35 | '&:disabled': {
36 | backgroundColor: '$slate100',
37 | boxShadow: 'inset 0 0 0 1px $colors$slate600',
38 | color: '$slate700',
39 | pointerEvents: 'none',
40 | },
41 |
42 | variants: {
43 | size: {
44 | '1': {
45 | height: '$5',
46 | px: '$2',
47 | fontSize: '$2',
48 | lineHeight: '25px',
49 | },
50 | '2': {
51 | height: '$6',
52 | px: '$3',
53 | fontSize: '$3',
54 | lineHeight: '35px',
55 | },
56 | },
57 | variant: {
58 | primary: {
59 | backgroundColor: '$coolGray900',
60 | color: '$background',
61 | '&:hover': {
62 | backgroundColor: '$coolGray700',
63 | },
64 | '&:active': {
65 | backgroundColor: '$coolGray500',
66 | },
67 | '&:focus': {
68 | backgroundColor: '$coolGray500',
69 | boxShadow: 'inset 0 0 0 1px $colors$text, 0 0 0 1px $colors$text',
70 | },
71 | },
72 | secondary: {
73 | backgroundColor: '$background',
74 | color: '$text',
75 | boxShadow:
76 | 'inset 0 0 0 1px $colors$coolGray300, 0 0 0 1px $colors$coolGray300',
77 | '&:hover': {
78 | boxShadow:
79 | 'inset 0 0 0 1px $colors$coolGray500, 0 0 0 1px $colors$coolGray500',
80 | },
81 | '&:active': {
82 | boxShadow:
83 | 'inset 0 0 0 1px $colors$coolGray500, 0 0 0 1px $colors$coolGray500',
84 | },
85 | '&:focus': {
86 | backgroundColor: '$coolGray100',
87 | boxShadow:
88 | 'inset 0 0 0 1px $colors$coolGray500, 0 0 0 1px $colors$coolGray500',
89 | },
90 | },
91 | ghost: {
92 | mixBlendMode: 'multiply',
93 | backgroundColor: 'transparent',
94 | color: '$hiContrast',
95 | '&:hover': {
96 | backgroundColor: '$coolGray100',
97 | },
98 | '&:active': {
99 | boxShadow:
100 | 'inset 0 0 0 1px $colors$coolGray300, 0 0 0 1px $colors$coolGray300',
101 | },
102 | '&:focus': {
103 | backgroundColor: '$coolGray100',
104 | boxShadow:
105 | 'inset 0 0 0 1px $colors$coolGray300, 0 0 0 1px $colors$coolGray300',
106 | },
107 | },
108 | },
109 | state: {
110 | active: {
111 | backgroundColor: '$slate300',
112 | boxShadow: 'inset 0 0 0 1px $colors$slate700',
113 | color: '$slate900',
114 | '@hover': {
115 | '&:hover': {
116 | backgroundColor: '$slate400',
117 | boxShadow: 'inset 0 0 0 1px $colors$slate700',
118 | },
119 | },
120 | '&:active': {
121 | backgroundColor: '$slate400',
122 | },
123 | '&:focus': {
124 | boxShadow:
125 | 'inset 0 0 0 1px $colors$slate700, 0 0 0 1px $colors$slate700',
126 | },
127 | },
128 | waiting: {
129 | backgroundColor: '$slate300',
130 | boxShadow: 'inset 0 0 0 1px $colors$slate700',
131 | color: 'transparent',
132 | pointerEvents: 'none',
133 | '@hover': {
134 | '&:hover': {
135 | backgroundColor: '$slate400',
136 | boxShadow: 'inset 0 0 0 1px $colors$slate700',
137 | },
138 | },
139 | '&:active': {
140 | backgroundColor: '$slate400',
141 | },
142 | '&:focus': {
143 | boxShadow: 'inset 0 0 0 1px $colors$slate700',
144 | },
145 | },
146 | },
147 | },
148 | defaultVariants: {
149 | size: '1',
150 | variant: 'primary',
151 | },
152 | })
153 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import {
3 | Box,
4 | Button,
5 | Flex,
6 | Grid,
7 | Link,
8 | Container,
9 | Heading,
10 | Text,
11 | Paragraph,
12 | Input,
13 | Textarea,
14 | } from './components'
15 | import { globalStyles } from './theme/stitches.config'
16 | import { Pre } from './helper-components/Pre'
17 |
18 | function App() {
19 | const [count, setCount] = useState(0)
20 | globalStyles()
21 | return (
22 |
23 |
24 |
32 |
33 |
34 | Links
35 |
36 |
41 | zander.wtf
42 |
43 |
44 | <Link href="https://zander.wtf">Click me</Link>
45 |
46 |
47 |
48 | Button
49 |
50 |
51 |
52 |
53 |
54 |
57 |
58 |
61 |
62 |
63 | <Button>Click me</Button>
64 | <Button size="2">Click me</Button>
65 | <Button variant="secondary">Click me</Button>
66 |
67 |
68 |
69 | Headings
70 |
71 |
72 | h1
73 |
74 |
75 | h2
76 |
77 |
78 | h3
79 |
80 |
81 | h4
82 |
83 |
84 | h5
85 |
86 |
87 | h6
88 |
89 |
90 |
91 | <Heading as="h1" variant="h1">Lorem ipsum</Heading>
92 |
93 |
94 |
95 |
96 | Text
97 |
98 | Lorem ipsum
99 |
100 | <Text>Lorem ipsum</Text>
101 |
102 |
103 |
104 | Paragraph
105 |
106 |
107 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere
108 | quae, repellendus doloremque exercitationem repudiandae rerum
109 | delectus ad possimus, fuga quam consequatur reiciendis eaque dolores
110 | rem quo, sit ipsum voluptatum aspernatur!
111 |
112 |
113 |
114 | <Paragraph>Lorem ipsum dolor sit amet consectetur adipisicing
115 | elit.</Paragraph>
116 |
117 |
118 |
119 |
120 | Forms
121 | Input
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | <Input defaultValue="Lorem ipsum dolor" />
131 |
132 | Textarea
133 |
136 |
137 | <Textarea rows={8}>Lorem ipsum dolor</Textarea>
138 |
139 |
140 |
141 | )
142 | }
143 |
144 | export default App
145 |
--------------------------------------------------------------------------------
/src/theme/tailwindColors.ts:
--------------------------------------------------------------------------------
1 | // source: https://github.com/tailwindlabs/tailwindcss/blob/master/colors.js
2 |
3 | export const tailwindColors = {
4 | black: '#000',
5 | white: '#fff',
6 |
7 | rose50: '#fff1f2',
8 | rose100: '#ffe4e6',
9 | rose200: '#fecdd3',
10 | rose300: '#fda4af',
11 | rose400: '#fb7185',
12 | rose500: '#f43f5e',
13 | rose600: '#e11d48',
14 | rose700: '#be123c',
15 | rose800: '#9f1239',
16 | rose900: '#881337',
17 |
18 | pink50: '#fdf2f8',
19 | pink100: '#fce7f3',
20 | pink200: '#fbcfe8',
21 | pink300: '#f9a8d4',
22 | pink400: '#f472b6',
23 | pink500: '#ec4899',
24 | pink600: '#db2777',
25 | pink700: '#be185d',
26 | pink800: '#9d174d',
27 | pink900: '#831843',
28 |
29 | fuchsia50: '#fdf4ff',
30 | fuchsia100: '#fae8ff',
31 | fuchsia200: '#f5d0fe',
32 | fuchsia300: '#f0abfc',
33 | fuchsia400: '#e879f9',
34 | fuchsia500: '#d946ef',
35 | fuchsia600: '#c026d3',
36 | fuchsia700: '#a21caf',
37 | fuchsia800: '#86198f',
38 | fuchsia900: '#701a75',
39 |
40 | purple50: '#faf5ff',
41 | purple100: '#f3e8ff',
42 | purple200: '#e9d5ff',
43 | purple300: '#d8b4fe',
44 | purple400: '#c084fc',
45 | purple500: '#a855f7',
46 | purple600: '#9333ea',
47 | purple700: '#7e22ce',
48 | purple800: '#6b21a8',
49 | purple900: '#581c87',
50 |
51 | violet50: '#f5f3ff',
52 | violet100: '#ede9fe',
53 | violet200: '#ddd6fe',
54 | violet300: '#c4b5fd',
55 | violet400: '#a78bfa',
56 | violet500: '#8b5cf6',
57 | violet600: '#7c3aed',
58 | violet700: '#6d28d9',
59 | violet800: '#5b21b6',
60 | violet900: '#4c1d95',
61 |
62 | indigo50: '#eef2ff',
63 | indigo100: '#e0e7ff',
64 | indigo200: '#c7d2fe',
65 | indigo300: '#a5b4fc',
66 | indigo400: '#818cf8',
67 | indigo500: '#6366f1',
68 | indigo600: '#4f46e5',
69 | indigo700: '#4338ca',
70 | indigo800: '#3730a3',
71 | indigo900: '#312e81',
72 |
73 | blue50: '#eff6ff',
74 | blue100: '#dbeafe',
75 | blue200: '#bfdbfe',
76 | blue300: '#93c5fd',
77 | blue400: '#60a5fa',
78 | blue500: '#3b82f6',
79 | blue600: '#2563eb',
80 | blue700: '#1d4ed8',
81 | blue800: '#1e40af',
82 | blue900: '#1e3a8a',
83 |
84 | lightBlue50: '#f0f9ff',
85 | lightBlue100: '#e0f2fe',
86 | lightBlue200: '#bae6fd',
87 | lightBlue300: '#7dd3fc',
88 | lightBlue400: '#38bdf8',
89 | lightBlue500: '#0ea5e9',
90 | lightBlue600: '#0284c7',
91 | lightBlue700: '#0369a1',
92 | lightBlue800: '#075985',
93 | lightBlue900: '#0c4a6e',
94 |
95 | cyan50: '#ecfeff',
96 | cyan100: '#cffafe',
97 | cyan200: '#a5f3fc',
98 | cyan300: '#67e8f9',
99 | cyan400: '#22d3ee',
100 | cyan500: '#06b6d4',
101 | cyan600: '#0891b2',
102 | cyan700: '#0e7490',
103 | cyan800: '#155e75',
104 | cyan900: '#164e63',
105 |
106 | teal50: '#f0fdfa',
107 | teal100: '#ccfbf1',
108 | teal200: '#99f6e4',
109 | teal300: '#5eead4',
110 | teal400: '#2dd4bf',
111 | teal500: '#14b8a6',
112 | teal600: '#0d9488',
113 | teal700: '#0f766e',
114 | teal800: '#115e59',
115 | teal900: '#134e4a',
116 |
117 | emerald50: '#ecfdf5',
118 | emerald100: '#d1fae5',
119 | emerald200: '#a7f3d0',
120 | emerald300: '#6ee7b7',
121 | emerald400: '#34d399',
122 | emerald500: '#10b981',
123 | emerald600: '#059669',
124 | emerald700: '#047857',
125 | emerald800: '#065f46',
126 | emerald900: '#064e3b',
127 |
128 | green50: '#f0fdf4',
129 | green100: '#dcfce7',
130 | green200: '#bbf7d0',
131 | green300: '#86efac',
132 | green400: '#4ade80',
133 | green500: '#22c55e',
134 | green600: '#16a34a',
135 | green700: '#15803d',
136 | green800: '#166534',
137 | green900: '#14532d',
138 |
139 | lime50: '#f7fee7',
140 | lime100: '#ecfccb',
141 | lime200: '#d9f99d',
142 | lime300: '#bef264',
143 | lime400: '#a3e635',
144 | lime500: '#84cc16',
145 | lime600: '#65a30d',
146 | lime700: '#4d7c0f',
147 | lime800: '#3f6212',
148 | lime900: '#365314',
149 |
150 | yellow50: '#fefce8',
151 | yellow100: '#fef9c3',
152 | yellow200: '#fef08a',
153 | yellow300: '#fde047',
154 | yellow400: '#facc15',
155 | yellow500: '#eab308',
156 | yellow600: '#ca8a04',
157 | yellow700: '#a16207',
158 | yellow800: '#854d0e',
159 | yellow900: '#713f12',
160 |
161 | amber50: '#fffbeb',
162 | amber100: '#fef3c7',
163 | amber200: '#fde68a',
164 | amber300: '#fcd34d',
165 | amber400: '#fbbf24',
166 | amber500: '#f59e0b',
167 | amber600: '#d97706',
168 | amber700: '#b45309',
169 | amber800: '#92400e',
170 | amber900: '#78350f',
171 |
172 | orange50: '#fff7ed',
173 | orange100: '#ffedd5',
174 | orange200: '#fed7aa',
175 | orange300: '#fdba74',
176 | orange400: '#fb923c',
177 | orange500: '#f97316',
178 | orange600: '#ea580c',
179 | orange700: '#c2410c',
180 | orange800: '#9a3412',
181 | orange900: '#7c2d12',
182 |
183 | red50: '#fef2f2',
184 | red100: '#fee2e2',
185 | red200: '#fecaca',
186 | red300: '#fca5a5',
187 | red400: '#f87171',
188 | red500: '#ef4444',
189 | red600: '#dc2626',
190 | red700: '#b91c1c',
191 | red800: '#991b1b',
192 | red900: '#7f1d1d',
193 |
194 | warmGray50: '#fafaf9',
195 | warmGray100: '#f5f5f4',
196 | warmGray200: '#e7e5e4',
197 | warmGray300: '#d6d3d1',
198 | warmGray400: '#a8a29e',
199 | warmGray500: '#78716c',
200 | warmGray600: '#57534e',
201 | warmGray700: '#44403c',
202 | warmGray800: '#292524',
203 | warmGray900: '#1c1917',
204 |
205 | trueGray50: '#fafafa',
206 | trueGray100: '#f5f5f5',
207 | trueGray200: '#e5e5e5',
208 | trueGray300: '#d4d4d4',
209 | trueGray400: '#a3a3a3',
210 | trueGray500: '#737373',
211 | trueGray600: '#525252',
212 | trueGray700: '#404040',
213 | trueGray800: '#262626',
214 | trueGray900: '#171717',
215 |
216 | gray50: '#fafafa',
217 | gray100: '#f4f4f5',
218 | gray200: '#e4e4e7',
219 | gray300: '#d4d4d8',
220 | gray400: '#a1a1aa',
221 | gray500: '#71717a',
222 | gray600: '#52525b',
223 | gray700: '#3f3f46',
224 | gray800: '#27272a',
225 | gray900: '#18181b',
226 |
227 | coolGray50: '#f9fafb',
228 | coolGray100: '#f3f4f6',
229 | coolGray200: '#e5e7eb',
230 | coolGray300: '#d1d5db',
231 | coolGray400: '#9ca3af',
232 | coolGray500: '#6b7280',
233 | coolGray600: '#4b5563',
234 | coolGray700: '#374151',
235 | coolGray800: '#1f2937',
236 | coolGray900: '#111827',
237 |
238 | blueGray50: '#f8fafc',
239 | blueGray100: '#f1f5f9',
240 | blueGray200: '#e2e8f0',
241 | blueGray300: '#cbd5e1',
242 | blueGray400: '#94a3b8',
243 | blueGray500: '#64748b',
244 | blueGray600: '#475569',
245 | blueGray700: '#334155',
246 | blueGray800: '#1e293b',
247 | blueGray900: '#0f172a',
248 | }
249 |
--------------------------------------------------------------------------------
/src/theme/stitches.config.ts:
--------------------------------------------------------------------------------
1 | import { createCss } from '@stitches/react'
2 | import type {
3 | AlignItemsProperty,
4 | BorderRadiusProperty,
5 | MarginTopProperty,
6 | MarginLeftProperty,
7 | PaddingTopProperty,
8 | WidthProperty,
9 | BackgroundProperty,
10 | } from '@stitches/react/types/css-types'
11 | import { tailwindColors } from './tailwindColors'
12 |
13 | type TLength = (string & {}) | 0
14 | type MarginProperty = MarginTopProperty
15 | type PaddingProperty = PaddingTopProperty
16 |
17 | export const { styled, theme, global } = createCss({
18 | theme: {
19 | colors: {
20 | ...tailwindColors,
21 |
22 | // Theme UI
23 | text: '$coolGray900',
24 | background: '$coolGray50',
25 | primary: '$blue500',
26 | secondary: '$red500',
27 | accent: '$emerald500',
28 | highlighted: 'blue',
29 | muted: '$coolGray300',
30 |
31 | focus: '$blue200',
32 | },
33 | fonts: {
34 | sans:
35 | '-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"',
36 | serif: 'Georgia, Cambria, "Times New Roman", Times, serif',
37 | mono:
38 | '"IBM Plex Mono", "JetBrains Mono", "Fira Code", "Input Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
39 | },
40 | fontSizes: {
41 | body: '1rem',
42 | 0: '0.625rem', // 10px
43 | 1: '0.75rem', // 12px
44 | 2: '0.875rem', // 14px
45 | 3: '1rem', // 16px - body, h5, h4
46 | 4: '1.125rem', // 18px
47 | 5: '1.25rem', // 20px
48 | 6: '1.375rem', // 22px
49 | 7: '1.5625rem', // 25px - h3
50 | 8: '1.75rem', // 28px
51 | 9: '2rem', // 32px - h2
52 | 10: '2.25rem', // 36px
53 | 11: '2.625rem', // 42px - h1
54 | 12: '2.875rem', // 46px
55 | 13: '3.1875rem', // 51px
56 | },
57 | fontWeights: {
58 | thin: '100',
59 | extralight: '200',
60 | light: '300',
61 | normal: '400',
62 | medium: '500',
63 | semibold: '600',
64 | bold: '700',
65 | extrabold: '800',
66 | black: '900',
67 | },
68 | letterSpacings: {
69 | tighter: '-0.05em',
70 | tight: '-0.025em',
71 | normal: '0em',
72 | wide: '0.025em',
73 | wider: '0.05em',
74 | widest: '0.1em',
75 | },
76 | lineHeights: {
77 | none: '1',
78 | tight: '1.25',
79 | snug: '1.375',
80 | normal: '1.5',
81 | relaxed: '1.625',
82 | loose: '2',
83 | body: '1.625',
84 | heading: 1.15,
85 | },
86 | borderWidths: {},
87 | borderStyles: {},
88 | shadows: {},
89 | transitions: {},
90 | space: {
91 | 0: '0rem', // 0px
92 | 1: '0.25rem', // 4px
93 | 2: '0.5rem', // 8px
94 | 3: '0.75rem', // 12px
95 | 4: '1rem', // 16px
96 | 5: '1.5rem', // 24px
97 | 6: '2rem', // 32px
98 | 7: '2.5rem', // 40px
99 | 8: '3rem', // 48px
100 | 9: '3.5rem', // 56px
101 | 10: '4rem', // 64px
102 | 11: '8rem', // 128px
103 | 12: '16rem', // 256px
104 | 13: '32rem', // 512px
105 | },
106 | sizes: {
107 | 1: '5px',
108 | 2: '10px',
109 | 3: '15px',
110 | 4: '20px',
111 | 5: '25px',
112 | 6: '35px',
113 | 7: '45px',
114 | 8: '65px',
115 | 9: '80px',
116 | container: '1200px',
117 | },
118 | radii: {
119 | none: '0',
120 | sm: '0.125rem',
121 | default: '0.25rem',
122 | m: '0.4rem',
123 | lg: '0.625rem',
124 | xl: '1rem',
125 | full: '9999px',
126 | round: '50%',
127 | pill: '9999px',
128 | },
129 | zIndices: {
130 | 1: '100',
131 | 2: '200',
132 | 3: '300',
133 | 4: '400',
134 | max: '999',
135 | },
136 | },
137 | media: {
138 | bp1: '(min-width: 640px)',
139 | bp2: '(min-width: 768px)',
140 | bp3: '(min-width: 1024px)',
141 | },
142 | utils: {
143 | m: () => (value: MarginProperty) => ({
144 | marginLeft: value,
145 | marginRight: value,
146 | marginTop: value,
147 | marginBottom: value,
148 | }),
149 | mx: () => (value: MarginProperty) => ({
150 | marginLeft: value,
151 | marginRight: value,
152 | }),
153 | ml: () => (value: MarginProperty) => ({
154 | marginLeft: value,
155 | }),
156 | mr: () => (value: MarginProperty) => ({
157 | marginRight: value,
158 | }),
159 | my: () => (value: MarginProperty) => ({
160 | marginTop: value,
161 | marginBottom: value,
162 | }),
163 | mt: () => (value: MarginProperty) => ({
164 | marginTop: value,
165 | }),
166 | mb: () => (value: MarginProperty) => ({
167 | marginBottom: value,
168 | }),
169 | p: () => (value: PaddingProperty) => ({
170 | paddingLeft: value,
171 | paddingRight: value,
172 | paddingTop: value,
173 | paddingBottom: value,
174 | }),
175 | px: () => (value: PaddingProperty) => ({
176 | paddingLeft: value,
177 | paddingRight: value,
178 | }),
179 | pl: () => (value: PaddingProperty) => ({
180 | paddingLeft: value,
181 | }),
182 | pr: () => (value: PaddingProperty) => ({
183 | paddingRight: value,
184 | }),
185 | py: () => (value: PaddingProperty) => ({
186 | paddingTop: value,
187 | paddingBottom: value,
188 | }),
189 | pt: () => (value: PaddingProperty) => ({
190 | paddingTop: value,
191 | }),
192 | pb: () => (value: PaddingProperty) => ({
193 | paddingBottom: value,
194 | }),
195 | bg: () => (value: BackgroundProperty) => ({
196 | background: value,
197 | }),
198 |
199 | // Non-Theme-UI utils
200 | size: () => (value: WidthProperty) => ({
201 | width: value,
202 | height: value,
203 | }),
204 | spaceX: () => (value: MarginLeftProperty) => ({
205 | '& > * + *': {
206 | marginLeft: value,
207 | },
208 | }),
209 | spaceY: () => (value: MarginTopProperty) => ({
210 | '& > * + *': {
211 | marginTop: value,
212 | },
213 | }),
214 | ai: () => (value: AlignItemsProperty = 'center') => ({
215 | alignItems: value,
216 | }),
217 | aic: () => () => ({
218 | alignItems: 'center',
219 | }),
220 | br: () => (value: BorderRadiusProperty) => ({
221 | borderRadius: value,
222 | }),
223 | },
224 | })
225 |
226 | export const globalStyles = global({
227 | body: {
228 | margin: 0,
229 | fontFamily: '$sans',
230 | bg: '$background',
231 | color: '$coolGray800',
232 | },
233 | '*, *::before, *::after': {
234 | boxSizing: 'border-box',
235 | },
236 | })
237 |
--------------------------------------------------------------------------------