All Projects → hswolff → create-class-names

hswolff / create-class-names

Licence: other
A utility to extend the values of a classNames object.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to create-class-names

stylenames
A simple JavaScript utility for conditionally joining inline styles together
Stars: ✭ 18 (+12.5%)
Mutual labels:  classnames, styles
Rollup Plugin Styles
🎨 Universal Rollup plugin for styles: PostCSS, Sass, Less, Stylus and more.
Stars: ✭ 116 (+625%)
Mutual labels:  css-modules, styles
highcharts-themes-collection
Highcharts themes collection
Stars: ✭ 30 (+87.5%)
Mutual labels:  styles
isomorphic-relay-boilerplate
No description or website provided.
Stars: ✭ 30 (+87.5%)
Mutual labels:  css-modules
docz-plugin-css
This package makes it possible to use preprocessors and css modules on docz
Stars: ✭ 41 (+156.25%)
Mutual labels:  styles
react-shop
Shop example for mobile web
Stars: ✭ 20 (+25%)
Mutual labels:  css-modules
parcelui
Parcel + Typescript + React/Preact + Router + CSS Modules + SASS + Jest + Api-Now + Github Actions CI
Stars: ✭ 32 (+100%)
Mutual labels:  css-modules
tds-community
TELUS Design System Community Components
Stars: ✭ 22 (+37.5%)
Mutual labels:  css-modules
ModernWpf
Modern styles and controls for your WPF applications without need WinRT
Stars: ✭ 65 (+306.25%)
Mutual labels:  styles
draper
🍸 React Native style utilities
Stars: ✭ 24 (+50%)
Mutual labels:  styles
modular-styles
Extract CSS into CSS Modules for any language!
Stars: ✭ 25 (+56.25%)
Mutual labels:  css-modules
typescript-css-modules-demo
A working demo of CSS Modules, using TypeScript, css-modulesify and typed-css-modules
Stars: ✭ 25 (+56.25%)
Mutual labels:  css-modules
webpack-typescript-react
Webpack 5 boilerplate with support of most common loaders and modules (see tags and description)
Stars: ✭ 185 (+1056.25%)
Mutual labels:  css-modules
tailwind-cascade
Override TailwindCSS classes for component composition
Stars: ✭ 28 (+75%)
Mutual labels:  classnames
css-modules-angular2
Css modules working with Angular2
Stars: ✭ 22 (+37.5%)
Mutual labels:  css-modules
11tyby
Simple 11ty setup using TypeScript, SASS, Preact with partial hydration, and other useful things. Aims to provide the DX of Gatsby, but using 11ty!
Stars: ✭ 38 (+137.5%)
Mutual labels:  css-modules
dolife
嘟嘟微生活——我司主项目基于React全家桶的前端架构。
Stars: ✭ 64 (+300%)
Mutual labels:  css-modules
rr-boilerplate
A lightweight React&Redux boilerplate
Stars: ✭ 35 (+118.75%)
Mutual labels:  css-modules
react-native-css-modules-with-media-queries-example
An example app to show how CSS Media Queries work in React Native.
Stars: ✭ 18 (+12.5%)
Mutual labels:  css-modules
movies
Real world isomorphic application for movies search, based on Webpack 5 / Express / React 17 + Redux-Saga / Bootstrap 4.6 + CSS Modules / i18next / SSR
Stars: ✭ 20 (+25%)
Mutual labels:  css-modules

create-class-names

build status npm version npm downloads

A utility to define an API for assigning custom classNames to nested elements in a React Component.

Useful for global styles, css-modules, and css-in-js.

Note: This is not a replacement for the very awesome classnames library. This is meant to be used in conjunction with classnames. Think of this library as a way to combine usages of classnames.

What is this?

Commonly React developers will manually expose a className prop to their React Component to allow users of that component to customize the style. Also it's very common to use classnames to concatenate those classNames.

function Banner(props) {
  const { children, className } = props;
  return (
    <div className={classNames('container', className)}>
      <span className="alert">&#x26a0;</span>
      <div className="text">{children}</div>
    </div>
  );
}

However if you want to expose the ability to customize the className for a child component, there's no clear way to do that. A common method is to expose another prop, such as textClassName.

function Banner(props) {
  const { children, textClassName } = props;
  return (
    <div className={classNames('container', className)}>
      <span className="alert">&#x26a0;</span>
      <div className={classNames('text', textClassName)}>{children}</div>
    </div>
  );
}

However this approach doesn't scale well.

Use a theme object

A more structured way to solve this is to use a theme object.

function Banner(props) {
  const { children, theme } = props;
  return (
    <div className={theme.container}>
      <span className={theme.alert}>&#x26a0;</span>
      <div className={theme.text}>
        <span className={theme.innerText}>{children}</span>
      </div>
    </div>
  );
}

Banner.defaultProps = {
  theme: {
    container: 'globalContainerClassName',
  },
};

This allows parent componets to customize what classNames are given however it then becomes difficult to keep the default classNames.

const Page = () => (
  <Banner
    theme={{
      container: 'customContainerClass',
    }}
    {/* This removes the default className */}
  />
);

However keeping the default value is very cumbersome.

const Page = () => (
  <Banner
    theme={{
      container: `${Banner.defaultProps.theme.container} customContainerClass`,
    }}
  />
);

That's where createClassNames comes to the rescue!

Usage

const base = {
  container: 'container',
};

// 1. This is essentially a shallow clone of base.
const result = createClassNames(base);

assert.deepEquals(result, {
  container: 'container',
});

// 2. Extend the base classNames.
// If the extended values are not strings then they are ignored.
const result = createClassNames(base, {
  container: 'pageContainer',
});

assert.deepEquals(result, {
  container: 'container pageContainer',
});

// 3. Provide a default custom merge function.
const result = createClassNames(
  base,
  {
    container: 'pageContainer',
  },
  (val, baseVal, key, result) => {
    return val;
  }
);

assert.deepEquals(result, {
  container: 'pageContainer',
});

// 4. Provide a custom merge behavior per property.
const result = createClassNames(base, {
  container: (baseVal, key, result) => {
    return `${baseVal} ${baseVal}`;
  },
});

assert.deepEquals(result, {
  container: 'container container',
});

Example

Live demo on Codesandbox.

Edit createClassNames Demo

A more in depth example with React.

// With css-modules
import styles from './Banner.css';
const baseStyles = styles;

// With global styles
const baseStyles = {
  container: 'container',
  alert: 'alert',
  text: 'text',
  // Empty string here because there is no base styles but you want
  // to allow parent components the ability to customize that element.
  innerText: '',
};

function Banner(props) {
  const { children, theme } = props;

  // Merges base and theme className values onto the same property.
  const theme = createClassNames(baseStyles, theme);

  return (
    <div className={theme.container}>
      <span className={theme.alert}>&#x26a0;</span>
      <div className={theme.text}>
        <span className={theme.innerText}>{children}</span>
      </div>
    </div>
  );
}

const Page = () => (
    {/* Just uses default styles */}
    <Banner>Default Styles</Banner>

    {/* Customizing classNames */}
    <Banner
      theme={{
        container: 'secondBanner',
        alert: 'secondBannerAlert',
        innerText: 'secondBannerInnerText'
      }}
    >
      Custom Styles
    </Banner>

    {/*
        If you pass a function as a value for a property then you can customize
        what resulting className is given.
    */}
    <Banner
      theme={{
        container: (baseStyleValue, key, result) => {
            // baseStyleValue === 'container'
            // key === 'container'
            // result === the resulting theme object
            return 'secondBanner';
        }
      }}
    >
      Over-riding container style
    </Banner>
)
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].