Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hswolff/create-class-names

A utility to extend the values of a classNames object.
https://github.com/hswolff/create-class-names

classnames css-modules react styles

Last synced: 2 days ago
JSON representation

A utility to extend the values of a classNames object.

Awesome Lists containing this project

README

        

# create-class-names

[![build status](https://img.shields.io/travis/hswolff/create-class-names/master.svg?style=flat-square)](https://travis-ci.org/hswolff/create-class-names)
[![npm version](https://img.shields.io/npm/v/create-class-names.svg?style=flat-square)](https://www.npmjs.com/package/create-class-names)
[![npm downloads](https://img.shields.io/npm/dm/create-class-names.svg?style=flat-square)](https://www.npmjs.com/package/create-class-names)

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](https://github.com/JedWatson/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](https://github.com/JedWatson/classnames) to concatenate those classNames.

```js
function Banner(props) {
const { children, className } = props;
return (



{children}


);
}
```

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`.

```js
function Banner(props) {
const { children, textClassName } = props;
return (



{children}


);
}
```

However this approach doesn't scale well.

## Use a theme object

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

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




{children}


);
}

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.

```js
const Page = () => (

);
```

However keeping the default value is very cumbersome.

```js
const Page = () => (

);
```

That's where `createClassNames` comes to the rescue!

## Usage

```js
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](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/13zn2oj594)

A more in depth example with React.

```js
// 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 (




{children}


);
}

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

{/* Customizing classNames */}

Custom Styles

{/*
If you pass a function as a value for a property then you can customize
what resulting className is given.
*/}
{
// baseStyleValue === 'container'
// key === 'container'
// result === the resulting theme object
return 'secondBanner';
}
}}
>
Over-riding container style

)
```