` // Set classList property
+
+// Property binding sets both property AND attribute (unless object/boolean)
+```
+
+**Special Cases:**
+```javascript
+// SVG support
+html`
`
+
+// Self-closing tags
+html`

`
+
+// Comments - preserved in template
+html`
Title
`
+
+// Doctype - only at root
+html`...`
+
+// Template tag
+html`
${() => repeatContent()}`
+
+// Slot handling
+html`
${() => slottedContent}`
+```
+
+### Component System: `$.component(tagName, setupFn, observedAttributes)`
+Full Web Components integration with reactive properties.
+
+**Complete Component Lifecycle:**
+```javascript
+$.component('my-counter', (props, ctx) => {
+ // 1. Initialization phase
+ console.log('Component initializing');
+
+ // Local state (not exposed as attribute)
+ const internalCount = $(0);
+
+ // Computed from props
+ const doubled = $(() => props.count() * 2);
+
+ // 2. Setup side effects
+ const stopEffect = $.effect(() => {
+ console.log('Count changed:', props.count());
+ });
+
+ // 3. Register cleanup
+ ctx.onUnmount(() => {
+ console.log('Cleaning up');
+ stopEffect();
+ });
+
+ // 4. Access DOM
+ ctx.select('button')?.classList.add('primary');
+
+ // 5. Handle slots
+ const defaultSlot = ctx.slot(); // unnamed slots
+ const headerSlot = ctx.slot('header'); // named slots
+
+ // 6. Emit events
+ const handleClick = () => {
+ ctx.emit('increment', { value: props.count() });
+ ctx.emit('counter-changed', { count: props.count() }, { bubbles: false }); // Custom options
+ };
+
+ // 7. Return template (must return Node or DocumentFragment)
+ return html`
+
+ Default header
+
+ Default content
+
+ `;
+}, ['count', 'min', 'max']); // Observed attributes
+
+// Usage examples:
+const el = document.createElement('my-counter');
+el.count = 5; // Set property
+el.setAttribute('min', '0'); // Set attribute
+el.min = 10; // Property takes precedence
+console.log(el.count); // Get property -> 10
+console.log(el.getAttribute('count')); // Get attribute -> "10"
+
+// Special attribute conversion:
+el.disabled = ''; // true (except for 'value')
+el.disabled = 'false'; // false
+el.value = ''; // "" (empty string preserved)
+el.checked = 'false'; // false
+
+// Event listening:
+el.addEventListener('increment', (e) => {
+ console.log(e.detail); // { value: x }
+});
+
+// Dynamic component creation:
+const container = document.getElementById('app');
+container.innerHTML = '
';
+
+// Or programmatically:
+const counter = new (customElements.get('my-counter'))();
+counter.count = 5;
+container.appendChild(counter);
+```
+
+**Advanced Component Patterns:**
+```javascript
+// Context/provider pattern
+$.component('theme-provider', (props, ctx) => {
+ const theme = $(props.theme());
+
+ // Provide to children via custom event
+ ctx.emit('theme-provider', { theme });
+
+ return html`
`;
+}, ['theme']);
+
+$.component('themed-button', (props, ctx) => {
+ const theme = $('light');
+
+ // Consume from parent
+ ctx.host.addEventListener('theme-provider', (e) => {
+ theme(e.detail.theme);
+ });
+
+ return html`
`;
+});
+
+// Compound components
+$.component('tabs', (props, ctx) => {
+ const activeTab = $(0);
+
+ return html`
+
+
+
+ ${() => props.tabs()[activeTab()].content}
+
+
+ `;
+}, ['tabs']);
+
+// Form association
+$.component('my-input', (props, ctx) => {
+ // Integrate with parent forms
+ const form = ctx.host.closest('form');
+ if (form) {
+ form.addEventListener('reset', () => {
+ props.value(props.defaultValue());
+ });
+ }
+
+ return html`
`;
+}, ['value', 'defaultValue']);
+```
+
+### Persistent Storage: `$.storage(key, initialValue, storage?)`
+Automatic persistence with change detection.
+
+```javascript
+// Basic localStorage
+const settings = $.storage('app-settings', { theme: 'light' });
+settings({ ...settings(), theme: 'dark' }); // Auto-saves
+
+// Session storage
+const tempData = $.storage('temp', null, sessionStorage);
+
+// Multiple stores
+const userPrefs = $.storage('user-prefs', {});
+const appState = $.storage('app-state', {});
+
+// Auto-cleanup
+const cache = $.storage('cache', {});
+cache(null); // Removes from storage
+
+// Complex objects - automatic JSON serialization
+const complex = $.storage('complex', {
+ date: new Date(),
+ regex: /pattern/,
+ nested: { array: [1,2,3] }
+});
+
+// Storage events - auto sync across tabs
+window.addEventListener('storage', (e) => {
+ if (e.key === 'app-settings') {
+ // Signal auto-updates on next read
+ }
+});
+
+// Migration pattern
+const data = $.storage('v2-data', () => {
+ // Migration from v1
+ const old = localStorage.getItem('v1-data');
+ return old ? migrate(JSON.parse(old)) : defaultValue;
+});
+
+// Computed from storage
+const config = $(() => {
+ const base = settings();
+ return { ...base, computed: derive(base) };
+});
+```
+
+### Router: `$.router(routes)` and `$.router.go()`
+Hash-based routing with parameter extraction.
+
+```javascript
+// Route definitions - comprehensive examples
+const router = $.router([
+ // Static routes
+ { path: '/', component: () => html`
` },
+ { path: '/about', component: () => html`
` },
+
+ // Parameterized routes
+ {
+ path: '/users/:id',
+ component: (params) => html`
`
+ },
+ {
+ path: '/posts/:postId/comments/:commentId',
+ component: (params) => html`
+
+ Post ${params.postId}, Comment ${params.commentId}
+
+ `
+ },
+
+ // Regex routes with capture groups
+ {
+ path: /^\/products\/(?
\w+)\/(?\d+)$/,
+ component: (params) => html`
+
+ `
+ },
+ {
+ path: /\/archive\/(\d{4})\/(\d{2})/,
+ component: (params) => html`Archive: ${params[0]}-${params[1]}`
+ },
+
+ // Query parameter handling (manual)
+ {
+ path: '/search',
+ component: () => {
+ const searchParams = new URLSearchParams(window.location.search);
+ return html``;
+ }
+ },
+
+ // Nested routers
+ {
+ path: '/dashboard',
+ component: () => {
+ return $.router([
+ { path: '/', component: dashboardHome },
+ { path: '/settings', component: dashboardSettings }
+ ]);
+ }
+ },
+
+ // Guard pattern
+ {
+ path: '/admin',
+ component: (params) => {
+ if (!isAuthenticated()) {
+ $.router.go('/login');
+ return html``;
+ }
+ return html``;
+ }
+ },
+
+ // Catch-all / 404
+ {
+ path: /.*/,
+ component: () => html``
+ }
+]);
+
+// Navigation
+$.router.go('/users/123'); // Basic
+$.router.go('users/123'); // Auto-adds leading slash
+$.router.go('/search?q=test'); // Query strings preserved
+
+// Advanced navigation
+const navigateWithState = (path, state) => {
+ history.replaceState(state, '', `#${path}`);
+ window.dispatchEvent(new HashChangeEvent('hashchange'));
+};
+
+// Route change detection
+window.addEventListener('hashchange', () => {
+ console.log('Route changed to:', window.location.hash);
+});
+
+// Current route signal (if needed)
+const currentRoute = $(() => window.location.hash.replace(/^#/, '') || '/');
+
+// Route params as signals
+const routeParams = $(null);
+$.effect(() => {
+ const match = currentRoute().match(/\/users\/(\d+)/);
+ routeParams(match ? { id: match[1] } : null);
+});
+
+// Lazy loading routes
+const router = $.router([
+ {
+ path: '/heavy',
+ component: async (params) => {
+ const module = await import('./heavy-page.js');
+ return module.default(params);
+ }
+ }
+]);
+```
+
+### Fetch: `$.fetch(url, data, loading?)`
+Simplified data fetching with loading state.
+
+```javascript
+// Basic usage
+const result = await $.fetch('/api/users', { id: 123 });
+
+// With loading signal
+const isLoading = $(false);
+const data = await $.fetch('/api/search', { query: 'test' }, isLoading);
+console.log(isLoading()); // false after completion
+
+// Error handling (returns null on error)
+const result = await $.fetch('/api/fail', {}) || fallbackData;
+
+// Integration with signals
+const userData = $(null);
+const loading = $(false);
+
+async function loadUser(id) {
+ userData(await $.fetch(`/api/users/${id}`, {}, loading));
+}
+
+// Reactive fetch with effect
+$.effect(() => {
+ const id = currentUserId();
+ loadUser(id);
+});
+
+// Retry logic wrapper
+async function fetchWithRetry(url, data, retries = 3) {
+ for (let i = 0; i < retries; i++) {
+ const result = await $.fetch(url, data);
+ if (result) return result;
+ await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
+ }
+ return null;
+}
+
+// Request cancellation
+let abortController = null;
+
+$.effect(() => {
+ if (abortController) abortController.abort();
+ abortController = new AbortController();
+
+ const id = userId();
+ fetch(`/api/user/${id}`, { signal: abortController.signal })
+ .then(r => r.json())
+ .then(data => userData(data));
+});
+```
+
+### WebSocket: `$.ws(url, options)`
+Full-featured WebSocket client with reactive state.
+
+```javascript
+// Basic connection
+const ws = $.ws('wss://api.example.com/ws');
+
+// With options
+const ws = $.ws('wss://api.example.com/ws', {
+ reconnect: true,
+ maxReconnect: 10,
+ reconnectInterval: 2000 // Base interval for exponential backoff
+});
+
+// Reactive state
+$.effect(() => {
+ console.log('Status:', ws.status()); // 'connecting', 'connected', 'disconnected', 'error'
+ console.log('Messages:', ws.messages()); // Array of received messages
+ console.log('Error:', ws.error()); // Last error or null
+});
+
+// Sending messages
+ws.send({ type: 'join', room: 'general' });
+ws.send('plain text message');
+
+// Auto-reconnect with backoff
+// Attempts: 2s, 4s, 8s, 16s, 32s (until maxReconnect)
+
+// Message filtering
+const commands = $(() => ws.messages().filter(m => m.type === 'command'));
+const events = $(() => ws.messages().filter(m => m.type === 'event'));
+
+// Send with acknowledgment
+async function sendWithAck(data, timeout = 5000) {
+ return new Promise((resolve, reject) => {
+ const id = Math.random();
+ const message = { ...data, id };
+
+ const handler = (msg) => {
+ if (msg.ack === id) {
+ ws.messages.off(handler);
+ resolve(msg);
+ }
+ };
+
+ // Need message event listener pattern
+ const timeoutId = setTimeout(() => {
+ ws.messages.off(handler);
+ reject(new Error('Ack timeout'));
+ }, timeout);
+
+ // Custom listener would be needed for this pattern
+ ws.send(message);
+ });
+}
+
+// Reconnection handling
+$.effect(() => {
+ if (ws.status() === 'connected') {
+ console.log('Connected, sending init...');
+ ws.send({ type: 'init', token: authToken() });
+ }
+});
+
+// Binary data
+const ws = $.ws('wss://example.com/binary');
+ws.send(new Blob([data]));
+ws.send(new ArrayBuffer(8));
+
+// Heartbeat / ping-pong
+$.effect(() => {
+ if (ws.status() !== 'connected') return;
+
+ const interval = setInterval(() => {
+ ws.send({ type: 'ping' });
+ }, 30000);
+
+ return () => clearInterval(interval);
+});
+
+// Queue messages while disconnected
+const messageQueue = $([]);
+$.effect(() => {
+ if (ws.status() === 'connected') {
+ messageQueue().forEach(msg => ws.send(msg));
+ messageQueue([]);
+ }
+});
+
+function sendOrQueue(data) {
+ if (ws.status() === 'connected') {
+ ws.send(data);
+ } else {
+ messageQueue([...messageQueue(), data]);
+ }
+}
+```
+
+### Advanced Patterns & Best Practices
+
+**State Management Patterns:**
+```javascript
+// Store pattern
+const createStore = (initial) => {
+ const state = $(initial);
+
+ return {
+ state,
+ actions: {
+ update: (fn) => state(fn(state())),
+ reset: () => state(initial)
+ }
+ };
+};
+
+// Selector pattern
+const selectUser = (id) => $(() =>
+ users().find(u => u.id === id)
+);
+
+// Computed selector
+const selectVisibleTodos = $(() =>
+ todos().filter(t =>
+ filter() === 'all' ||
+ (filter() === 'active' && !t.completed) ||
+ (filter() === 'completed' && t.completed)
+ )
+);
+
+// Action pattern
+const increment = () => count(c => c + 1);
+const addTodo = (text) => todos(t => [...t, { text, completed: false }]);
+const toggleTodo = (id) => todos(t => t.map(todo =>
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
+));
+```
+
+**Performance Optimization:**
+```javascript
+// Memoization
+const expensiveComputation = $(() => {
+ console.log('Computing...');
+ return bigData().filter(heavyFilter).map(transform);
+});
+
+// Batch updates
+count(1);
+count(2);
+count(3); // Single effect run
+
+// Manual flush if needed
+import { flushEffectQueue } from 'sigpro';
+flushEffectQueue(); // Force immediate effect processing
+
+// Lazy computed - only computes when accessed
+const lazyValue = $(() => {
+ if (!cache) cache = expensive();
+ return cache;
+});
+
+// Effect debouncing
+let timeout;
+$.effect(() => {
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ console.log('Debounced:', value());
+ }, 100);
+});
+```
+
+**Error Boundaries & Recovery:**
+```javascript
+// Effect error handling
+$.effect(() => {
+ try {
+ riskyOperation();
+ } catch (e) {
+ errorSignal(e);
+ }
+});
+
+// Component error boundary
+$.component('error-boundary', (props, ctx) => {
+ const error = $(null);
+
+ const handleError = (e) => {
+ error(e.error);
+ e.preventDefault();
+ };
+
+ ctx.host.addEventListener('error', handleError);
+ ctx.onUnmount(() => ctx.host.removeEventListener('error', handleError));
+
+ return () => error()
+ ? html``
+ : html``;
+});
+
+// Global error handler
+window.addEventListener('error', (e) => {
+ console.error('SigPro error:', e.error);
+});
+```
+
+**Testing Utilities:**
+```javascript
+// Test helper pattern
+const createTestHarness = () => {
+ const effects = [];
+ const origEffect = $.effect;
+
+ $.effect = (fn) => {
+ const stop = origEffect(fn);
+ effects.push(stop);
+ return stop;
+ };
+
+ return {
+ cleanup: () => effects.forEach(stop => stop()),
+ restore: () => { $.effect = origEffect; }
+ };
+};
+
+// Async test helper
+const waitForEffect = () => new Promise(r => setTimeout(r, 0));
+
+// Usage in tests
+test('counter increments', async () => {
+ const count = $(0);
+ const calls = [];
+
+ $.effect(() => {
+ calls.push(count());
+ });
+
+ count(1);
+ await waitForEffect(); // Wait for effect queue
+ expect(calls).toEqual([0, 1]);
+});
+```
+
+**TypeScript Integration (conceptual):**
+```typescript
+// Type patterns (even though library is JS)
+type Signal = {
+ (): T;
+ (value: T | ((prev: T) => T)): void;
+};
+
+type Computed = () => T;
+
+type Effect = (fn: () => (void | (() => void))) => () => void;
+
+// Component props typing
+interface ComponentProps {
+ [key: string]: Signal;
+}
+
+interface ComponentContext {
+ select: (selector: string) => Element | null;
+ slot: (name?: string) => Node[];
+ emit: (name: string, detail?: any, options?: CustomEventInit) => void;
+ host: HTMLElement;
+ onUnmount: (fn: () => void) => void;
+}
+```
+
+**Migration from Other Frameworks:**
+```javascript
+// From Vue:
+// ref(0) -> $(0)
+// computed -> $(() => value)
+// watch -> $.effect
+// onMounted -> $.effect (runs immediately)
+
+// From React:
+// useState -> $ (but get/set combined)
+// useEffect -> $.effect
+// useMemo -> $(() => value)
+// useCallback -> Just use function, dependencies automatic
+
+// From Svelte:
+// let count = 0 -> const count = $(0)
+// $: doubled = count * 2 -> const doubled = $(() => count())
+```
+
+## Internal Architecture (for deep understanding)
+
+```javascript
+// Reactivity graph structure
+{
+ activeEffect: Effect | null, // Currently executing effect
+ effectQueue: Set, // Pending effects
+ signal: {
+ subscribers: Set, // Effects depending on this signal
+ value: any, // Current value
+ isDirty?: boolean // For computed signals
+ },
+ effect: {
+ dependencies: Set>, // Signals this effect depends on
+ cleanupHandlers: Set, // Cleanup functions
+ markDirty?: Function // For computed signal effects
+ }
+}
+
+// Flush mechanism
+// 1. Signal update adds affected effects to queue
+// 2. Schedules microtask flush
+// 3. Effects run in order added
+// 4. Each effect cleans up old dependencies
+// 5. New dependencies tracked during run
+```
+
+## Complete Example Application
+
+```javascript
+// app.js
+import { $, html, $.component, $.router, $.storage, $.ws } from 'sigpro';
+
+// State
+const todos = $.storage('todos', []);
+const filter = $('all');
+const user = $(null);
+const ws = $.ws('wss://api.example.com/todos');
+
+// Computed
+const filteredTodos = $(() => {
+ const all = todos();
+ switch(filter()) {
+ case 'active': return all.filter(t => !t.completed);
+ case 'completed': return all.filter(t => t.completed);
+ default: return all;
+ }
+});
+
+const stats = $(() => {
+ const all = todos();
+ return {
+ total: all.length,
+ active: all.filter(t => !t.completed).length,
+ completed: all.filter(t => t.completed).length
+ };
+});
+
+// Actions
+const addTodo = (text) => {
+ const newTodo = {
+ id: Date.now(),
+ text,
+ completed: false,
+ createdAt: new Date().toISOString()
+ };
+ todos([...todos(), newTodo]);
+ ws.send({ type: 'add', todo: newTodo });
+};
+
+const toggleTodo = (id) => {
+ todos(todos().map(t =>
+ t.id === id ? { ...t, completed: !t.completed } : t
+ ));
+};
+
+const removeTodo = (id) => {
+ todos(todos().filter(t => t.id !== id));
+};
+
+// Sync with WebSocket
+$.effect(() => {
+ ws.messages().forEach(msg => {
+ if (msg.type === 'sync') {
+ todos(msg.todos);
+ }
+ });
+});
+
+// Components
+$.component('todo-app', () => html`
+
+
+
+
addTodo(e.detail)}>
+
+
+
+
+
+
+
+
filteredTodos()}
+ @toggle=${(e) => toggleTodo(e.detail)}
+ @remove=${(e) => removeTodo(e.detail)}
+ >
+
+ ${() => ws.status() !== 'connected' ? html`
+
+ Offline - ${() => ws.status()}
+
+ ` : ''}
+
+`);
+
+$.component('todo-input', (props, ctx) => {
+ const text = $('');
+
+ const handleSubmit = () => {
+ if (text().trim()) {
+ ctx.emit('add', text().trim());
+ text('');
+ }
+ };
+
+ return html`
+
+
+
+
+ `;
+});
+
+$.component('todo-list', (props) => html`
+
+`, ['todos']);
+
+$.component('todo-stats', (props) => html`
+
+ Total: ${() => props.stats().total}
+ Active: ${() => props.stats().active}
+ Completed: ${() => props.stats().completed}
+
+`, ['stats']);
+
+// Router setup
+const router = $.router([
+ { path: '/', component: () => html`` },
+ { path: '/settings', component: () => html`` },
+ { path: '/about', component: () => html`` }
+]);
+
+// Mount app
+document.body.appendChild(router);
+```
+
+This comprehensive documentation should enable any AI to build sophisticated applications leveraging all of SigPro's capabilities. Each API is explained with multiple examples, edge cases, advanced patterns, and integration scenarios.