├── .circleci └── config.yml ├── .gitignore ├── .storybook ├── main.ts ├── preview-head.html ├── preview.ts └── theme-decorator.tsx ├── LICENSE ├── README.md ├── docs ├── creating-components.md ├── design-graph.md ├── design-principles.md ├── design.md ├── develop.md └── overview.md ├── example ├── .npmignore ├── App.tsx ├── Navigation.tsx ├── R2-logo.png ├── imgs │ └── user.png ├── index.css ├── index.html ├── index.tsx ├── package-lock.json ├── package.json └── tsconfig.json ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── components │ ├── aspect-image │ │ ├── aspect-image.stories.tsx │ │ ├── aspect-image.tsx │ │ └── index.ts │ ├── aspect-ratio │ │ ├── aspect-ratio.stories.tsx │ │ ├── aspect-ratio.tsx │ │ └── index.ts │ ├── box │ │ ├── box.stories.tsx │ │ ├── box.test.tsx │ │ ├── box.tsx │ │ └── index.ts │ ├── button │ │ ├── button.stories.tsx │ │ ├── button.test.tsx │ │ ├── button.tsx │ │ └── index.ts │ ├── checkbox │ │ ├── checkbox.stories.tsx │ │ ├── checkbox.tsx │ │ └── index.ts │ ├── flex │ │ ├── flex.stories.tsx │ │ ├── flex.tsx │ │ └── index.ts │ ├── form-control │ │ ├── field.stories.tsx │ │ ├── field.tsx │ │ ├── fieldset.stories.tsx │ │ ├── index.ts │ │ └── messages.tsx │ ├── grid │ │ ├── grid.stories.tsx │ │ ├── grid.tsx │ │ └── index.ts │ ├── heading │ │ ├── heading.stories.tsx │ │ ├── heading.test.tsx │ │ ├── heading.tsx │ │ └── index.ts │ ├── hello │ │ ├── hello.stories.tsx │ │ ├── hello.test.tsx │ │ ├── hello.tsx │ │ └── index.ts │ ├── icons │ │ ├── check-circle.svg │ │ ├── check-circle.tsx │ │ ├── checkbox-blank.svg │ │ ├── checkbox-blank.tsx │ │ ├── checkbox-checked.svg │ │ ├── checkbox-checked.tsx │ │ ├── checkbox-indeterminate.svg │ │ ├── checkbox-indeterminate.tsx │ │ ├── close.svg │ │ ├── close.tsx │ │ ├── error.svg │ │ ├── error.tsx │ │ ├── expand-more.tsx │ │ ├── expand_more.svg │ │ ├── icons.stories.tsx │ │ ├── index.tsx │ │ ├── info.svg │ │ ├── info.tsx │ │ ├── loader.svg │ │ ├── loader.tsx │ │ ├── place.svg │ │ ├── place.tsx │ │ ├── radio-checked.svg │ │ ├── radio-checked.tsx │ │ ├── radio-unchecked.svg │ │ ├── radio-unchecked.tsx │ │ ├── search.svg │ │ ├── search.tsx │ │ ├── uuid.ts │ │ ├── visibility-off.tsx │ │ ├── visibility.svg │ │ ├── visibility.tsx │ │ └── visibility_off.svg │ ├── image │ │ ├── image.stories.tsx │ │ ├── image.tsx │ │ └── index.ts │ ├── index.ts │ ├── input │ │ ├── index.ts │ │ ├── input.stories.tsx │ │ └── input.tsx │ ├── label │ │ ├── index.ts │ │ ├── label.stories.tsx │ │ └── label.tsx │ ├── link │ │ ├── index.ts │ │ ├── link.stories.tsx │ │ └── link.tsx │ ├── radio │ │ ├── index.ts │ │ ├── radio.stories.tsx │ │ └── radio.tsx │ ├── select │ │ ├── index.ts │ │ ├── select.stories.tsx │ │ └── select.tsx │ ├── shared-styles.ts │ ├── spinner │ │ ├── index.ts │ │ ├── spinner.stories.tsx │ │ └── spinner.tsx │ ├── stack │ │ ├── index.ts │ │ ├── stack.stories.tsx │ │ └── stack.tsx │ ├── text-area │ │ ├── index.ts │ │ ├── text-area.stories.tsx │ │ └── text-area.tsx │ ├── text │ │ ├── index.ts │ │ ├── text.stories.tsx │ │ └── text.tsx │ └── typography-functions.ts ├── custom.d.ts ├── imgs │ ├── back-to-files.png │ ├── community-duplicate.png │ ├── component-hierarchy.svg │ ├── design-systems_16_9.png │ ├── dg-error.svg │ ├── dg-field.svg │ ├── dg-hint.svg │ ├── dg-input.svg │ ├── dg-label.svg │ ├── drafts.png │ ├── duplicate-to-your-drafts.png │ ├── duplicated-file.png │ ├── hearts_triangles.png │ ├── img.png │ ├── radius.png │ ├── rangle_badge.svg │ ├── rangle_badge_black.svg │ ├── rangle_badge_red.svg │ ├── typography-scale.png │ └── user.png ├── index.ts └── theme.ts └── tsconfig.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # JavaScript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2.1 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:lts 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | # - restore_cache: 24 | # keys: 25 | # - yarn-packages-{{ checksum "yarn.lock" }} 26 | 27 | - run: 28 | name: Install Dependencies 29 | command: npm install 30 | 31 | - run: 32 | name: Build Component Library 33 | command: npm run build 34 | 35 | - run: 36 | name: Build Storybook 37 | command: npm run build-storybook 38 | 39 | # - save_cache: 40 | # paths: 41 | # - node_modules 42 | # - packages/app/node_modules 43 | # - packages/ds/node_modules 44 | # key: yarn-packages-{{ checksum "yarn.lock" }} 45 | 46 | # - run: 47 | # name: Lint Component Library 48 | # command: yarn workspace ds lint 49 | 50 | - run: 51 | name: Test Component Library 52 | command: npm run test 53 | 54 | # - run: 55 | # name: Build Demo App 56 | # command: yarn workspace app build 57 | 58 | # - run: 59 | # name: Run Visual Tests 60 | # command: yarn workspace ds run chromatic 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /packages/**/node_modules 3 | /.pnp 4 | .pnp.js 5 | **/*.cache/ 6 | /dist 7 | /storybook-static 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /packages/ds/build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # IDE files 27 | .idea/ 28 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpackFinal: async (config, { configType }) => { 3 | // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' 4 | // You can change the configuration based on that. 5 | // 'PRODUCTION' is used when building the static version of storybook. 6 | 7 | config.resolve.alias['crypto'] = "crypto-browserify"; 8 | config.resolve.alias['stream'] = "stream-browserify"; 9 | 10 | // Return the altered config 11 | return config; 12 | }, 13 | core: { 14 | builder: "webpack5", 15 | }, 16 | stories: ['../src/**/*.stories.@(js|tsx|mdx)'], 17 | addons: [ 18 | '@storybook/addon-links', 19 | '@storybook/addon-docs', 20 | '@storybook/addon-essentials', 21 | '@storybook/addon-controls', 22 | '@storybook/addon-a11y', 23 | '@storybook/addon-viewport/register', 24 | '@storybook/addon-storysource' 25 | ], 26 | typescript: { 27 | check: true, 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import { addDecorator, addParameters } from '@storybook/react'; 2 | import ThemeDecorator from './theme-decorator'; 3 | // import { withKnobs } from '@storybook/addon-knobs'; 4 | // import { withA11y } from '@storybook/addon-a11y'; 5 | 6 | addDecorator(ThemeDecorator); 7 | // addDecorator(withKnobs); 8 | // addDecorator(withA11y); 9 | 10 | addParameters({ 11 | options: { 12 | showRoots: true, 13 | }, 14 | }); 15 | 16 | 17 | addParameters({ 18 | viewport: { 19 | viewports: { 20 | small: { 21 | name: 'Small (40em)', 22 | styles: { 23 | width: 40 * 16 + 'px', 24 | height: '100%', 25 | }, 26 | }, 27 | medium: { 28 | name: 'Medium (56em)', 29 | styles: { 30 | width: 56 * 16 + 'px', 31 | height: '100%', 32 | }, 33 | }, 34 | large: { 35 | name: 'Large (64em)', 36 | styles: { 37 | width: 64 * 16 + 'px', 38 | height: '100%', 39 | }, 40 | }, 41 | responsive: { 42 | name: 'Responsive', 43 | styles: { 44 | width: '100%', 45 | height: '100%', 46 | }, 47 | }, 48 | }, 49 | defaultViewport: 'Small Mobile', 50 | }, 51 | }); -------------------------------------------------------------------------------- /.storybook/theme-decorator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from 'styled-components'; 3 | import { theme, Box } from '../src'; 4 | 5 | const ThemeDecorator = (storyFn: any) => ( 6 | <> 7 | 8 | 9 | {storyFn({ id: id => 'light-' + id })} 10 | 11 | 12 | 13 | 14 | {storyFn({ id: id => 'dark-' + id })} 15 | 16 | 17 | 18 | ); 19 | 20 | export default ThemeDecorator; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 rangle.io 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radius Design System Kit 2 | 3 | This is 1 of 3 repos that is part of an ecosystem of open-source tools and libraries that allow you to accelerate your design system! This repository contains several branches that enable the boilerplate creation of design system instances for React. 4 | 5 | [rangle.io/radius](https://rangle.io/radius) 6 | 7 | [Figma File](https://www.figma.com/file/RqENxZWAzGiEWM7COch1Sc/Radius-Design-Kit) 8 | 9 | [Storybook Docs](https://radius-ds.netlify.com) 10 | 11 | [Chromatic](https://www.chromaticqa.com/library?appId=5e44874935df3b0022b9d890) 12 | 13 | See also: 14 | [Radius Angular](https://github.com/rangle/radius-angular) 15 | [Radius Workspace](https://github.com/rangle/radius-workspace) 16 | 17 | ## Contribution 18 | 19 | We are currently working to make Radius for React more flexible with regards to accelerating the creation of design systems for integrating with vanilla css, css modules, emotion and styled-components. 20 | 21 | Respective versions of such design systems can be found in the following branches: 22 | 23 | ``` 24 | basic-css - CSS modules 25 | basic-emotion - the emotion library, has theme 26 | basic-styled - the styled-components library, has theme 27 | ``` 28 | 29 | Please create pull requests against the branches above to contribute. 30 | 31 | ## What's Inside 32 | 33 | ``` 34 | . 35 | ├── README.md 36 | ├── demo/ 37 | ├── dist/ 38 | ├── docs/ 39 | ├── example/ 40 | ├── jest.config.js 41 | ├── package.json 42 | ├── src/ 43 | └── tsconfig.json 44 | ``` 45 | 46 | ### Are you using Radius? 47 | 48 | We would love to hear about how you are using Radius or any feedback or feature requests. Open an [issue](https://github.com/rangle/radius/issues/new). 49 | 50 | ### 🚀 Quick start 51 | 52 | To get started you can just clone the repository. 53 | 54 | --- 55 | 56 | # TSDX React w/ Storybook User Guide 57 | 58 | Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it. 59 | 60 | > This TSDX setup is meant for developing React component libraries (not apps!) that can be published to NPM. If you’re looking to build a React-based app, you should use `create-react-app`, `razzle`, `nextjs`, `gatsby`, or `react-static`. 61 | 62 | > If you’re new to TypeScript and React, checkout [this handy cheatsheet](https://github.com/sw-yx/react-typescript-cheatsheet/) 63 | 64 | ## Commands 65 | 66 | TSDX scaffolds your new library inside `/src`, and also sets up a [Parcel-based](https://parceljs.org) playground for it inside `/example`. 67 | 68 | The recommended workflow is to run TSDX in one terminal: 69 | 70 | ```bash 71 | npm start # or yarn start 72 | ``` 73 | 74 | This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`. 75 | 76 | Then run either Storybook or the example playground: 77 | 78 | ### Storybook 79 | 80 | Run inside another terminal: 81 | 82 | ```bash 83 | yarn storybook 84 | ``` 85 | 86 | This loads the stories from `./stories`. 87 | 88 | > NOTE: Stories should reference the components as if using the library, similar to the example playground. This means importing from the root project directory. This has been aliased in the tsconfig and the storybook webpack config as a helper. 89 | 90 | ### Example 91 | 92 | Then run the example inside another: 93 | 94 | ```bash 95 | cd example 96 | npm i # or yarn to install dependencies 97 | npm start # or yarn start 98 | ``` 99 | 100 | The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**, we use [Parcel's aliasing](https://parceljs.org/module_resolution.html#aliases). 101 | 102 | To do a one-off build, use `npm run build` or `yarn build`. 103 | 104 | To run tests, use `npm test` or `yarn test`. 105 | 106 | ## Configuration 107 | 108 | Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly. 109 | 110 | ### Jest 111 | 112 | Jest tests are set up to run with `npm test` or `yarn test`. 113 | 114 | ### Bundle analysis 115 | 116 | Calculates the real cost of your library using [size-limit](https://github.com/ai/size-limit) with `npm run size` and visulize it with `npm run analyze`. 117 | 118 | #### React Testing Library 119 | 120 | We do not set up `react-testing-library` for you yet, we welcome contributions and documentation on this. 121 | 122 | ### Rollup 123 | 124 | TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details. 125 | 126 | ### TypeScript 127 | 128 | `tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs. 129 | 130 | ## Continuous Integration 131 | 132 | ### GitHub Actions 133 | 134 | Two actions are added by default: 135 | 136 | - `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix 137 | - `size` which comments cost comparison of your library on every pull request using [size-limit](https://github.com/ai/size-limit) 138 | 139 | ## Optimizations 140 | 141 | Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations: 142 | 143 | ```js 144 | // ./types/index.d.ts 145 | declare var __DEV__: boolean; 146 | 147 | // inside your code... 148 | if (__DEV__) { 149 | console.log('foo'); 150 | } 151 | ``` 152 | 153 | You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions. 154 | 155 | ## Module Formats 156 | 157 | CJS, ESModules, and UMD module formats are supported. 158 | 159 | The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found. 160 | 161 | ## Deploying the Example Playground 162 | 163 | The Playground is just a simple [Parcel](https://parceljs.org) app, you can deploy it anywhere you would normally deploy that. Here are some guidelines for **manually** deploying with the Netlify CLI (`npm i -g netlify-cli`): 164 | 165 | ```bash 166 | cd example # if not already in the example folder 167 | npm run build # builds to dist 168 | netlify deploy # deploy the dist folder 169 | ``` 170 | 171 | Alternatively, if you already have a git repo connected, you can set up continuous deployment with Netlify: 172 | 173 | ```bash 174 | netlify init 175 | # build command: yarn build && cd example && yarn && yarn build 176 | # directory to deploy: example/dist 177 | # pick yes for netlify.toml 178 | ``` 179 | 180 | ## Named Exports 181 | 182 | Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library. 183 | 184 | ## Including Styles 185 | 186 | There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like. 187 | 188 | For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader. 189 | 190 | ## Publishing to NPM 191 | 192 | We recommend using [np](https://github.com/sindresorhus/np). 193 | 194 | ## Usage with Lerna 195 | 196 | When creating a new package with TSDX within a project set up with Lerna, you might encounter a `Cannot resolve dependency` error when trying to run the `example` project. To fix that you will need to make changes to the `package.json` file _inside the `example` directory_. 197 | 198 | The problem is that due to the nature of how dependencies are installed in Lerna projects, the aliases in the example project's `package.json` might not point to the right place, as those dependencies might have been installed in the root of your Lerna project. 199 | 200 | Change the `alias` to point to where those packages are actually installed. This depends on the directory structure of your Lerna project, so the actual path might be different from the diff below. 201 | 202 | ```diff 203 | "alias": { 204 | - "react": "../node_modules/react", 205 | - "react-dom": "../node_modules/react-dom" 206 | + "react": "../../../node_modules/react", 207 | + "react-dom": "../../../node_modules/react-dom" 208 | }, 209 | ``` 210 | 211 | An alternative to fixing this problem would be to remove aliases altogether and define the dependencies referenced as aliases as dev dependencies instead. [However, that might cause other problems.](https://github.com/palmerhq/tsdx/issues/64) 212 | -------------------------------------------------------------------------------- /docs/creating-components.md: -------------------------------------------------------------------------------- 1 | # Creating Components 2 | 3 | Components allow you to split the UI into independent and reusable pieces. However, not all components are built the same way. A component might be created using styled-components or a combination of styled-components and Styled System or as a composition of other components. Radius uses the following hierarchy to reason as to what technique to use for creating components. 4 | 5 | 6 | 7 | - **Elements** are basic reusable building blocks of the system. 8 | 9 | - **Patterns** are reusable building blocks that are comprised of other building blocks. 10 | 11 | - **Features** are a set of patterns, elements, & styles that come together to support a specific user task. Sometimes referred to as container components. 12 | 13 | - **Layouts** are how features come together to form a page. 14 | 15 | The design system would generally be limited to Elements and Patterns. Features and Layouts should ideally be created in the application. There are some scenarios in which you might want to include _recipes_ to demonstrate how one might build a certain Feature. 16 | 17 | Note, this is a framework for guiding architectural choices. We _do not_ recommend categorizing components in code. 18 | 19 | ### Forwarding Refs 20 | 21 | > Encapsulation is desirable for application-level components (Features), it can be inconvenient for highly reusable “leaf” components like FancyButton or MyTextInput. These components tend to be used throughout the application in a similar manner as a regular DOM button and input, and accessing their DOM nodes may be unavoidable for managing focus, selection, or animations. 22 | > 23 | > Ref forwarding is an opt-in feature that lets some components take a ref they receive, and pass it further down (in other words, “forward” it) to a child. 24 | > 25 | > — [Forwarding Refs](https://reactjs.org/docs/forwarding-refs.html) 26 | 27 | Each Radius component returns a single HTML element that accepts all HTML props, including className, style and accessibility attributes. 28 | 29 | ### Elements 30 | 31 | An element is a styled-component that uses style functions from Styled System. Styled-components handles ref forwarding here. 32 | 33 | ```js 34 | const textVariants = variant({ 35 | variants: { 36 | big: { 37 | fontSize: 4, 38 | lineHeight: 'heading', 39 | }, 40 | small: { 41 | fontSize: 1, 42 | lineHeight: 'body', 43 | }, 44 | }, 45 | }); 46 | 47 | export const Text = styled.p( 48 | textVariants, 49 | compose(space, color, layout, flexbox, border, position, typography) 50 | ); 51 | ``` 52 | 53 | ### Patterns 54 | 55 | Radius comes with many patterns that you can use in your application either standalone or as part of your feature. In the case you'd like to create a new pattern using Radius' approach, here is a sample breakdown of a Radius pattern 56 | 57 | Patterns are comprised of elements. Therefore, we have to pick which component to `forwardRef` from. 58 | 59 | `StyledComponentProps` is a utility that styled-components uses to create types for a styled component. It merges the typings for style function props and the typings for the HTML element that is being forwarded. 60 | 61 | ```js 62 | import { StyledComponentProps } from 'styled-components'; 63 | 64 | type AspectRatioProps = StyledComponentProps< 65 | 'div', 66 | any, 67 | { 68 | /** The ratio to apply */ 69 | ratio?: number; 70 | } & BoxProps, 71 | never 72 | >; 73 | 74 | export const AspectRatio = forwardRef( 75 | ({ ratio = 16 / 9, children, ...props }, ref) => ( 76 | 77 | 78 | 79 | {children} 80 | 81 | 82 | ) 83 | ); 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/design-graph.md: -------------------------------------------------------------------------------- 1 | # Design Graph 2 | 3 | ##### The Design Graph is a constraint-based system for organizing styles in UI design. It consists of three parts: design tokens, theme and variants. 4 | 5 | ### Design Tokens 6 | 7 | They define the visual characteristics of a brand or a product such as typography, colors, icons, and spacing. They make it easier to maintain a scalable, consistent system for UI development. Radius maintains all design tokens in a `theme.ts` file. 8 | 9 | The theme is a collection of design tokens. You might have multiple versions of the theme. For example, to switch between different brands or perhaps color modes (dark & light). The shape of the theme object conforms to the [System UI Theme Specification](https://system-ui.com/theme/). 10 | 11 | ### Styled System 12 | 13 | We use [Styled System](https://styled-system.com/getting-started) to connect component styles to design tokens. For example: 14 | 15 | ```js 16 | // styled-components is a CSS-in-JS Library 17 | import styled from 'styled-components'; 18 | 19 | // styled-system provides functions that add props to React components 20 | // which allow you to control styles based on design tokens. 21 | import { space, layout, color } from 'styled-system'; 22 | 23 | // The Box component is now connected to Space and Color tokens 24 | export const Box = styled.div(space, color); 25 | ``` 26 | 27 | Now, this component will have style props available to set foreground color, background color, margin and padding values. 28 | 29 | ```js 30 | /** 31 | * Color maps to theme.colors.textEditorial 32 | * background color maps to theme.colors.background[1] 33 | * Padding maps to theme.space[3] 34 | */ 35 | 36 | Tomato 37 | 38 | ``` 39 | 40 | ### Variants 41 | 42 | There are situations where you might want to support slight stylistic variations of components. For example, a button might have primary, secondary and transparent variants. Or typography variants similar to how text styles work in Sketch and Figma. 43 | 44 | [Styled System variants](https://styled-system.com/variants) allow you to apply a set of visual styles to a component using a single prop. These variants also map to your design tokens, defined in `theme.ts`. 45 | ```js 46 | // Design tokens in theme.ts 47 | { 48 | fonts: { 49 | body: '"Helvetica Neue", sans-serif', 50 | heading: '"Roboto", sans-serif', 51 | }, 52 | fontWeights: { 53 | regular: 400, 54 | medium: 500, 55 | bold: 700, 56 | }, 57 | lineHeights: { 58 | solid: 1, 59 | title: 1.25, 60 | copy: 1.5, 61 | }, 62 | fontSizes: [12, 14, 16, 20, 24, 32, 48] 63 | } 64 | ``` 65 | ```js 66 | // Text variants 67 | const textVariants = variant({ 68 | variants:{ 69 | body: { 70 | fontFamily: 'body', 71 | fontWeight: 'regular', 72 | lineHeight: 'copy', 73 | fontSize: 2, 74 | }, 75 | caption: { 76 | fontFamily: 'body', 77 | fontWeight: 'medium', 78 | lineHeight: 'copy', 79 | fontSize: 2, 80 | }, 81 | label: { 82 | fontFamily: 'heading', 83 | fontWeight: 'regular', 84 | lineHeight: 'solid', 85 | fontSize: 1, 86 | }, 87 | } 88 | }) 89 | ``` 90 | 91 | The Text component can now use the `variant` prop to switch between lead, body and label styles. 92 | 93 | ```js 94 | 95 | 96 | 97 | ``` 98 | 99 | ### Example 100 | 101 | 102 | 103 | The Field component is a composed of ``, `