0.0.5 ā€¢ Published 2 years ago

create-styles v0.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

šŸ–¼ Create Styles


Create dynamic style sheets and link them to functional components using the React hook pattern.

  • āœ… Fully featured TypeScript support
  • āœ… Build on top of @emotion/react: As fast and lightweight as emotion
  • āœ… Supports all emotion features: lazy evaluation, dynamic theming, etc.
  • āœ… Server side rendering support: Next.js, Gatsby or any other environment
  • āœ… @emotion cache support
const useStyles = createStyles()(({theme, params}) => ({
    root: /* */,
    container: /* */,
    text: /* */,
}));

const MyComponent = () => {
    const { classes } = useStyles({ color: 'red', fontSize: 10 });
    return (
    <div className={classes.root}>
        {/* */}
    </div>
);
}

šŸ’» Installation

$ yarn add create-styles @emotion/react
# or
$ npm install create-styles @emotion/react

ā›³ļø Code Sandbox

šŸŸØ Javascript support

Although create-styles has been designed with Typescript in mind it can, of course, be used in any vanilla JS project.

ā³ Quick Start

šŸŖ Basic usage

./styles.ts

First, instantiate the method createStyles() using the makeCreateStyles() method.

import { makeCreateStyles } from 'create-styles';

// Theme hook that returns the current theme of the app. 
function useTheme(): Theme {
    return {
        primaryColor: "#32CD32",
    };
}

// Initialization of the 'createStyles()' method using the 'makeCreateStyles()' method.
// The theme hook is given as the only parameter,
// and should expose the apps theme to the 'createStyles()' method.
export const createStyles = makeCreateStyles(useTheme);

./MyComponent.tsx

Use the instantiated createStyles() method to create dynamic styles and link them to functional components using the React hook pattern.

import React from 'react';
import { css } from '@emotion/react';
import { createStyles } from "./styles";

type MyComponentStyles = { color: "red" | "blue", fontSize: number }

// Specify dynamic styles and access them later with the returned 'useStyles()' hook.
// (Note: Double method ('createStyle()()') due to partial type inference of 'TStyles')
const useStyles = createStyles<MyComponentStyles>()(
    ({theme, params}) => ({
        // The styles of the specified classes can be created with a css object 
        root: {
            backgroundColor: params.color,
            "&:hover": {
                backgroundColor: theme.primaryColor,
            },
        },
        // or with the common 'css()' method of '@emotion/react'
        text: css`
           font-weight: bold;
           font-size: ${params.fontSize}px;
           color: black;
        `
    }),
);

export const Demo: React.FC = (props) => {
    const { className } = props;
    const [color, setColor] = useState<"red" | "blue">("red");

    // Use the created 'useStyles()' hook to access the specified stylesheet as classes
    // and some utilities like 'cx()'.
    const { classes, cx } = useStyles({ color, fontSize: 10 });

    return (
        <div className={cx(classes.root, className)}>
            <p className={classes.text}>hello world</p>
        </div>
    );
}

šŸ”— Classes merging with cx()

To merge class names, use the cx() method. It has the same API as the popular clsx package but is optimized for the use with emotion.

The key advantage of cx() is that it detects emotion generated class names ensuring styles are overwritten in the correct order. Emotion generated styles are applied from left to right. Subsequent styles overwrite property values of previous styles.

import React from 'react';
import { createStyles } from "./styles";

const useStyles = createStyles(({theme}) => ({
  button: {
    backgroundColor: theme.colors.darkBlue,
    border: 0,
    color: theme.colors.white,
    borderRadius: 5,
    padding: '10px 20px',
    cursor: 'pointer',
  },

  active: {
    backgroundColor: theme.colors.lightBlue,
    color: theme.colors.black,
  },
    
  bold: {
    fontWeight: 'bold',
  }  
}));

const Demo: React.FC = () => {
  const [active, setActive] = useState(0);
  const { classes, cx } = useStyles();

  return (
    <div>
      <button
        // Merge class names with the 'cx()' method
        className={cx(classes.button, { [classes.active]: active === 0 })}
        onClick={() => setActive(0)}
        type="button"
      >
        First
      </button>
      <button
        // Merge class names with the 'cx()' method  
        className={cx(classes.button, classes.bold, { [classes.active]: active === 1 })}
        onClick={() => setActive(1)}
        type="button"
      >
        Second (Bold)
      </button>
    </div>
  );
}

āš—ļø Composition and nested selectors

Since createStyles() generates scoped class names based on the specified stylesheet, you have to create a reference to the selector (e.g. 'button') to get the static selector (e.g. 'prefix-ref_button_1'). The createStyles() method receives the createRef() method to handle the creation of static selectors.

import React from 'react';
import { createStyles } from "./styles";

const useStyles = createStyles(({theme, params, createRef, assignRef}) => {
  // Create reference for future use
  const button = createRef('button'); // Returns a static selector (e.g. 'prefix-ref_button_1')

  return {
    // Assign ref variant 1:
    button: {
      // Assign the reference to the selector via the 'ref' property
      ref: button,

      // and add any other style properties
      backgroundColor: theme.colors.blue,
      color: theme.colors.white,
      padding: `10px 20px`,
      borderRadius: 5,
      cursor: 'pointer',
    },
      
    // Assign ref variant 2:
    // Assign the reference to the selector via the 'assignRef()' method
    button2: assignRef(button, {
        // and add any other style properties
        backgroundColor: theme.colors.blue,
        color: theme.colors.white,
        padding: `10px 20px`,
        borderRadius: 5,
        cursor: 'pointer',
    }),

    container: {
      display: 'flex',
      justifyContent: 'center',
      backgroundColor: theme.colors.white,
      padding: 10,

      // Reference button with the previously created static selector
      [`&:hover .${button}`]: {
        backgroundColor: theme.colors.violet[6],
      },
    },
  };
});

const Demo: React.FC = () => {
  const { classes } = useStyles();
  return (
    <div className={classes.container}>
      <button className={classes.button} type="button">
        Hover container to change button color
      </button>
    </div>
  );
}

šŸŽ„ Keyframes

You can define animations using the keyframes helper from @emotion/react. keyframes takes in a css keyframe definition and returns an object you can use in styles. You can use strings or objects just like css.

import React from 'react';
import { keyframes } from '@emotion/react';
import { createStyles } from "./styles";

// Create keyframes with the 'keyframes()' method
const bounce = keyframes`
  from, 20%, 53%, 80%, to {
    transform: translate3d(0,0,0);
  }

  40%, 43% {
    transform: translate3d(0, -30px, 0);
  }

  70% {
    transform: translate3d(0, -15px, 0);
  }

  90% {
    transform: translate3d(0,-4px,0);
  }
`

const useStyles = createStyles(({theme}) => ({
  container: {
    textAlign: 'center',
    // Use specified 'bounce' keyframes in the 'container' styles  
    animation: `${bounce} 3s ease-in-out infinite`,
  },
}));

const Demo: React.FC = () => {
  const { classes } = useStyles();
  return <div className={classes.container}>Keyframes demo</div>;
}

šŸŒ Global styles

Sometimes you might want to insert global css styles. You can use the component to do this.

import React from 'react';
import { GlobalStyles } from 'create-styles';

export function App() {
  return (
      <>
          <GlobalStyles
              styles={{
                  '*, *::before, *::after': {
                      boxSizing: 'border-box',
                  },

                  body: {
                      backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark : theme.white,
                      color: theme.colorScheme === 'dark' ? theme.colors.dark : theme.black,
                      lineHeight: 20,
                  },
              }}
          />
          <YourApp />
      </>
  );
}

šŸŒˆ normalize.css

The NormalizeCss component sets the normalized styles specified in normalize.css globally.

import {NormalizeCSS} from 'create-styles';

function App() {
    return (
        <>
          <NormalizeCSS />
          <YourApp />
        </>
    );
}

āœļø Inline styles

If you plan to style reusable components with the createStyles() API, you may want to customize the components later with, for example, inline styles. You can easily achieve this specifying the styles property (e.g. partial of specified stylesheet) in the useStyles() hook's configuration object.

Button.tsx

Create Button component.

import React from 'react';
import { createStyles } from "./styles";

const useStyles = createStyles<ButtonStyles>()(({theme, params: { color, radius }}) => ({
    root: {
        color: theme.colors.white,
        backgroundColor: color,
        borderRadius: radius,
        padding: '10px 20px',
        cursor: 'pointer',
    },
}));

type ButtonStyles = {
    color: string;
    radius: number;
};

// Create a type that represents the created stylesheet type (extracted from the 'useStyles()' hook).
// Can then be used to add a typesafe styles property to the Button component.
export type ExtractedStylesType = ExtractStylesType<typeof useStyles>;

export const Button: React.FC<ButtonProps> = (props) => {
  const { color = 'blue', radius = 0, styles = {}, onClick } = props;
  // Pass the 'styles' property to the 'useStyles()' hook
  const { classes } = useStyles({ color, radius }, { styles, name: 'Button' });

  return (
    <button type="button" className={classes.root} onClick={onClick}>
      {color} button with {radius}px radius
    </button>
  );
};

type ButtonProps = {
  color?: string;
  radius?: number;
  styles?: ExtractedStylesType; // Complete typesafety based on the created stylesheet
  onClick: () => void;
};

./MyComponent.tsx

Use the created Button component and specify inline styles using the styles property.

import React from 'react';
import { css } from '@emotion/react';
import { Button } from './Button';

const MyComponent: React.FC = () => {
  return (
    <div>
        <Button
            onClick={() => setToggled(!toggled)}
            // Inline styles using the 'styles' property
            styles={{
              root: css`
                  background: rebeccapurple;
              `,
            }}
        />
    </div>
  );
};

šŸ”Ø API documentation

coming soon


ā“ FAQ

šŸŽ‰ Inspired by:

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago