deno.land / x / clazzx@0.0.2 / README.md
ClazzX is a small (< 1kb gzipped) typesafe utility library for composing HTML classes. Unlike Vanilla Extract, Stitches, or CVA (all fantastic options) ClazzX takes a different approach, using state rather than variants to compose styles.
The classic approach is to create a series of variants that can be selected:
className={myButton({size: "small", intent="primary"})}
// AND
<Button size="small" intent="primary" />
In comparison, ClazzX models combinations of styles based on state.
className={myButton.classes({small: true, primary: true})}
// AND
<Button small primary />
This simplifies the logic of tying the styling of a component to complex state. Instead of a series of nested ternary operators, simple logical operators can be used to clearly define the appropriate styles.
className={myButton.classes({
primary: true,
loading: isLoading,
error: isError
})}
//AND
<Button primary loading={isLoading} error={isError}>
// Versus
className={myButton({
intent=isLoading ? "loading"
: isError ? "error"
: "primary"
})}
// AND
<Button intent={isLoading ? "loading" : isError ? "error" : "primary"}>
// Deno
import {Style, clx} from "https://deno.land/x/clazzx/mod.ts"
// NPM
npm i 'clazzx'
// Yarn
yarn add 'clazzx'
import { Style } from 'clazzx'
const MyCustomStyle exends Style {
// the base class will always be applied
base = "border rounded-md font-sans"
small = "px-2 py-1 text-sm"
md = "px-3 py-2 text-base"
lg = "px-4 py-3 text-lg"
// you can use both strings and string arrays freely
primary = [
"bg-primary",
"border-primary-dark",
"text-primary-content"
]
secondary = [
"bg-secondary",
"border-secondary-dark",
"text-secondary-content"
]
}
const input = new MyCustomStyle()
function MyCustomComponent(){
return <input className={input.classes({ md: true, secondary: true })}/>
}
You can set default classes that are applied if nothing is passed th the classes method.
default = [this.md, this.secondary, "hover:scale-105"]
If you want to conditionally apply styles when two or more states are true, you can create compound states.
compounds = [
{
state: ["primary", "large"],
classes: "shadow-md" // will only be applied if both states are true
}
]
The constructor takes in an options object with the following properties:
before: string | string[]
: Classes to be applied before the conditional classes.after: string | string[]
: Classes to be applied after the conditional classes.Classes are applied in the order of the states.
className={btn.classes({rounded: true, square: true})}
// className="border-md border-none"
<Button primary secondary small medium>
// className="bg-primary bg-secondary text-sm text-md"
The order of classes works like this:
before
||
default classesafter
StyleProps is an exported type that gives you access to the parameters of the style class. You can use this to strongly type components.
function MyButton({...props}: StyleProps<MyStyleClass>){}
import {Style, StyleProps} from "clazzx"
class LinkStyles extends Style {
base: "font-link no-underline transition duration-200"
primary: "text-link hover:text-link-hover"
secondary: "text-secondary hover:text-secondary-hover"
active: "text-active hover:text-active-hover underline"
default = this.primary
}
const link = new MyCustomLink()
interface Link {
children: ReactNode
props: StyleProps<MyCustomLink> & HTMLAttributes<HTMLAnchorElement>
}
function Link({children, ...props}: Link){
return <a {...props} className={link.classes({...props})}>{children}</a>
}
function App(){
const router = useRouter()
// ...
return (
// ...
<Link href="/about" secondary active={router.isCurrentPage}>
My Link
</Link>
// ...
)
}
... more coming soon
Version Info