@use-pico/cls
Version:
Type-safe, composable styling system for React, Vue, Svelte, and vanilla JS
1,543 lines (1,290 loc) • 81.5 kB
Markdown
# CLS - Type-Safe Styling System
> **Type-safe, composable styling system** for React, Vue, Svelte, and vanilla JS
[](https://badge.fury.io/js/@use-pico%2Fcls)
[](https://opensource.org/licenses/MIT)
## 🚀 Quick Start <a id="quick-start"></a>
```typescript
import { cls } from '@use-pico/cls';
const Button = cls(
{
tokens: ["color.bg.primary", "color.text.primary"],
slot: ["root"],
variant: { size: ["sm", "md", "lg"] }
},
({ what, def }) => ({
token: def.token({
"color.bg.primary": what.css(["bg-blue-600"]),
"color.text.primary": what.css(["text-white"])
}),
rules: [
def.root({ root: what.token(["color.bg.primary", "color.text.primary"]) }),
def.rule(what.variant({ size: "lg" }), { root: what.css(["px-6", "py-3"]) })
]
})
);
// Usage
const classes = Button.create(({ what }) => ({
variant: what.variant({ size: "lg" })
}));
console.log(classes.root()); // "bg-blue-600 text-white px-6 py-3"
// With React
import { useCls } from '@use-pico/cls';
function MyButton({ size = "md" }) {
const classes = useCls(Button, ({ what }) => ({
variant: what.variant({ size })
}));
return <button className={classes.root()}>Click me</button>;
}
```
## ✨ Why CLS? <a id="why-cls"></a>
- **🔒 Type Safety** - Catch styling errors at compile time with full TypeScript support
- **🧩 Composable** - Build design systems with inheritance and composition
- **⚡ Performance** - Lazy evaluation, smart caching, and minimal runtime overhead
- **🌐 Framework Agnostic** - Works with React, Vue, Svelte, vanilla JS, or any framework
- **🎨 Design System Ready** - Tokens, variants, and slots for scalable styling
- **🛠 Developer Experience** - Excellent IDE support and intuitive API
## 🛠 Installation <a id="installation"></a>
```bash
npm install @use-pico/cls
# or
bun add @use-pico/cls
# or
yarn add @use-pico/cls
```
## 📖 Basic Usage <a id="basic-usage"></a>
### Simple Component
```typescript
const Card = cls(
{
tokens: ["color.bg", "color.text"],
slot: ["root", "title", "content"],
variant: { theme: ["light", "dark"] }
},
({ what, def }) => ({
token: def.token({
"color.bg": { light: what.css(["bg-white"]), dark: what.css(["bg-gray-800"]) },
"color.text": { light: what.css(["text-gray-900"]), dark: what.css(["text-white"]) }
}),
rules: [
def.root({
root: what.both(["rounded-lg", "shadow-md"], ["color.bg.light"]),
title: what.css(["text-xl", "font-bold"]),
content: what.css(["p-4"])
}),
def.rule(
what.variant({ theme: "dark" }),
{ root: what.token(["color.bg.dark", "color.text.dark"]) }
)
]
})
);
```
### With React
```tsx
import { cls } from '@use-pico/cls';
import { useCls } from '@use-pico/cls/react';
const MyComponent = ({ theme = "light" }) => {
const classes = useCls(Card, ({ what }) => ({
variant: what.variant({ theme })
}));
return (
<div className={classes.root()}>
<h2 className={classes.title()}>Card Title</h2>
<div className={classes.content()}>Card content here</div>
</div>
);
};
```
> **💡 Pro Tip**: This is just the beginning! Check out the [React Integration](#16-react-integration) section for advanced patterns, context providers, HOCs, and more comprehensive examples.
> **📋 Current Implementation**: The examples in this README reflect the current implementation. The `useCls` hook accepts 3 parameters: `clsInstance`, `userConfigFn?`, and `internalConfigFn?`. This allows for flexible configuration merging between user props, internal logic, and context inheritance.
## 🎯 Key Features <a id="key-features"></a>
### Type-Safe Variants
```typescript
// TypeScript ensures only valid variants are used
const classes = Button.create(({ what }) => ({
variant: what.variant({
size: "lg", // ✅ Valid
// size: "xl" // ❌ TypeScript error
})
}));
```
### Token Inheritance
```typescript
const PrimaryButton = Button.extend(
{
tokens: ["color.bg.primary"],
slot: ["root"],
variant: {}
},
({ what, def }) => ({
token: def.token({
"color.bg.primary": what.css(["bg-blue-600"]) // Overrides parent
})
})
);
```
### Runtime Overrides
```typescript
const classes = Button.create(({ what }) => ({
variant: what.variant({ size: "lg" }),
token: {
"color.bg.primary": what.css(["bg-indigo-600"]) // Runtime override
}
}));
```
## 🔗 Community & Support <a id="community--support"></a>
- [GitHub Issues](https://github.com/use-pico/pico/issues) - Report bugs and request features
- [Discussions](https://github.com/use-pico/pico/discussions) - Ask questions and share ideas
## 📚 Documentation <a id="documentation"></a>
This document serves as both the **main README** and **comprehensive technical guide**. Keep reading for:
- [Core Concepts](#2-core-concepts) - Understanding the mental model
- [API Reference](#4-main-api) - Complete API documentation
- [Advanced Features](#5-key-concepts) - Tokens, slots, variants, and rules
- [React Integration](#16-react-integration) - **Complete React guide** with hooks, HOCs, and context
- [Best Practices](#15-best-practices) - How to build great design systems
- [Performance Guide](#12-performance-features) - Optimization strategies
> **🚀 React Developers**: Check out the [detailed React documentation](./docs/04-react-integration/README.md) for comprehensive examples, patterns, and best practices!
---
# CLS Library Design
> **Note**: This document serves as a single source of truth for this library.
## Overview
CLS (Class List System) is a **type-safe, composable styling system** that provides a structured approach to managing CSS classes, design tokens, and component variants. It's framework-agnostic and can be used with React, Vue, Svelte, vanilla JavaScript, or any other framework. It combines the flexibility of utility-first CSS with the maintainability of design systems.
## Table of Contents <a id="table-of-contents"></a>
- [🚀 Quick Start](#quick-start)
- [✨ Why CLS?](#why-cls)
- [🛠 Installation](#installation)
- [📖 Basic Usage](#basic-usage)
- [🎯 Key Features](#key-features)
- [🔗 Community & Support](#community--support)
- [📚 Documentation](#documentation)
**Technical Documentation:**
- [1. Core Principles](#1-core-principles)
- [1.1 Type Safety First](#11-type-safety-first)
- [1.2 Inheritance as Foundation](#12-inheritance-as-foundation)
- [1.3 Declarative Configuration](#13-declarative-configuration)
- [1.4 Performance Optimized](#14-performance-optimized)
- [2. Core Concepts](#2-core-concepts)
- [2.1 Contract](#21-contract)
- [2.2 Definition](#22-definition)
- [2.3 CLS Instance](#23-cls-instance)
- [2.4 Contract vs Definition: The Mental Model](#24-contract-vs-definition)
- [3. Architecture](#3-architecture)
- [3.1 Core Components](#31-core-components)
- [3.2 Data Flow](#32-data-flow)
- [4. Main API](#4-main-api)
- [5. Key Concepts](#5-key-concepts)
- [5.1 What Utility](#51-what-utility)
- [5.2 Definition Callback](#52-definition-callback)
- [5.3 Tokens](#53-tokens)
- [5.4 Slots](#54-slots)
- [5.5 Variants](#55-variants)
- [5.6 Rules](#56-rules)
- [6. CLS Instance Methods](#6-cls-instance-methods)
- [6.1 Create Method](#61-create-method)
- [6.1.1 Slot Configuration Merging](#611-slot-configuration-merging)
- [6.2 Extend Method](#62-extend-method)
- [6.3 Use Method](#63-use-method)
- [7. Contract Structure](#7-contract-structure)
- [7.1 Token Contract](#71-token-contract)
- [7.2 Slot Contract](#72-slot-contract)
- [7.3 Variant Contract](#73-variant-contract)
- [8. Definition Structure](#8-definition-structure)
- [8.1 Token Definition](#81-token-definition)
- [8.2 Rules](#82-rules)
- [8.3 Defaults](#83-defaults)
- [9. Create Method Usage](#9-create-method-usage)
- [10. Styling Resolution](#10-styling-resolution)
- [10.1 Resolution Order](#101-resolution-order)
- [10.2 Token Resolution Process](#102-token-resolution-process)
- [10.3 Rule Evaluation Process](#103-rule-evaluation-process)
- [11. Inheritance System](#11-inheritance-system)
- [11.1 Contract Inheritance](#111-contract-inheritance)
- [11.2 Token Inheritance](#112-token-inheritance)
- [11.3 Variant Inheritance](#113-variant-inheritance)
- [12. Performance Features](#12-performance-features)
- [12.1 Caching Strategy](#121-caching-strategy)
- [12.2 Memory Management](#122-memory-management)
- [12.3 Runtime Optimization](#123-runtime-optimization)
- [13. Type System](#13-type-system)
- [13.1 Generic Constraints](#131-generic-constraints)
- [13.2 Type Inference](#132-type-inference)
- [13.3 Type Safety Features](#133-type-safety-features)
- [14. Integration Patterns](#14-integration-patterns)
- [14.1 Framework Integration](#141-framework-integration)
- [14.2 Design System Integration](#142-design-system-integration)
- [14.3 Build System Integration](#143-build-system-integration)
- [15. Best Practices](#15-best-practices)
- [15.1 Contract Design](#151-contract-design)
- [15.2 Definition Design](#152-definition-design)
- [15.3 Component Design](#153-component-design)
- [15.4 Performance Optimization](#154-performance-optimization)
- [16. React Integration](#16-react-integration)
- [16.1 useCls Hook](#161-usecls-hook)
- [16.2 Component Patterns](#162-component-patterns)
- [16.2.1 The `cls` Prop and Slot Merging](#1621-the-cls-prop-and-slot-merging)
- [16.3 withCls HOC](#163-withcls-hoc)
- [16.4 Context Integration](#164-context-integration)
- [16.5 Provider Architecture](#165-provider-architecture)
- [16.6 React Type System](#166-react-type-system)
- [16.7 Advanced Patterns](#167-advanced-patterns)
## 1. Core Principles <a id="1-core-principles"></a>
**[← Previous: Table of Contents](#table-of-contents)** | **[→ Next Chapter: Core Concepts](#2-core-concepts)**
### 1.1 Type Safety First <a id="11-type-safety-first"></a>
- All styling configurations are fully type-checked at compile time
- Contract definitions ensure consistency across the design system
- Inheritance chains maintain type safety through the entire hierarchy
### 1.2 Inheritance as Foundation <a id="12-inheritance-as-foundation"></a>
- Components can be extended and composed rather than rewritten
- Design tokens can be inherited and overridden at any level
- Slots provide granular control over component parts
### 1.3 Declarative Configuration <a id="13-declarative-configuration"></a>
- Styling rules are defined declaratively through contracts and definitions
- Variant combinations are resolved automatically
- Token resolution happens at runtime based on current state
### 1.4 Performance Optimized <a id="14-performance-optimized"></a>
- Lazy evaluation of slot functions via Proxy
- Caching of resolved configurations
- Minimal runtime overhead with maximum flexibility
---
## 2. Core Concepts <a id="2-core-concepts"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: Core Principles](#1-core-principles)** | **[→ Next Chapter: Architecture](#3-architecture)**
### 2.1 Contract <a id="21-contract"></a>
A contract defines the **structure** of a component's styling system:
- **Tokens**: Named design values organized by groups and variants
- **Slots**: Named parts of a component that can receive styles
- **Variants**: Configurable properties that affect component appearance
- **Inheritance**: Optional parent contract for extending functionality
### 2.2 Definition <a id="22-definition"></a>
A definition provides **concrete styling values** for a contract:
- **Token Definitions**: CSS classes or token references for each token variant
- **Rules**: Conditional styling based on variant combinations
- **Defaults**: Default values for variants
### 2.3 CLS Instance <a id="23-cls-instance"></a>
The main interface that combines contract and definition:
- **`create()`**: Generates styled instances with optional overrides
- **`extend()`**: Creates new instances with additional functionality
- **`use()`**: Type-safe assignment of compatible instances
### 2.4 Contract vs Definition: The Mental Model <a id="24-contract-vs-definition"></a>
> **💡 Key Insight**: Think of **Contract** as the "interface" and **Definition** as the "implementation"
**Contract = Structure (What can be styled?)**
```typescript
// Contract defines WHAT can be styled
{
tokens: ["color.text", "color.bg"], // What design tokens exist?
slot: ["root", "label"], // What parts can be styled?
variant: { // What variations are possible?
size: ["sm", "md", "lg"],
variant: ["default", "primary"]
}
}
```
**Definition = Values (How is it styled?)**
```typescript
// Definition defines HOW it's styled
{
token: {
"color.text.default": what.css(["text-gray-900"]),
"color.text.primary": what.css(["text-white"]),
"color.bg.default": what.css(["bg-gray-100"]),
"color.bg.primary": what.css(["bg-blue-600"])
},
rules: [
// How do variants affect styling?
def.root({
root: what.both(["inline-flex", "items-center"], ["color.text.default", "color.bg.default"])
}),
def.rule(
what.variant({ size: "lg" }),
{ root: what.css(["px-6", "py-3"]) }
),
def.rule(
what.variant({ variant: "primary" }),
{ root: what.token(["color.text.primary", "color.bg.primary"]) }
)
],
defaults: { size: "md", variant: "default" }
}
```
**The Relationship:**
- **Contract** is like a TypeScript interface - it defines the shape
- **Definition** is like the implementation - it provides the actual values
- **CLS Instance** combines both to create a working styling system
- **Inheritance** allows contracts to extend other contracts, and definitions to override inherited values
**Why This Separation Matters:**
- **Reusability**: Same contract can have multiple definitions (themes, variants)
- **Type Safety**: Contract ensures all required styling points are defined
- **Composability**: Contracts can be extended, definitions can be overridden
- **Clarity**: Clear separation between structure and styling values
- **Inheritance**: Contract declarations enforce type safety - you must implement declared tokens
---
## 3. Architecture <a id="3-architecture"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: Core Concepts](#2-core-concepts)** | **[→ Next Chapter: Main API](#4-main-api)**
### 3.1 Core Components <a id="31-core-components"></a>
The CLS system consists of three main components that work together:
- **Contract**: Defines the structure (tokens, slots, variants) - see [Section 2.1-2.3](#2-core-concepts)
- **Definition**: Provides concrete styling values - see [Section 2.1-2.3](#2-core-concepts)
- **CLS Instance**: Main interface with `create()`, `extend()`, `use()` methods - see [Section 6](#6-cls-instance-methods)
### 3.2 Data Flow <a id="32-data-flow"></a>
1. **Contract Definition**: Define the structure (tokens, slots, variants)
2. **Definition Creation**: Provide concrete styling values
3. **CLS Instance**: Combine contract and definition
4. **Instance Creation**: Generate styled components with overrides
5. **Slot Resolution**: Apply rules and resolve tokens to CSS classes
---
## 4. Main API <a id="4-main-api"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: Architecture](#3-architecture)** | **[→ Next Chapter: Key Concepts](#5-key-concepts)**
### `cls(contract, definitionFn)`
Creates a cls instance for component styling with tokens, slots, and variants.
**Parameters:**
- `contract`: Defines the structure (tokens, slots, and variants)
- `definitionFn`: A callback function that receives `{ what, override, def }` and returns the definition
**Returns:** A cls instance with `create()`, `extend()`, `use()`, and contract properties
**Example:**
```typescript
// Basic button with variants
const Button = cls(
{
tokens: ["color.text.default", "color.text.primary", "color.bg.default", "color.bg.primary"],
slot: ["root", "label"],
variant: {
size: ["sm", "md", "lg"],
variant: ["default", "primary"]
}
},
({ what, override, def }) => ({
token: def.token({
"color.text.default": what.css(["text-gray-900"]),
"color.text.primary": what.css(["text-white"]),
"color.bg.default": what.css(["bg-gray-100"]),
"color.bg.primary": what.css(["bg-blue-600"])
}),
rules: [
def.root({
root: what.both(["inline-flex", "items-center"], ["color.text.default", "color.bg.default"]),
label: what.css(["font-medium"])
}),
def.rule(
what.variant({ size: "lg" }),
{
root: what.css(["px-6", "py-3"])
}
),
def.rule(
what.variant({ variant: "primary" }),
{
root: what.token(["color.text.primary", "color.bg.primary"])
}
)
],
defaults: def.defaults({
size: "md",
variant: "default"
})
})
);
```
---
## 5. Key Concepts <a id="5-key-concepts"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: Main API](#4-main-api)** | **[→ Next Chapter: CLS Instance Methods](#6-cls-instance-methods)**
### 5.1 What Utility <a id="51-what-utility"></a>
The `what` utility is your **styling toolkit** - it provides type-safe helpers for creating styling configurations. Think of it as a set of specialized functions that ensure your styling is both correct and type-safe.
> **🎯 Mental Model**: `what` = "What styling do I want to apply?"
#### Core Helper Functions
**📝 Styling Helpers**
```typescript
what.css(classes) // Pure CSS classes only
what.token(tokens) // Design token references only
what.both(classes, tokens) // Both CSS classes + token references
```
**🔧 Configuration Helpers**
```typescript
what.variant(variant) // Type-safe variant values
what.slot(slot) // Slot-specific configurations
```
#### Styling Helper Examples
**CSS-Only Styling**
```typescript
// ✅ Good: Pure CSS classes
root: what.css(["inline-flex", "items-center", "rounded-md"])
// ❌ Avoid: Raw strings (no type safety)
root: "inline-flex items-center rounded-md"
```
**Token-Only Styling**
```typescript
// ✅ Good: Design token references
root: what.token(["color.text.primary", "color.bg.primary"])
// ❌ Avoid: Hardcoded values
root: "text-white bg-blue-600"
```
**Mixed Styling (Most Common)**
```typescript
// ✅ Good: Both CSS classes and tokens
root: what.both(
["rounded-md", "shadow-lg"], // Layout/behavior classes
["color.text.primary", "color.bg.primary"] // Design tokens
)
```
#### Type-Safe Variant Usage
The `what.variant()` helper is **crucial for type safety** - it ensures you only use valid variant combinations:
```typescript
// ✅ Type-safe variant usage
const classes = Button.create(({ what }) => ({
variant: what.variant({
size: "lg", // ✅ Valid: "lg" is in ["sm", "md", "lg"]
variant: "primary" // ✅ Valid: "primary" is in ["default", "primary"]
})
}));
// ❌ TypeScript will catch invalid variants
const classes = Button.create(({ what }) => ({
variant: what.variant({
size: "xl", // ❌ Error: "xl" is not in ["sm", "md", "lg"]
variant: "invalid" // ❌ Error: "invalid" is not in ["default", "primary"]
})
}));
```
#### When to Use Each Helper
> **💡 Decision Tree**:
> - **Pure CSS classes** → `what.css()`
> - **Design tokens only** → `what.token()`
> - **Mixed styling** → `what.both()`
> - **Variant values** → `what.variant()`
**Real-World Example:**
```typescript
const Button = cls(
{
tokens: ["color.text.primary", "color.bg.primary", "spacing.padding.md"],
slot: ["root", "icon", "label"],
variant: {
size: ["sm", "md", "lg"],
variant: ["default", "primary"]
}
},
({ what, def }) => ({
token: def.token({
"color.text.primary": what.css(["text-white"]),
"color.bg.primary": what.css(["bg-blue-600"]),
"spacing.padding.md": what.css(["px-4", "py-2"])
}),
rules: [
def.root({
// Mixed styling: layout + design tokens
root: what.both(
["inline-flex", "items-center", "rounded-md"], // Layout
["color.text.primary", "color.bg.primary"] // Design
),
// Pure CSS for utility classes
icon: what.css(["mr-2", "w-4", "h-4"]),
// Pure tokens for consistent styling
label: what.token(["color.text.primary"])
}),
def.rule(
what.variant({ size: "lg" }), // Type-safe variant
{
root: what.css(["px-6", "py-3"]) // Pure CSS for size-specific styling
}
)
]
})
);
```
### 5.2 Definition Callback <a id="52-definition-callback"></a>
The definition function receives a `WhatUtil` object with three main interfaces that serve different purposes:
```typescript
({ what, override, def }) => ({ ... })
```
> **🎯 Mental Model**:
> - `what` = "What styling do I want?"
> - `def` = "Define the default behavior"
> - `override` = "Override and replace previous styles"
#### The Three Interfaces Explained
**`what` - Your Styling Toolkit**
- **Purpose**: Create type-safe styling configurations
- **When to use**: Always use for styling values
- **Examples**: `what.css()`, `what.token()`, `what.both()`, `what.variant()`
**`def` - Define Default Behavior**
- **Purpose**: Create definitions that **append** to or **extend** existing styles
- **Behavior**: Accumulative - adds to previous styles
- **When to use**: For base styles, extensions, and additive styling
**`override` - Override and Replace**
- **Purpose**: Create definitions that **replace** previous styles completely
- **Behavior**: Destructive - clears previous styles and applies only new ones
- **When to use**: When you want clean slate styling
#### Def vs Override: The Key Difference
> **💡 Think of it like CSS specificity**:
> - `def` = "Add these styles" (like normal CSS rules)
> - `override` = "Replace all previous styles" (like `!important`)
**Def Helpers (Accumulative)**
```typescript
// ✅ Adds to existing styles
def.root({
root: what.css(["px-4", "py-2"]) // Adds padding to existing styles
})
def.rule(
what.variant({ size: "lg" }),
{
root: what.css(["px-6", "py-3"]) // Adds larger padding when size="lg"
}
)
```
**Override Helpers (Replacement)**
```typescript
// ✅ Replaces all previous styles
override.root({
root: what.css(["px-4", "py-2"]) // ONLY these styles, nothing else
})
override.rule(
what.variant({ size: "lg" }),
{
root: what.css(["px-6", "py-3"]) // ONLY these styles when size="lg"
}
)
```
#### Helper Function Reference
**`def` - Definition Helpers (Accumulative)**
```typescript
def.root(slotConfig, override = false) // Default slot configuration
def.rule(match, slotConfig, override = false) // Conditional rule
def.token(tokenDefinition) // Token definitions
def.defaults(defaultValues) // Default variant values
```
**`override` - Override Helpers (Replacement)**
```typescript
override.root(slotConfig, override = true) // Default slot with override=true
override.rule(match, slotConfig, override = true) // Conditional rule with override=true
override.token(partialTokens) // Partial token overrides
```
**`what` - Styling Helpers**
```typescript
what.css(classes) // CSS classes only
what.token(tokens) // Token references only
what.both(classes, tokens) // Both classes and tokens
what.variant(variant) // Type-safe variant values
what.slot(slot) // Slot configurations
```
#### Complete Example: Def vs Override in Action
```typescript
const Button = cls(
{
tokens: ["color.text", "color.bg"],
slot: ["root", "label"],
variant: {
size: ["sm", "md", "lg"],
variant: ["default", "primary"]
}
},
({ what, override, def }) => ({
token: def.token({
"color.text": {
default: ["text-gray-900"],
primary: ["text-white"]
},
"color.bg": {
default: ["bg-gray-100"],
primary: ["bg-blue-600"]
}
}),
rules: [
// Base styles (accumulative)
def.root({
root: what.both(["inline-flex", "items-center"], ["color.text.default", "color.bg.default"]),
label: what.css(["font-medium"])
}),
// Size variants (accumulative - adds to base)
def.rule(
what.variant({ size: "lg" }),
{
root: what.css(["px-6", "py-3"]) // Adds larger padding
}
),
// Primary variant (override - replaces base colors)
override.rule(
what.variant({ variant: "primary" }),
{
root: what.token(["color.text.primary", "color.bg.primary"]) // Replaces colors completely
}
)
],
defaults: def.defaults({
size: "md",
variant: "default"
})
})
);
```
**Result Behavior:**
- **Default button**: `"inline-flex items-center text-gray-900 bg-gray-100 font-medium"`
- **Large button**: `"inline-flex items-center text-gray-900 bg-gray-100 font-medium px-6 py-3"` (adds padding)
- **Primary button**: `"inline-flex items-center text-white bg-blue-600 font-medium"` (replaces colors)
- **Large primary button**: `"inline-flex items-center text-white bg-blue-600 font-medium px-6 py-3"` (replaces colors, adds padding)
### 5.3 Tokens <a id="53-tokens"></a>
Tokens are the **foundation of your design system** - they represent reusable design values that can be referenced throughout your components.
> **🎯 Mental Model**: Tokens = "Named design values that can be reused and inherited"
#### Token Structure
```typescript
// Token naming: group.variant
// In contract: flat array of token names
tokens: [
"color.text.default", "color.text.primary", "color.text.secondary",
"color.bg.default", "color.bg.primary", "color.bg.secondary",
"spacing.padding.sm", "spacing.padding.md", "spacing.padding.lg"
]
// In definition: each token gets a What<T> object
token: {
"color.text.default": what.css(["text-gray-900"]),
"color.text.primary": what.css(["text-white"]),
"color.text.secondary": what.css(["text-gray-700"]),
"color.bg.default": what.css(["bg-gray-100"]),
"color.bg.primary": what.css(["bg-blue-600"]),
"color.bg.secondary": what.css(["bg-gray-200"])
}
```
#### How Tokens Work
**🔍 Token Resolution Process**
1. **Lookup**: Find token in current contract or inheritance chain
2. **Selection**: Choose appropriate variant based on current state
3. **Extraction**: Get CSS classes from token definition
4. **Combination**: Merge multiple tokens if needed
5. **Override**: Apply any runtime overrides
**⚡ Key Features**
- **Recursive Resolution**: Tokens can reference other tokens
- **Circular Detection**: Automatic prevention of infinite loops
- **Inheritance**: Tokens follow contract hierarchy
- **Runtime Overrides**: Values can be changed at creation time
#### Token Inheritance Behavior
> **💡 Important**: Token inheritance is **always replacement-based**, not accumulation-based
**How Token Inheritance Actually Works**
```typescript
// Parent contract
{
tokens: ["color.bg.default"],
token: {
"color.bg.default": what.css(["bg-gray-100"])
}
}
// Child contract
{
tokens: ["color.bg.default"], // Same token declared
token: {
"color.bg.default": what.css(["bg-blue-100"]) // REPLACES parent definition
}
}
// Result: Child definition wins, parent is discarded
// Final token value: "bg-blue-100"
```
**Key Points:**
- **Token definitions are merged by direct assignment** - child values replace parent values
- **Type system enforces implementation** - when you declare a token in a child contract, you must provide its definition
- **Contract declarations affect type safety** - they ensure all required tokens are implemented
- **Inheritance order matters** - later definitions override earlier ones
> **⚠️ Important Distinction**: Token inheritance is **different** from variant and default inheritance:
> - **Tokens**: Direct replacement (child replaces parent) + Type enforcement
> - **Variants**: Union merging (child combines with parent)
> - **Defaults**: Object merging (child overrides parent)
#### Token Override Examples
**Runtime Token Overrides**
```typescript
// Override specific tokens at creation time
const buttonClasses = Button.create(({ what }) => ({
token: {
"color.text.primary": what.css(["text-blue-600"]) // Override only primary text
// Other tokens remain unchanged
}
}));
```
**Component-Level Overrides**
```typescript
const MyComponent = ({ cls }: Component<typeof Button>) => {
return (
<button className={cls(({ what }) => ({
token: {
"color.bg.primary": what.css(["bg-indigo-600"]) // Override primary background
}
}))}>
Click me
</button>
);
};
```
#### Advanced: Token Chain Resolution
> **🚀 Power Feature**: Tokens can reference other tokens, creating dependency chains
**Simple Token Chain**
```typescript
// Base tokens
"color.bg.primary": what.css(["bg-blue-600"]),
"color.text.primary": what.css(["text-white"]),
// Composite token that references base tokens
"button.primary": what.token([
"color.bg.primary", // References another token
"color.text.primary" // References another token
])
```
**Multi-Level Token Chain**
```typescript
// Level 1: Base spacing
"spacing.sm": what.css(["px-2", "py-1"]),
"spacing.md": what.css(["px-4", "py-2"]),
// Level 2: Padding tokens that reference spacing
"padding.small": what.token(["spacing.sm"]),
"padding.medium": what.token(["spacing.md"]),
// Level 3: Button tokens that reference padding
"button.small": what.both(
["rounded", "font-medium"], // CSS classes
["padding.small"] // References padding.small → spacing.sm
)
```
**Circular Dependency Protection**
```typescript
// ❌ This would cause an error
{
"token.a": what.token(["token.b"]),
"token.b": what.token(["token.c"]),
"token.c": what.token(["token.a"]) // Circular reference!
}
// Error: Circular dependency detected in token references
```
#### Complete Example: Button with Token Chains
```typescript
const ButtonWithTokens = cls(
{
tokens: [
"color.bg.primary", "color.text.primary",
"button.base", "button.primary"
],
slot: ["root"],
variant: {
variant: ["default", "primary"]
}
},
({ what, def }) => ({
token: def.token({
// Base design tokens
"color.bg.primary": what.css(["bg-blue-600"]),
"color.text.primary": what.css(["text-white"]),
// Composite button tokens
"button.base": what.both(
["px-4", "py-2", "rounded", "font-medium"], // Layout
["color.text.primary"] // Design
),
"button.primary": what.token([
"button.base", // References base button
"color.bg.primary" // References primary background
])
}),
rules: [
def.root({
root: what.token(["button.base"])
}),
def.rule(
what.variant({ variant: "primary" }),
{
root: what.token(["button.primary"])
}
)
]
})
);
// Usage - token resolution happens automatically
const instance = ButtonWithTokens.create();
console.log(instance.root()); // "px-4 py-2 rounded font-medium text-white"
const primaryInstance = ButtonWithTokens.create(({ what }) => ({
variant: what.variant({ variant: "primary" })
}));
console.log(primaryInstance.root()); // "px-4 py-2 rounded font-medium text-white bg-blue-600"
```
### 5.4 Slots <a id="54-slots"></a>
Slots represent named parts of a component that can receive independent styling:
```typescript
// Component slots
["root", "icon", "label", "badge"]
```
**Slot Behavior:**
- Each slot is a function that returns CSS classes
- Slots can receive variant overrides
- Slots can be overridden at creation time
- Slots support both class and token assignments
### 5.5 Variants <a id="55-variants"></a>
Variants are configurable properties that control component appearance:
```typescript
// Variant definitions
{
size: ["sm", "md", "lg"],
variant: ["primary", "secondary"],
disabled: ["bool"] // Special "bool" type becomes boolean
}
```
**Variant Types:**
- **String Variants**: Discrete values (size, variant, etc.)
- **Boolean Variants**: True/false states (disabled, loading, etc.)
- **Default Values**: Predefined fallbacks for each variant
### 5.6 Rules <a id="56-rules"></a>
Rules define **conditional styling** based on variant combinations. They're the heart of dynamic styling in CLS.
> **🎯 Mental Model**: Rules = "Apply these styles when these conditions are met"
#### Rule Structure
```typescript
// Basic rule structure
{
match: what.variant({ size: "lg", variant: "primary" }), // When to apply
slot: {
root: what.css(["text-lg"]), // What to apply
label: what.token(["color.bg.primary"])
},
override: false // Optional: how to apply (append vs replace)
}
```
#### Rule Matching Behavior
**🔍 How Rules Work**
1. **Condition Testing**: Check if current variants match the rule
2. **Slot Application**: Apply styling to matching slots
3. **Order Processing**: Rules are evaluated in definition order
4. **Override Handling**: Clear previous styles if override is true
**⚡ Key Features**
- **Multiple Rules**: Multiple rules can apply to the same variant combination
- **Order Matters**: Later rules take precedence over earlier ones
- **Boolean Variants**: True/false values are matched against boolean variants
- **Partial Matching**: Rules can match on subset of variants
#### Override vs Append Behavior
> **💡 Critical Distinction**: Rules can either add to or replace previous styles
**Append Mode (Default)**
```typescript
// ✅ Adds to existing styles
def.rule(
what.variant({ size: "lg" }),
{
root: what.css(["px-6", "py-3"]) // Adds padding to existing styles
}
)
// Result: Previous styles + new padding
```
**Override Mode (Destructive)**
```typescript
// ✅ Replaces all previous styles
override.rule(
what.variant({ size: "lg" }),
{
root: what.css(["px-6", "py-3"]) // ONLY these styles, nothing else
}
)
// Result: Only the new padding, previous styles discarded
```
#### Real-World Rule Examples
**Size-Based Styling**
```typescript
// Different padding for different sizes
def.rule(what.variant({ size: "sm" }), {
root: what.css(["px-2", "py-1"])
}),
def.rule(what.variant({ size: "md" }), {
root: what.css(["px-4", "py-2"])
}),
def.rule(what.variant({ size: "lg" }), {
root: what.css(["px-6", "py-3"])
})
```
**Variant-Based Styling**
```typescript
// Different colors for different variants
def.rule(what.variant({ variant: "primary" }), {
root: what.token(["color.bg.primary", "color.text.primary"])
}),
def.rule(what.variant({ variant: "secondary" }), {
root: what.token(["color.bg.secondary", "color.text.secondary"])
})
```
**Complex Conditional Styling**
```typescript
// Multiple conditions combined
def.rule(what.variant({ size: "lg", variant: "primary" }), {
root: what.both(
["px-8", "py-4", "text-lg"], // Size-specific layout
["color.bg.primary", "color.text.primary"] // Variant-specific colors
)
})
```
**Boolean Variant Styling**
```typescript
// Boolean variants for state-based styling
def.rule(what.variant({ disabled: true }), {
root: what.css(["opacity-50", "cursor-not-allowed"]),
label: what.css(["line-through"])
})
```
---
## 6. CLS Instance Methods <a id="6-cls-instance-methods"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: Key Concepts](#5-key-concepts)** | **[→ Next Chapter: Contract Structure](#7-contract-structure)**
### 6.1 `create(userConfigFn?, internalConfigFn?)` <a id="61-create-method"></a>
Generates styled instances with optional overrides. Both parameters are **callback functions** that receive the `what` utility.
**Parameters:**
- `userConfigFn`: Callback function that receives [`{ what }`](#51-what-utility) and returns user configuration
- `internalConfigFn`: Callback function that receives [`{ what }`](#51-what-utility) and returns internal configuration
**Configuration Options:**
- **`variant`**: Override variant values
- **`slot`**: Override slot styling (append mode)
- **`override`**: Hard override slot styling (replace mode)
- **`token`**: Override token definitions
> **Note:** The [`what`](#51-what-utility) utility should be used for `slot`, `override`, and `variant` options as it provides proper type-checks and ensures type safety. The `what.variant()` helper is particularly useful for ensuring variant values are correctly typed.
**Slot Configuration Behavior:**
- **`slot` configurations append** classes and tokens to existing slot styling
- **`override` configurations replace** all previous slot styling completely
- **Multiple slot sources combine** by appending their classes/tokens (user config + internal config + component rules)
- **Slot merging preserves order**: Component rules → Internal slot config → User slot config
**Precedence Rules:**
1. User config takes precedence over internal config
2. Override config takes precedence over slot config
3. Later rules take precedence over earlier rules
4. Local slot overrides take precedence over global overrides
> **For detailed usage examples, see [Section 9: Create Method Usage](#9-create-method-usage)**
**Example:**
```typescript
// Basic usage with variants
const classes = Button.create(({ what }) => ({
variant: what.variant({ variant: "primary", size: "lg" })
}));
// Using what.variant() for type-safe variant values
const classes = Button.create(({ what }) => ({
variant: what.variant({ variant: "primary", size: "lg" })
}));
// With slot overrides using what utility
const classes = Button.create(({ what }) => ({
variant: what.variant({ variant: "primary" }),
slot: {
icon: what.css(["mr-2", "animate-spin"]),
label: what.token(["color.text.hover"])
}
}));
// With token overrides
const classes = Button.create(({ what }) => ({
token: {
"color.text.primary": what.css(["text-blue-600"])
}
}));
// With hard overrides
const classes = Button.create(({ what }) => ({
override: {
root: what.css(["bg-red-500", "text-white"])
}
}));
// Combined user and internal configs
const classes = Button.create(
({ what }) => ({
variant: what.variant({ variant: "primary" })
}),
({ what }) => ({
slot: {
root: what.css(["shadow-lg"])
}
})
);
```
### 6.1.1 Slot Configuration Merging <a id="611-slot-configuration-merging"></a>
Understanding how slot configurations are merged is crucial for building composable components. CLS uses an **append-based merging strategy** for slot configurations.
#### How Slot Merging Works
**🔍 Merging Strategy:**
1. **Component Rules**: Base styling from component definition
2. **Internal Config**: Component-controlled slot overrides (append)
3. **User Config**: User-provided slot overrides (append)
4. **Override Config**: Hard overrides that replace everything (replace)
**⚡ Key Behavior:**
- **`slot` configurations append** classes and tokens to existing styling
- **`override` configurations replace** all previous styling completely
- **Multiple sources combine** by appending their classes/tokens in order
- **Class arrays are merged** using efficient concatenation
#### Real-World Example: Icon Component
```typescript
// Icon component definition
const IconCls = cls(
{
tokens: ["color.text.default"],
slot: ["root"],
variant: { size: ["sm", "md", "lg"] }
},
({ what, def }) => ({
token: def.token({
"color.text.default": what.css(["text-gray-600"])
}),
rules: [
def.root({
root: what.both(["icon-base"], ["color.text.default"]) // Base styling
}),
def.rule(
what.variant({ size: "lg" }),
{ root: what.css(["w-6", "h-6"]) } // Size-specific styling
)
]
})
);
// Icon component with internal and user configs
function Icon({ icon, cls, ...props }) {
const slots = useCls(
IconCls,
cls, // User config function
isString(icon) ? ({ what }) => ({
slot: what.slot({
root: what.css([icon]) // Internal config: append icon class
})
}) : undefined
);
return <div className={slots.root()} {...props} />;
}
// Usage with user config
<Icon
icon="icon-[mdi-light--home]"
cls={({ what }) => ({
slot: what.slot({
root: what.css(["animate-pulse"]) // User config: append animation
})
})}
/>
// Result: "icon-base text-gray-600 icon-[mdi-light--home] animate-pulse"
// Order: Component rules → Internal config → User config
```
#### Slot vs Override: The Difference
**Slot Configuration (Append Mode):**
```typescript
// ✅ Adds to existing styles
const classes = Button.create(({ what }) => ({
slot: {
root: what.css(["mr-2", "animate-spin"]) // Appends to existing root styles
}
}));
// Result: Previous styles + new styles
```
**Override Configuration (Replace Mode):**
```typescript
// ✅ Replaces all previous styles
const classes = Button.create(({ what }) => ({
override: {
root: what.css(["bg-red-500", "text-white"]) // Replaces all root styles
}
}));
// Result: Only the new styles, previous styles discarded
```
#### React Integration: useCls Merging
The `useCls` hook combines multiple configuration sources:
```typescript
// useCls(tva, userConfigFn, internalConfigFn)
const slots = useCls(
ButtonCls, // CLS instance
({ what }) => ({ // User config (cls prop)
slot: what.slot({
root: what.css(["user-class"])
})
}),
({ what }) => ({ // Internal config (component logic)
slot: what.slot({
root: what.css(["internal-class"])
})
})
);
// Result: Component rules + internal-class + user-class
```
### 6.2 `extend(childContract, childDefinitionFn)` <a id="62-extend-method"></a>
Creates new CLS instances with additional functionality, inheriting from a parent.
**Parameters:**
- `childContract`: Extended contract with new tokens, slots, or variants
- `childDefinitionFn`: Callback function that receives the `what` utility and returns the child definition
**Example:**
```typescript
const PrimaryButton = Button.extend(
{
tokens: ["color.text.default", "color.text.primary", "color.text.secondary", "color.bg.default", "color.bg.primary", "color.bg.secondary"],
slot: ["root", "label", "icon"],
variant: {
size: ["sm", "md", "lg", "xl"],
variant: ["default", "primary", "secondary"],
loading: ["bool"]
}
},
({ what, def }) => ({
token: def.token({
"color.text.default": what.css(["text-gray-900"]),
"color.text.primary": what.css(["text-white"]),
"color.text.secondary": what.css(["text-gray-700"]),
"color.bg.default": what.css(["bg-gray-100"]),
"color.bg.primary": what.css(["bg-blue-600"]),
"color.bg.secondary": what.css(["bg-gray-200"])
}),
rules: [
def.root({
root: what.both(["inline-flex", "items-center", "rounded-md"], ["color.text.default", "color.bg.default"]),
label: what.css(["font-medium"]),
icon: what.css(["mr-2"])
}),
def.rule(
what.variant({ size: "xl" }),
{
root: what.css(["px-8", "py-4", "text-lg"])
}
),
def.rule(
what.variant({ loading: true }),
{
root: what.css(["opacity-75", "cursor-not-allowed"]),
icon: what.css(["animate-spin"])
}
)
],
defaults: def.defaults({
size: "md",
variant: "primary",
loading: false
})
})
);
```
### 6.3 `use(sub)` <a id="63-use-method"></a>
Provides type-safe assignment of compatible CLS instances.
**Parameters:**
- `sub`: A CLS instance that must be derived from the current instance
**Returns:** The current CLS instance for chaining
**Example:**
```typescript
const ButtonGroup = Button.use(PrimaryButton);
// ButtonGroup now has access to PrimaryButton's extended functionality
```
---
## 7. Contract Structure <a id="7-contract-structure"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: CLS Instance Methods](#6-cls-instance-methods)** | **[→ Next Chapter: Definition Structure](#8-definition-structure)**
### 7.1 Token Contract <a id="71-token-contract"></a>
```typescript
type TokenContract = readonly string[];
// Example - flat array of token names
[
"color.text.default", "color.text.primary", "color.text.secondary",
"color.bg.default", "color.bg.primary", "color.bg.secondary",
"spacing.padding.sm", "spacing.padding.md", "spacing.padding.lg"
]
```
**Note**: The actual implementation uses a flat array of token names rather than a nested object structure. This simplifies the type system and makes token inheritance more straightforward.
### 7.2 Slot Contract <a id="72-slot-contract"></a>
```typescript
type SlotContract = readonly string[];
// Example
["root", "icon", "label", "badge"]
```
### 7.3 Variant Contract <a id="73-variant-contract"></a>
```typescript
type VariantContract = Record<string, readonly string[]>;
// Example
{
size: ["sm", "md", "lg"],
variant: ["primary", "secondary"],
disabled: ["bool"] // Special "bool" type becomes boolean
}
```
---
## 8. Definition Structure <a id="8-definition-structure"></a>
**[↑ Back to Top](#table-of-contents)** | **[← Previous Chapter: Contract Structure](#7-contract-structure)** | **[→ Next Chapter: Create Method Usage](#9-create-method-usage)**
### 8.1 Token Definition <a id="81-token-definition"></a>
Token definitions use `What<T>` objects that can contain CSS classes, token references, or both:
```typescript
type TokenDefinitionRequired<TContract> = {
[K in TContract["tokens"][number]]: What<TContract>;
};
// Example - Token definitions use What<T> objects
{
"color.text.default": what.css(["text-gray-900"]),
"color.text.primary": what.css(["text-white"]),
"color.text.secondary": what.css(["text-gray-700"]),
"color.bg.default": what.css(["bg-gray-100"]),
"color.bg.primary": what.css(["bg-blue-600"]),
"color.bg.secondary": what.css(["bg-gray-200"])
}
```
#### Token Chain Resolution Examples
> **🚀 Advanced Feature**: Tokens can reference other tokens, creating powerful dependency chains
**Simple Token References**
```typescript
// Base color tokens
"color.bg.primary": what.css(["bg-blue-600"]),
"color.text.primary": what.css(["text-white"]),
// Button token that references color tokens
"button.primary": what.token([
"color.bg.primary", // References another token
"color.text.primary" // References another token
])
```
**Multi-Level Token Chains**
```typescript
// Level 1: Base spacing tokens
"spacing.xs": what.css(["px-1", "py-0.5"]),
"spacing.sm": what.css(["px-2", "py-1"]),
"spacing.md": what.css(["px-4", "py-2"]),
"spacing.lg": what.css(["px-6", "py-3"]),
// Level 2: Padding tokens that reference spacing
"padding.small": what.token(["spacing.sm"]),
"padding.medium": what.token(["spacing.md"]),
"padding.large": what.token(["spacing.lg"]),
// Level 3: Button tokens that reference padding
"button.small": what.both(
["rounded", "font-medium"], // CSS classes
["padding.small"] // References padding.small → spacing.sm
),
"button.medium": what.both(
["rounded", "font-medium"], // CSS classes
["padding.medium"] // References padding.medium → spacing.md
),
"button.large": what.both(
["rounded", "font-medium"], // CSS classes
["padding.large"] // References padding.large → spacing.lg
)
```
#### Circular Dependency Protection
> **🛡️ Safety Feature**: The system automatically detects and prevents circular dependencies
```typescript
// ❌ This would cause a circular dependency error
{
"token.a": what.token(["token.b"]),
"token.b": what.token(["token.c"]),
"token.c": what.token(["token.a"]) // Circular reference!
}
// Error: Circular dependency detected in token references: token.a -> token.b -> token.c -> token.a
```
#### Complete Component Example
```typescript
const ButtonWithTokenChains = cls(
{
tokens: [
"color.bg.primary", "color.bg.secondary",
"color.text.primary", "color.text.secondary",
"button.base", "button.primary", "button.secondary"
],
slot: ["root"],
variant: {
variant: ["default", "primary", "secondary"]
}
},
({ what, def }) => ({
token: def.token({
// Base color tokens
"color.bg.primary": what.css(["bg-blue-600"]),
"color.bg.secondary": what.css(["bg-gray-600"]),
"color.text.primary": what.css(["text-white"]),
"color.text.secondary": what.css(["text-gray-200"]),
// Base button token that references color tokens
"button.base": what.both(
["px-4", "py-2", "rounded", "font-medium"], // Layout classes
["color.text.primary"] // Design token
),
// Primary button that references base button and primary colors
"button.primary": what.token([
"button.base", // References base button
"color.bg.primary" // References primary background
]),
// Secondary button that references base button and secondary colors
"button.secondary": what.token([
"button.base", // References base button
"color.bg.secondary", // References secondary background
"color.text.secondary" // References secondary text
])
}),
rules: [
def.root({
root: what.token(["button.base"])
}),
def.rule(
what.variant({ variant: "primary" }),
{
root: what.token(["button.primary"])
}
),
def.rule(