├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── babel-jest.config.js ├── babel.config.js ├── examples └── react │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ ├── App.tsx │ ├── Box.tsx │ ├── Button │ │ ├── Button.css.ts │ │ └── Button.tsx │ ├── Text │ │ ├── Text.css.ts │ │ └── Text.tsx │ ├── atoms.css.ts │ ├── main.tsx │ └── styled.css.ts │ └── vite.config.ts ├── jest.config.js ├── package.json ├── packages ├── core │ ├── README.md │ ├── package.json │ └── src │ │ └── index.tsx └── react │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ ├── index.ts │ ├── styled.ts │ ├── styledRuntime.ts │ └── types.ts │ └── styledRuntime │ └── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tests └── core │ └── core.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | node_modules 4 | dist 5 | README.md 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Victor Tortolero 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 | # 🍰 Dessert Box 2 | 3 | A set of utilities to build UI and components with [vanilla-extract][vanilla-extract]. 4 | 5 | 6 | - [🍰 Dessert Box](#-dessert-box) 7 | - [Installation](#installation) 8 | - [Styled](#styled) 9 | - [Box](#box) 10 | - [Usage](#usage) 11 | - [Escape Hatch](#escape-hatch) 12 | - [Variants](#variants) 13 | - [API](#api) 14 | - [createBox(options: { atoms: AtomsFn, defaultClassName?: string })](#createboxoptions--atoms-atomsfn-defaultclassname-string-) 15 | - [createBoxWithAtomsProp(options: { atoms: AtomsFn, defaultClassName?: string })](#createboxwithatomspropoptions--atoms-atomsfn-defaultclassname-string-) 16 | - [Running the example app](#running-the-example-app) 17 | - [How does it work?](#how-does-it-work) 18 | - [Thanks](#thanks) 19 | - [FAQ](#faq) 20 | 21 | ## Installation 22 | 23 | :warning: If you are not familiar with [vanilla-extract][vanilla-extract], we recommend going trough their docs first and have a working setup before trying any of the dessert box packages. 24 | 25 | ```sh 26 | $ npm install @dessert-box/react 27 | ``` 28 | 29 | ## Styled 30 | 31 | A `styled-components`-like API to create components in your `*.css.ts` files. To use this, you can define your components 32 | 33 | ```ts 34 | // styledComponents.css.ts 35 | import { styled } from '@dessert-box/react'; 36 | 37 | export const StyledHeadline = styled('h1', { 38 | color: 'white', 39 | fontSize: '16px', 40 | fontWeight: 'bold' 41 | }); 42 | ``` 43 | 44 | ```ts 45 | import { StyledHeadline } from './styledComponents.css.ts'; 46 | 47 | function App() { 48 | return ( 49 | I have 0 runtime styles 🤯 50 | ) 51 | } 52 | ``` 53 | 54 | ## Box 55 | 56 | A box component that will make consuming your [sprinkles][sprinkles] from a breeze. It provides a zero-CSS-runtime `` component (similar to the one in [Braid](https://seek-oss.github.io/braid-design-system/components/Box) or [Chakra](https://chakra-ui.com/docs/layout/box)). 57 | 58 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 59 | 60 | It works by consuming `atoms` created with [`vanilla-extract`][vanilla-extract]) and [`sprinkles`][sprinkles]. Shout out to the team at Seek for making these awesome libraries! 61 | 62 | 1. Step 1, create your Box with your `atoms` created with sprinkles: 63 | 64 | ```tsx 65 | // Box.tsx 66 | import { createBox } from '@dessert-box/react'; 67 | import { atoms } from './sprinkles.css'; 68 | 69 | const Box = createBox({ 70 | atoms, 71 | // optional: pass your CSS reset className here 72 | // useful if you want to scope your reset to your Box element 73 | defaultClassName: 'resetClassName', 74 | }); 75 | 76 | export default Box; 77 | ``` 78 | 79 | 2. Step 2, import it enjoy the sweetness: 80 | 81 | ```tsx 82 | // OtherFileOrComponent.tsx 83 | import Box from './Box'; 84 | 85 | const MyComponent = () => { 86 | return What a sweet treat!; 87 | }; 88 | ``` 89 | 90 | **Wondering why using a Box component may be a good idea? or what is a Box component? Check the [FAQ](#FAQ).** 91 | 92 | > Wondering how to use `variants` with this library? Check out the [variants](#variants) section. 93 | 94 | ![dessert-box](https://img.shields.io/bundlephobia/minzip/dessert-box.svg) 95 | 96 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 97 | 98 | ### Usage 99 | 100 | Install the package: 101 | 102 | ``` 103 | $ npm install @dessert-box/react 104 | ``` 105 | 106 | Configure [vanilla-extract](https://github.com/seek-oss/vanilla-extract) and [`sprinkles`](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles) and have your atoms ready: 107 | 108 | ```js 109 | // atoms.css.ts 110 | import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; 111 | 112 | const space = { 113 | none: 0, 114 | small: 4, 115 | medium: 8, 116 | large: 16, 117 | }; 118 | 119 | const colors = { 120 | primary: 'blue', 121 | // ... 122 | }; 123 | 124 | const atomicStyles = defineProperties({ 125 | conditions: { 126 | mobile: {}, 127 | tablet: { '@media': 'screen and (min-width: 768px)' }, 128 | desktop: { '@media': 'screen and (min-width: 1024px)' }, 129 | }, 130 | properties: { 131 | padding: space, 132 | backgroundColor: colors, 133 | // ... 134 | }, 135 | // ... 136 | }); 137 | 138 | export const atoms = createSprinkles(atomicStyles); 139 | ``` 140 | 141 | > Check `sprinkles` [docs](https://github.com/seek-oss/vanilla-extract/tree/3360bdfc9220024e7ffa49b3b198b72743d4e264/packages/sprinkles#setup) for more context into how to create these atoms. 142 | 143 | Now let's create our `` using these atoms: 144 | 145 | ```tsx 146 | // Box.ts 147 | import { createBox } from '@dessert-box/react'; 148 | import { atoms } from './sprinkles.css'; 149 | 150 | const { Box } = createBox({ atoms }); 151 | 152 | export default Box; 153 | ``` 154 | 155 | ```tsx 156 | // otherFile.tsx 157 | import Box from './Box'; 158 | 159 | const App = () => { 160 | return Hello; 161 | }; 162 | ``` 163 | 164 | Notice we can pass every property, shorthand, or condition we can normally pass to our `atomsFn` function. For example, we could leverage the conditions for responsive design we have here: 165 | 166 | ```jsx 167 | 168 | ``` 169 | 170 | If you need to render a tag different than a `div`, you can use the `as` prop: 171 | 172 | ```jsx 173 | 174 | Link to example 175 | 176 | ``` 177 | 178 | ### Escape Hatch 179 | 180 | We have a way of specifying an arbitrary value (non design token) for any of your atoms properties like the following: 181 | 182 | ```jsx 183 | 184 | Link to example 185 | 186 | ``` 187 | 188 | This is useful for those cases where we need to exit our system to accomplish something. 189 | 190 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 191 | 192 | ### Variants 193 | 194 | The official [@vanilla-extract/recipes][recipes] package has an excelent API for dealing with variants, this can be combined with our `Box` component to create [variant-based components](https://ped.ro/blog/variant-driven-components): 195 | 196 | NOTE: (Assuming you already have created your `Box` component following the example above). 197 | 198 | 1. First define your recipe using the `recipe` function: 199 | 200 | ```tsx 201 | // Button.css.ts 202 | import { recipe } from '@vanilla-extract/recipes'; 203 | import { atoms } from '../atoms.css'; 204 | 205 | export const buttonRecipe = recipe({ 206 | variants: { 207 | kind: { 208 | primary: atoms({ background: 'blue50' }), 209 | secondary: atoms({ background: 'yellow' }), 210 | }, 211 | size: { 212 | md: atoms({ fontSize: 'large' }), 213 | lg: atoms({ fontSize: 'extraLarge' }), 214 | }, 215 | }, 216 | }); 217 | 218 | export type ButtonVariants = Parameters[0]; 219 | ``` 220 | 221 | 2. Then use the `recipes` function to create variants and apply them to your `Box`: 222 | 223 | ```tsx 224 | // Button.tsx 225 | import { Box } from './Box'; 226 | import { buttonRecipe, ButtonVariants } from './Button.css'; 227 | 228 | type Props = { 229 | children: React.ReactNode; 230 | } & ButtonVariants; 231 | 232 | export const Button = ({ 233 | children, 234 | size = 'md', 235 | kind = 'secondary', 236 | }: Props) => { 237 | return ( 238 | 239 | {children} 240 | 241 | ); 242 | }; 243 | 244 | export default Button; 245 | ``` 246 | 247 | For more context, refer to [@vanilla-extract/recipe][recipes] or feel free [to open an issue in this project](https://github.com/TheMightyPenguin/dessert-box/issues/new) if the integration is not working as you'd expect! 248 | 249 | ## API 250 | 251 | ### createBox(options: { atoms: AtomsFn, defaultClassName?: string }) 252 | 253 | Creates a `` component that takes atoms at the root level. 254 | 255 | ```jsx 256 | import { createBox } from '@dessert-box/react'; 257 | import { atoms } from './atoms.css'; 258 | 259 | const Box = createBox({ atoms }); 260 | 261 | ; 262 | ``` 263 | 264 | ### createBoxWithAtomsProp(options: { atoms: AtomsFn, defaultClassName?: string }) 265 | 266 | Creates a `` component that takes atoms as a prop called `atoms`. 267 | 268 | ```jsx 269 | import { createBoxWithAtomsProp } from '@dessert-box/react'; 270 | import { atoms } from './atoms.css'; 271 | 272 | const Box = createBoxWithAtomsProp({ atoms }); 273 | 274 | ; 275 | ``` 276 | 277 | ## Running the example app 278 | 279 | Run `npm install` then `npm run build` in the root folder (the one with this README file). 280 | 281 | Then move into the example folder `cd example` and run `npm install` and `npm start`. 282 | 283 | ## How does it work? 284 | 285 | This works by depending on build-time generated CSS by [sprinkles](https://github.com/seek-oss/vanilla-extract/tree/3360bdfc9220024e7ffa49b3b198b72743d4e264/packages/sprinkles), and then using the `atomsFn` function to lookup classNames in runtime. So it does have a runtime footprint, but should be pretty minimal. I'm still experimenting to see if it's possible to remove that, but other approaches may lead to other constraints or similar runtime. 286 | 287 | ## Thanks 288 | 289 | - Thanks to the team at Seek for [vanilla-extract](https://github.com/seek-oss/vanilla-extract) and [`sprinkles`](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles), this would not be possible without these great libs and the technical feats they accomplish. 290 | 291 | ## FAQ 292 | 293 | - What is a Box component? 294 | 295 | > It's a generic element that allows you to prototype fast and takes a variety of styling props (think of exposing a lot of CSS attributes as props on a component). 296 | 297 | - Why should I use a Box component? 298 | 299 | > There are many versions and flavors of a Box component, some are more [flexible](https://chakra-ui.com/docs/layout/box), while others are more [restrictive](https://seek-oss.github.io/braid-design-system/components/Box). The Box in this library falls into the latter category (restrictive), and it's more geared towards being the a lower level API of your Design System (or serving as inspiration for it). 300 | 301 | This Box component is meant to be used as a primitive for consuming design tokens, giving you a nice balance between flexibility and constraints. You can use it as an lower level API to implement your other components (Buttons, Card, Layout components, ...), and also as a prototyping and general usage component: 302 | 303 | - As a prototyping tool, it allows you to use all of your design tokens to generate new designs and evaluate if you need to iterate on your foundations, or to validate if they work for your use cases. 304 | - For general usage you can still have the guarantee that users of your system won't do anything impossible (e.g.: using a value that is not part of the design tokens) but still have a productive experience working on UI. 305 | 306 | [sprinkles]: https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles 307 | [vanilla-extract]: https://github.com/seek-oss/vanilla-extract 308 | [recipes]: https://github.com/seek-oss/vanilla-extract#recipes-api 309 | -------------------------------------------------------------------------------- /babel-jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./babel.config'); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | plugins: [...(baseConfig.plugins ?? []), '@vanilla-extract/babel-plugin'], 6 | }; 7 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@babel/preset-typescript', 4 | '@babel/preset-react', 5 | ['@babel/preset-env', { targets: { node: 12 } }], 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Example 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dessert-box-example-react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "author": "Victor Tortolero ", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build", 10 | "preview": "vite preview" 11 | }, 12 | "keywords": [], 13 | "license": "MIT", 14 | "dependencies": { 15 | "@dessert-box/react": "*", 16 | "@vanilla-extract/css": "^1.11.0", 17 | "@vanilla-extract/recipes": "^0.4.0", 18 | "@vanilla-extract/sprinkles": "^1.6.0", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.0.24", 24 | "@types/react-dom": "^18.0.8", 25 | "@vanilla-extract/vite-plugin": "^3.6.1", 26 | "@vitejs/plugin-react": "^2.2.0", 27 | "vite": "^3.2.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/react/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@dessert-box/react': ^0.4.0 5 | '@types/react': ^18.0.24 6 | '@types/react-dom': ^18.0.8 7 | '@vanilla-extract/css': ^1.9.1 8 | '@vanilla-extract/recipes': ^0.2.5 9 | '@vanilla-extract/sprinkles': ^1.5.0 10 | '@vanilla-extract/vite-plugin': ^3.6.1 11 | '@vitejs/plugin-react': ^2.2.0 12 | react: ^18.2.0 13 | react-dom: ^18.2.0 14 | vite: ^3.2.2 15 | 16 | dependencies: 17 | '@dessert-box/react': 0.4.0_react@18.2.0 18 | '@vanilla-extract/css': 1.9.1 19 | '@vanilla-extract/recipes': 0.2.5_@vanilla-extract+css@1.9.1 20 | '@vanilla-extract/sprinkles': 1.5.0_@vanilla-extract+css@1.9.1 21 | react: 18.2.0 22 | react-dom: 18.2.0_react@18.2.0 23 | 24 | devDependencies: 25 | '@types/react': 18.0.24 26 | '@types/react-dom': 18.0.8 27 | '@vanilla-extract/vite-plugin': 3.6.1_vite@3.2.2 28 | '@vitejs/plugin-react': 2.2.0_vite@3.2.2 29 | vite: 3.2.2 30 | 31 | packages: 32 | 33 | /@ampproject/remapping/2.2.0: 34 | resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} 35 | engines: {node: '>=6.0.0'} 36 | dependencies: 37 | '@jridgewell/gen-mapping': 0.1.1 38 | '@jridgewell/trace-mapping': 0.3.17 39 | dev: true 40 | 41 | /@babel/code-frame/7.18.6: 42 | resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} 43 | engines: {node: '>=6.9.0'} 44 | dependencies: 45 | '@babel/highlight': 7.18.6 46 | dev: true 47 | 48 | /@babel/compat-data/7.20.0: 49 | resolution: {integrity: sha512-Gt9jszFJYq7qzXVK4slhc6NzJXnOVmRECWcVjF/T23rNXD9NtWQ0W3qxdg+p9wWIB+VQw3GYV/U2Ha9bRTfs4w==} 50 | engines: {node: '>=6.9.0'} 51 | dev: true 52 | 53 | /@babel/core/7.19.6: 54 | resolution: {integrity: sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==} 55 | engines: {node: '>=6.9.0'} 56 | dependencies: 57 | '@ampproject/remapping': 2.2.0 58 | '@babel/code-frame': 7.18.6 59 | '@babel/generator': 7.20.0 60 | '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.19.6 61 | '@babel/helper-module-transforms': 7.19.6 62 | '@babel/helpers': 7.20.0 63 | '@babel/parser': 7.20.0 64 | '@babel/template': 7.18.10 65 | '@babel/traverse': 7.20.0 66 | '@babel/types': 7.20.0 67 | convert-source-map: 1.9.0 68 | debug: 4.3.4 69 | gensync: 1.0.0-beta.2 70 | json5: 2.2.1 71 | semver: 6.3.0 72 | transitivePeerDependencies: 73 | - supports-color 74 | dev: true 75 | 76 | /@babel/generator/7.20.0: 77 | resolution: {integrity: sha512-GUPcXxWibClgmYJuIwC2Bc2Lg+8b9VjaJ+HlNdACEVt+Wlr1eoU1OPZjZRm7Hzl0gaTsUZNQfeihvZJhG7oc3w==} 78 | engines: {node: '>=6.9.0'} 79 | dependencies: 80 | '@babel/types': 7.20.0 81 | '@jridgewell/gen-mapping': 0.3.2 82 | jsesc: 2.5.2 83 | dev: true 84 | 85 | /@babel/helper-annotate-as-pure/7.18.6: 86 | resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} 87 | engines: {node: '>=6.9.0'} 88 | dependencies: 89 | '@babel/types': 7.20.0 90 | dev: true 91 | 92 | /@babel/helper-compilation-targets/7.20.0_@babel+core@7.19.6: 93 | resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} 94 | engines: {node: '>=6.9.0'} 95 | peerDependencies: 96 | '@babel/core': ^7.0.0 97 | dependencies: 98 | '@babel/compat-data': 7.20.0 99 | '@babel/core': 7.19.6 100 | '@babel/helper-validator-option': 7.18.6 101 | browserslist: 4.21.4 102 | semver: 6.3.0 103 | dev: true 104 | 105 | /@babel/helper-environment-visitor/7.18.9: 106 | resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} 107 | engines: {node: '>=6.9.0'} 108 | dev: true 109 | 110 | /@babel/helper-function-name/7.19.0: 111 | resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} 112 | engines: {node: '>=6.9.0'} 113 | dependencies: 114 | '@babel/template': 7.18.10 115 | '@babel/types': 7.20.0 116 | dev: true 117 | 118 | /@babel/helper-hoist-variables/7.18.6: 119 | resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} 120 | engines: {node: '>=6.9.0'} 121 | dependencies: 122 | '@babel/types': 7.20.0 123 | dev: true 124 | 125 | /@babel/helper-module-imports/7.18.6: 126 | resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} 127 | engines: {node: '>=6.9.0'} 128 | dependencies: 129 | '@babel/types': 7.20.0 130 | dev: true 131 | 132 | /@babel/helper-module-transforms/7.19.6: 133 | resolution: {integrity: sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==} 134 | engines: {node: '>=6.9.0'} 135 | dependencies: 136 | '@babel/helper-environment-visitor': 7.18.9 137 | '@babel/helper-module-imports': 7.18.6 138 | '@babel/helper-simple-access': 7.19.4 139 | '@babel/helper-split-export-declaration': 7.18.6 140 | '@babel/helper-validator-identifier': 7.19.1 141 | '@babel/template': 7.18.10 142 | '@babel/traverse': 7.20.0 143 | '@babel/types': 7.20.0 144 | transitivePeerDependencies: 145 | - supports-color 146 | dev: true 147 | 148 | /@babel/helper-plugin-utils/7.19.0: 149 | resolution: {integrity: sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==} 150 | engines: {node: '>=6.9.0'} 151 | dev: true 152 | 153 | /@babel/helper-simple-access/7.19.4: 154 | resolution: {integrity: sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==} 155 | engines: {node: '>=6.9.0'} 156 | dependencies: 157 | '@babel/types': 7.20.0 158 | dev: true 159 | 160 | /@babel/helper-split-export-declaration/7.18.6: 161 | resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} 162 | engines: {node: '>=6.9.0'} 163 | dependencies: 164 | '@babel/types': 7.20.0 165 | dev: true 166 | 167 | /@babel/helper-string-parser/7.19.4: 168 | resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} 169 | engines: {node: '>=6.9.0'} 170 | dev: true 171 | 172 | /@babel/helper-validator-identifier/7.19.1: 173 | resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} 174 | engines: {node: '>=6.9.0'} 175 | dev: true 176 | 177 | /@babel/helper-validator-option/7.18.6: 178 | resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} 179 | engines: {node: '>=6.9.0'} 180 | dev: true 181 | 182 | /@babel/helpers/7.20.0: 183 | resolution: {integrity: sha512-aGMjYraN0zosCEthoGLdqot1oRsmxVTQRHadsUPz5QM44Zej2PYRz7XiDE7GqnkZnNtLbOuxqoZw42vkU7+XEQ==} 184 | engines: {node: '>=6.9.0'} 185 | dependencies: 186 | '@babel/template': 7.18.10 187 | '@babel/traverse': 7.20.0 188 | '@babel/types': 7.20.0 189 | transitivePeerDependencies: 190 | - supports-color 191 | dev: true 192 | 193 | /@babel/highlight/7.18.6: 194 | resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} 195 | engines: {node: '>=6.9.0'} 196 | dependencies: 197 | '@babel/helper-validator-identifier': 7.19.1 198 | chalk: 2.4.2 199 | js-tokens: 4.0.0 200 | dev: true 201 | 202 | /@babel/parser/7.20.0: 203 | resolution: {integrity: sha512-G9VgAhEaICnz8iiJeGJQyVl6J2nTjbW0xeisva0PK6XcKsga7BIaqm4ZF8Rg1Wbaqmy6znspNqhPaPkyukujzg==} 204 | engines: {node: '>=6.0.0'} 205 | hasBin: true 206 | dependencies: 207 | '@babel/types': 7.20.0 208 | dev: true 209 | 210 | /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.19.6: 211 | resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} 212 | engines: {node: '>=6.9.0'} 213 | peerDependencies: 214 | '@babel/core': ^7.0.0-0 215 | dependencies: 216 | '@babel/core': 7.19.6 217 | '@babel/helper-plugin-utils': 7.19.0 218 | dev: true 219 | 220 | /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.19.6: 221 | resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} 222 | engines: {node: '>=6.9.0'} 223 | peerDependencies: 224 | '@babel/core': ^7.0.0-0 225 | dependencies: 226 | '@babel/core': 7.19.6 227 | '@babel/helper-plugin-utils': 7.19.0 228 | dev: true 229 | 230 | /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.19.6: 231 | resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} 232 | engines: {node: '>=6.9.0'} 233 | peerDependencies: 234 | '@babel/core': ^7.0.0-0 235 | dependencies: 236 | '@babel/core': 7.19.6 237 | '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.19.6 238 | dev: true 239 | 240 | /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.19.6: 241 | resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} 242 | engines: {node: '>=6.9.0'} 243 | peerDependencies: 244 | '@babel/core': ^7.0.0-0 245 | dependencies: 246 | '@babel/core': 7.19.6 247 | '@babel/helper-plugin-utils': 7.19.0 248 | dev: true 249 | 250 | /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.19.6: 251 | resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} 252 | engines: {node: '>=6.9.0'} 253 | peerDependencies: 254 | '@babel/core': ^7.0.0-0 255 | dependencies: 256 | '@babel/core': 7.19.6 257 | '@babel/helper-plugin-utils': 7.19.0 258 | dev: true 259 | 260 | /@babel/plugin-transform-react-jsx/7.19.0_@babel+core@7.19.6: 261 | resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==} 262 | engines: {node: '>=6.9.0'} 263 | peerDependencies: 264 | '@babel/core': ^7.0.0-0 265 | dependencies: 266 | '@babel/core': 7.19.6 267 | '@babel/helper-annotate-as-pure': 7.18.6 268 | '@babel/helper-module-imports': 7.18.6 269 | '@babel/helper-plugin-utils': 7.19.0 270 | '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.19.6 271 | '@babel/types': 7.20.0 272 | dev: true 273 | 274 | /@babel/runtime/7.20.0: 275 | resolution: {integrity: sha512-NDYdls71fTXoU8TZHfbBWg7DiZfNzClcKui/+kyi6ppD2L1qnWW3VV6CjtaBXSUGGhiTWJ6ereOIkUvenif66Q==} 276 | engines: {node: '>=6.9.0'} 277 | dependencies: 278 | regenerator-runtime: 0.13.10 279 | 280 | /@babel/template/7.18.10: 281 | resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} 282 | engines: {node: '>=6.9.0'} 283 | dependencies: 284 | '@babel/code-frame': 7.18.6 285 | '@babel/parser': 7.20.0 286 | '@babel/types': 7.20.0 287 | dev: true 288 | 289 | /@babel/traverse/7.20.0: 290 | resolution: {integrity: sha512-5+cAXQNARgjRUK0JWu2UBwja4JLSO/rBMPJzpsKb+oBF5xlUuCfljQepS4XypBQoiigL0VQjTZy6WiONtUdScQ==} 291 | engines: {node: '>=6.9.0'} 292 | dependencies: 293 | '@babel/code-frame': 7.18.6 294 | '@babel/generator': 7.20.0 295 | '@babel/helper-environment-visitor': 7.18.9 296 | '@babel/helper-function-name': 7.19.0 297 | '@babel/helper-hoist-variables': 7.18.6 298 | '@babel/helper-split-export-declaration': 7.18.6 299 | '@babel/parser': 7.20.0 300 | '@babel/types': 7.20.0 301 | debug: 4.3.4 302 | globals: 11.12.0 303 | transitivePeerDependencies: 304 | - supports-color 305 | dev: true 306 | 307 | /@babel/types/7.20.0: 308 | resolution: {integrity: sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==} 309 | engines: {node: '>=6.9.0'} 310 | dependencies: 311 | '@babel/helper-string-parser': 7.19.4 312 | '@babel/helper-validator-identifier': 7.19.1 313 | to-fast-properties: 2.0.0 314 | dev: true 315 | 316 | /@dessert-box/core/0.2.0: 317 | resolution: {integrity: sha512-Vqaec6i0cvS1r54kU6CfOQECi6dPWzz6DVVxJABOxqPmVk8fOSy6pIKsK0YyPFGRpZK/FXkJ1YhURUOW1OxOVQ==} 318 | dev: false 319 | 320 | /@dessert-box/react/0.4.0_react@18.2.0: 321 | resolution: {integrity: sha512-r2sqkX4y+fDLtRGGpCitI5ckzLZl3DPMUhKBstf3qoZbfoAVHB0HRHgrfRni3F0qZZgXowR880lL8IDpPpRiGg==} 322 | peerDependencies: 323 | react: '>=16.8.0' 324 | dependencies: 325 | '@dessert-box/core': 0.2.0 326 | react: 18.2.0 327 | dev: false 328 | 329 | /@emotion/hash/0.8.0: 330 | resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} 331 | 332 | /@esbuild/android-arm/0.15.12: 333 | resolution: {integrity: sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA==} 334 | engines: {node: '>=12'} 335 | cpu: [arm] 336 | os: [android] 337 | requiresBuild: true 338 | dev: true 339 | optional: true 340 | 341 | /@esbuild/linux-loong64/0.15.12: 342 | resolution: {integrity: sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw==} 343 | engines: {node: '>=12'} 344 | cpu: [loong64] 345 | os: [linux] 346 | requiresBuild: true 347 | dev: true 348 | optional: true 349 | 350 | /@jridgewell/gen-mapping/0.1.1: 351 | resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} 352 | engines: {node: '>=6.0.0'} 353 | dependencies: 354 | '@jridgewell/set-array': 1.1.2 355 | '@jridgewell/sourcemap-codec': 1.4.14 356 | dev: true 357 | 358 | /@jridgewell/gen-mapping/0.3.2: 359 | resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} 360 | engines: {node: '>=6.0.0'} 361 | dependencies: 362 | '@jridgewell/set-array': 1.1.2 363 | '@jridgewell/sourcemap-codec': 1.4.14 364 | '@jridgewell/trace-mapping': 0.3.17 365 | dev: true 366 | 367 | /@jridgewell/resolve-uri/3.1.0: 368 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 369 | engines: {node: '>=6.0.0'} 370 | dev: true 371 | 372 | /@jridgewell/set-array/1.1.2: 373 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 374 | engines: {node: '>=6.0.0'} 375 | dev: true 376 | 377 | /@jridgewell/sourcemap-codec/1.4.14: 378 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 379 | dev: true 380 | 381 | /@jridgewell/trace-mapping/0.3.17: 382 | resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} 383 | dependencies: 384 | '@jridgewell/resolve-uri': 3.1.0 385 | '@jridgewell/sourcemap-codec': 1.4.14 386 | dev: true 387 | 388 | /@types/prop-types/15.7.5: 389 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} 390 | dev: true 391 | 392 | /@types/react-dom/18.0.8: 393 | resolution: {integrity: sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==} 394 | dependencies: 395 | '@types/react': 18.0.24 396 | dev: true 397 | 398 | /@types/react/18.0.24: 399 | resolution: {integrity: sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==} 400 | dependencies: 401 | '@types/prop-types': 15.7.5 402 | '@types/scheduler': 0.16.2 403 | csstype: 3.1.1 404 | dev: true 405 | 406 | /@types/scheduler/0.16.2: 407 | resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} 408 | dev: true 409 | 410 | /@vanilla-extract/babel-plugin-debug-ids/1.0.0: 411 | resolution: {integrity: sha512-Q2Nh/0FEAENfcphAv+fvcMoKfl3bhPWO/2x3MPviNAhsTsvuvYPuRtLjcXwoe4aJ8MxxI46JLY33j8NBEzpTIg==} 412 | dependencies: 413 | '@babel/core': 7.19.6 414 | transitivePeerDependencies: 415 | - supports-color 416 | dev: true 417 | 418 | /@vanilla-extract/css/1.9.1: 419 | resolution: {integrity: sha512-pu2SFiff5jRhPwvGoj8cM5l/qIyLvigOmy22ss5DGjwV5pJYezRjDLxWumi2luIwioMWvh9EozCjyfH8nq+7fQ==} 420 | dependencies: 421 | '@emotion/hash': 0.8.0 422 | '@vanilla-extract/private': 1.0.3 423 | ahocorasick: 1.0.2 424 | chalk: 4.1.2 425 | css-what: 5.1.0 426 | cssesc: 3.0.0 427 | csstype: 3.1.1 428 | deep-object-diff: 1.1.7 429 | deepmerge: 4.2.2 430 | media-query-parser: 2.0.2 431 | outdent: 0.8.0 432 | 433 | /@vanilla-extract/integration/6.0.0: 434 | resolution: {integrity: sha512-uBz4QAhKYswyhN3LYd4duuN3uq7WT1jKckd0sP2+Y8gL+6UXdhN9QOUvNBfvofkrYYWqS8efH4hnsGyyWW8f+w==} 435 | dependencies: 436 | '@babel/core': 7.19.6 437 | '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.19.6 438 | '@vanilla-extract/babel-plugin-debug-ids': 1.0.0 439 | '@vanilla-extract/css': 1.9.1 440 | esbuild: 0.11.23 441 | eval: 0.1.6 442 | find-up: 5.0.0 443 | javascript-stringify: 2.1.0 444 | lodash: 4.17.21 445 | outdent: 0.8.0 446 | transitivePeerDependencies: 447 | - supports-color 448 | dev: true 449 | 450 | /@vanilla-extract/private/1.0.3: 451 | resolution: {integrity: sha512-17kVyLq3ePTKOkveHxXuIJZtGYs+cSoev7BlP+Lf4916qfDhk/HBjvlYDe8egrea7LNPHKwSZJK/bzZC+Q6AwQ==} 452 | 453 | /@vanilla-extract/recipes/0.2.5_@vanilla-extract+css@1.9.1: 454 | resolution: {integrity: sha512-OWXUUiFJdswD3+Xg8f8avuw/vAHZRFS4oHqFeoV1TcO8cfbDQ0zmkreBHvyspoJU+qsyWK48yPHKSptqNRPy9Q==} 455 | peerDependencies: 456 | '@vanilla-extract/css': ^1.0.0 457 | dependencies: 458 | '@vanilla-extract/css': 1.9.1 459 | dev: false 460 | 461 | /@vanilla-extract/sprinkles/1.5.0_@vanilla-extract+css@1.9.1: 462 | resolution: {integrity: sha512-W58f2Rzz5lLmk0jbhgStVlZl5wEiPB1Ur3fRvUaBM+MrifZ3qskmFq/CiH//fEYeG5Dh9vF1qRviMMH46cX9Nw==} 463 | peerDependencies: 464 | '@vanilla-extract/css': ^1.0.0 465 | dependencies: 466 | '@vanilla-extract/css': 1.9.1 467 | dev: false 468 | 469 | /@vanilla-extract/vite-plugin/3.6.1_vite@3.2.2: 470 | resolution: {integrity: sha512-Pn3LhndH0NYPLdJejoExS1KdoFE2ZhtGnc2aH3EJDXKqobmDucdtmNZiIJ2NtI/1/cJ9ZnHuzXxpTE6V9yy8Cg==} 471 | peerDependencies: 472 | vite: ^2.2.3 || ^3.0.0 473 | dependencies: 474 | '@vanilla-extract/integration': 6.0.0 475 | outdent: 0.8.0 476 | postcss: 8.4.18 477 | postcss-load-config: 3.1.4_postcss@8.4.18 478 | vite: 3.2.2 479 | transitivePeerDependencies: 480 | - supports-color 481 | - ts-node 482 | dev: true 483 | 484 | /@vitejs/plugin-react/2.2.0_vite@3.2.2: 485 | resolution: {integrity: sha512-FFpefhvExd1toVRlokZgxgy2JtnBOdp4ZDsq7ldCWaqGSGn9UhWMAVm/1lxPL14JfNS5yGz+s9yFrQY6shoStA==} 486 | engines: {node: ^14.18.0 || >=16.0.0} 487 | peerDependencies: 488 | vite: ^3.0.0 489 | dependencies: 490 | '@babel/core': 7.19.6 491 | '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.19.6 492 | '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.19.6 493 | '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.19.6 494 | '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.19.6 495 | magic-string: 0.26.7 496 | react-refresh: 0.14.0 497 | vite: 3.2.2 498 | transitivePeerDependencies: 499 | - supports-color 500 | dev: true 501 | 502 | /ahocorasick/1.0.2: 503 | resolution: {integrity: sha512-hCOfMzbFx5IDutmWLAt6MZwOUjIfSM9G9FyVxytmE4Rs/5YDPWQrD/+IR1w+FweD9H2oOZEnv36TmkjhNURBVA==} 504 | 505 | /ansi-styles/3.2.1: 506 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 507 | engines: {node: '>=4'} 508 | dependencies: 509 | color-convert: 1.9.3 510 | dev: true 511 | 512 | /ansi-styles/4.3.0: 513 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 514 | engines: {node: '>=8'} 515 | dependencies: 516 | color-convert: 2.0.1 517 | 518 | /browserslist/4.21.4: 519 | resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} 520 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 521 | hasBin: true 522 | dependencies: 523 | caniuse-lite: 1.0.30001427 524 | electron-to-chromium: 1.4.284 525 | node-releases: 2.0.6 526 | update-browserslist-db: 1.0.10_browserslist@4.21.4 527 | dev: true 528 | 529 | /caniuse-lite/1.0.30001427: 530 | resolution: {integrity: sha512-lfXQ73oB9c8DP5Suxaszm+Ta2sr/4tf8+381GkIm1MLj/YdLf+rEDyDSRCzeltuyTVGm+/s18gdZ0q+Wmp8VsQ==} 531 | dev: true 532 | 533 | /chalk/2.4.2: 534 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 535 | engines: {node: '>=4'} 536 | dependencies: 537 | ansi-styles: 3.2.1 538 | escape-string-regexp: 1.0.5 539 | supports-color: 5.5.0 540 | dev: true 541 | 542 | /chalk/4.1.2: 543 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 544 | engines: {node: '>=10'} 545 | dependencies: 546 | ansi-styles: 4.3.0 547 | supports-color: 7.2.0 548 | 549 | /color-convert/1.9.3: 550 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 551 | dependencies: 552 | color-name: 1.1.3 553 | dev: true 554 | 555 | /color-convert/2.0.1: 556 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 557 | engines: {node: '>=7.0.0'} 558 | dependencies: 559 | color-name: 1.1.4 560 | 561 | /color-name/1.1.3: 562 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 563 | dev: true 564 | 565 | /color-name/1.1.4: 566 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 567 | 568 | /convert-source-map/1.9.0: 569 | resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} 570 | dev: true 571 | 572 | /css-what/5.1.0: 573 | resolution: {integrity: sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==} 574 | engines: {node: '>= 6'} 575 | 576 | /cssesc/3.0.0: 577 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 578 | engines: {node: '>=4'} 579 | hasBin: true 580 | 581 | /csstype/3.1.1: 582 | resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} 583 | 584 | /debug/4.3.4: 585 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 586 | engines: {node: '>=6.0'} 587 | peerDependencies: 588 | supports-color: '*' 589 | peerDependenciesMeta: 590 | supports-color: 591 | optional: true 592 | dependencies: 593 | ms: 2.1.2 594 | dev: true 595 | 596 | /deep-object-diff/1.1.7: 597 | resolution: {integrity: sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==} 598 | 599 | /deepmerge/4.2.2: 600 | resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} 601 | engines: {node: '>=0.10.0'} 602 | 603 | /electron-to-chromium/1.4.284: 604 | resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} 605 | dev: true 606 | 607 | /esbuild-android-64/0.15.12: 608 | resolution: {integrity: sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q==} 609 | engines: {node: '>=12'} 610 | cpu: [x64] 611 | os: [android] 612 | requiresBuild: true 613 | dev: true 614 | optional: true 615 | 616 | /esbuild-android-arm64/0.15.12: 617 | resolution: {integrity: sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA==} 618 | engines: {node: '>=12'} 619 | cpu: [arm64] 620 | os: [android] 621 | requiresBuild: true 622 | dev: true 623 | optional: true 624 | 625 | /esbuild-darwin-64/0.15.12: 626 | resolution: {integrity: sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q==} 627 | engines: {node: '>=12'} 628 | cpu: [x64] 629 | os: [darwin] 630 | requiresBuild: true 631 | dev: true 632 | optional: true 633 | 634 | /esbuild-darwin-arm64/0.15.12: 635 | resolution: {integrity: sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw==} 636 | engines: {node: '>=12'} 637 | cpu: [arm64] 638 | os: [darwin] 639 | requiresBuild: true 640 | dev: true 641 | optional: true 642 | 643 | /esbuild-freebsd-64/0.15.12: 644 | resolution: {integrity: sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw==} 645 | engines: {node: '>=12'} 646 | cpu: [x64] 647 | os: [freebsd] 648 | requiresBuild: true 649 | dev: true 650 | optional: true 651 | 652 | /esbuild-freebsd-arm64/0.15.12: 653 | resolution: {integrity: sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g==} 654 | engines: {node: '>=12'} 655 | cpu: [arm64] 656 | os: [freebsd] 657 | requiresBuild: true 658 | dev: true 659 | optional: true 660 | 661 | /esbuild-linux-32/0.15.12: 662 | resolution: {integrity: sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA==} 663 | engines: {node: '>=12'} 664 | cpu: [ia32] 665 | os: [linux] 666 | requiresBuild: true 667 | dev: true 668 | optional: true 669 | 670 | /esbuild-linux-64/0.15.12: 671 | resolution: {integrity: sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA==} 672 | engines: {node: '>=12'} 673 | cpu: [x64] 674 | os: [linux] 675 | requiresBuild: true 676 | dev: true 677 | optional: true 678 | 679 | /esbuild-linux-arm/0.15.12: 680 | resolution: {integrity: sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A==} 681 | engines: {node: '>=12'} 682 | cpu: [arm] 683 | os: [linux] 684 | requiresBuild: true 685 | dev: true 686 | optional: true 687 | 688 | /esbuild-linux-arm64/0.15.12: 689 | resolution: {integrity: sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ==} 690 | engines: {node: '>=12'} 691 | cpu: [arm64] 692 | os: [linux] 693 | requiresBuild: true 694 | dev: true 695 | optional: true 696 | 697 | /esbuild-linux-mips64le/0.15.12: 698 | resolution: {integrity: sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A==} 699 | engines: {node: '>=12'} 700 | cpu: [mips64el] 701 | os: [linux] 702 | requiresBuild: true 703 | dev: true 704 | optional: true 705 | 706 | /esbuild-linux-ppc64le/0.15.12: 707 | resolution: {integrity: sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg==} 708 | engines: {node: '>=12'} 709 | cpu: [ppc64] 710 | os: [linux] 711 | requiresBuild: true 712 | dev: true 713 | optional: true 714 | 715 | /esbuild-linux-riscv64/0.15.12: 716 | resolution: {integrity: sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA==} 717 | engines: {node: '>=12'} 718 | cpu: [riscv64] 719 | os: [linux] 720 | requiresBuild: true 721 | dev: true 722 | optional: true 723 | 724 | /esbuild-linux-s390x/0.15.12: 725 | resolution: {integrity: sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww==} 726 | engines: {node: '>=12'} 727 | cpu: [s390x] 728 | os: [linux] 729 | requiresBuild: true 730 | dev: true 731 | optional: true 732 | 733 | /esbuild-netbsd-64/0.15.12: 734 | resolution: {integrity: sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w==} 735 | engines: {node: '>=12'} 736 | cpu: [x64] 737 | os: [netbsd] 738 | requiresBuild: true 739 | dev: true 740 | optional: true 741 | 742 | /esbuild-openbsd-64/0.15.12: 743 | resolution: {integrity: sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw==} 744 | engines: {node: '>=12'} 745 | cpu: [x64] 746 | os: [openbsd] 747 | requiresBuild: true 748 | dev: true 749 | optional: true 750 | 751 | /esbuild-sunos-64/0.15.12: 752 | resolution: {integrity: sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg==} 753 | engines: {node: '>=12'} 754 | cpu: [x64] 755 | os: [sunos] 756 | requiresBuild: true 757 | dev: true 758 | optional: true 759 | 760 | /esbuild-windows-32/0.15.12: 761 | resolution: {integrity: sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw==} 762 | engines: {node: '>=12'} 763 | cpu: [ia32] 764 | os: [win32] 765 | requiresBuild: true 766 | dev: true 767 | optional: true 768 | 769 | /esbuild-windows-64/0.15.12: 770 | resolution: {integrity: sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA==} 771 | engines: {node: '>=12'} 772 | cpu: [x64] 773 | os: [win32] 774 | requiresBuild: true 775 | dev: true 776 | optional: true 777 | 778 | /esbuild-windows-arm64/0.15.12: 779 | resolution: {integrity: sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA==} 780 | engines: {node: '>=12'} 781 | cpu: [arm64] 782 | os: [win32] 783 | requiresBuild: true 784 | dev: true 785 | optional: true 786 | 787 | /esbuild/0.11.23: 788 | resolution: {integrity: sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q==} 789 | hasBin: true 790 | requiresBuild: true 791 | dev: true 792 | 793 | /esbuild/0.15.12: 794 | resolution: {integrity: sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng==} 795 | engines: {node: '>=12'} 796 | hasBin: true 797 | requiresBuild: true 798 | optionalDependencies: 799 | '@esbuild/android-arm': 0.15.12 800 | '@esbuild/linux-loong64': 0.15.12 801 | esbuild-android-64: 0.15.12 802 | esbuild-android-arm64: 0.15.12 803 | esbuild-darwin-64: 0.15.12 804 | esbuild-darwin-arm64: 0.15.12 805 | esbuild-freebsd-64: 0.15.12 806 | esbuild-freebsd-arm64: 0.15.12 807 | esbuild-linux-32: 0.15.12 808 | esbuild-linux-64: 0.15.12 809 | esbuild-linux-arm: 0.15.12 810 | esbuild-linux-arm64: 0.15.12 811 | esbuild-linux-mips64le: 0.15.12 812 | esbuild-linux-ppc64le: 0.15.12 813 | esbuild-linux-riscv64: 0.15.12 814 | esbuild-linux-s390x: 0.15.12 815 | esbuild-netbsd-64: 0.15.12 816 | esbuild-openbsd-64: 0.15.12 817 | esbuild-sunos-64: 0.15.12 818 | esbuild-windows-32: 0.15.12 819 | esbuild-windows-64: 0.15.12 820 | esbuild-windows-arm64: 0.15.12 821 | dev: true 822 | 823 | /escalade/3.1.1: 824 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 825 | engines: {node: '>=6'} 826 | dev: true 827 | 828 | /escape-string-regexp/1.0.5: 829 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 830 | engines: {node: '>=0.8.0'} 831 | dev: true 832 | 833 | /eval/0.1.6: 834 | resolution: {integrity: sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==} 835 | engines: {node: '>= 0.8'} 836 | dependencies: 837 | require-like: 0.1.2 838 | dev: true 839 | 840 | /find-up/5.0.0: 841 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 842 | engines: {node: '>=10'} 843 | dependencies: 844 | locate-path: 6.0.0 845 | path-exists: 4.0.0 846 | dev: true 847 | 848 | /fsevents/2.3.2: 849 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 850 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 851 | os: [darwin] 852 | requiresBuild: true 853 | dev: true 854 | optional: true 855 | 856 | /function-bind/1.1.1: 857 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 858 | dev: true 859 | 860 | /gensync/1.0.0-beta.2: 861 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 862 | engines: {node: '>=6.9.0'} 863 | dev: true 864 | 865 | /globals/11.12.0: 866 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 867 | engines: {node: '>=4'} 868 | dev: true 869 | 870 | /has-flag/3.0.0: 871 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 872 | engines: {node: '>=4'} 873 | dev: true 874 | 875 | /has-flag/4.0.0: 876 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 877 | engines: {node: '>=8'} 878 | 879 | /has/1.0.3: 880 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 881 | engines: {node: '>= 0.4.0'} 882 | dependencies: 883 | function-bind: 1.1.1 884 | dev: true 885 | 886 | /is-core-module/2.11.0: 887 | resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} 888 | dependencies: 889 | has: 1.0.3 890 | dev: true 891 | 892 | /javascript-stringify/2.1.0: 893 | resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} 894 | dev: true 895 | 896 | /js-tokens/4.0.0: 897 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 898 | 899 | /jsesc/2.5.2: 900 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} 901 | engines: {node: '>=4'} 902 | hasBin: true 903 | dev: true 904 | 905 | /json5/2.2.1: 906 | resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} 907 | engines: {node: '>=6'} 908 | hasBin: true 909 | dev: true 910 | 911 | /lilconfig/2.0.6: 912 | resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} 913 | engines: {node: '>=10'} 914 | dev: true 915 | 916 | /locate-path/6.0.0: 917 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 918 | engines: {node: '>=10'} 919 | dependencies: 920 | p-locate: 5.0.0 921 | dev: true 922 | 923 | /lodash/4.17.21: 924 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 925 | dev: true 926 | 927 | /loose-envify/1.4.0: 928 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 929 | hasBin: true 930 | dependencies: 931 | js-tokens: 4.0.0 932 | dev: false 933 | 934 | /magic-string/0.26.7: 935 | resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} 936 | engines: {node: '>=12'} 937 | dependencies: 938 | sourcemap-codec: 1.4.8 939 | dev: true 940 | 941 | /media-query-parser/2.0.2: 942 | resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} 943 | dependencies: 944 | '@babel/runtime': 7.20.0 945 | 946 | /ms/2.1.2: 947 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 948 | dev: true 949 | 950 | /nanoid/3.3.4: 951 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 952 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 953 | hasBin: true 954 | dev: true 955 | 956 | /node-releases/2.0.6: 957 | resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} 958 | dev: true 959 | 960 | /outdent/0.8.0: 961 | resolution: {integrity: sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==} 962 | 963 | /p-limit/3.1.0: 964 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 965 | engines: {node: '>=10'} 966 | dependencies: 967 | yocto-queue: 0.1.0 968 | dev: true 969 | 970 | /p-locate/5.0.0: 971 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 972 | engines: {node: '>=10'} 973 | dependencies: 974 | p-limit: 3.1.0 975 | dev: true 976 | 977 | /path-exists/4.0.0: 978 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 979 | engines: {node: '>=8'} 980 | dev: true 981 | 982 | /path-parse/1.0.7: 983 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 984 | dev: true 985 | 986 | /picocolors/1.0.0: 987 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 988 | dev: true 989 | 990 | /postcss-load-config/3.1.4_postcss@8.4.18: 991 | resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} 992 | engines: {node: '>= 10'} 993 | peerDependencies: 994 | postcss: '>=8.0.9' 995 | ts-node: '>=9.0.0' 996 | peerDependenciesMeta: 997 | postcss: 998 | optional: true 999 | ts-node: 1000 | optional: true 1001 | dependencies: 1002 | lilconfig: 2.0.6 1003 | postcss: 8.4.18 1004 | yaml: 1.10.2 1005 | dev: true 1006 | 1007 | /postcss/8.4.18: 1008 | resolution: {integrity: sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==} 1009 | engines: {node: ^10 || ^12 || >=14} 1010 | dependencies: 1011 | nanoid: 3.3.4 1012 | picocolors: 1.0.0 1013 | source-map-js: 1.0.2 1014 | dev: true 1015 | 1016 | /react-dom/18.2.0_react@18.2.0: 1017 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} 1018 | peerDependencies: 1019 | react: ^18.2.0 1020 | dependencies: 1021 | loose-envify: 1.4.0 1022 | react: 18.2.0 1023 | scheduler: 0.23.0 1024 | dev: false 1025 | 1026 | /react-refresh/0.14.0: 1027 | resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} 1028 | engines: {node: '>=0.10.0'} 1029 | dev: true 1030 | 1031 | /react/18.2.0: 1032 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 1033 | engines: {node: '>=0.10.0'} 1034 | dependencies: 1035 | loose-envify: 1.4.0 1036 | dev: false 1037 | 1038 | /regenerator-runtime/0.13.10: 1039 | resolution: {integrity: sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==} 1040 | 1041 | /require-like/0.1.2: 1042 | resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} 1043 | dev: true 1044 | 1045 | /resolve/1.22.1: 1046 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} 1047 | hasBin: true 1048 | dependencies: 1049 | is-core-module: 2.11.0 1050 | path-parse: 1.0.7 1051 | supports-preserve-symlinks-flag: 1.0.0 1052 | dev: true 1053 | 1054 | /rollup/2.79.1: 1055 | resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} 1056 | engines: {node: '>=10.0.0'} 1057 | hasBin: true 1058 | optionalDependencies: 1059 | fsevents: 2.3.2 1060 | dev: true 1061 | 1062 | /scheduler/0.23.0: 1063 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} 1064 | dependencies: 1065 | loose-envify: 1.4.0 1066 | dev: false 1067 | 1068 | /semver/6.3.0: 1069 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} 1070 | hasBin: true 1071 | dev: true 1072 | 1073 | /source-map-js/1.0.2: 1074 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 1075 | engines: {node: '>=0.10.0'} 1076 | dev: true 1077 | 1078 | /sourcemap-codec/1.4.8: 1079 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} 1080 | dev: true 1081 | 1082 | /supports-color/5.5.0: 1083 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 1084 | engines: {node: '>=4'} 1085 | dependencies: 1086 | has-flag: 3.0.0 1087 | dev: true 1088 | 1089 | /supports-color/7.2.0: 1090 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1091 | engines: {node: '>=8'} 1092 | dependencies: 1093 | has-flag: 4.0.0 1094 | 1095 | /supports-preserve-symlinks-flag/1.0.0: 1096 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1097 | engines: {node: '>= 0.4'} 1098 | dev: true 1099 | 1100 | /to-fast-properties/2.0.0: 1101 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 1102 | engines: {node: '>=4'} 1103 | dev: true 1104 | 1105 | /update-browserslist-db/1.0.10_browserslist@4.21.4: 1106 | resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} 1107 | hasBin: true 1108 | peerDependencies: 1109 | browserslist: '>= 4.21.0' 1110 | dependencies: 1111 | browserslist: 4.21.4 1112 | escalade: 3.1.1 1113 | picocolors: 1.0.0 1114 | dev: true 1115 | 1116 | /vite/3.2.2: 1117 | resolution: {integrity: sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==} 1118 | engines: {node: ^14.18.0 || >=16.0.0} 1119 | hasBin: true 1120 | peerDependencies: 1121 | less: '*' 1122 | sass: '*' 1123 | stylus: '*' 1124 | sugarss: '*' 1125 | terser: ^5.4.0 1126 | peerDependenciesMeta: 1127 | less: 1128 | optional: true 1129 | sass: 1130 | optional: true 1131 | stylus: 1132 | optional: true 1133 | sugarss: 1134 | optional: true 1135 | terser: 1136 | optional: true 1137 | dependencies: 1138 | esbuild: 0.15.12 1139 | postcss: 8.4.18 1140 | resolve: 1.22.1 1141 | rollup: 2.79.1 1142 | optionalDependencies: 1143 | fsevents: 2.3.2 1144 | dev: true 1145 | 1146 | /yaml/1.10.2: 1147 | resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} 1148 | engines: {node: '>= 6'} 1149 | dev: true 1150 | 1151 | /yocto-queue/0.1.0: 1152 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1153 | engines: {node: '>=10'} 1154 | dev: true 1155 | -------------------------------------------------------------------------------- /examples/react/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react'; 2 | import { createBoxWithAtomsProp } from '@dessert-box/react'; 3 | import { themeClass, atoms } from './atoms.css'; 4 | import Box from './Box'; 5 | import Text from './Text/Text'; 6 | import Button from './Button/Button'; 7 | import { StyledText, StyledLink } from './styled.css'; 8 | 9 | const BoxWithAtomsProp = createBoxWithAtomsProp({ atoms }); 10 | 11 | const AsBoxButton = ( 12 | props: React.ComponentProps & React.ComponentProps, 13 | ) => ; 14 | 15 | export const App = () => { 16 | const ref = useRef(null); 17 | 18 | return ( 19 | 20 | Styled text 21 | Link 22 | 23 | Hello 24 | 25 | 31 | With atoms 32 | 33 | 34 | No props box 35 | header 1 text 36 | header 2 text 37 | paragraph text 38 | 39 | 42 | 43 | 46 | 47 | 53 | With atoms + className 54 | 55 | 56 | Without atoms or className 57 | 58 | 65 | With atoms as a prop 66 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /examples/react/src/Box.tsx: -------------------------------------------------------------------------------- 1 | import { createBox } from '@dessert-box/react'; 2 | import { atoms } from './atoms.css'; 3 | 4 | export const Box = createBox({ atoms }); 5 | 6 | export default Box; 7 | -------------------------------------------------------------------------------- /examples/react/src/Button/Button.css.ts: -------------------------------------------------------------------------------- 1 | import { recipe } from '@vanilla-extract/recipes'; 2 | import { atoms } from '../atoms.css'; 3 | 4 | export const buttonRecipe = recipe({ 5 | variants: { 6 | kind: { 7 | primary: atoms({ background: 'blue50' }), 8 | secondary: atoms({ background: 'yellow' }), 9 | }, 10 | size: { 11 | md: atoms({ fontSize: 'large' }), 12 | lg: atoms({ fontSize: 'extraLarge' }), 13 | }, 14 | }, 15 | }); 16 | 17 | export type ButtonVariants = Parameters[0]; 18 | -------------------------------------------------------------------------------- /examples/react/src/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { buttonRecipe, ButtonVariants } from './Button.css'; 3 | import { Box } from '../Box'; 4 | 5 | type Props = { 6 | children: React.ReactNode; 7 | } & ButtonVariants; 8 | 9 | export const Button = ({ 10 | children, 11 | size = 'md', 12 | kind = 'secondary', 13 | }: Props) => { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | export default Button; 22 | -------------------------------------------------------------------------------- /examples/react/src/Text/Text.css.ts: -------------------------------------------------------------------------------- 1 | import { recipe } from '@vanilla-extract/recipes'; 2 | import { atoms } from '../atoms.css'; 3 | 4 | export const textRecipe = recipe({ 5 | variants: { 6 | kind: { 7 | h1: atoms({ 8 | fontSize: 'extraLarge', 9 | fontWeight: '600', 10 | }), 11 | h2: atoms({ 12 | fontSize: 'large', 13 | fontWeight: '400', 14 | }), 15 | p: atoms({ 16 | fontSize: 'medium', 17 | }), 18 | }, 19 | }, 20 | }); 21 | 22 | export type TextVariants = Parameters[0]; 23 | -------------------------------------------------------------------------------- /examples/react/src/Text/Text.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { textRecipe, TextVariants } from './Text.css'; 3 | import { Box } from '../Box'; 4 | 5 | type Props = { 6 | children: React.ReactNode; 7 | } & TextVariants; 8 | 9 | const Text = ({ kind, children }: Props) => { 10 | return {children}; 11 | }; 12 | 13 | export default Text; 14 | -------------------------------------------------------------------------------- /examples/react/src/atoms.css.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@vanilla-extract/css'; 2 | import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; 3 | 4 | export const [themeClass, vars] = createTheme({ 5 | color: { 6 | blue50: '#eff6ff', 7 | blue100: '#dbeafe', 8 | blue200: '#bfdbfe', 9 | yellow: '#aaff00', 10 | }, 11 | font: { 12 | body: 'arial', 13 | }, 14 | fontSize: { 15 | small: '12px', 16 | medium: '16px', 17 | large: '24px', 18 | extraLarge: '32px', 19 | }, 20 | fontWeight: { 21 | '400': '400', 22 | '600': '600', 23 | }, 24 | space: { 25 | none: '0', 26 | small: '4px', 27 | medium: '8px', 28 | large: '16px', 29 | extraLarge: '32px', 30 | }, 31 | }); 32 | 33 | const layoutStyles = defineProperties({ 34 | conditions: { 35 | mobile: {}, 36 | tablet: { '@media': 'screen and (min-width: 768px)' }, 37 | desktop: { '@media': 'screen and (min-width: 1024px)' }, 38 | }, 39 | defaultCondition: 'mobile', 40 | properties: { 41 | display: ['none', 'block', 'flex'], 42 | flexDirection: ['row', 'column'], 43 | paddingTop: vars.space, 44 | paddingBottom: vars.space, 45 | paddingLeft: vars.space, 46 | paddingRight: vars.space, 47 | margin: vars.space, 48 | width: ['16px', '100%'], 49 | fontSize: vars.fontSize, 50 | fontWeight: vars.fontWeight, 51 | // etc. 52 | }, 53 | shorthands: { 54 | padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'], 55 | paddingX: ['paddingLeft', 'paddingRight'], 56 | paddingY: ['paddingTop', 'paddingBottom'], 57 | }, 58 | }); 59 | 60 | const colorStyles = defineProperties({ 61 | properties: { 62 | color: vars.color, 63 | background: vars.color, 64 | // etc. 65 | }, 66 | }); 67 | 68 | export const atoms = createSprinkles(layoutStyles, colorStyles); 69 | -------------------------------------------------------------------------------- /examples/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | 4 | import { App } from './App'; 5 | 6 | const root = createRoot(document.getElementById('root')!); 7 | root.render( 8 | 9 | 10 | , 11 | ); 12 | -------------------------------------------------------------------------------- /examples/react/src/styled.css.ts: -------------------------------------------------------------------------------- 1 | import { styled } from '@dessert-box/react'; 2 | 3 | export const StyledText = styled('div', { 4 | backgroundColor: 'red', 5 | color: 'white', 6 | }); 7 | 8 | export const StyledLink = styled('a', { 9 | textDecoration: 'none', 10 | }); 11 | -------------------------------------------------------------------------------- /examples/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; 3 | import { defineConfig } from 'vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), vanillaExtractPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '\\.tsx?$': ['babel-jest', { configFile: './babel-jest.config.js' }], 4 | }, 5 | testMatch: ['**/?(*.)+(test).[jt]s?(x)'], 6 | testTimeout: 10000, 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dessert-box", 3 | "version": "0.0.0", 4 | "author": "Victor Tortolero ", 5 | "license": "MIT", 6 | "private": true, 7 | "scripts": { 8 | "dev": "preconstruct dev", 9 | "watch": "preconstruct watch", 10 | "build": "preconstruct build", 11 | "release": "pnpm build && npm publish", 12 | "test": "jest", 13 | "format": "prettier --write .", 14 | "lint": "prettier --check .", 15 | "postinstall": "pnpm dev" 16 | }, 17 | "preconstruct": { 18 | "packages": [ 19 | "packages/*" 20 | ] 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.15.5", 24 | "@babel/preset-env": "^7.15.6", 25 | "@babel/preset-react": "^7.14.5", 26 | "@babel/preset-typescript": "^7.15.0", 27 | "@preconstruct/cli": "^2.1.4", 28 | "@types/jest": "^27.0.1", 29 | "@vanilla-extract/babel-plugin": "^1.1.0", 30 | "babel-jest": "^27.2.1", 31 | "jest": "^27.2.1", 32 | "prettier": "2.4.1", 33 | "tsup": "^6.7.0", 34 | "typescript": "^4.2.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # 🍰 Dessert Box 2 | 3 | A library to easily consume your design tokens from a React component, meant to be used with [vanilla-extract][vanilla-extract]. 4 | 5 | This library will make consuming your [sprinkles][sprinkles] from a react component a breeze. It provides a zero-CSS-runtime `` component (similar to the one in [Braid](https://seek-oss.github.io/braid-design-system/components/Box) or [Chakra](https://chakra-ui.com/docs/layout/box)). 6 | 7 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 8 | 9 | It works by consuming `atoms` created with [`vanilla-extract`][vanilla-extract]) and [`sprinkles`][sprinkles]. Shout out to the team at Seek for making these awesome libraries! 10 | 11 | 1. Step 1, create your Box with your `atoms` created with sprinkles: 12 | 13 | ```tsx 14 | // Box.tsx 15 | import { createBox } from '@dessert-box/react'; 16 | import { atoms } from './sprinkles.css'; 17 | 18 | const { Box } = createBox({ 19 | atoms, 20 | // optional: pass your CSS reset className here 21 | // useful if you want to scope your reset to your Box element 22 | defaultClassName: 'resetClassName', 23 | }); 24 | 25 | export default Box; 26 | ``` 27 | 28 | 2. Step 2, import it enjoy the sweetness: 29 | 30 | ```tsx 31 | // OtherFileOrComponent.tsx 32 | import Box from './Box'; 33 | 34 | const MyComponent = () => { 35 | return What a sweet treat!; 36 | }; 37 | ``` 38 | 39 | **Wondering why using a Box component may be a good idea? or what is a Box component? Check the [FAQ](#FAQ).** 40 | 41 | > Wondering how to use `variants` with this library? Check out the [variants](#variants) section. 42 | 43 | ![dessert-box](https://img.shields.io/bundlephobia/minzip/dessert-box.svg) 44 | 45 | - [🍰 Dessert Box](#-dessert-box) 46 | - [Usage](#usage) 47 | - [Variants](#variants) 48 | - [API](#api) 49 | - [createBox(options: { atoms: AtomsFn, defaultClassName?: string })](#createboxoptions--atoms-atomsfn-defaultclassname-string-) 50 | - [createBoxWithAtomsProp(options: { atoms: AtomsFn, defaultClassName?: string })](#createboxwithatomspropoptions--atoms-atomsfn-defaultclassname-string-) 51 | - [Running the example app](#running-the-example-app) 52 | - [How does it work?](#how-does-it-work) 53 | - [Thanks](#thanks) 54 | - [FAQ](#faq) 55 | 56 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 57 | 58 | ## Usage 59 | 60 | Install the package: 61 | 62 | ``` 63 | $ npm install @dessert-box/react 64 | ``` 65 | 66 | Configure [vanilla-extract](https://github.com/seek-oss/vanilla-extract) and [`sprinkles`](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles) and have your atoms ready: 67 | 68 | ```js 69 | // atoms.css.ts 70 | import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; 71 | 72 | const space = { 73 | none: 0, 74 | small: 4, 75 | medium: 8, 76 | large: 16, 77 | }; 78 | 79 | const colors = { 80 | primary: 'blue', 81 | // ... 82 | }; 83 | 84 | const atomicStyles = defineProperties({ 85 | conditions: { 86 | mobile: {}, 87 | tablet: { '@media': 'screen and (min-width: 768px)' }, 88 | desktop: { '@media': 'screen and (min-width: 1024px)' }, 89 | }, 90 | properties: { 91 | padding: space, 92 | backgroundColor: colors, 93 | // ... 94 | }, 95 | // ... 96 | }); 97 | 98 | export const atoms = createSprinkles(atomicStyles); 99 | ``` 100 | 101 | > Check `sprinkles` [docs](https://github.com/seek-oss/vanilla-extract/tree/3360bdfc9220024e7ffa49b3b198b72743d4e264/packages/sprinkles#setup) for more context into how to create these atoms. 102 | 103 | Now let's create our `` using these atoms: 104 | 105 | ```tsx 106 | // Box.ts 107 | import { createBox } from '@dessert-box/react'; 108 | import { atoms } from './sprinkles.css'; 109 | 110 | const { Box } = createBox({ atoms }); 111 | 112 | export default Box; 113 | ``` 114 | 115 | ```tsx 116 | // otherFile.tsx 117 | import Box from './Box'; 118 | 119 | const App = () => { 120 | return Hello; 121 | }; 122 | ``` 123 | 124 | Notice we can pass every property, shorthand, or condition we can normally pass to our `atomsFn` function. For example, we could leverage the conditions for responsive design we have here: 125 | 126 | ```jsx 127 | 128 | ``` 129 | 130 | If you need to render a tag different than a `div`, you can use the `as` prop: 131 | 132 | ```jsx 133 | 134 | Link to example 135 | 136 | ``` 137 | 138 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 139 | 140 | ### Variants 141 | 142 | The official [@vanilla-extract/recipes][recipes] package has an excelent API for dealing with variants, this can be combined with our `Box` component to create [variant-based components](https://ped.ro/blog/variant-driven-components): 143 | 144 | NOTE: (Assuming you already have created your `Box` component following the example above). 145 | 146 | 1. First define your recipe using the `recipe` function: 147 | 148 | ```tsx 149 | // Button.css.ts 150 | import { recipe } from '@vanilla-extract/recipes'; 151 | import { atoms } from '../atoms.css'; 152 | 153 | export const buttonRecipe = recipe({ 154 | variants: { 155 | kind: { 156 | primary: atoms({ background: 'blue50' }), 157 | secondary: atoms({ background: 'yellow' }), 158 | }, 159 | size: { 160 | md: atoms({ fontSize: 'large' }), 161 | lg: atoms({ fontSize: 'extraLarge' }), 162 | }, 163 | }, 164 | }); 165 | 166 | export type ButtonVariants = Parameters[0]; 167 | ``` 168 | 169 | 2. Then use the `recipes` function to create variants and apply them to your `Box`: 170 | 171 | ```tsx 172 | // Button.tsx 173 | import { Box } from './Box'; 174 | import { buttonRecipe, ButtonVariants } from './Button.css'; 175 | 176 | type Props = { 177 | children: React.ReactNode; 178 | } & ButtonVariants; 179 | 180 | export const Button = ({ 181 | children, 182 | size = 'md', 183 | kind = 'secondary', 184 | }: Props) => { 185 | return ( 186 | 187 | {children} 188 | 189 | ); 190 | }; 191 | 192 | export default Button; 193 | ``` 194 | 195 | For more context, refer to [@vanilla-extract/recipe][recipes] or feel free [to open an issue in this project](https://github.com/TheMightyPenguin/dessert-box/issues/new) if the integration is not working as you'd expect! 196 | 197 | ## API 198 | 199 | ### createBox(options: { atoms: AtomsFn, defaultClassName?: string }) 200 | 201 | Creates a `` component that takes atoms at the root level. 202 | 203 | ```jsx 204 | import { createBox } from '@dessert-box/react'; 205 | import { atoms } from './atoms.css'; 206 | 207 | const Box = createBox({ atoms }); 208 | 209 | ; 210 | ``` 211 | 212 | ### createBoxWithAtomsProp(options: { atoms: AtomsFn, defaultClassName?: string }) 213 | 214 | Creates a `` component that takes atoms as a prop called `atoms`. 215 | 216 | ```jsx 217 | import { createBoxWithAtomsProp } from '@dessert-box/react'; 218 | import { atoms } from './atoms.css'; 219 | 220 | const Box = createBoxWithAtomsProp({ atoms }); 221 | 222 | ; 223 | ``` 224 | 225 | ## Running the example app 226 | 227 | Run `npm install` then `npm run build` in the root folder (the one with this README file). 228 | 229 | Then move into the example folder `cd example` and run `npm install` and `npm start`. 230 | 231 | ## How does it work? 232 | 233 | This works by depending on build-time generated CSS by [sprinkles](https://github.com/seek-oss/vanilla-extract/tree/3360bdfc9220024e7ffa49b3b198b72743d4e264/packages/sprinkles), and then using the `atomsFn` function to lookup classNames in runtime. So it does have a runtime footprint, but should be pretty minimal. I'm still experimenting to see if it's possible to remove that, but other approaches may lead to other constraints or similar runtime. 234 | 235 | ## Thanks 236 | 237 | - Thanks to the team at Seek for [vanilla-extract](https://github.com/seek-oss/vanilla-extract) and [`sprinkles`](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles), this would not be possible without these great libs and the technical feats they accomplish. 238 | 239 | ## FAQ 240 | 241 | - What is a Box component? 242 | 243 | > It's a generic element that allows you to prototype fast and takes a variety of styling props (think of exposing a lot of CSS attributes as props on a component). 244 | 245 | - Why should I use a Box component? 246 | 247 | > There are many versions and flavors of a Box component, some are more [flexible](https://chakra-ui.com/docs/layout/box), while others are more [restrictive](https://seek-oss.github.io/braid-design-system/components/Box). The Box in this library falls into the latter category (restrictive), and it's more geared towards being the a lower level API of your Design System (or serving as inspiration for it). 248 | 249 | This Box component is meant to be used as a primitive for consuming design tokens, giving you a nice balance between flexibility and constraints. You can use it as an lower level API to implement your other components (Buttons, Card, Layout components, ...), and also as a prototyping and general usage component: 250 | 251 | - As a prototyping tool, it allows you to use all of your design tokens to generate new designs and evaluate if you need to iterate on your foundations, or to validate if they work for your use cases. 252 | - For general usage you can still have the guarantee that users of your system won't do anything impossible (e.g.: using a value that is not part of the design tokens) but still have a productive experience working on UI. 253 | 254 | [sprinkles]: https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles 255 | [vanilla-extract]: https://github.com/seek-oss/vanilla-extract 256 | [recipes]: https://github.com/seek-oss/vanilla-extract#recipes-api 257 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dessert-box/core", 3 | "version": "0.2.0", 4 | "description": "", 5 | "author": "Victor Tortolero ", 6 | "main": "dist/dessert-box-core.cjs.js", 7 | "module": "dist/dessert-box-core.esm.js", 8 | "types": "dist/dessert-box-core.cjs.d.ts", 9 | "sideEffects": false, 10 | "license": "MIT", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/TheMightyPenguin/dessert-box.git", 14 | "directory": "packages/core" 15 | }, 16 | "keywords": [] 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/index.tsx: -------------------------------------------------------------------------------- 1 | export interface AtomsFnBase { 2 | (...args: any): string; 3 | properties: Set; 4 | } 5 | 6 | export function composeClassNames(...classNames: Array) { 7 | const classes = classNames 8 | .filter((className) => { 9 | return Boolean(className) && className !== ' '; 10 | }) 11 | .map((className) => { 12 | return className?.toString().trim(); 13 | }) as Array; 14 | return classes.length === 0 ? undefined : classes.join(' '); 15 | } 16 | 17 | export function extractAtomsFromProps( 18 | props: any, 19 | atomsFn: AtomsFn, 20 | ) { 21 | let hasAtomProps = false; 22 | let atomProps: Record = {}; 23 | let otherProps: Record = {}; 24 | let customProps: Record = {}; 25 | 26 | for (const key in props) { 27 | if (key[0] === '_' && key[1] === '_') { 28 | const actualKey = key.substring(2); 29 | customProps[actualKey] = props[key]; 30 | } else if (atomsFn.properties.has(key)) { 31 | hasAtomProps = true; 32 | atomProps[key] = props[key]; 33 | } else { 34 | otherProps[key] = props[key]; 35 | } 36 | } 37 | 38 | return { hasAtomProps, atomProps, otherProps, customProps }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # 🍰 Dessert Box 2 | 3 | A library to easily consume your design tokens from a React component, meant to be used with [vanilla-extract][vanilla-extract]. 4 | 5 | This library will make consuming your [sprinkles][sprinkles] from a react component a breeze. It provides a zero-CSS-runtime `` component (similar to the one in [Braid](https://seek-oss.github.io/braid-design-system/components/Box) or [Chakra](https://chakra-ui.com/docs/layout/box)). 6 | 7 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 8 | 9 | It works by consuming `atoms` created with [`vanilla-extract`][vanilla-extract]) and [`sprinkles`][sprinkles]. Shout out to the team at Seek for making these awesome libraries! 10 | 11 | 1. Step 1, create your Box with your `atoms` created with sprinkles: 12 | 13 | ```tsx 14 | // Box.tsx 15 | import { createBox } from '@dessert-box/react'; 16 | import { atoms } from './sprinkles.css'; 17 | 18 | const { Box } = createBox({ 19 | atoms, 20 | // optional: pass your CSS reset className here 21 | // useful if you want to scope your reset to your Box element 22 | defaultClassName: 'resetClassName', 23 | }); 24 | 25 | export default Box; 26 | ``` 27 | 28 | 2. Step 2, import it enjoy the sweetness: 29 | 30 | ```tsx 31 | // OtherFileOrComponent.tsx 32 | import Box from './Box'; 33 | 34 | const MyComponent = () => { 35 | return What a sweet treat!; 36 | }; 37 | ``` 38 | 39 | **Wondering why using a Box component may be a good idea? or what is a Box component? Check the [FAQ](#FAQ).** 40 | 41 | > Wondering how to use `variants` with this library? Check out the [variants](#variants) section. 42 | 43 | ![dessert-box](https://img.shields.io/bundlephobia/minzip/dessert-box.svg) 44 | 45 | - [🍰 Dessert Box](#-dessert-box) 46 | - [Usage](#usage) 47 | - [Variants](#variants) 48 | - [API](#api) 49 | - [createBox(options: { atoms: AtomsFn, defaultClassName?: string })](#createboxoptions--atoms-atomsfn-defaultclassname-string-) 50 | - [createBoxWithAtomsProp(options: { atoms: AtomsFn, defaultClassName?: string })](#createboxwithatomspropoptions--atoms-atomsfn-defaultclassname-string-) 51 | - [Running the example app](#running-the-example-app) 52 | - [How does it work?](#how-does-it-work) 53 | - [Thanks](#thanks) 54 | - [FAQ](#faq) 55 | 56 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 57 | 58 | ## Usage 59 | 60 | Install the package: 61 | 62 | ``` 63 | $ npm install @dessert-box/react 64 | ``` 65 | 66 | Configure [vanilla-extract](https://github.com/seek-oss/vanilla-extract) and [`sprinkles`](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles) and have your atoms ready: 67 | 68 | ```js 69 | // atoms.css.ts 70 | import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; 71 | 72 | const space = { 73 | none: 0, 74 | small: 4, 75 | medium: 8, 76 | large: 16, 77 | }; 78 | 79 | const colors = { 80 | primary: 'blue', 81 | // ... 82 | }; 83 | 84 | const atomicStyles = defineProperties({ 85 | conditions: { 86 | mobile: {}, 87 | tablet: { '@media': 'screen and (min-width: 768px)' }, 88 | desktop: { '@media': 'screen and (min-width: 1024px)' }, 89 | }, 90 | properties: { 91 | padding: space, 92 | backgroundColor: colors, 93 | // ... 94 | }, 95 | // ... 96 | }); 97 | 98 | export const atoms = createSprinkles(atomicStyles); 99 | ``` 100 | 101 | > Check `sprinkles` [docs](https://github.com/seek-oss/vanilla-extract/tree/3360bdfc9220024e7ffa49b3b198b72743d4e264/packages/sprinkles#setup) for more context into how to create these atoms. 102 | 103 | Now let's create our `` using these atoms: 104 | 105 | ```tsx 106 | // Box.ts 107 | import { createBox } from '@dessert-box/react'; 108 | import { atoms } from './sprinkles.css'; 109 | 110 | const { Box } = createBox({ atoms }); 111 | 112 | export default Box; 113 | ``` 114 | 115 | ```tsx 116 | // otherFile.tsx 117 | import Box from './Box'; 118 | 119 | const App = () => { 120 | return Hello; 121 | }; 122 | ``` 123 | 124 | Notice we can pass every property, shorthand, or condition we can normally pass to our `atomsFn` function. For example, we could leverage the conditions for responsive design we have here: 125 | 126 | ```jsx 127 | 128 | ``` 129 | 130 | If you need to render a tag different than a `div`, you can use the `as` prop: 131 | 132 | ```jsx 133 | 134 | Link to example 135 | 136 | ``` 137 | 138 | [Try it on CodeSandbox!](https://codesandbox.io/s/dessert-box-demo-wxgy8?file=/src/App.tsx) 139 | 140 | ### Variants 141 | 142 | The official [@vanilla-extract/recipes][recipes] package has an excelent API for dealing with variants, this can be combined with our `Box` component to create [variant-based components](https://ped.ro/blog/variant-driven-components): 143 | 144 | NOTE: (Assuming you already have created your `Box` component following the example above). 145 | 146 | 1. First define your recipe using the `recipe` function: 147 | 148 | ```tsx 149 | // Button.css.ts 150 | import { recipe } from '@vanilla-extract/recipes'; 151 | import { atoms } from '../atoms.css'; 152 | 153 | export const buttonRecipe = recipe({ 154 | variants: { 155 | kind: { 156 | primary: atoms({ background: 'blue50' }), 157 | secondary: atoms({ background: 'yellow' }), 158 | }, 159 | size: { 160 | md: atoms({ fontSize: 'large' }), 161 | lg: atoms({ fontSize: 'extraLarge' }), 162 | }, 163 | }, 164 | }); 165 | 166 | export type ButtonVariants = Parameters[0]; 167 | ``` 168 | 169 | 2. Then use the `recipes` function to create variants and apply them to your `Box`: 170 | 171 | ```tsx 172 | // Button.tsx 173 | import { Box } from './Box'; 174 | import { buttonRecipe, ButtonVariants } from './Button.css'; 175 | 176 | type Props = { 177 | children: React.ReactNode; 178 | } & ButtonVariants; 179 | 180 | export const Button = ({ 181 | children, 182 | size = 'md', 183 | kind = 'secondary', 184 | }: Props) => { 185 | return ( 186 | 187 | {children} 188 | 189 | ); 190 | }; 191 | 192 | export default Button; 193 | ``` 194 | 195 | For more context, refer to [@vanilla-extract/recipe][recipes] or feel free [to open an issue in this project](https://github.com/TheMightyPenguin/dessert-box/issues/new) if the integration is not working as you'd expect! 196 | 197 | ## API 198 | 199 | ### createBox(options: { atoms: AtomsFn, defaultClassName?: string }) 200 | 201 | Creates a `` component that takes atoms at the root level. 202 | 203 | ```jsx 204 | import { createBox } from '@dessert-box/react'; 205 | import { atoms } from './atoms.css'; 206 | 207 | const Box = createBox({ atoms }); 208 | 209 | ; 210 | ``` 211 | 212 | ### createBoxWithAtomsProp(options: { atoms: AtomsFn, defaultClassName?: string }) 213 | 214 | Creates a `` component that takes atoms as a prop called `atoms`. 215 | 216 | ```jsx 217 | import { createBoxWithAtomsProp } from '@dessert-box/react'; 218 | import { atoms } from './atoms.css'; 219 | 220 | const Box = createBoxWithAtomsProp({ atoms }); 221 | 222 | ; 223 | ``` 224 | 225 | ## Running the example app 226 | 227 | Run `npm install` then `npm run build` in the root folder (the one with this README file). 228 | 229 | Then move into the example folder `cd example` and run `npm install` and `npm start`. 230 | 231 | ## How does it work? 232 | 233 | This works by depending on build-time generated CSS by [sprinkles](https://github.com/seek-oss/vanilla-extract/tree/3360bdfc9220024e7ffa49b3b198b72743d4e264/packages/sprinkles), and then using the `atomsFn` function to lookup classNames in runtime. So it does have a runtime footprint, but should be pretty minimal. I'm still experimenting to see if it's possible to remove that, but other approaches may lead to other constraints or similar runtime. 234 | 235 | ## Thanks 236 | 237 | - Thanks to the team at Seek for [vanilla-extract](https://github.com/seek-oss/vanilla-extract) and [`sprinkles`](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles), this would not be possible without these great libs and the technical feats they accomplish. 238 | 239 | ## FAQ 240 | 241 | - What is a Box component? 242 | 243 | > It's a generic element that allows you to prototype fast and takes a variety of styling props (think of exposing a lot of CSS attributes as props on a component). 244 | 245 | - Why should I use a Box component? 246 | 247 | > There are many versions and flavors of a Box component, some are more [flexible](https://chakra-ui.com/docs/layout/box), while others are more [restrictive](https://seek-oss.github.io/braid-design-system/components/Box). The Box in this library falls into the latter category (restrictive), and it's more geared towards being the a lower level API of your Design System (or serving as inspiration for it). 248 | 249 | This Box component is meant to be used as a primitive for consuming design tokens, giving you a nice balance between flexibility and constraints. You can use it as an lower level API to implement your other components (Buttons, Card, Layout components, ...), and also as a prototyping and general usage component: 250 | 251 | - As a prototyping tool, it allows you to use all of your design tokens to generate new designs and evaluate if you need to iterate on your foundations, or to validate if they work for your use cases. 252 | - For general usage you can still have the guarantee that users of your system won't do anything impossible (e.g.: using a value that is not part of the design tokens) but still have a productive experience working on UI. 253 | 254 | [sprinkles]: https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles 255 | [vanilla-extract]: https://github.com/seek-oss/vanilla-extract 256 | [recipes]: https://github.com/seek-oss/vanilla-extract#recipes-api 257 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dessert-box/react", 3 | "version": "0.7.5", 4 | "description": "", 5 | "author": "Victor Tortolero ", 6 | "main": "dist/dessert-box-react.cjs.js", 7 | "module": "dist/dessert-box-react.esm.js", 8 | "types": "dist/dessert-box-react.cjs.d.ts", 9 | "exports": { 10 | "./package.json": "./package.json", 11 | ".": { 12 | "module": "./dist/dessert-box-react.esm.js", 13 | "default": "./dist/dessert-box-react.cjs.js" 14 | }, 15 | "./styledRuntime": { 16 | "module": "./styledRuntime/dist/dessert-box-react-styledRuntime.esm.js", 17 | "default": "./styledRuntime/dist/dessert-box-react-styledRuntime.cjs.js" 18 | } 19 | }, 20 | "files": [ 21 | "/dist", 22 | "/styledRuntime" 23 | ], 24 | "preconstruct": { 25 | "entrypoints": [ 26 | "index.ts", 27 | "styledRuntime.ts" 28 | ] 29 | }, 30 | "sideEffects": false, 31 | "license": "MIT", 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/TheMightyPenguin/dessert-box.git", 35 | "directory": "packages/react" 36 | }, 37 | "keywords": [], 38 | "devDependencies": { 39 | "@types/react": "^17.0.4", 40 | "@vanilla-extract/css": "^1.11.0", 41 | "react": ">=16.8.0" 42 | }, 43 | "peerDependencies": { 44 | "@vanilla-extract/css": ">=1.11.0", 45 | "react": ">=16.8.0" 46 | }, 47 | "dependencies": { 48 | "@dessert-box/core": "workspace:0.2.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/react/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@dessert-box/core': ^0.2.0 5 | '@types/react': ^17.0.4 6 | react: '>=16.8.0' 7 | 8 | dependencies: 9 | '@dessert-box/core': 0.2.0 10 | 11 | devDependencies: 12 | '@types/react': 17.0.52 13 | react: 18.2.0 14 | 15 | packages: 16 | 17 | /@dessert-box/core/0.2.0: 18 | resolution: {integrity: sha512-Vqaec6i0cvS1r54kU6CfOQECi6dPWzz6DVVxJABOxqPmVk8fOSy6pIKsK0YyPFGRpZK/FXkJ1YhURUOW1OxOVQ==} 19 | dev: false 20 | 21 | /@types/prop-types/15.7.5: 22 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} 23 | dev: true 24 | 25 | /@types/react/17.0.52: 26 | resolution: {integrity: sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==} 27 | dependencies: 28 | '@types/prop-types': 15.7.5 29 | '@types/scheduler': 0.16.2 30 | csstype: 3.1.1 31 | dev: true 32 | 33 | /@types/scheduler/0.16.2: 34 | resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} 35 | dev: true 36 | 37 | /csstype/3.1.1: 38 | resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} 39 | dev: true 40 | 41 | /js-tokens/4.0.0: 42 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 43 | dev: true 44 | 45 | /loose-envify/1.4.0: 46 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 47 | hasBin: true 48 | dependencies: 49 | js-tokens: 4.0.0 50 | dev: true 51 | 52 | /react/18.2.0: 53 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 54 | engines: {node: '>=0.10.0'} 55 | dependencies: 56 | loose-envify: 1.4.0 57 | dev: true 58 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AtomsFnBase, 3 | composeClassNames, 4 | extractAtomsFromProps, 5 | } from '@dessert-box/core'; 6 | import React, { 7 | createElement, 8 | ForwardedRef, 9 | forwardRef, 10 | ReactElement, 11 | } from 'react'; 12 | import type { CreateBoxParams } from './types'; 13 | 14 | export { styled } from './styled'; 15 | 16 | // adapted from https://github.com/kripod/react-polymorphic-box 17 | type AsProp = { 18 | as?: TType; 19 | }; 20 | type BaseBoxProps = AsProp & 21 | Omit, keyof AsProp>; 22 | 23 | type PolymorphicComponentProps = Props & 24 | BaseBoxProps; 25 | 26 | type PolymorphicComponent< 27 | Props, 28 | DefaultType extends React.ElementType = 'div', 29 | > = ( 30 | props: PolymorphicComponentProps, 31 | ) => React.ReactElement | null; 32 | // 33 | 34 | type OverrideTokens = { 35 | [K in keyof T as K extends string ? `__${K}` : number]: 36 | | Extract 37 | | {}; 38 | }; 39 | 40 | type Tokens = Parameters[0]; 41 | type BoxProps< 42 | AtomsFn extends AtomsFnBase, 43 | TType extends React.ElementType, 44 | > = PolymorphicComponentProps< 45 | TType, 46 | Tokens & OverrideTokens> 47 | >; 48 | 49 | const defaultElement = 'div'; 50 | 51 | export function createBox({ 52 | atoms: atomsFn, 53 | defaultClassName, 54 | }: CreateBoxParams) { 55 | const Box: ( 56 | props: BoxProps, 57 | ) => null | ReactElement> = forwardRef( 58 | ( 59 | { className, style, as, ...props }: BoxProps, 60 | ref: ForwardedRef, TType>>, 61 | ) => { 62 | const Element = as || defaultElement; 63 | const { atomProps, customProps, otherProps } = extractAtomsFromProps( 64 | props, 65 | atomsFn, 66 | ); 67 | 68 | return createElement(Element, { 69 | ref, 70 | style: { ...style, ...customProps }, 71 | ...otherProps, 72 | className: composeClassNames( 73 | className, 74 | atomsFn(atomProps), 75 | defaultClassName, 76 | ), 77 | }); 78 | }, 79 | ); 80 | 81 | (Box as any).displayName = 'DessertBox'; 82 | 83 | return Box; 84 | } 85 | 86 | type BoxWithAtomsProps< 87 | AtomsFn extends AtomsFnBase, 88 | TType extends React.ElementType, 89 | > = PolymorphicComponentProps< 90 | TType, 91 | { atoms?: Tokens & OverrideTokens> } 92 | >; 93 | 94 | export function createBoxWithAtomsProp({ 95 | atoms: atomsFn, 96 | defaultClassName, 97 | }: CreateBoxParams) { 98 | const Box: ( 99 | props: BoxWithAtomsProps, 100 | ) => null | ReactElement = forwardRef( 101 | ( 102 | { 103 | className, 104 | style, 105 | atoms, 106 | as, 107 | ...props 108 | }: BoxWithAtomsProps, 109 | ref: ForwardedRef< 110 | PolymorphicComponent, TType> 111 | >, 112 | ) => { 113 | const Element = as || defaultElement; 114 | 115 | return createElement(Element, { 116 | ref, 117 | ...props, 118 | className: composeClassNames( 119 | className, 120 | atomsFn(atoms), 121 | defaultClassName, 122 | ), 123 | }); 124 | }, 125 | ); 126 | 127 | (Box as any).displayName = 'DessertBox'; 128 | 129 | return Box; 130 | } 131 | -------------------------------------------------------------------------------- /packages/react/src/styled.ts: -------------------------------------------------------------------------------- 1 | import { addFunctionSerializer } from '@vanilla-extract/css/functionSerializer'; 2 | import { ComplexStyleRule, style } from '@vanilla-extract/css'; 3 | import { styledRuntime } from './styledRuntime'; 4 | 5 | export function styled( 6 | el: T, 7 | rules: ComplexStyleRule, 8 | ) { 9 | const className = style(rules); 10 | const args = [el, className] as const; 11 | 12 | const Component = styledRuntime(el, className); 13 | 14 | addFunctionSerializer(Component, { 15 | importPath: '@dessert-box/react/styledRuntime', 16 | importName: 'styledRuntime', 17 | // TODO: Fix this type, was complaining about string not being assignable to Serializable from VE lib 18 | args: args as any, 19 | }); 20 | 21 | return Component; 22 | } 23 | -------------------------------------------------------------------------------- /packages/react/src/styledRuntime.ts: -------------------------------------------------------------------------------- 1 | import { createElement } from 'react'; 2 | 3 | export function styledRuntime( 4 | el: T, 5 | className: string, 6 | ) { 7 | const Component = function Component(props: React.ComponentProps) { 8 | return createElement(el, { 9 | ...props, 10 | className: [props.className, className].filter(Boolean).join(' '), 11 | }); 12 | }; 13 | 14 | Component.displayName = `DBStyled(${el})`; 15 | 16 | return Component; 17 | } 18 | -------------------------------------------------------------------------------- /packages/react/src/types.ts: -------------------------------------------------------------------------------- 1 | export type CreateBoxParams = { 2 | atoms: AtomsFn; 3 | defaultClassName?: string; 4 | }; 5 | -------------------------------------------------------------------------------- /packages/react/styledRuntime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/dessert-box-react-styledRuntime.cjs.js", 3 | "module": "dist/dessert-box-react-styledRuntime.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" 3 | - "examples/*" 4 | -------------------------------------------------------------------------------- /tests/core/core.test.ts: -------------------------------------------------------------------------------- 1 | import { extractAtomsFromProps, composeClassNames } from '@dessert-box/core'; 2 | 3 | describe('@dessert-box/core', () => { 4 | describe('composeClassNames', () => { 5 | it('should not include falsy values', () => { 6 | // @ts-ignore 7 | expect(composeClassNames('hello', false, undefined, '', 0, 'world')).toBe( 8 | 'hello world', 9 | ); 10 | }); 11 | 12 | it('should skip empty strings', () => { 13 | expect(composeClassNames('hello', ' ', ' ', 'world')).toBe('hello world'); 14 | }); 15 | 16 | it('should strip whitespaces', () => { 17 | expect(composeClassNames(' hello ', ' world ')).toBe('hello world'); 18 | }); 19 | }); 20 | 21 | describe('extractAtomsFromProps', () => { 22 | function createMockAtoms() { 23 | const atoms = () => 'mock'; 24 | atoms.properties = new Set(['padding', 'color']); 25 | return atoms; 26 | } 27 | 28 | it('hasAtomProps should be true if it has valid atoms', () => { 29 | const atoms = createMockAtoms(); 30 | const onClick = () => {}; 31 | const { hasAtomProps } = extractAtomsFromProps( 32 | { padding: 'small', onClick }, 33 | atoms, 34 | ); 35 | expect(hasAtomProps).toBeTruthy(); 36 | }); 37 | 38 | it('hasAtomProps should be false if it does not have valid atoms', () => { 39 | const atoms = createMockAtoms(); 40 | const onClick = () => {}; 41 | const { hasAtomProps } = extractAtomsFromProps({ onClick }, atoms); 42 | expect(hasAtomProps).toBeFalsy(); 43 | }); 44 | 45 | it('atomProps should contain atoms', () => { 46 | const atoms = createMockAtoms(); 47 | const onClick = () => {}; 48 | const { atomProps } = extractAtomsFromProps( 49 | { padding: 'small', onClick, color: 'red' }, 50 | atoms, 51 | ); 52 | expect(atomProps).toEqual({ 53 | padding: 'small', 54 | color: 'red', 55 | }); 56 | }); 57 | 58 | it('otherProps should contain the non-atoms props', () => { 59 | const atoms = createMockAtoms(); 60 | const onClick = () => {}; 61 | const { otherProps } = extractAtomsFromProps( 62 | { padding: 'small', onClick, color: 'red' }, 63 | atoms, 64 | ); 65 | expect(otherProps).toEqual({ onClick }); 66 | }); 67 | 68 | it('customProps should contain props prefixed with "__"', () => { 69 | const atoms = createMockAtoms(); 70 | const { customProps } = extractAtomsFromProps( 71 | { padding: 'small', color: 'red', __width: '42px' }, 72 | atoms, 73 | ); 74 | expect(customProps).toEqual({ _width: '42px' }); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": ["DOM", "ESNext"], 6 | "jsx": "preserve", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "isolatedModules": true 12 | }, 13 | "exclude": ["examples"] 14 | } 15 | --------------------------------------------------------------------------------