Atomic Reactivity
Powered by Signals. Only updates what changes. No Virtual DOM overhead, no heavy re-renders.
diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..5d32f7d --- /dev/null +++ b/docs/404.html @@ -0,0 +1,22 @@ + + +
+ + +$( ) The $ function is the heart of SigPro. It is a Unified Reactive Constructor that handles state, derivations, and side effects through a single, consistent interface.
$( input ) Depending on what you pass into $( ), SigPro creates a different type of reactive primitive:
| Input Type | Result | Internal Behavior |
|---|---|---|
| Value (String, Number, Object...) | Signal | Creates a piece of mutable state. |
| Function | Computed / Effect | Creates a derived value that tracks dependencies. |
A Signal is a "box" that holds a value. It provides a getter/setter function to interact with that value.
const $state = $(initialValue);const $name = $("Alice");
+
+// Read the value (Getter)
+console.log($name()); // "Alice"
+
+// Update the value (Setter)
+$name("Bob");
+
+// Update based on previous value
+$name(current => current + " Smith");When you pass a function to $( ) that returns a value, SigPro creates a Computed Signal. It automatically tracks which signals are used inside it and re-runs only when they change.
const $derived = $(() => logic);const $price = $(100);
+const $qty = $(2);
+
+// Automatically tracks $price and $qty
+const $total = $(() => $price() * $qty());
+
+console.log($total()); // 200
+
+$qty(3); // $total updates to 300 automaticallyAn Effect is a function passed to $( ) that does not return a value (or returns undefined). SigPro treats this as a subscription that performs an action whenever its dependencies change.
$(() => { action });const $theme = $("light");
+
+// This effect runs every time $theme changes
+$(() => {
+ document.body.className = $theme();
+ console.log("Theme updated to:", $theme());
+});
+
+$theme("dark"); // Logs: Theme updated to: dark| Primitive | Logic Type | Returns Value? | Typical Use Case |
|---|---|---|---|
| Signal | Static | Yes (Mutable) | const $user = $("Guest") |
| Computed | Read-only | Yes (Automatic) | const $isLoggedIn = $(() => $user() !== "Guest") |
| Effect | Imperative | No | $(() => localStorage.setItem('user', $user())) |
In SigPro, we use the $ prefix (e.g., $count) for variables that hold a reactive function. This makes it easy to distinguish between a standard variable and a reactive one at a glance:
let count = 0; // Static
+const $count = $(0); // Reactive (Function)$.html The $.html function is the architect of your UI. It creates standard HTML elements and wires them directly to your signals without the need for a Virtual DOM.
$.html(tag, [props], [content]) | Parameter | Type | Required | Description |
|---|---|---|---|
| tag | string | Yes | Any valid HTML5 tag (e.g., 'div', 'button', 'input'). |
| props | Object | No | Attributes, event listeners, and reactive bindings. |
| content | any | No | Text, Nodes, Arrays, or Reactive Functions. |
const myButton = $.html('button', { class: 'btn-primary' }, 'Click me');To avoid repetitive $.html calls, SigPro automatically exposes common tags to the global window object. This allows for a clean, declarative syntax.
// Instead of $.html('div', ...), just use:
+div({ id: 'wrapper' }, [
+ h1("Welcome"),
+ p("This is SigPro.")
+]);SigPro distinguishes between static attributes and reactive bindings using the $ prefix.
$): Automatically updates the DOM when the signal changes.| Property | Syntax | Result |
|---|---|---|
| Attribute | { id: 'main' } | id="main" |
| Event | { onclick: fn } | Adds an event listener. |
| Reactive Attr | { $class: $theme } | Updates class whenever $theme() changes. |
| Boolean Attr | { $disabled: $isBusy } | Toggles the disabled attribute automatically. |
For form inputs, SigPro provides a powerful shortcut using $value or $checked. It automatically handles the event listening and the value synchronization.
const $text = $("Type here...");
+
+input({
+ type: 'text',
+ $value: $text // Syncs input -> signal and signal -> input
+});
+
+p(["You typed: ", $text]);The content argument is incredibly flexible. If you pass a function, SigPro treats it as a reactive "portal" that re-renders only that specific part of the DOM.
const $count = $(0);
+
+// Text node updates surgically
+div(["Count: ", $count]);
+
+// Conditional rendering with a function
+div(() => {
+ return $count() > 10
+ ? h1("High Score!")
+ : p("Keep going...");
+});When a reactive function in the content returns a new Node, SigPro uses replaceWith() to swap the old node for the new one. This ensures that:
| Input | Behavior |
|---|---|
| String / Number | Appended as a TextNode. |
| HTMLElement | Appended directly to the parent. |
| Array | Each item is processed and appended in order. |
Function () => ... | Creates a live reactive zone that updates automatically. |
$.mount The $.mount function is the entry point of your reactive world. It takes a SigPro component (or a plain DOM node) and injects it into the real document.
$.mount(node, [target]) | Parameter | Type | Default | Description |
|---|---|---|---|
| node | HTMLElement or Function | Required | The component or element to render. |
| target | string or HTMLElement | document.body | Where to mount the app (CSS selector or Element). |
In a modern app (like our main.js example), you usually want to control the entire page. By default, $.mount clears the target's existing HTML before mounting.
// src/main.js
+import { $ } from 'SigPro';
+import App from './App.js';
+
+$.mount(App); // Mounts to <body> by defaultIf you have an existing HTML structure and only want SigPro to manage a specific part (like a #root div), pass a CSS selector or a reference.
<div id="sidebar"></div>
+<div id="app-root"></div>// Local mount to a specific ID
+$.mount(MyComponent, '#app-root');
+
+// Or using a direct DOM reference
+const sidebar = document.getElementById('sidebar');
+$.mount(SidebarComponent, sidebar);One of SigPro's strengths is that it works perfectly alongside "Old School" HTML. You can create a reactive "island" inside a static page.
// A small reactive widget in a static .js file
+const CounterWidget = () => {
+ const $c = $(0);
+ return button({ onclick: () => $c(v => v + 1) }, [
+ "Clicks: ", $c
+ ]);
+};
+
+// Mount it into an existing div in your HTML
+$.mount(CounterWidget, '#counter-container');When $.mount is called, it performs two critical steps:
target.innerHTML = ''. This ensures no "zombie" HTML from previous renders or static placeholders interferes with your app.In a standard Vite/ESM project, you initialize SigPro globally in main.js. This makes the $ and the tag helpers (div, button, etc.) available everywhere in your project.
// main.js - Global Initialization
+import 'SigPro';
+
+// Now any other file can just use:
+$.mount(() => h1("Global App"));If you are worried about polluting the global window object, you can import and use SigPro locally within a specific module.
// widget.js - Local usage
+import { $ } from 'SigPro';
+
+const myNode = $.html('div', 'Local Widget');
+$.mount(myNode, '#widget-target');| Goal | Code |
|---|---|
| Mount to body | $.mount(App) |
| Mount to ID | $.mount(App, '#id') |
| Mount to Element | $.mount(App, myElement) |
| Reactive Widget | $.mount(() => div("Hi"), '#widget') |
This is a high-level summary of the SigPro core API. For detailed guides and edge cases, please refer to the specific documentation for each module.
$( ) The $ function is a polymorphic constructor. It creates Signals (state) or Computed Effects (logic) based on the input type.
| Usage | Input Type | Returns | Description |
|---|---|---|---|
| Signal | any | Function | A getter/setter for reactive state. |
| Computed | Function | Function | A read-only signal that auto-updates when its dependencies change. |
Example:
const $count = $(0); // Signal
+const $double = $(() => $count() * 2); // Computed$.html SigPro uses a hyperscript-style engine to create live DOM nodes.
| Argument | Type | Required | Description |
|---|---|---|---|
| tag | string | Yes | Standard HTML tag (e.g., 'div', 'button'). |
| props | Object | No | Attributes (id), Events (onclick), or Reactive Props ($value). |
| content | any | No | String, Node, Array, or Reactive Function. |
Example:
$.html('button', { onclick: () => alert('Hi!') }, 'Click Me');To keep your code clean, SigPro automatically exposes common HTML tags to the global scope.
| Category | Available Tags |
|---|---|
| Layout | div, section, main, nav, header, footer, span |
| Typography | h1, h2, h3, p, label, a, li, ul, ol |
| Forms | input, button, form, select, option |
| Media | img, video, audio, canvas |
Example:
// No imports needed!
+div([
+ h1("Title"),
+ button("Ok")
+]);Methods to initialize your application and extend the engine.
| Method | Signature | Description |
|---|---|---|
$.mount | (node, target) | Wipes the target (default: body) and renders the component. |
$.plugin | (source) | Registers a function or loads external .js scripts as plugins. |
Example:
$.plugin([UI, Router]);
+$.mount(App, '#root');| Feature | Syntax | Description |
|---|---|---|
| Text Binding | p(["Value: ", $sig]) | Updates text content automatically. |
| Attributes | div({ id: $sig }) | Static attribute assignment. |
| Reactive Attr | div({ $class: $sig }) | Attribute updates when $sig changes. |
| Two-way Binding | input({ $value: $sig }) | Syncs input value and signal automatically. |
| Conditional | div(() => $sig() > 0 ? "Yes" : "No") | Re-renders only the content when the condition changes. |
| Feature | SigPro Approach | Benefit |
|---|---|---|
| Update Logic | Fine-grained (Surgical) | Blazing fast updates. |
| DOM | Native Nodes | Zero abstraction cost. |
| Syntax | Pure JavaScript | No build-tool lock-in. |
| Footprint | Modular | Load only what you use. |
In SigPro, you don't need to write $.html('div', ...) every time. To keep your code clean and readable, the engine automatically generates global helper functions for all standard HTML tags.
When SigPro initializes, it runs a proxy loop that creates a function for every common HTML tag and attaches it to the window object.
$.html('button', { onclick: ... }, 'Click')button({ onclick: ... }, 'Click')This approach gives you a "DSL" (Domain Specific Language) that feels like HTML but is actually pure JavaScript.
The following tags are available globally by default:
| Category | Available Functions |
|---|---|
| Layout | div, span, section, main, nav, header, footer, article, aside |
| Typography | h1, h2, h3, p, ul, ol, li, a, label, strong, em |
| Forms | form, input, button, select, option, textarea |
| Table | table, thead, tbody, tr, th, td |
| Media | img, video, audio, canvas, svg |
The tag functions are highly flexible and accept arguments in different orders to suit your coding style.
The most common pattern.
div({ class: 'card' }, [
+ h1("Title"),
+ p("Description")
+]);If you don't need attributes, you can skip the object entirely.
div([
+ h1("Just Content"),
+ p("No attributes object needed here.")
+]);For elements that only contain a string.
button("Submit"); // Equivalent to <button>Submit</button>Since these helpers are just wrappers around $.html, they support full reactivity out of the box.
const $loading = $(true);
+
+div([
+ $loading() ? span("Loading...") : h1("Data Ready!"),
+ button({
+ $disabled: $loading, // Reactive attribute
+ onclick: () => $loading(false)
+ }, "Stop Loading")
+]);If you are curious about how this happens without a compiler, here is the logic inside the SigPro core:
const tags = ['div', 'span', 'p', 'button', ...];
+
+tags.forEach(tag => {
+ window[tag] = (props, content) => $.html(tag, props, content);
+});Because these are attached to window, they are available in any file in your project as soon as SigPro is loaded, making your components look like this:
// No imports required for tags!
+export default () =>
+ section({ id: 'hero' }, [
+ h1("Fast. Atomic. Simple."),
+ p("Built with SigPro.")
+ ]);To better understand the translation, here is a complete example of a User Card component. Notice how SigPro attributes with the $ prefix map to reactive behavior, while standard attributes remain static.
const $online = $(true);
+
+export const UserCard = () => (
+ div({ class: 'user-card' }, [
+ img({ src: 'avatar.png', alt: 'User' }),
+
+ div({ class: 'info' }, [
+ h2("John Doe"),
+ p({
+ $class: () => $online() ? 'status-on' : 'status-off'
+ }, [
+ "Status: ",
+ () => $online() ? "Online" : "Offline"
+ ])
+ ]),
+
+ button({
+ onclick: () => $online(!$online())
+ }, "Toggle Status")
+ ])
+);<div class="user-card">
+ <img src="avatar.png" alt="User">
+
+ <div class="info">
+ <h2>John Doe</h2>
+ <p class="status-on">
+ Status: Online
+ </p>
+ </div>
+
+ <button>Toggle Status</button>
+</div>div([...]) in JS translates directly to nested tags in HTML.class is set once. $class is "live"; SigPro listens to the $online signal and updates the class name without re-rendering the whole card.[...] in SigPro is the equivalent of the children inside an HTML tag.() => $online() ? ... creates a TextNode in the HTML that changes its text content surgically whenever the signal toggles.window or $ (though in SigPro, using them globally is the intended "clean" way).$.html('my-custom-element', { ... }).SigPro is a lightweight, atomic reactive engine designed to build modern web interfaces with zero overhead. It focuses on high performance through fine-grained reactivity.
You can install SigPro via your favorite package manager:
npm install SigPropnpm add SigProyarn add SigProbun add SigProThe core of SigPro is the $ function, which creates reactive state (Signals) and computed effects.
Create a main.js file and try this:
import { $ } from 'SigPro';
+
+// 1. Create a reactive signal
+const $name = $("World");
+
+// 2. Define a reactive component
+const App = () => div({ class: 'container' }, [
+ h1(["Hello, ", $name, "!"]),
+
+ input({
+ type: 'text',
+ $value: $name, // Two-way binding
+ placeholder: 'Enter your name...'
+ }),
+
+ button({
+ onclick: () => $name("SigPro")
+ }, "Set to SigPro")
+]);
+
+// 3. Mount the application
+$.mount(App, '#app');SigPro doesn't use a Virtual DOM. Instead, it creates real DOM nodes and binds them directly to your data:
$(value) creates a getter/setter function.By default, SigPro exports common HTML tags to the global scope (window) when initialized. This allows you to write clean, declarative UI without importing every single tag:
// Instead of $.html('div', ...), just use:
+div([
+ h1("Clean Syntax"),
+ p("No more boilerplate.")
+]);After years of building applications with React, Vue, and Svelte—investing countless hours mastering unique mental models, proprietary syntaxes, and complex build tools—we reached a realization: the web platform has evolved, but frameworks have become layers of abstraction that often move us further away from the browser.
SigPro is the answer to a simple question: Why fight the platform when we can embrace it?
SigPro bypasses the overhead of the Virtual DOM and heavy compilers by using modern browser primitives. It treats the DOM as a first-class citizen, not as a side effect of a state change.
| Browser Primitive | What It Enables |
|---|---|
| Closures & Proxies | Automatic dependency tracking without heavy overhead. |
| ES Modules | Native modularity and lazy loading without complex bundlers. |
| Direct DOM APIs | Surgical updates that are faster than any reconciliation algorithm. |
| Microtask Queues | Batching updates efficiently to ensure 60fps performance. |
SigPro strips away the complexity, delivering a reactive programming model that feels like a framework but stays remarkably close to Vanilla JS:
// Pure, Atomic, Reactive.
+const $count = $(0);
+
+const Counter = () => div([
+ p(["Count: ", $count]),
+ button({ onclick: () => $count(c => c + 1) }, "Increment")
+]);SigPro isn't just lighter; it's architecturally faster because it skips the "diffing" phase entirely.
| Metric | SigPro | SolidJS | Svelte | Vue | React |
|---|---|---|---|---|---|
| Bundle Size (gzip) | 🥇 < 2KB | 🥈 7KB | 🥉 16KB | 20KB | 45KB |
| Architecture | Atomic | Atomic | Compiled | V-DOM | V-DOM |
| Initial Render | 🥇 Fastest | 🥈 Fast | 🥉 Fast | Average | Slow |
| Update Perf | 🥇 Surgical | 🥇 Surgical | 🥈 Fast | 🥉 Average | Slow |
| Dependencies | 🥇 0 | 🥇 0 | 🥇 0 | 🥈 2 | 🥉 5+ |
| Build Step | 🥇 Optional | 🥈 Required | 🥈 Required | 🥇 Optional | 🥈 Required |
SigPro is built on four fundamental pillars:
Automatic dependency tracking with no manual subscriptions. When a signal changes, only the exact text nodes or attributes that depend on it update—instantly and surgically.
No Virtual DOM diffing. No tree reconciliation. We don't guess what changed; we know exactly where the update needs to happen. Performance scales with your data, not the size of your component tree.
The core is a tiny, powerful engine. Need Routing? Fetching? Global UI? Just plug it in. This keeps your production bundles "pay-only-for-what-you-use."
There is no "magic" hidden in a black-box compiler. What you write is what the browser executes. Debugging is straightforward because there is no framework layer between your code and the DevTools.
`,28)])])}const y=e(l,[["render",n]]);export{c as __pageData,y as default}; diff --git a/docs/assets/guide_why.md.lyU7T5_c.lean.js b/docs/assets/guide_why.md.lyU7T5_c.lean.js new file mode 100644 index 0000000..cd2cec3 --- /dev/null +++ b/docs/assets/guide_why.md.lyU7T5_c.lean.js @@ -0,0 +1 @@ +import{_ as e,o as i,c as a,ae as s}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Why SigPro?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/why.md","filePath":"guide/why.md"}'),l={name:"guide/why.md"};function n(r,t,o,h,d,g){return i(),a("div",null,[...t[0]||(t[0]=[s("",28)])])}const y=e(l,[["render",n]]);export{c as __pageData,y as default}; diff --git a/docs/assets/index.md.BWH7zN4c.js b/docs/assets/index.md.BWH7zN4c.js new file mode 100644 index 0000000..d0f80bb --- /dev/null +++ b/docs/assets/index.md.BWH7zN4c.js @@ -0,0 +1,17 @@ +import{_ as s,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"SigPro","text":"Atomic Unified Reactive Engine","tagline":"Fine-grained reactivity, built-in routing, and modular plugins. All under 2KB.","image":{"src":"/logo.png","alt":"SigPro Logo"},"actions":[{"theme":"brand","text":"Get Started","link":"/guide/getting-started"},{"theme":"alt","text":"View on GitHub","link":"https://git.natxocc.com/sigpro/"}]},"features":[{"title":"Atomic Reactivity","details":"Powered by Signals. Only updates what changes. No Virtual DOM overhead, no heavy re-renders."},{"title":"Zero Dependencies","details":"Written in pure Vanilla JS. Maximum performance with the smallest footprint possible."},{"title":"Modular Ecosystem","details":"Official plugins for UI components, dynamic Routing, Fetch, and Storage. Load only what you need."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),n={name:"index.md"};function l(h,i,p,r,k,o){return a(),t("div",null,[...i[0]||(i[0]=[e(`"SigPro returns the joy of web development by making the browser the hero again."
SigPro isn't just another framework; it's a high-performance engine. It strips away the complexity of massive bundles and returns to the essence of the web, enhanced with reactive superpowers.
import { $ } from 'sigpro2';
+
+// A reactive state Signal
+const $count = $(0);
+
+// A Computed signal that updates automatically
+const $double = $(() => $count() * 2);
+
+// UI that breathes with your data
+const Counter = () => div([
+ h1(["Count: ", $count]),
+ p(["Double: ", $double]),
+ button({ onclick: () => $count(c => c + 1) }, "Increment")
+]);
+
+$.mount(Counter);Unlike frameworks that diff complex trees (V-DOM), SigPro binds your signals directly to real DOM text nodes and attributes. If the data changes, the node changes. Period.
Extend core capabilities in a single line. Add global UI helpers, routing, or state persistence seamlessly.
import { UI, Router } from 'sigpro/plugins';
+$.plugin([UI, Router]);With our dedicated Vite plugin, manage your routes simply by creating files in src/pages/. It supports native Lazy Loading out of the box for lightning-fast initial loads.
npm install sigpropnpm add sigproyarn add sigprobun add sigproSigPro is an open-source project. Whether you want to contribute, report a bug, or just talk about reactivity, join us on our official repository.
Built with ❤️ by NatxoCC_debug The Debug Plugin is a lightweight reactive listener. Once attached to a signal or a computed function, it automatically monitors changes, compares values, and formats the output in the browser console.
console.table() when the signal contains an object or array.Object.is to prevent redundant logging if the value hasn't actually changed.To use _debug, you only need the SigPro core. Register the plugin in your main.js. You can conditionally load it so it only runs during development.
import { $ } from 'sigpro';
+import { Debug } from 'sigpro/plugins';
+
+// Only load Debug in development mode
+const plugins = [];
+if (import.meta.env.DEV) plugins.push(Debug);
+
+$.plugin(plugins).then(() => {
+ import('./App.js').then(app => $.mount(app.default));
+});npm install sigpropnpm add sigproyarn add sigprobun add sigproCall _debug anywhere in your component. It stays active in the background, watching the signal's lifecycle.
export default () => {
+ const $count = $(0);
+ const $user = $({ name: "Guest", role: "Viewer" });
+
+ // Start tracking
+ _debug($count, "Main Counter");
+ _debug($user, "User Session");
+
+ return div([
+ button({ onclick: () => $count(c => c + 1) }, "Increment"),
+ button({ onclick: () => $user({ name: "Admin", role: "Super" }) }, "Promote")
+ ]);
+};When a signal changes, the console displays a structured block:
SigPro Debug: Main Counter).You can also debug computed functions to see exactly when derived state is recalculated.
const $price = $(100);
+const $tax = $(0.21);
+const $total = $(() => $price() * (1 + $tax()));
+
+// Monitor the result of the calculation
+_debug($total, "Final Invoice Total");_debug? console.log inside your reactive functions.console.table makes debugging large API responses much faster._fetch The Fetch Plugin provides a reactive wrapper around the native browser Fetch API. Instead of managing complex async/await flows within your UI, _fetch returns a "Reactive Tripod" (Data, Loading, and Error) that your components can listen to automatically.
When you call _fetch, it returns three signals immediately. Your UI declares how to react to these signals as they change from their initial state to the final response.
$data: Initialized as null. Automatically holds the JSON response on success.$loading: Initialized as true. Flips to false once the request settles.$error: Initialized as null. Holds the error message if the request fails.Register the Fetch plugin in your main.js. By convention, we load it alongside the UI and Router to have the full SigPro ecosystem ready.
import { $ } from 'sigpro';
+import { Fetch } from 'sigpro/plugins';
+
+$.plugin([Fetch]).then(() => {
+ // Now _fetch() is available globally
+ import('./App.js').then(app => $.mount(app.default));
+});Use _fetch inside your component to get live updates. The UI updates surgically whenever a signal changes.
export default () => {
+ const { $data, $loading, $error } = _fetch('https://api.github.com/users/octocat');
+
+ return div({ class: 'p-6 flex flex-col gap-4' }, [
+ h1("Profile Details"),
+
+ // 1. Loading State (using SigPro UI button)
+ () => $loading() && _button({ $loading: true }, "Fetching..."),
+
+ // 2. Error State
+ () => $error() && div({ class: 'alert alert-error' }, $error()),
+
+ // 3. Success State
+ () => $data() && div({ class: 'card bg-base-200 p-4' }, [
+ img({ src: $data().avatar_url, class: 'w-16 rounded-full' }),
+ h2($data().name),
+ p($data().bio)
+ ])
+ ]);
+};_fetch accepts the same RequestInit options as the standard fetch() (methods, headers, body, etc.).
const { $data, $loading } = _fetch('/api/v1/update', {
+ method: 'PATCH',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ status: 'active' })
+});_fetch instead of native Fetch? useEffect required: Since SigPro is natively reactive, you don't need lifecycle hooks to trigger re-renders; the signals handle it._prefix pattern as the rest of the official plugin ecosystem.$error signal.Router The SigPro Router handles URL changes via hashes (#) and maps them to components. It supports dynamic parameters (like :id) and asynchronous loading for heavy pages.
/user/:id.The Router is usually included in the official plugins package.
npm install -D tailwindcss @tailwindcss/vite daisyui@nextpnpm add -D tailwindcss @tailwindcss/vite daisyui@nextyarn add -D tailwindcss @tailwindcss/vite daisyui@nextbun add -d tailwindcss @tailwindcss/vite daisyui@nextIn your App.js (or a dedicated routes file), define your navigation map.
const routes = [
+ { path: '/', component: () => h1("Home Page") },
+ {
+ path: '/admin',
+ // Lazy Loading: This file is only fetched when needed
+ component: () => import('./pages/Admin.js')
+ },
+ { path: '/user/:id', component: (params) => h2(\`User ID: \${params.id}\`) },
+ { path: '*', component: () => div("404 - Page Not Found") }
+];
+
+export default () => div([
+ _navbar({ title: "My App" }),
+ _router(routes) // The router is now a global tag
+]);_router.go) To move between pages programmatically (e.g., inside an onclick event), use the global _router.go helper.
_button({
+ onclick: () => _router.go('/admin')
+}, "Go to Admin")The router tracks the window.location.hash and uses a reactive signal to trigger a re-render of the specific area where _router(routes) is placed.
import()), it shows a loading state and swaps the content once the module arrives.Since you are using the UI Plugin, you can easily create active states in your navigation menus by checking the current hash.
// Example of a reactive sidebar menu
+_menu({
+ items: [
+ {
+ label: 'Dashboard',
+ active: () => window.location.hash === '#/',
+ onclick: () => _router.go('/')
+ },
+ {
+ label: 'Settings',
+ active: () => window.location.hash === '#/settings',
+ onclick: () => _router.go('/settings')
+ }
+ ]
+})_storage The Storage plugin synchronizes a signal with a specific key in your browser's localStorage. It handles both the initial hydration (loading data when the app starts) and automatic saving whenever the signal's value changes.
When you "attach" a signal to _storage, two things happen:
localStorage. If it does, it parses the JSON and updates the signal immediately.Register the Storage plugin in your main.js. Since this is a logic-only plugin, it doesn't require any CSS or UI dependencies.
import { $ } from 'sigpro';
+import { Storage } from 'sigpro/plugins';
+
+$.plugin(Storage).then(() => {
+ import('./App.js').then(app => $.mount(app.default));
+});npm install sigpropnpm add sigproyarn add sigprobun add sigproYou can wrap any signal with _storage. It is common practice to do this right after creating the signal.
export default () => {
+ // 1. Create a signal with a default value
+ const $theme = $( 'light' );
+
+ // 2. Persist it. If 'user_theme' exists in localStorage,
+ // $theme will be updated to that value instantly.
+ _storage($theme, 'user_theme');
+
+ return div({ class: () => \`app-\${$theme()}\` }, [
+ h1(\`Current Theme: \${$theme()}\`),
+ button({
+ onclick: () => $theme(t => t === 'light' ? 'dark' : 'light')
+ }, "Toggle Theme")
+ ]);
+};Since the plugin uses JSON.parse and JSON.stringify internally, it works perfectly with complex state structures.
const $settings = $({
+ notifications: true,
+ fontSize: 16
+});
+
+// Automatically saves the whole object whenever any property changes
+_storage($settings, 'app_settings');_storage? localStorage.getItem or setItem logic inside your components._storage returns the signal, you can persist it inline.try/catch block to prevent your app from crashing if the stored JSON is corrupted.localStorage clean.You can chain plugins to create a fully monitored and persistent state:
const $score = _storage($(0), 'high_score');
+
+// Now it's saved to disk AND logged to console on every change
+_debug($score, "Game Score");UI The SigPro UI plugin is a high-level component library built on top of the reactive core. It leverages Tailwind CSS v4 for utility styling and daisyUI v5 for semantic components.
To use these components, you must install the styling engine. SigPro UI provides the logic, but Tailwind and daisyUI provide the visuals.
npm install -D tailwindcss @tailwindcss/vite daisyui@nextpnpm add -D tailwindcss @tailwindcss/vite daisyui@nextyarn add -D tailwindcss @tailwindcss/vite daisyui@nextbun add -d tailwindcss @tailwindcss/vite daisyui@nextWould you like to continue with the Router.md documentation now?
app.css) In Tailwind v4, configuration is handled directly in your CSS. Create a src/app.css file:
/* src/app.css */
+@import "tailwindcss";
+
+/* Import daisyUI v5 as a Tailwind v4 plugin */
+@plugin "daisyui";
+
+/* Optional: Configure themes */
+@custom-variant dark (&:where(.dark, [data-theme="dark"], [data-theme="dark"] *)));You must import your CSS and register the UI plugin in your entry point. This populates the global scope with reactive component helpers (prefixed with _).
// main.js
+import './app.css';
+import { $ } from 'sigpro';
+import { UI } from 'sigpro/plugins';
+
+$.plugin(UI).then(() => {
+ // Global components like _button and _input are now ready
+ import('./App.js').then(app => $.mount(app.default));
+});_tags) SigPro UI components are more than just HTML; they are Reactive Functional Components that manage complex states (loading, errors, accessibility) automatically.
_button) The _button automatically handles spinners and disabled states based on signals.
| Property | Type | Description |
|---|---|---|
$loading | signal | If true, shows a spinner and disables the button. |
$disabled | signal | Manually disables the button (logic-bound). |
icon | node/str | Prepends an icon to the text. |
badge | string | Appends a small badge to the button. |
_button({
+ $loading: $isSaving,
+ icon: '💾',
+ class: 'btn-primary'
+}, "Save Data")_input, _select, _checkbox) These components wrap the raw input in a fieldset with integrated labels and tooltips.
label: Field title displayed above the input.tip: Displays a ? badge that shows a tooltip on hover.$error: A signal that, when populated, turns the input red and displays the message.$value: Two-way binding. Updates the signal on input and the input on signal change._input({
+ label: "Username",
+ tip: "Choose a unique name",
+ $value: $name,
+ $error: $nameError
+})_modal) The _modal is surgically mounted. If the $open signal is false, the component is completely removed from the DOM, optimizing performance.
const $showModal = $(false);
+
+_modal({ $open: $showModal, title: "Alert" }, [
+ p("Are you sure you want to proceed?"),
+ _button({ onclick: () => doAction() }, "Confirm")
+])_tabs, _drawer, _navbar) Designed to work seamlessly with the Router.
| Component | Key Logic |
|---|---|
_tabs | Accepts an active property (signal or function) to highlight the current tab. |
_drawer | A responsive sidebar that toggles via an ID or an $open signal. |
_navbar | Standard top bar with shadow and glass effect support. |
_menu | Vertical navigation list with active state support. |
Once $.plugin(UI) is active, these tags are available project-wide:
| Tag | Category | Use Case |
|---|---|---|
_fieldset | Layout | Grouping related inputs with a legend. |
_accordion | Content | Collapsible sections (FAQs). |
_badge | Feedback | Status indicators (Success, Warning). |
_tooltip | Feedback | Descriptive text on hover. |
_range | Input | Reactive slider for numerical values. |
With the UI ready and styled via Tailwind v4, we can move to the Router.md. We will explain how to link _tabs and _menu to different URL paths for a full SPA experience.
Would you like to start with the Router configuration?
`,40)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/plugins_core.ui.md.DDLum7rv.lean.js b/docs/assets/plugins_core.ui.md.DDLum7rv.lean.js new file mode 100644 index 0000000..69a4077 --- /dev/null +++ b/docs/assets/plugins_core.ui.md.DDLum7rv.lean.js @@ -0,0 +1 @@ +import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Official UI Plugin: UI","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.ui.md","filePath":"plugins/core.ui.md"}'),n={name:"plugins/core.ui.md"};function l(h,s,p,o,d,r){return t(),a("div",null,[...s[0]||(s[0]=[e("",40)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/plugins_custom.md.D2KGTblR.js b/docs/assets/plugins_custom.md.D2KGTblR.js new file mode 100644 index 0000000..d93a9f0 --- /dev/null +++ b/docs/assets/plugins_custom.md.D2KGTblR.js @@ -0,0 +1,48 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"Creating Custom Plugins","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/custom.md","filePath":"plugins/custom.md"}'),l={name:"plugins/custom.md"};function h(e,s,p,k,r,d){return a(),n("div",null,[...s[0]||(s[0]=[t(`There are two main ways to expose a plugin's functionality: Static/Manual Imports (cleaner for large projects) or Global/Automatic Window Injection (easier for quick scripts and global helpers).
A plugin is a standard JavaScript function. By convention, if a plugin adds a global helper or component, it should be prefixed with an underscore (_).
// plugins/my-utils.js
+export const MyUtils = ($) => {
+
+ // 1. Attach to the SigPro instance
+ $.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
+
+ // 2. Attach to the Window (Global access)
+ window._hello = (name) => div(\`Hello, \${$.capitalize(name)}!\`);
+
+ // 3. You can also return values if needed
+ return { version: '1.0.0' };
+};This approach keeps your global namespace clean. You import the logic only where you need it, but the plugin still initializes the core $ extensions.
// main.js
+import { $ } from 'sigpro';
+import { MyUtils } from './plugins/my-utils.js';
+
+$.plugin(MyUtils);
+
+// App.js
+export default () => {
+ const name = "sigpro";
+ // $.capitalize was added by the plugin
+ return h1($.capitalize(name));
+};If your plugin defines global tags (like _button or _hello), you should attach them to the window object inside the plugin function. This makes them available everywhere without imports.
// plugins/theme.js
+export const Theme = ($) => {
+ const $dark = $(false);
+
+ window._themeToggle = () => button({
+ onclick: () => $dark(v => !v),
+ class: () => $dark() ? 'bg-black text-white' : 'bg-white text-black'
+ }, "Toggle Mode");
+};
+
+// main.js
+$.plugin(Theme).then(() => {
+ // _themeToggle is now a global function
+ $.mount(App);
+});If your plugin needs to load external data or scripts before the app starts, make it async. SigPro will wait for it.
export const ConfigLoader = async ($) => {
+ const res = await fetch('/config.json');
+ const config = await res.json();
+
+ $.config = config; // Attach loaded config to SigPro
+};
+
+// Usage
+$.plugin(ConfigLoader).then(() => {
+ console.log("Config loaded:", $.config);
+ $.mount(App);
+});| Rule | Description |
|---|---|
| Prefixing | Use _ for UI components (_modal) and $. for logic ($.fetch). |
| Idempotency | Ensure calling $.plugin(MyPlugin) twice doesn't break the app. |
| Encapsulation | Use the $ instance passed as an argument rather than importing it again inside the plugin. |
| Reactivity | Always use $(...) for internal state so the app stays reactive. |
Custom plugins don't require extra packages, but ensure your build tool (Vite/Bun) is configured to handle the module imports.
npm install sigpropnpm add sigproyarn add sigprobun add sigpro$.plugin The plugin system is the engine's way of growing. It allows you to inject new functionality directly into the $ object or load external resources.
A plugin in SigPro is simply a function that receives the core instance. When you run $.plugin(MyPlugin), the engine hands over the $ object so the plugin can attach new methods or register global tags (like div(), span(), etc.).
// A plugin that adds a simple logger to any signal
+const Logger = ($) => {
+ $.watch = (target, label = "Log") => {
+ $(() => console.log(\`[\${label}]:\`, target()));
+ };
+};
+
+// Activation
+$.plugin(Logger);
+const $count = $(0);
+$.watch($count, "Counter"); // Now available globally via $Since plugins often set up global variables (like the HTML tags), the order of initialization is critical. Here are the two ways to start your app:
This is the most robust way. It ensures all global tags (div, button, etc.) are created before your App code is even read by the browser.
// main.js
+import { $ } from 'sigpro';
+import { UI, Router } from 'sigpro/plugins';
+
+// 1. Load plugins first
+$.plugin([UI, Router]).then(() => {
+
+ // 2. Import your app only after the environment is ready
+ import('./App.js').then(appFile => {
+ const MyApp = appFile.default;
+ $.mount(MyApp, '#app');
+ });
+
+});Use this only if you prefer not to use global tags and want to use $.html directly in your components. This allows for standard static imports.
// main.js
+import { $ } from 'sigpro';
+import { UI } from 'sigpro/plugins';
+import MyApp from './App.js'; // Static import works here
+
+$.plugin(UI);
+$.mount(MyApp, '#app');Warning: In this mode, if
App.jsusesdiv()instead of$.html('div'), it will throw aReferenceError.
You can pass a URL or an Array of URLs. SigPro will inject them as <script> tags and return a Promise that resolves when the scripts are fully loaded and executed.
// Loading external libraries as plugins
+await $.plugin([
+ 'https://cdn.jsdelivr.net/npm/chart.js',
+ 'https://cdn.example.com/custom-ui-lib.js'
+]);
+
+console.log("External resources are ready to use!");The $.plugin method adapts to whatever you throw at it:
| Input Type | Action | Behavior |
|---|---|---|
| Function | Executes fn($) | Synchronous / Immediate |
| String (URL) | Injects <script src="..."> | Asynchronous (Returns Promise) |
| Array | Processes each item in the list | Returns Promise if any item is Async |
.then()? Using $.plugin([...]).then(...) is like giving your app a "Pre-flight Check". It guarantees that:
The sigproRouter plugin for Vite automates route generation by scanning your pages directory. It creates a virtual module that you can import directly into your code, eliminating the need to maintain a manual routes array.
To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure.
my-sigpro-app/
+├── src/
+│ ├── pages/
+│ │ ├── index.js → #/
+│ │ ├── about.js → #/about
+│ │ ├── users/
+│ │ │ └── [id].js → #/users/:id
+│ │ └── blog/
+│ │ ├── index.js → #/blog
+│ │ └── [slug].js → #/blog/:slug
+│ ├── App.js (Optional App Shell)
+│ └── main.js (Entry Point)
+├── vite.config.js
+└── package.jsonAdd the plugin to your vite.config.js.
// vite.config.js
+import { defineConfig } from 'vite';
+import { sigproRouter } from 'sigpro/vite';
+
+export default defineConfig({
+ plugins: [sigproRouter()]
+});You can implement the router either directly in your entry point or inside an App component to support persistent layouts (like a navbar that doesn't re-render).
main.js Best for simple apps where the router occupies the entire viewport.
// src/main.js
+import { $ } from 'sigpro';
+import { Router } from 'sigpro/plugins';
+import { routes } from 'virtual:sigpro-routes';
+
+$.plugin(Router).then(() => {
+ $.mount(_router(routes), '#app');
+});App.js (With Layout) Recommended for apps with a fixed Sidebar or Navbar.
// src/main.js
+import { $ } from 'sigpro';
+import { Router } from 'sigpro/plugins';
+
+$.plugin(Router).then(() => {
+ import('./App.js').then(app => $.mount(app.default, '#app'));
+});
+
+// src/App.js
+import { routes } from 'virtual:sigpro-routes';
+
+export default () => {
+ return div({ class: 'layout' }, [
+ header([
+ h1("SigPro App"),
+ nav([
+ a({ href: '#/' }, "Home"),
+ a({ href: '#/blog' }, "Blog")
+ ])
+ ]),
+ // The router only swaps the content inside this <main> tag
+ main(_router(routes))
+ ]);
+};| File Path | Generated Route | Logic |
|---|---|---|
index.js | / | Home page |
about.js | /about | Static path |
[id].js | /:id | Dynamic parameter |
blog/index.js | /blog | Folder index |
_utils.js | Ignored | Files starting with _ are skipped |
npm install sigpropnpm add sigproyarn add sigprobun add sigproFine-grained reactivity, built-in routing, and modular plugins. All under 2KB.

SigPro isn't just another framework; it's a high-performance engine. It strips away the complexity of massive bundles and returns to the essence of the web, enhanced with reactive superpowers.
import { $ } from 'sigpro2';
+
+// A reactive state Signal
+const $count = $(0);
+
+// A Computed signal that updates automatically
+const $double = $(() => $count() * 2);
+
+// UI that breathes with your data
+const Counter = () => div([
+ h1(["Count: ", $count]),
+ p(["Double: ", $double]),
+ button({ onclick: () => $count(c => c + 1) }, "Increment")
+]);
+
+$.mount(Counter);Unlike frameworks that diff complex trees (V-DOM), SigPro binds your signals directly to real DOM text nodes and attributes. If the data changes, the node changes. Period.
Extend core capabilities in a single line. Add global UI helpers, routing, or state persistence seamlessly.
import { UI, Router } from 'sigpro/plugins';
+$.plugin([UI, Router]);With our dedicated Vite plugin, manage your routes simply by creating files in src/pages/. It supports native Lazy Loading out of the box for lightning-fast initial loads.
npm install sigpropnpm add sigproyarn add sigprobun add sigproSigPro is an open-source project. Whether you want to contribute, report a bug, or just talk about reactivity, join us on our official repository.
Built with ❤️ by NatxoCC