Skip to main content

Unwind

Style→Extend→Unwind

Build powerful, composable and truly reusable UI components that stay out of everyone's way.

Start using Unwind

npm install @unwind/class-name

1First–class type safety

Unwind was built with Typescript and great DX in mind providing you with confidence, auto-completion in you IDE and speed when developing UI.

import type { ClassNameProp } from '@unwind/class-name'import { defineClassName, mergeClassNames, resolveClassName } from '@unwind/class-name'import { memo, PropsWithChildren } from 'react'const unwindClassName = defineClassName('unwind-input')type FunkyButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof unwindClassName>;}>export const FunkyButton = memo<FunkyButtonProps>(({ className, children }) => {  return (    <button className={resolveClassName({}, mergeClassNames(unwindClassName, className))}>      {children}    </button>  )})FunkyButton.displayName = 'FunkyButton'

2Start small, go BIG

Never again get scared by the dreadful complexity of the nested components. Unwind has you covered.

  1. Start with string or string[]
  2. Derive classes from state with callback()
  3. Target any sub-component with object

String class names

import type { ClassNameProp } from '@unwind/class-name'import { defineClassName, mergeClassNames, resolveClassName } from '@unwind/class-name'import { memo, PropsWithChildren } from 'react'const unwindClassName = defineClassName('unwind-input')type FunkyButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof unwindClassName>;}>export const FunkyButton = memo<FunkyButtonProps>(({ className, children }) => {  return (    <button className={resolveClassName({}, mergeClassNames(unwindClassName, className))}>      {children}    </button>  )})FunkyButton.displayName = 'FunkyButton'

Array class names

import type { ClassNameProp } from '@unwind/class-name'import { defineClassName, mergeClassNames, resolveClassName } from '@unwind/class-name'import { memo, PropsWithChildren } from 'react'const unwindClassName = defineClassName(['unwind-input', 'active'])type FunkyButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof unwindClassName>;}>export const FunkyButton = memo<FunkyButtonProps>(({ className, children }) => {  return (    <button className={resolveClassName({}, mergeClassNames(unwindClassName, className))}>      {children}    </button>  )})FunkyButton.displayName = 'FunkyButton'

Callback class names

import { ClassNameProp, defineClassName, mergeClassNames, resolveClassName } from '@unwind/class-name';import { memo, PropsWithChildren, useCallback, useState } from 'react';const unwindClassName = defineClassName(  ({ active }: { active: boolean }, previous) => [...previous, 'unwind-input', `active:${active}`],)type FunkyButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof unwindClassName>;}>export const FunkyButton = memo<FunkyButtonProps>(({ className, children }) => {  const [active, setActive] = useState(false)  const onClick = useCallback(() => { setActive(!active) }, [active])  return (    <button      className={resolveClassName({ active }, mergeClassNames(unwindClassName, className))}      onClick={onClick}    >      {children}    </button>  )})FunkyButton.displayName = 'FunkyButton'

Object class names

import { ClassNameProp, defineClassName, HOST_KEY, mergeClassNames, resolveClassName } from '@unwind/class-name';import React, { memo, PropsWithChildren, ReactNode, useCallback, useState } from 'react';type UnwindClassNameState = {  active: boolean}// $ property represents the host element (container) keyconst unwindClassName = defineClassName({  [HOST_KEY]: ({ active }: UnwindClassNameState, previous) => [...previous, 'unwind-input', `active:${active}`],  icon: ({ active }: UnwindClassNameState, previous) => [...previous, 'unwind-input-icon', `active:${active}`],})type FunkyButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof unwindClassName>;  icon?: ReactNode}>export const FunkyButton = memo<FunkyButtonProps>(({ className, icon, children }) => {  const [active, setActive] = useState(false)  const onClick = useCallback(() => {    setActive(!active)  }, [active])  const styles = mergeClassNames(unwindClassName, className)  return (    <button      className={resolveClassName({ active }, styles)}      onClick={onClick}    >      {icon && <span className={resolveClassName({ active }, styles.icon)}>{icon}</span>}      {children}    </button>  )})FunkyButton.displayName = 'FunkyButton'

3Add, filter or redefine everything

With unwind components you are not tied to tied to adding more and more CSS classes to change the behavior or look of underlying DOM elements.

You can do whatever you need.

  1. Add more classes with string or string[]
  2. Filter outer class name with callback()
  3. Replace outer class name with callback()
  4. Replace outer & inner class name with object

Add string to className

<FunkyButton className={'hello world'} icon="👋">  Click me!</FunkyButton>

Add array of strings to className

<FunkyButton className={['hello', 'world']} icon="👋">  Click me!</FunkyButton>

Filter previous className values with callback

<FunkyButton  className={({ active }, previous) => [    // 👇 keep the 'active'/'not:active' <button> classes    ...previous.filter(v => v.match(/active/)),  ]}  icon="👋">  Click me!</FunkyButton>

Replace className with callback

<FunkyButton  // Omit the second 👇 parameter altogether to discard values from previous call  className={({ active }) => [    'border-0 text-white rounded-full px-3 py-1 flex gap-x-2 items-center',    active ? 'bg-blue-600' : 'bg-blue-500',  ]}  icon="👋">  Click me!</FunkyButton>

Replace className with object

<FunkyButton  className={{    [HOST_KEY]: ({ active }) => [      'border-0 text-white rounded-full px-3 py-1 flex gap-x-2 items-center',      `${active ? 'bg-blue-600' : 'bg-blue-500'}`,    ],    icon: ({ active }) => [      'border rounded-full w-7 h-7 -ml-2 inline-flex items-center justify-center',      active ? 'bg-white border-transparent' : 'border-white',    ],  }}  icon="👋">  Click me!</FunkyButton>

Ready to build truly customizable components?

Get started with Unwind →