# SigPro ??
A minimalist reactive library for building web interfaces with signals, effects, and native web components. No compilation, no virtual DOM, just pure JavaScript and intelligent reactivity.
[](https://www.npmjs.com/package/sigpro)
[](https://bundlephobia.com/package/sigpro)
[](https://github.com/yourusername/sigpro/blob/main/LICENSE)
## ?? Installation
```bash
npm install sigpro
```
Or directly in the browser:
```html
```
## ?? Philosophy
SigPro (Signal Professional) embraces the web platform. Built on top of Custom Elements and reactive proxies, it offers a development experience similar to modern frameworks but with a minimal footprint and zero dependencies.
**Core Principles:**
- ?? **True Reactivity** - Automatic dependency tracking, no manual subscriptions
- ? **Surgical Updates** - Only the exact nodes that depend on changed values are updated
- ?? **Web Standards** - Built on Custom Elements, no custom rendering engine
- ?? **Intuitive API** - Learn once, use everywhere
- ?? **Predictable** - No magic, just signals and effects
## ?? API Reference
---
### `$(initialValue)` - Signals
Creates a reactive value that notifies dependents when changed.
#### Basic Signal (Getter/Setter)
```typescript
import { $ } from 'sigpro';
// Create a signal
const count = $(0);
// Read value (outside reactive context)
console.log(count()); // 0
// Write value
count(5);
count(prev => prev + 1); // Use function for previous value
// Read with dependency tracking (inside effect)
$$(() => {
console.log(count()); // Will be registered as dependency
});
```
#### Computed Signal
```typescript
import { $, $$ } from 'sigpro';
const firstName = $('John');
const lastName = $('Doe');
// Computed signal - automatically updates when dependencies change
const fullName = $(() => `${firstName()} ${lastName()}`);
console.log(fullName()); // "John Doe"
firstName('Jane');
console.log(fullName()); // "Jane Doe"
// Computed signals cache until dependencies change
const expensiveComputation = $(() => {
console.log('Computing...');
return firstName().length + lastName().length;
});
console.log(expensiveComputation()); // "Computing..." 7
console.log(expensiveComputation()); // 7 (cached, no log)
```
#### Signal with Custom Equality
```typescript
import { $ } from 'sigpro';
const user = $({ id: 1, name: 'John' });
// Signals use Object.is comparison
user({ id: 1, name: 'John' }); // Won't trigger updates (same values, new object)
user({ id: 1, name: 'Jane' }); // Will trigger updates
```
**Parameters:**
- `initialValue`: Initial value or getter function for computed signal
**Returns:** Function that acts as getter/setter with the following signature:
```typescript
type Signal = {
(): T; // Getter
(value: T | ((prev: T) => T)): void; // Setter
}
```
---
### `$$(effect)` - Effects
Executes a function and automatically re-runs it when its dependencies change.
#### Basic Effect
```typescript
import { $, $$ } from 'sigpro';
const count = $(0);
const name = $('World');
// Effect runs immediately and on dependency changes
$$(() => {
console.log(`Count is: ${count()}`); // Only depends on count
});
// Log: "Count is: 0"
count(1);
// Log: "Count is: 1"
name('Universe'); // No log (name is not a dependency)
```
#### Effect with Cleanup
```typescript
import { $, $$ } from 'sigpro';
const userId = $(1);
$$(() => {
const id = userId();
let isSubscribed = true;
// Simulate API subscription
const subscription = api.subscribe(id, (data) => {
if (isSubscribed) {
console.log('New data:', data);
}
});
// Return cleanup function
return () => {
isSubscribed = false;
subscription.unsubscribe();
};
});
userId(2); // Previous subscription cleaned up, new one created
```
#### Nested Effects
```typescript
import { $, $$ } from 'sigpro';
const show = $(true);
const count = $(0);
$$(() => {
if (!show()) return;
// This effect is nested inside the conditional
// It will only be active when show() is true
$$(() => {
console.log('Count changed:', count());
});
});
show(false); // Inner effect is automatically cleaned up
count(1); // No log (inner effect not active)
show(true); // Inner effect recreated, logs "Count changed: 1"
```
#### Manual Effect Control
```typescript
import { $, $$ } from 'sigpro';
const count = $(0);
// Stop effect manually
const stop = $$(() => {
console.log('Effect running:', count());
});
count(1); // Log: "Effect running: 1"
stop();
count(2); // No log
```
**Parameters:**
- `effect`: Function to execute. Can return a cleanup function
**Returns:** Function to stop the effect
---
### `html` - Template Literal Tag
Creates reactive DOM fragments using template literals with intelligent binding.
#### Basic Usage
```typescript
import { $, html } from 'sigpro';
const count = $(0);
const name = $('World');
const fragment = html`
Hello ${name}
Count: ${count}
`;
document.body.appendChild(fragment);
```
#### Directive Reference
##### `@event` - Event Listeners
```typescript
import { html } from 'sigpro';
const handleClick = (event) => console.log('Clicked!', event);
const handleInput = (value) => console.log('Input:', value);
html`
console.log(e.target.value)} />
`
```
##### `:property` - Two-way Binding
Automatically syncs between signal and DOM element.
```typescript
import { $, html } from 'sigpro';
const text = $('');
const checked = $(false);
const selected = $('option1');
html`
You typed: ${text}
Checkbox is: ${() => checked() ? 'checked' : 'unchecked'}