vwf .

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 kept

Usage 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 TypeExamplesResolution
Paddingp-4 vs p-8, px-2 vs px-6Keep last
Backgroundbg-white vs bg-skyKeep last
Border Radiusrounded-lg vs rounded-xlKeep last
Text Colortext-midnight vs text-skyKeep last
Width/Heightw-full vs w-1/2Keep 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