npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mystical

v15.0.3

Published

CSS prop constraint based styling

Downloads

238

Readme

🌌 mystical

Build themeable, robust and maintainable React component libraries and applications following the System UI Theme Specification.

Overview

  • Mystical is a small (< 12 KB) runtime CSS-in-JS library, inspired by theme-ui. Built on Emotion.
  • A powerful, declarative approach to altering the styles of a component based on its props with the useModifiers hook.
  • Array values for defining media query breakpoint values, e.g. margin: [0, 3].

Table of Contents

Install

npm i mystical

Guide

Context

Wrap your app with MysticalProvider:

import MysticalProvider from "mystical/MysticalProvider.js";

// Optional theme object
const theme = {
  // System UI theme values, See: https://system-ui.com/theme
};

const App = () => {
  return <MysticalProvider theme={theme}>...</MysticalProvider>;
};

Example Component

This Button component attempts to illustrate some of the important parts of the Mystical API:

  1. A css prop that transforms CSS property values from the theme, (like theme-ui)
  2. The concept of modifiers, the combination of a modifiers object with a useModifiers hook. This makes prop based variations of components simple and declarative.
import useModifiers from "mystical/useModifiers.js";

const modifiers = {
  variant: {
    primary: {
      color: "white",
      backgroundColor: "blue.600", // These values are picked up off the theme
      ":hover:not(:disabled)": {
        backgroundColor: "blue.500",
      },
      ":active:not(:disabled)": {
        backgroundColor: "blue.700",
      },
    },
  },
  size: {
    small: {
      fontSize: 0,
      lineHeight: "none",
      padding: "2 3", // Shorthand 1-4 properties such as padding are also translated to spacing values defined in the theme
    },
    medium: {
      fontSize: 1,
      lineHeight: "normal",
      padding: "2 4",
    },
    large: {
      fontSize: 2,
      lineHeight: "normal",
      padding: "3 5",
    },
  },
  shape: {
    square: { borderRadius: 1 },
    rounded: { borderRadius: 2 },
    pill: { borderRadius: 5 },
  },
};

function Button({
  variant = "primary",
  size = "small",
  shape = "rounded",
  modifiers: customModifiers,
  ...props
}) {
  const modifierStyle = useModifiers(
    { variant, size, shape },
    modifiers,
    customModifiers // optional
  );

  return (
    <button
      {...props}
      css={[
        // Objects passed within arrays are merged
        {
          color: "white",
          fontFamily: "body",
          border: 0,
          ":disabled": {
            opacity: "disabled",
          },
        },
        modifierStyle,
      ]}
    />
  );
}

Babel Configuration for JSX

Automatic (Recommended)

Configure @babel/preset-react to use the automatic runtime and point the importSource to mystical.

Example @babel/preset-react configuration:

{
  runtime: 'automatic',
  importSource: 'mystical',
  development: process.env.NODE_ENV === 'development',
}

Classic

If you wish to use the classic runtime instead, just add the @jsx pragma and import the jsx function:

/** @jsx jsx **/
import jsx from "mystical/jsx.js";

function MyComponent() {
  // ...
}

API

Theme Object

Your theme object should be structured following the convention outlined in the System UI theme specification in order for CSS values to be automatically translated.

CSS Prop

This is the primary method of applying styles to components and elements.

// Example theme:
const theme = {
  colors: {
    primary: "#1282A2",
  },
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
  // etc
};

// `padding` is keyed to the `space` property of the theme and looks up the third index in the array.
// `backgroundColor` is keyed to the `colors` property of the theme.
function Component() {
  return <div css={{ padding: 3, backgroundColor: "primary" }}>...</div>;
}
Theme Lookup

Just like theme-ui, values passed to CSS properties are automatically translated from the theme based on a lookup table, and will default to the literal value if there's no match.

Dot Properties

Arrays and Object theme values can be retrieved by using dot properties:

const theme = {
  colors: {
    red: ["#fed7d7", "#feb2b2", "#fc8181"],
  },
};

function Component() {
  return <div css={{ backgroundColor: "red.2" }}>...</div>;
}
Shorthand Properties

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.

Given the following example:

const theme = {
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
};

const Component = () => <div css={{ margin: "3 5" }}>...</div>;

...the following style is generated: margin: 16px 64px

Media Queries

Instead of explicitly writing media queries, simply pass an array. Breakpoints can also be skipped, e.g. ['100%', , '25%'].

function Component() {
  // Applies width 100% to all viewport widths,
  // 50% above the first breakpoint,
  // and 25% above the next breakpoint
  return <div css={{ width: ["100%", "50%", "25%"] }}>...</div>;
}
Merging Styles

The css prop also accepts an array of style objects which are deeply merged in order:

function Component() {
  return (
    <div css={[{ fontSize: 1 }, { fontSize: 2, color: "white" }]}>...</div>
  );
}

MysticalProvider

Provides the theme context, this is required for Mystical to function.

Parameters:

  • theme: The theme object.
  • options: Options (optional)
    • darkModeOff = false: When enabled, dark mode styles are ignored and not added to the output.
    • 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), or for forcing a certain page into dark mode regardless of user system preferences.
import MysticalProvider from "mystical/MysticalProvider.js";

// (Optional, defaults shown).
const options = {
  darkModeOff: false,
  darkModeForcedBoundary: false,
};

function App() {
  return (
    <MysticalProvider options={options} theme={theme}>
      ...
    </MysticalProvider>
  );
}

Global

Global style component that automatically removes its styles when unmounted.

import Global from "mystical/Global.js";

function App() {
  return (
    <div>
      <Global
        styles={{
          body: {
            backgroundColor: "white",
            border: 0,
          },
        }}
      />
      ...
    </div>
  );
}

keyframes

Install @emotion/react (npm i @emotion/react). See https://emotion.sh/docs/keyframes.

import { keyframes } from "@emotion/react";

const animationName = keyframes({
  // ...
});

function Component() {
  return (
    <div
      css={{
        animationName,
        // etc
      }}
    >
      ...
    </div>
  );
}

useTheme

A simple way to pick out values from the theme similar to using the css prop.

import useTheme from "mystical/useTheme.js";

function Component() {
  const purple = useTheme("colors", "purple");

  return <div>The colour purple is {purple}!</div>;
}

useMystical

Provides access to the complete theme object.

import useMystical from "mystical/useMystical.js";

function Component() {
  const { theme } = useMystical();

  return JSON.stringify(theme, null, 2);
}

useModifiers

A declarative API for handling prop based variations to component styles. This example demonstrates applying modifier styles to a component with multiple elements. See the Button component above for another example.

import useModifiers from "mystical/useModifiers.js";

const modifiers = {
  // `default` is a special key for applying and overwriting default styles across each element (experimental).
  default: {
    title: { fontFamily: "heading" },
    subtitle: { fontFamily: "body" },
  },
  size: {
    small: {
      title: { fontSize: 3 },
      subtitle: { fontSize: 0 },
    },
    large: {
      title: { fontSize: 5 },
      subtitle: { fontSize: 2 },
    },
  },
};

function Component({ size = "small", modifiers: customModifiers }) {
  const modifierStyle = useModifiers(
    { size },
    modifiers,
    customModifiers // Optional secondary modifiers object that will merge with `modifiers`.
  );

  return (
    <div>
      <div css={modifierStyle.title}>{title}</div>
      <div css={modifierStyle.subtitle}>{subtitle}</div>
    </div>
  );
}

darkColorMode

A helper utility for applying dark mode styles.

import darkColorMode from "mystical/darkColorMode.js";

function Component() {
  return (
    <div css={{ color: "black", [darkColorMode]: { color: "white" } }}>
      This text is black in light mode and white in dark mode.
    </div>
  );
}

Contributors

License

MIT