UNPKG

woby

Version:

A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.

276 lines (218 loc) 8.06 kB
# Class Management in Woby Woby provides powerful built-in class management that supports complex class expressions with full reactive observable support, similar to popular libraries like `classnames` and `clsx`. ## Overview Unlike other frameworks that require external libraries for advanced class management, Woby has this functionality built-in. This eliminates the need for dependencies like `classnames` or `clsx` while providing the same powerful features with full reactivity support. ## Supported Class Expression Types ### 1. String Classes Simple string classes work exactly as you'd expect: ```tsx <div class="button primary">Click me</div> ``` ### 2. Array-based Classes Arrays allow you to combine multiple classes, including conditional ones: ```tsx // Simple array <div class={['button', 'primary']}>Click me</div> // Array with conditional classes const isActive = $(false) <div class={['button', isActive() ? 'active' : 'inactive']}>Toggle me</div> // Nested arrays <div class={['base', ['secondary', ['tertiary']]]}>Nested classes</div> // Complex mixed array <div class={[ "red", () => ($$(value) % 2 === 0 ? "bold" : ""), { hidden: true, italic: false }, ['hello', ['world']] ]}>Complex classes</div> ``` ### 3. Object-based Classes Objects provide a clean way to conditionally apply classes where keys are class names and values are boolean conditions: ```tsx const error = $(false) const warning = $(false) const disabled = $(true) <div class={{ 'base': true, // Always applied 'error': error, // Applied when error is truthy 'warning': warning, // Applied when warning is truthy 'disabled': disabled, // Applied when disabled is truthy 'success': !error && !warning // Applied when neither error nor warning }}>Status element</div> ``` ### 4. Function-based Classes Functions allow for dynamic class computation: ```tsx const count = $(0) // Simple function <div class={() => count() > 5 ? 'high-count' : 'low-count'}> Count: {count} </div> // Function returning complex expression <div class={() => [ 'base', count() > 10 ? 'large' : 'small', { 'even': count() % 2 === 0 } ]}> Dynamic element </div> ``` ### 5. Observable-based Classes Woby's reactivity system works seamlessly with class expressions: ```tsx const theme = $('dark') const size = $('medium') const isActive = $(false) <div class={[ 'button', () => `theme-${$$(theme)}`, // Reactive theme class () => `size-${$$(size)}`, // Reactive size class { 'active': isActive } // Reactive boolean class ]}> Themed button </div> ``` ## Advanced Patterns ### Conditional Styling with Complex Logic ```tsx const status = $('success') const isLoading = $(false) const isUrgent = $(false) const statusClass = useMemo(() => { switch ($$(status)) { case 'success': return 'bg-green-100 text-green-800' case 'warning': return 'bg-yellow-100 text-yellow-800' case 'error': return 'bg-red-100 text-red-800' default: return 'bg-gray-100 text-gray-800' } }) <div class={[ 'inline-flex items-center px-3 py-1 rounded-full text-sm font-medium', statusClass, { 'animate-pulse': isLoading, 'border-2 border-red-500': isUrgent && status === 'error' } ]}> <span class={[ 'w-2 h-2 rounded-full mr-2', () => { switch ($$(status)) { case 'success': return 'bg-green-500' case 'warning': return 'bg-yellow-500' case 'error': return 'bg-red-500' default: return 'bg-gray-500' } } ]} /> {status} </div> ``` ### Integration with Tailwind CSS Woby works seamlessly with Tailwind CSS for utility-first styling: ```tsx const Button = ({ variant = 'primary', size = 'md', disabled = false, children }) => { return ( <button class={[ // Base classes 'inline-flex items-center justify-center rounded-md font-medium transition-colors', // Size variants () => { switch ($$(size)) { case 'sm': return 'text-xs px-2.5 py-1.5' case 'lg': return 'text-base px-6 py-3' case 'xl': return 'text-lg px-8 py-4' default: // md return 'text-sm px-4 py-2' } }, // Variant styles () => { if ($$(disabled)) { return 'bg-gray-100 text-gray-400 cursor-not-allowed' } switch ($$(variant)) { case 'secondary': return 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50' case 'danger': return 'bg-red-600 text-white hover:bg-red-700' case 'ghost': return 'bg-transparent text-gray-700 hover:bg-gray-100' default: // primary return 'bg-blue-600 text-white hover:bg-blue-700' } }, // Additional states { 'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2': true, 'opacity-75': disabled } ]}> {children} </button> ) } ``` ## Reactive Elements and useMemo All reactive elements in class expressions should be wrapped in `useMemo` or arrow functions `() =>` to ensure proper reactivity: ```tsx // ✅ Correct - wrapped in arrow function const isActive = $(false) <div class={() => $$(isActive) ? 'active' : 'inactive'}>Content</div> // ✅ Correct - observables automatically handled in objects <div class={{ 'active': isActive }}>Content</div> // ✅ Complex expression with useMemo const dynamicClass = useMemo(() => ({ 'active': $$(isActive), 'disabled': $$(isDisabled), 'loading': $$(isLoading) })) <div class={dynamicClass}>Content</div> // ❌ Incorrect - will not update reactively <div class={{ 'active': isActive() ? 'yes' : 'no' }}>Content</div> ``` ## Migration from CLSX/Classnames If you're familiar with `clsx` or `classnames`, Woby's class system works similarly: ```tsx // Instead of: clsx('foo', true && 'bar', 'baz') <div class={['foo', true && 'bar', 'baz']}>Content</div> // Instead of: clsx({ foo:true, bar:false, baz:isTrue() }) <div class={{ foo:true, bar:false, baz:isTrue() }}>Content</div> // Instead of: clsx(['foo', 0, false, 'bar']) <div class={['foo', 0, false, 'bar']}>Content</div> ``` ## Performance Considerations Woby's class management is optimized for performance: 1. **Fine-grained updates**: Only classes that change are updated in the DOM 2. **Memoization**: Complex expressions are memoized to prevent unnecessary recalculations 3. **Direct DOM manipulation**: No virtual DOM overhead 4. **Automatic dependency tracking**: Woby knows exactly what needs to be updated ## Best Practices 1. **Use objects for simple conditionals**: `{ 'class': boolean }` is clean and readable 2. **Use arrays for complex combinations**: Arrays allow mixing different types 3. **Wrap reactive expressions**: Use `() =>` or `useMemo` for reactive values 4. **Leverage Tailwind**: Woby's class system works excellently with utility-first CSS 5. **Keep it readable**: Complex class expressions can become hard to maintain ## Comparison with Other Libraries | Feature | Woby | CLSX | Classnames | |---------|------|------|------------| | Built-in | ✅ | ❌ | ❌ | | Reactivity | ✅ | ❌ | ❌ | | Array support | ✅ | ✅ | ✅ | | Object support | ✅ | ✅ | ✅ | | Function support | ✅ | ✅ | ✅ | | Nested arrays | ✅ | ✅ | ✅ | | Performance | ✅✅ | ✅ | ✅ | | Bundle size | Included | 239B | 500B+ | Woby's built-in class management provides all the features of external libraries while adding reactivity support and eliminating the need for additional dependencies.