{"id":16589900,"url":"https://github.com/dburles/mystical","last_synced_at":"2025-04-23T21:26:05.912Z","repository":{"id":44654595,"uuid":"261938948","full_name":"dburles/mystical","owner":"dburles","description":"🌌  A CSS-in-JS library for constraint based design","archived":false,"fork":false,"pushed_at":"2024-10-21T21:23:51.000Z","size":258,"stargazers_count":34,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-22T15:11:38.125Z","etag":null,"topics":["css","css-in-js","css-prop","cssinjs","hooks","react","theming"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dburles.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-07T03:20:40.000Z","updated_at":"2024-10-09T00:45:01.000Z","dependencies_parsed_at":"2024-05-21T04:44:12.778Z","dependency_job_id":"caac63bf-b022-4309-ba21-dc42340e8ee7","html_url":"https://github.com/dburles/mystical","commit_stats":{"total_commits":239,"total_committers":2,"mean_commits":119.5,"dds":0.004184100418409997,"last_synced_commit":"eaa684b44d212244d97c9c858f57c4e54349769a"},"previous_names":[],"tags_count":65,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburles%2Fmystical","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburles%2Fmystical/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburles%2Fmystical/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburles%2Fmystical/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dburles","download_url":"https://codeload.github.com/dburles/mystical/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250516249,"owners_count":21443589,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","css-in-js","css-prop","cssinjs","hooks","react","theming"],"created_at":"2024-10-11T23:10:21.211Z","updated_at":"2025-04-23T21:26:05.887Z","avatar_url":"https://github.com/dburles.png","language":"JavaScript","funding_links":[],"categories":["🤖 AI \u0026 Machine Learning"],"sub_categories":[],"readme":"# 🌌 mystical\n\nBuild themeable, robust and maintainable React component libraries and applications following the [System UI Theme Specification](https://github.com/system-ui/theme-specification).\n\n## Overview\n\n- Mystical is a small runtime CSS-in-JS library, inspired by [theme-ui](https://theme-ui.com/). Built on [Emotion](https://emotion.sh/).\n- A powerful, declarative approach to altering the styles of a component based on its props with the [useModifiers](#usemodifiers) hook. Which also allows for responsive props.\n- Array values for defining media query breakpoint values, e.g. `margin: [0, 3]`.\n\n## Table of Contents\n\n- [Installation](#install)\n- [Guide](#guide)\n  - [Context](#context)\n  - [Example Component](#example-component)\n- [JSX Configuration](#jsx-configuration)\n- [API](#api)\n  - [Theme Object](#theme-object)\n  - [CSS Prop](#css-prop)\n    - [Theme Lookup](#theme-lookup)\n    - [Dot Properties](#dot-properties)\n    - [Shorthand Properties](#shorthand-properties)\n    - [Media Queries](#media-queries)\n  - [MysticalProvider](#mysticalprovider)\n  - [Global](#global)\n  - [keyframes](#keyframes)\n  - [useTheme](#usetheme)\n  - [useMystical](#usemystical)\n  - [useModifiers](#usemodifiers)\n  - [darkColorMode](#darkcolormode)\n- [Contributors](#contributors)\n- [License](#license)\n\n## Install\n\n`npm i mystical`\n\n## Guide\n\n### Context\n\nWrap your app with [MysticalProvider](#mysticalprovider):\n\n```js\nimport MysticalProvider from \"mystical/MysticalProvider.mjs\";\n\n// Optional theme object\nconst theme = {\n  // System UI theme values, See: https://system-ui.com/theme\n};\n\nconst App = () =\u003e {\n  return \u003cMysticalProvider theme={theme}\u003e...\u003c/MysticalProvider\u003e;\n};\n```\n\n### Example Component\n\nThis `Button` component attempts to illustrate some of the important parts of the Mystical API:\n\n1. A [`css` prop](#css-prop) that transforms CSS property values from the theme, (like [theme-ui](https://theme-ui.com/))\n2. The concept of _modifiers_, the combination of a `modifiers` object with a [useModifiers hook](#usemodifiers). This makes prop based variations of components simple and declarative.\n\n```js\nimport useModifiers from \"mystical/useModifiers.mjs\";\n\nconst modifiers = {\n  variant: {\n    primary: {\n      color: \"white\",\n      backgroundColor: \"blue.600\", // These values are picked up off the theme\n      \":hover:not(:disabled)\": {\n        backgroundColor: \"blue.500\",\n      },\n      \":active:not(:disabled)\": {\n        backgroundColor: \"blue.700\",\n      },\n    },\n  },\n  size: {\n    small: {\n      fontSize: 0,\n      lineHeight: \"none\",\n      padding: \"2 3\", // Shorthand 1-4 properties such as padding are also translated to spacing values defined in the theme\n    },\n    medium: {\n      fontSize: 1,\n      lineHeight: \"normal\",\n      padding: \"2 4\",\n    },\n    large: {\n      fontSize: 2,\n      lineHeight: \"normal\",\n      padding: \"3 5\",\n    },\n  },\n  shape: {\n    square: { borderRadius: 1 },\n    rounded: { borderRadius: 2 },\n    pill: { borderRadius: 5 },\n  },\n};\n\nfunction Button({\n  variant = \"primary\",\n  size = \"small\",\n  shape = \"rounded\",\n  modifiers: customModifiers,\n  ...props\n}) {\n  const modifierStyle = useModifiers(\n    { variant, size, shape },\n    modifiers,\n    customModifiers, // optional\n  );\n\n  return (\n    \u003cbutton\n      {...props}\n      css={[\n        // Objects passed within arrays are merged\n        {\n          color: \"white\",\n          fontFamily: \"body\",\n          border: 0,\n          \":disabled\": {\n            opacity: \"disabled\",\n          },\n        },\n        modifierStyle,\n      ]}\n    /\u003e\n  );\n}\n```\n\n### JSX configuration\n\n### Babel\n\nConfigure [@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react) to use the `automatic` runtime and point the `importSource` to mystical.\n\nExample [@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react) configuration:\n\n```js\n{\n  runtime: \"automatic\",\n  importSource: \"mystical\",\n  development: process.env.NODE_ENV === \"development\",\n}\n```\n\n### SWC\n\n```json\n\"transform\": {\n  \"react\": {\n    \"runtime\": \"automatic\",\n    \"importSource\": \"mystical\"\n  }\n}\n```\n\n### Next.js\n\nIn `jsconfig.json` or `tsconfig.json`:\n\n```json\n\"compilerOptions\": {\n  \"jsxImportSource\": \"mystical\"\n}\n```\n\n### Vite\n\nIn Vite config:\n\n```\nesbuild: {\n  jsxImportSource: \"mystical\"\n}\n```\n\n#### Classic\n\nIf you wish to use the `classic` runtime instead, just add the `@jsx` pragma and import the `createElement` function:\n\n```js\n/** @jsx createElement **/\nimport createElement from \"mystical/createElement.mjs\";\n\nfunction MyComponent() {\n  // ...\n}\n```\n\n## API\n\n#### Theme Object\n\nYour theme object should be structured following the convention outlined in the [System UI theme specification](https://system-ui.com/theme) in order for CSS values to be [automatically translated](#theme-lookup).\n\n#### CSS Prop\n\nThis is the primary method of applying styles to components and elements.\n\n```js\n// Example theme:\nconst theme = {\n  colors: {\n    primary: \"#1282A2\",\n  },\n  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],\n  // etc\n};\n\n// `padding` is keyed to the `space` property of the theme and looks up the third index in the array.\n// `backgroundColor` is keyed to the `colors` property of the theme.\nfunction Component() {\n  return \u003cdiv css={{ padding: 3, backgroundColor: \"primary\" }}\u003e...\u003c/div\u003e;\n}\n```\n\n##### Theme Lookup\n\nJust like [theme-ui](https://theme-ui.com/), values passed to CSS properties are automatically translated from the theme based on a [lookup table](https://github.com/dburles/mystical/blob/master/private/themeTokens.js), and will default to the literal value if there's no match.\n\n##### Dot Properties\n\nArrays and Object theme values can be retrieved by using dot properties:\n\n```js\nconst theme = {\n  colors: {\n    red: [\"#fed7d7\", \"#feb2b2\", \"#fc8181\"],\n  },\n};\n\nfunction Component() {\n  return \u003cdiv css={{ backgroundColor: \"red.2\" }}\u003e...\u003c/div\u003e;\n}\n```\n\n##### Shorthand Properties\n\nCSS [Shorthand properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) that relate to edges of a box are also translated from the theme. That is: `margin`, `padding`, `borderWidth`, `borderRadius`, `borderColor` and `borderStyle`.\n\nGiven the following example:\n\n```js\nconst theme = {\n  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],\n};\n\nfunction Component() {\n  return \u003cdiv css={{ margin: \"3 5\" }}\u003e...\u003c/div\u003e;\n}\n```\n\n...the following style is generated: `margin: 16px 64px`\n\n##### Media Queries\n\nInstead of explicitly writing media queries, simply pass an array. Breakpoints can also be skipped, e.g. `['100%', , '25%']`.\n\n```js\nfunction Component() {\n  // Applies width 100% to all viewport widths,\n  // 50% above the first breakpoint,\n  // and 25% above the next breakpoint\n  return \u003cdiv css={{ width: [\"100%\", \"50%\", \"25%\"] }}\u003e...\u003c/div\u003e;\n}\n```\n\n##### Merging Styles\n\nThe css prop also accepts an array of style objects which are deeply merged in order:\n\n```js\nfunction Component() {\n  return (\n    \u003cdiv css={[{ fontSize: 1 }, { fontSize: 2, color: \"white\" }]}\u003e...\u003c/div\u003e\n  );\n}\n```\n\n#### MysticalProvider\n\nProvides the theme context, this is required for Mystical to function.\n\nParameters:\n\n- theme: The theme object.\n- options: Options (optional)\n  - `darkModeOff` = `false`: When enabled, dark mode styles are ignored and not added to the output.\n  - `darkModeForcedBoundary` = `false`: When enabled, Mystical also adds dark mode styles targeting `data-mystical-color-mode=\"dark\"`. This can be useful for development and visual testing environments (such as [Storybook](https://storybook.js.org/)), or for forcing a certain page into dark mode regardless of user system preferences.\n\n```js\nimport MysticalProvider from \"mystical/MysticalProvider.mjs\";\n\n// (Optional, defaults shown).\nconst options = {\n  darkModeOff: false,\n  darkModeForcedBoundary: false,\n};\n\nfunction App() {\n  return (\n    \u003cMysticalProvider options={options} theme={theme}\u003e\n      ...\n    \u003c/MysticalProvider\u003e\n  );\n}\n```\n\n#### Global\n\nGlobal style component that automatically removes its styles when unmounted.\n\n```js\nimport Global from \"mystical/Global.mjs\";\n\nfunction App() {\n  return (\n    \u003cdiv\u003e\n      \u003cGlobal\n        styles={{\n          body: {\n            backgroundColor: \"white\",\n            border: 0,\n          },\n        }}\n      /\u003e\n      ...\n    \u003c/div\u003e\n  );\n}\n```\n\n#### keyframes\n\nInstall [@emotion/react](https://www.npmjs.com/package/@emotion/react) (`npm i @emotion/react`). See https://emotion.sh/docs/keyframes.\n\n```js\nimport { keyframes } from \"@emotion/react\";\n\nconst animationName = keyframes({\n  // ...\n});\n\nfunction Component() {\n  return (\n    \u003cdiv\n      css={{\n        animationName,\n        // etc\n      }}\n    \u003e\n      ...\n    \u003c/div\u003e\n  );\n}\n```\n\n#### useTheme\n\nA simple way to pick out values from the theme similar to using the [`css` prop](#css-prop).\n\n```js\nimport useTheme from \"mystical/useTheme.mjs\";\n\nfunction Component() {\n  const purple = useTheme(\"colors\", \"purple\");\n\n  return \u003cdiv\u003eThe colour purple is {purple}!\u003c/div\u003e;\n}\n```\n\n#### useMystical\n\nProvides access to the complete [theme object](#theme-object).\n\n```js\nimport useMystical from \"mystical/useMystical.mjs\";\n\nfunction Component() {\n  const { theme } = useMystical();\n\n  return JSON.stringify(theme, null, 2);\n}\n```\n\n#### useModifiers\n\nA declarative API for handling prop based variations to component styles.\n\nOptionally an array can be passed as a prop to allow for responsive props:\n\n```js\nfunction Example() {\n  return (\n    \u003cdiv\u003e\n      \u003cButton size={[\"small\", \"medium\"]}\u003eResponsive size Button\u003c/Button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nThis example demonstrates applying modifier styles to a component with multiple elements. See the [`Button` component above](#example-component) for another example.\n\n```js\nimport useModifiers from \"mystical/useModifiers.mjs\";\n\nconst modifiers = {\n  // `default` is a special key for applying and overwriting default styles across each element (experimental).\n  default: {\n    title: { fontFamily: \"heading\" },\n    subtitle: { fontFamily: \"body\" },\n  },\n  size: {\n    small: {\n      title: { fontSize: 3 },\n      subtitle: { fontSize: 0 },\n    },\n    large: {\n      title: { fontSize: 5 },\n      subtitle: { fontSize: 2 },\n    },\n  },\n};\n\nfunction Component({ size = \"small\", modifiers: customModifiers }) {\n  const modifierStyle = useModifiers(\n    { size },\n    modifiers,\n    customModifiers, // Optional secondary modifiers object that will merge with `modifiers`.\n  );\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv css={modifierStyle.title}\u003e{title}\u003c/div\u003e\n      \u003cdiv css={modifierStyle.subtitle}\u003e{subtitle}\u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n#### darkColorMode\n\nA helper utility for applying dark mode styles.\n\n```js\nimport darkColorMode from \"mystical/darkColorMode.mjs\";\n\nfunction Component() {\n  return (\n    \u003cdiv css={{ color: \"black\", [darkColorMode]: { color: \"white\" } }}\u003e\n      This text is black in light mode and white in dark mode.\n    \u003c/div\u003e\n  );\n}\n```\n\n### Contributors\n\n- [David Burles](https://github.com/dburles)\n- [Alaister Young](https://github.com/alaister)\n- [Jayden Seric](https://github.com/jaydenseric)\n\n#### License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdburles%2Fmystical","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdburles%2Fmystical","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdburles%2Fmystical/lists"}