Utilities & Helpers
Flexible argument parsing, intelligent tailwind css overwrite for extreme flexibility, universal value resolver
🔧 parseArgs()
Flexible component argument parsing that enables React-like component APIs with multiple calling patterns.
All Supported Calling Patterns
// Pattern 1: No arguments
Component();
// Pattern 2: Props only
Component({ variant: 'primary', size: 'lg' });
// Pattern 3: Children only (string)
Component('Hello World');
// Pattern 4: Children only (elements)
Component(Icon(), 'Text', Button());
// Pattern 5: Props + children (all types)
Component({ variant: 'primary' }, 'Hello', Icon());Implementation Example
import { parseArgs } from '../utils.js';
export const MyComponent = (...args) => {
// Extract props and children
const [props, children] = parseArgs(args);
// Destructure props with defaults
const {
variant = 'default',
size = 'md',
class: className,
...htmlProps
} = props;
return div(
{
...htmlProps,
class: cx(baseClass, variantClass[variant], className)
},
...children
);
};How It Works
- • If first arg is an object → extracts as props
- • If first arg is not an object → all args become children
- • Returns [props, children] array for destructuring
- • Always returns valid props object (defaults to {})
- • Always returns children array (defaults to [])
🎨 cx()
Smart class name joiner that filters out falsy values for clean conditional styling.
Basic Usage
import { cx } from '../utils.js';
// Simple concatenation
cx('btn', 'btn-primary', 'text-white')
// → 'btn btn-primary text-white'
// Conditional classes (falsy values filtered out)
const isActive = true;
const isDisabled = false;
cx(
'btn',
isActive && 'btn-active',
isDisabled && 'btn-disabled',
'px-4 py-2'
)
// → 'btn btn-active px-4 py-2'
// With dynamic values
cx(
'base-class',
variant === 'primary' && 'bg-sky',
variant === 'secondary' && 'bg-earth',
size === 'lg' && 'text-lg',
className // User's custom classes
)Real Component Example
export const Button = (...args) => {
const [props, children] = parseArgs(args);
const {
variant = 'default',
size = 'md',
disabled = false,
loading = false,
class: className,
...htmlProps
} = props;
return button(
{
...htmlProps,
class: cx(
'btn', // Base class
variant === 'solid' && 'btn-solid',
variant === 'outline' && 'btn-outline',
size === 'sm' && 'text-sm px-2 py-1',
size === 'lg' && 'text-lg px-6 py-3',
disabled && 'opacity-50 cursor-not-allowed',
loading && 'animate-pulse',
className // User overrides
),
disabled: disabled || loading
},
loading ? 'Loading...' : children
);
};Key Features
- • Automatically filters: false, null, undefined, empty
- • Handles arrays and nested values
- • Returns single space-separated string
- • Perfect for conditional styling with && operator
- • No dependencies - pure JavaScript
⚡ filterClassName()
Intelligent Tailwind CSS class conflict resolution that allows user overrides while preventing duplicate utilities.
The Problem It Solves
// Without filterClassName - conflicts!
const baseClass = 'p-4 bg-white rounded-lg';
const userClass = 'p-8 bg-sky rounded-xl';
// Result: 'p-4 bg-white rounded-lg p-8 bg-sky rounded-xl'
// Browser sees: p-8 (last wins) but p-4 is still in DOM
// With filterClassName - smart resolution!
filterClassName(baseClass, userClass)
// Result: 'bg-sky rounded-xl p-8'
// Conflicts removed, only final values keptUsage in Components
import { filterClassName } from '../utils.js';
export const Card = (...args) => {
const [props, children] = parseArgs(args);
const { class: className, ...htmlProps } = props;
const baseClass = 'p-4 bg-white rounded-lg shadow-sm border border-mist';
return div(
{
...htmlProps,
class: filterClassName(baseClass, className)
},
...children
);
};
// Usage examples:
Card({ class: 'p-8' })
// → 'bg-white rounded-lg shadow-sm border border-mist p-8'
Card({ class: 'bg-sky/10 rounded-xl' })
// → 'p-4 shadow-sm border border-mist bg-sky/10 rounded-xl'
Card({ class: 'p-6 bg-gradient-to-r from-sky to-earth' })
// → 'rounded-lg shadow-sm border border-mist p-6 bg-gradient-to-r from-sky to-earth'Conflict Detection Rules
| Utility Type | Examples | Resolution |
|---|---|---|
| Padding | p-4 vs p-8, px-2 vs px-6 | Keep last |
| Background | bg-white vs bg-sky | Keep last |
| Border Radius | rounded-lg vs rounded-xl | Keep last |
| Text Color | text-midnight vs text-sky | Keep last |
| Width/Height | w-full vs w-1/2 | Keep last |
Best Practices
- • Use in components for flexible styling (Card, Input, Modal)
- • Always put base classes first, user classes last
- • Combine with utility classes (vwfcolor-*) for consistency
- • Document which classes can be overridden
- • Keep base classes minimal - user has full Tailwind power
🔄 val()
Universal value resolver that normalizes primitives, state objects, and functions into consistent values.
What It Does
import { val } from '../utils.js';
// Primitives - returns as-is
val('hello') // → 'hello'
val(42) // → 42
val(true) // → true
// VanJS state - extracts .val
const count = van.state(5);
val(count) // → 5
// Functions - calls and returns result
val(() => 'computed') // → 'computed'
val(() => count.val * 2) // → 10
// Edge cases
val(null) // → null
val(undefined) // → undefined
val({}) // → {} (objects returned as-is)Use Cases
// Accepting flexible prop types
export const Component = (...args) => {
const [props] = parseArgs(args);
const {
text, // Could be string, state, or function
count, // Could be number, state, or function
enabled // Could be boolean, state, or function
} = props;
return div(
{ class: 'component' },
p('Text: ', val(text)),
p('Count: ', val(count)),
p('Enabled: ', val(enabled) ? 'Yes' : 'No')
);
};
// All these work:
Component({ text: 'Hello', count: 5, enabled: true });
Component({ text: van.state('Hello'), count: van.state(5) });
Component({ text: () => 'Hello', count: () => 5 });When to Use
- ✅When accepting primitives, state, functions as props
- ✅When normalizing mixed value types in utilities
- ⚠️NOT for rendering - use state or arrow functions
- ❌NOT as a replacement for proper reactive functions
- ✅When extdracting values for non-reactive operations (logging, API calls)
📋 Quick Reference
When to Use Each
- • parseArgs() - All custom components
- • cx() - Conditional class names
- • filterClassName() - Flexible user styling
- • val() - Polymorphic value resolution
Import Paths
// All utilities from utils.js
import {
parseArgs,
cx,
filterClassName,
val
} from '../utils.js';✨ Best Practices
Component Design
- • Always use parseArgs for flexible APIs
- • Provide sensible defaults for all props
- • Use cx() for conditional styling
- • Document which classes can be overridden
Styling Strategy
- • Use filterClassName for flexible components
- • Combine with vwfcolor-* utilities
- • Keep base classes minimal
- • Let users have full Tailwind power
Performance
- • All utilities are pure functions
- • No dependencies or external libs
- • Minimal performance overhead
- • Tree-shakeable imports
Common Patterns
- • parseArgs → cx → filterClassName
- • Extract props, merge classes, apply
- • Spread htmlProps for DOM attributes
- • Spread children for flexible content
Explore vwf
Discover what vwf can do