PraxisJS

cx()

cx() — compose class names from strings, objects, and arrays. Pairs naturally with @Styled class names.

cx() — class composition

cx() composes class names from any combination of strings, objects, and arrays. Falsy values are filtered automatically. It pairs naturally with @Styled class names.

import { cx } from '@praxisjs/css'

Signatures

// Plain strings
cx('base', 'modifier')
// → "base modifier"

// Object — key added when value is truthy
cx('base', { active: true, disabled: false, loading: 0 })
// → "base active"

// Arrays — recursively flattened
cx('a', ['b', false, 'c'])
// → "a b c"

// Falsy values filtered — null, undefined, false, 0 (unless top-level), ""
cx(null, undefined, false, '', 'valid')
// → "valid"

// With @Styled class names
cx(this.$card.$root, { [this.$card.$selected]: this.selected })

With @Styled

The most common use: compose the base class with conditional modifier classes from the same stylesheet.

class BtnStyles extends Stylesheet {
  $base     = this.css({ display: 'inline-flex', padding: '8px 16px' })
  $primary  = this.css({ background: 'var(--accent)', color: 'white' })
  $ghost    = this.css({ background: 'transparent', color: 'var(--accent)' })
  $disabled = this.css({ opacity: 0.4, pointerEvents: 'none' })
  $loading  = this.css({ cursor: 'wait' })
}

@Component()
class Button extends StatefulComponent {
  @State() variant: 'primary' | 'ghost' = 'primary'
  @State() disabled = false
  @State() loading  = false

  @Styled(BtnStyles) $btn!: BtnStyles

  render() {
    return (
      <button
        class={() => cx(
          this.$btn.$base,
          this.variant === 'primary' ? this.$btn.$primary : this.$btn.$ghost,
          { [this.$btn.$disabled]: this.disabled },
          { [this.$btn.$loading]:  this.loading  },
        )}
        disabled={() => this.disabled}
      >
        <slot />
      </button>
    )
  }
}

Type signature

type ClassValue =
  | string
  | number
  | boolean
  | null
  | undefined
  | ClassValue[]
  | Record<string, unknown>

function cx(...args: ClassValue[]): string

Numeric values (including 0) are converted to strings. Booleans, null, and undefined are filtered — except for 0 as a top-level argument which is included as "0".


Static class names

@Styled class names are strings — they can be used without cx() for static assignments:

render() {
  return (
    // Static — no reactive wrapper needed
    <h2 class={this.$card.$title}>Title</h2>
  )
}

Use cx() (inside an arrow function) only when the class list changes reactively:

render() {
  return (
    // Reactive — arrow function re-evaluates when this.selected changes
    <div class={() => cx(this.$card.$root, { [this.$card.$selected]: this.selected })}>
      ...
    </div>
  )
}
Storybook
Live demo — cx()

On this page