sigpro 1.0.14 → 1.2.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/Readme.md +164 -1008
  2. package/dist/sigpro.editor.js +1 -0
  3. package/dist/sigpro.grid.js +78 -0
  4. package/dist/sigpro.js +1 -0
  5. package/dist/sigpro.ui.css +2 -0
  6. package/dist/sigpro.ui.js +1 -0
  7. package/dist/sigpro.utils.js +1 -0
  8. package/dist/sigpro.vite.js +4 -0
  9. package/package.json +64 -14
  10. package/sigpro.d.ts +395 -0
  11. package/.github/workflows/publish.yml +0 -25
  12. package/bun.lock +0 -385
  13. package/docs/404.html +0 -22
  14. package/docs/api/components.html +0 -595
  15. package/docs/api/effects.html +0 -787
  16. package/docs/api/fetch.html +0 -873
  17. package/docs/api/pages.html +0 -405
  18. package/docs/api/quick.html +0 -217
  19. package/docs/api/routing.html +0 -628
  20. package/docs/api/signals.html +0 -683
  21. package/docs/api/storage.html +0 -820
  22. package/docs/assets/api_components.md.BlFwj17l.js +0 -571
  23. package/docs/assets/api_components.md.BlFwj17l.lean.js +0 -1
  24. package/docs/assets/api_effects.md.Br_yStBS.js +0 -763
  25. package/docs/assets/api_effects.md.Br_yStBS.lean.js +0 -1
  26. package/docs/assets/api_fetch.md.DQLBJSoq.js +0 -849
  27. package/docs/assets/api_fetch.md.DQLBJSoq.lean.js +0 -1
  28. package/docs/assets/api_pages.md.BP19nHXw.js +0 -381
  29. package/docs/assets/api_pages.md.BP19nHXw.lean.js +0 -1
  30. package/docs/assets/api_quick.md.BDS3ttnt.js +0 -193
  31. package/docs/assets/api_quick.md.BDS3ttnt.lean.js +0 -1
  32. package/docs/assets/api_routing.md.7SNAZXtp.js +0 -604
  33. package/docs/assets/api_routing.md.7SNAZXtp.lean.js +0 -1
  34. package/docs/assets/api_signals.md.CrW68-BA.js +0 -659
  35. package/docs/assets/api_signals.md.CrW68-BA.lean.js +0 -1
  36. package/docs/assets/api_storage.md.COEWBXHk.js +0 -796
  37. package/docs/assets/api_storage.md.COEWBXHk.lean.js +0 -1
  38. package/docs/assets/app.DtmzNmNl.js +0 -1
  39. package/docs/assets/chunks/framework.C8AWLET_.js +0 -19
  40. package/docs/assets/chunks/theme.yfWKMLQM.js +0 -1
  41. package/docs/assets/guide_getting-started.md.BeQpK3vd.js +0 -172
  42. package/docs/assets/guide_getting-started.md.BeQpK3vd.lean.js +0 -1
  43. package/docs/assets/guide_why.md.DXchYMN-.js +0 -23
  44. package/docs/assets/guide_why.md.DXchYMN-.lean.js +0 -1
  45. package/docs/assets/index.md.uvMJmU4o.js +0 -1
  46. package/docs/assets/index.md.uvMJmU4o.lean.js +0 -1
  47. package/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  48. package/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  49. package/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  50. package/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  51. package/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  52. package/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  53. package/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  54. package/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  55. package/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  56. package/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  57. package/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  58. package/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  59. package/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  60. package/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  61. package/docs/assets/style.DJRheFKp.css +0 -1
  62. package/docs/assets/ui_intro.md.gZ21GFqo.js +0 -1
  63. package/docs/assets/ui_intro.md.gZ21GFqo.lean.js +0 -1
  64. package/docs/assets/vite_plugin.md.gDWEi8f0.js +0 -225
  65. package/docs/assets/vite_plugin.md.gDWEi8f0.lean.js +0 -1
  66. package/docs/guide/getting-started.html +0 -196
  67. package/docs/guide/why.html +0 -47
  68. package/docs/hashmap.json +0 -1
  69. package/docs/index.html +0 -25
  70. package/docs/logo.svg +0 -118
  71. package/docs/ui/intro.html +0 -25
  72. package/docs/vite/plugin.html +0 -249
  73. package/docs/vp-icons.css +0 -1
  74. package/index.js +0 -3
  75. package/packages/docs/.vitepress/cache/deps/@theme_index.js +0 -275
  76. package/packages/docs/.vitepress/cache/deps/@theme_index.js.map +0 -7
  77. package/packages/docs/.vitepress/cache/deps/_metadata.json +0 -40
  78. package/packages/docs/.vitepress/cache/deps/chunk-3S55Y3P7.js +0 -12951
  79. package/packages/docs/.vitepress/cache/deps/chunk-3S55Y3P7.js.map +0 -7
  80. package/packages/docs/.vitepress/cache/deps/chunk-RLEUDPPB.js +0 -9719
  81. package/packages/docs/.vitepress/cache/deps/chunk-RLEUDPPB.js.map +0 -7
  82. package/packages/docs/.vitepress/cache/deps/package.json +0 -3
  83. package/packages/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +0 -4505
  84. package/packages/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +0 -7
  85. package/packages/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +0 -583
  86. package/packages/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +0 -7
  87. package/packages/docs/.vitepress/cache/deps/vue.js +0 -347
  88. package/packages/docs/.vitepress/cache/deps/vue.js.map +0 -7
  89. package/packages/docs/.vitepress/config.js +0 -68
  90. package/packages/docs/api/components.md +0 -760
  91. package/packages/docs/api/effects.md +0 -1039
  92. package/packages/docs/api/fetch.md +0 -998
  93. package/packages/docs/api/pages.md +0 -497
  94. package/packages/docs/api/quick.md +0 -436
  95. package/packages/docs/api/routing.md +0 -784
  96. package/packages/docs/api/signals.md +0 -899
  97. package/packages/docs/api/storage.md +0 -952
  98. package/packages/docs/guide/getting-started.md +0 -308
  99. package/packages/docs/guide/why.md +0 -135
  100. package/packages/docs/index.md +0 -84
  101. package/packages/docs/logo.svg +0 -118
  102. package/packages/docs/public/logo.svg +0 -118
  103. package/packages/docs/ui/intro.md +0 -16
  104. package/packages/docs/vite/plugin.md +0 -423
  105. package/packages/sigpro/plugin.js +0 -91
  106. package/packages/sigpro/plugin.min.js +0 -1
  107. package/packages/sigpro/sigpro.js +0 -631
  108. package/packages/sigpro/sigpro.min.js +0 -1
  109. package/vite.config.js +0 -24
package/Readme.md CHANGED
@@ -1,1008 +1,164 @@
1
- # SigPro 🚀
2
-
3
- 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.
4
-
5
- **~3KB** gzipped ⚡
6
-
7
- [![npm version](https://img.shields.io/npm/v/sigpro.svg)](https://www.npmjs.com/package/sigpro)
8
- [![bundle size](https://img.shields.io/bundlephobia/minzip/sigpro)](https://bundlephobia.com/package/sigpro)
9
- [![license](https://img.shields.io/npm/l/sigpro)](https://github.com/natxocc/sigpro/blob/main/LICENSE)
10
-
11
- ## ❓ Why?
12
-
13
- After years of building applications with React, Vue, and Svelte—investing countless hours mastering their unique mental models, build tools, and update cycles—I kept circling back to the same realization: no matter how sophisticated the framework, it all eventually compiles down to HTML, CSS, and vanilla JavaScript. The web platform has evolved tremendously, yet many libraries continue to reinvent the wheel, creating parallel universes with their own rules, their own syntaxes, and their own steep learning curves.
14
-
15
- **SigPro is my answer to a simple question:** Why fight the platform when we can embrace it?
16
-
17
- Modern browsers now offer powerful primitives—Custom Elements, Shadow DOM, CSS custom properties, and microtask queues—that make true reactivity possible without virtual DOM diffing, without compilers, and without lock-in. SigPro strips away the complexity, delivering a reactive programming model that feels familiar but stays remarkably close to vanilla JS. No JSX transformations, no template compilers, no proprietary syntax to learn—just functions, signals, and template literals that work exactly as you'd expect.
18
-
19
- What emerged is a library that proves we've reached a turning point: the web is finally mature enough that we don't need to abstract it anymore. We can build reactive, component-based applications using virtually pure JavaScript, leveraging the platform's latest advances instead of working against them. SigPro isn't just another frameworkit's a return to fundamentals, showing that the dream of simple, powerful reactivity is now achievable with the tools browsers give us out of the box.
20
-
21
- ## 📊 Comparison Table
22
-
23
- | Metric | SigPro | Solid | Svelte | Vue | React |
24
- |--------|--------|-------|--------|-----|-------|
25
- | **Bundle Size** (gzip) | 🥇 **5.2KB** | 🥈 15KB | 🥉 16.6KB | 20.4KB | 43.9KB |
26
- | **Time to Interactive** | 🥇 **0.8s** | 🥈 1.3s | 🥉 1.4s | 1.6s | 2.3s |
27
- | **Initial Render** (ms) | 🥇 **124ms** | 🥈 198ms | 🥉 287ms | 298ms | 452ms |
28
- | **Update Performance** (ms) | 🥇 **4ms** | 🥈 5ms | 🥈 5ms | 🥉 7ms | 18ms |
29
- | **Code Splitting** | 🥇 **Zero overhead** | 🥈 Minimal | 🥉 Moderate | 🥉 Moderate | High |
30
- | **Learning Curve** (hours) | 🥇 **2h** | 🥈 20h | 🥉 30h | 40h | 60h |
31
- | **Dependencies** | 🥇 0 | 🥇 0 | 🥇 0 | 🥈 2 | 🥉 5 |
32
- | **Compilation Required** | 🥇 No | 🥇 No | 🥈 Yes | 🥇 No | 🥇 No |
33
- | **Browser Native** | 🥇 **Yes** | 🥈 Partial | 🥉 Partial | 🥉 Partial | No |
34
- | **Framework Lock-in** | 🥇 **None** | 🥈 Medium | 🥉 High | 🥈 Medium | 🥉 High |
35
- | **Longevity** (standards-based) | 🥇 **10+ years** | 🥈 5 years | 🥉 3 years | 🥈 5 years | 🥈 5 years |
36
-
37
- **The Verdict:** While other frameworks build parallel universes with proprietary syntax and compilation steps, SigPro embraces the web platform. The result isn't just smaller bundles or faster rendering—it's code that will still run 10 years from now, in any browser, without maintenance.
38
-
39
- *"Stop fighting the platform. Start building with it."*
40
-
41
- ## 📦 Installation
42
-
43
- ```bash
44
- npm install sigpro
45
- ```
46
- or
47
- ```bash
48
- bun add sigpro
49
- ```
50
- or more simple:
51
-
52
- copy `sigpro.js` file where you want to use it.
53
-
54
- ## 🎯 Philosophy
55
-
56
- SigPro (Signal Professional) embraces the web platform. Built on top of Custom Elements and reactive signals, it offers a development experience similar to modern frameworks but with a minimal footprint and zero dependencies.
57
-
58
- **Core Principles:**
59
- - 📡 **True Reactivity** - Automatic dependency tracking, no manual subscriptions
60
- - ⚡ **Surgical Updates** - Only the exact nodes that depend on changed values are updated
61
- - 🧩 **Web Standards** - Built on Custom Elements, no custom rendering engine
62
- - 🎨 **Intuitive API** - Learn once, use everywhere
63
- - 🔬 **Predictable** - No magic, just signals and effects
64
-
65
- ## 💡 Hint for VS Code
66
-
67
- For the best development experience with SigPro, install these VS Code extensions:
68
-
69
- - **Prettier** Automatically formats your template literals for better readability
70
- - **lit-html** Adds syntax highlighting and inline HTML color previews inside `html` tagged templates
71
-
72
- This combination gives you framework-level developer experience without the framework complexity—syntax highlighting, color previews, and automatic formatting for your reactive templates, all while writing pure JavaScript.
73
-
74
- ```javascript
75
- // With lit-html extension, this gets full syntax highlighting and color previews!
76
- html`
77
- <div style="color: #ff4444; background: linear-gradient(45deg, blue, green)">
78
- <h1>Beautiful highlighted template</h1>
79
- </div>
80
- `
81
- ```
82
-
83
- # SigPro API - Quick Reference
84
-
85
- | Function | Description | Example |
86
- |----------|-------------|---------|
87
- | **`$`** | Reactive signal (getter/setter) | `const count = $(0); count(5); count()` |
88
- | **`$.effect`** | Runs effect when dependencies change | `$.effect(() => console.log(count()))` |
89
- | **`$.page`** | Creates a page with automatic cleanup | `export default $.page(() => { ... })` |
90
- | **`$.component`** | Creates reactive Web Component | `$.component('my-menu', setup, ['items'])` |
91
- | **`$.fetch`** | Fetch wrapper with loading signal | `const data = await $.fetch('/api', data, loading)` |
92
- | **`$.router`** | Hash-based router with params | `$.router([{path:'/', component:Home}])` |
93
- | **`$.storage`** | Persistent signal (localStorage) | `const theme = $.storage('theme', 'light')` |
94
- | **`html`** | Template literal for reactive HTML | `` html`<div>${count}</div>` `` |
95
-
96
- ```javascript
97
- import { $, html } from "sigpro";
98
- ```
99
-
100
- ---
101
-
102
- ## 📚 API Reference
103
-
104
- ---
105
-
106
- ### `$(initialValue)` - Signals
107
-
108
- Creates a reactive value that notifies dependents when changed.
109
-
110
- #### Basic Signal (Getter/Setter)
111
-
112
- ```javascript
113
- import { $ } from 'sigpro';
114
-
115
- // Create a signal
116
- const count = $(0);
117
-
118
- // Read value
119
- console.log(count()); // 0
120
-
121
- // Write value
122
- count(5);
123
- count(prev => prev + 1); // Use function for previous value
124
-
125
- // Read with dependency tracking (inside effect)
126
- $.effect(() => {
127
- console.log(count()); // Will be registered as dependency
128
- });
129
- ```
130
-
131
- #### Computed Signal
132
-
133
- ```javascript
134
- import { $ } from 'sigpro';
135
-
136
- const firstName = $('John');
137
- const lastName = $('Doe');
138
-
139
- // Computed signal - automatically updates when dependencies change
140
- const fullName = $(() => `${firstName()} ${lastName()}`);
141
-
142
- console.log(fullName()); // "John Doe"
143
-
144
- firstName('Jane');
145
- console.log(fullName()); // "Jane Doe"
146
- ```
147
-
148
- **Returns:** Function that acts as getter/setter
149
-
150
- ---
151
-
152
- ### `$.effect(effectFn)` - Effects
153
-
154
- Executes a function and automatically re-runs it when its dependencies change.
155
-
156
- #### Basic Effect
157
-
158
- ```javascript
159
- import { $ } from 'sigpro';
160
-
161
- const count = $(0);
162
-
163
- $.effect(() => {
164
- console.log(`Count is: ${count()}`);
165
- });
166
- // Log: "Count is: 0"
167
-
168
- count(1);
169
- // Log: "Count is: 1"
170
- ```
171
-
172
- #### Effect with Cleanup
173
-
174
- ```javascript
175
- import { $ } from 'sigpro';
176
-
177
- const userId = $(1);
178
-
179
- $.effect(() => {
180
- const id = userId();
181
-
182
- // Simulate subscription
183
- const timer = setInterval(() => {
184
- console.log('Polling user', id);
185
- }, 1000);
186
-
187
- // Return cleanup function
188
- return () => clearInterval(timer);
189
- });
190
-
191
- userId(2); // Previous timer cleared, new one created
192
- ```
193
-
194
- **Parameters:**
195
- - `effectFn`: Function to execute. Can return a cleanup function
196
-
197
- **Returns:** Function to stop the effect
198
-
199
- ---
200
-
201
- ### `$.page(setupFunction)` - Pages
202
-
203
- Creates a page with automatic cleanup of all signals and effects when navigated away.
204
-
205
- ```javascript
206
- // pages/about.js
207
- import { html, $ } from "sigpro";
208
-
209
- export default $.page(() => {
210
- const count = $(0);
211
- const loading = $(false);
212
-
213
- $.effect(() => {
214
- if (loading()) {
215
- // Fetch data...
216
- }
217
- });
218
-
219
- return html`
220
- <div>
221
- <h1>About Page</h1>
222
- <p>Count: ${count}</p>
223
- <button @click=${() => count(c => c + 1)}>Increment</button>
224
- </div>
225
- `;
226
- });
227
- ```
228
-
229
- **With parameters:**
230
- ```javascript
231
- // pages/user.js
232
- export default $.page(({ params }) => {
233
- const userId = params.id;
234
- const userData = $(null);
235
-
236
- $.effect(() => {
237
- fetch(`/api/users/${userId}`)
238
- .then(r => r.json())
239
- .then(userData);
240
- });
241
-
242
- return html`<div>User: ${userData}</div>`;
243
- });
244
- ```
245
-
246
- **Parameters:**
247
- - `setupFunction`: Function that returns the page content. Receives `{ params, onUnmount }`
248
-
249
- **Returns:** A function that creates page instances with props
250
-
251
- ---
252
-
253
- ### `$.component(tagName, setupFunction, observedAttributes, useShadowDOM)` - Web Components
254
-
255
- Creates Custom Elements with reactive properties. Choose between **Light DOM** (default) or **Shadow DOM** for style encapsulation.
256
-
257
- ### Parameters
258
-
259
- | Parameter | Type | Default | Description |
260
- |-----------|------|---------|-------------|
261
- | `tagName` | `string` | (required) | Custom element tag name (must include a hyphen, e.g., `my-button`) |
262
- | `setupFunction` | `Function` | (required) | Function that renders the component |
263
- | `observedAttributes` | `string[]` | `[]` | Observed attributes that react to changes |
264
- | `useShadowDOM` | `boolean` | `false` | `true` = Shadow DOM (encapsulated), `false` = Light DOM (inherits styles) |
265
-
266
- ---
267
-
268
- #### 🏠 **Light DOM** (`useShadowDOM = false`) - Default
269
-
270
- The component **inherits global styles** from the application. Ideal for components that should visually integrate with the rest of the interface.
271
-
272
- ##### Example: Button with Tailwind CSS
273
-
274
- ```javascript
275
- // button-tailwind.js
276
- import { $, html } from 'sigpro';
277
-
278
- $.component('tw-button', (props, { slot, emit }) => {
279
- const variant = props.variant() || 'primary';
280
-
281
- const variants = {
282
- primary: 'bg-blue-500 hover:bg-blue-600 text-white',
283
- secondary: 'bg-gray-500 hover:bg-gray-600 text-white',
284
- outline: 'border border-blue-500 text-blue-500 hover:bg-blue-50'
285
- };
286
-
287
- return html`
288
- <button
289
- class="px-4 py-2 rounded font-semibold transition-colors ${variants[variant]}"
290
- @click=${() => emit('click')}
291
- >
292
- ${slot()}
293
- </button>
294
- `;
295
- }, ['variant']); // Observe the 'variant' attribute
296
- ```
297
-
298
- **Usage in HTML:**
299
- ```html
300
- <!-- These buttons will inherit global Tailwind styles -->
301
- <tw-button variant="primary" @click=${handleClick}>
302
- Save changes
303
- </tw-button>
304
-
305
- <tw-button variant="outline">
306
- Cancel
307
- </tw-button>
308
- ```
309
-
310
- ##### Example: Form Input with Validation
311
-
312
- ```javascript
313
- // form-input.js
314
- $.component('form-input', (props, { emit }) => {
315
- const handleInput = (e) => {
316
- const value = e.target.value;
317
- props.value(value);
318
- emit('update', value);
319
-
320
- // Simple validation
321
- if (props.pattern()) {
322
- const regex = new RegExp(props.pattern());
323
- const isValid = regex.test(value);
324
- emit('validate', isValid);
325
- }
326
- };
327
-
328
- return html`
329
- <div class="form-group">
330
- <label class="form-label">${props.label()}</label>
331
- <input
332
- type="${props.type() || 'text'}"
333
- class="form-control ${props.error() ? 'is-invalid' : ''}"
334
- :value=${props.value}
335
- @input=${handleInput}
336
- placeholder="${props.placeholder() || ''}"
337
- ?disabled=${props.disabled}
338
- />
339
- ${props.error() ? html`
340
- <div class="invalid-feedback">${props.error()}</div>
341
- ` : ''}
342
- </div>
343
- `;
344
- }, ['label', 'type', 'value', 'error', 'placeholder', 'disabled', 'pattern']);
345
- ```
346
-
347
- **Usage:**
348
- ```html
349
- <form-input
350
- label="Email"
351
- type="email"
352
- :value=${email}
353
- pattern="^[^\s@]+@[^\s@]+\.[^\s@]+$"
354
- @update=${(e) => email(e.detail)}
355
- @validate=${(e) => setEmailValid(e.detail)}
356
- >
357
- </form-input>
358
- ```
359
-
360
- ##### Example: Card that uses global design system
361
-
362
- ```javascript
363
- // content-card.js
364
- $.component('content-card', (props, { slot }) => {
365
- return html`
366
- <div class="card shadow-sm">
367
- <div class="card-header bg-white">
368
- <h3 class="card-title h5 mb-0">${props.title()}</h3>
369
- </div>
370
- <div class="card-body">
371
- ${slot()}
372
- </div>
373
- ${props.footer() ? html`
374
- <div class="card-footer bg-white">
375
- ${props.footer()}
376
- </div>
377
- ` : ''}
378
- </div>
379
- `;
380
- }, ['title', 'footer']);
381
- ```
382
-
383
- **Usage:**
384
- ```html
385
- <content-card title="Recent Activity">
386
- <p>Your dashboard updates will appear here.</p>
387
- </content-card>
388
- ```
389
-
390
- ---
391
-
392
- #### 🛡️ **Shadow DOM** (`useShadowDOM = true`) - Encapsulated
393
-
394
- The component **encapsulates its styles** completely. External styles don't affect it, and its styles don't leak out. Perfect for:
395
- - UI libraries distributed across projects
396
- - Third-party widgets
397
- - Components with very specific styling needs
398
-
399
- ##### Example: Calendar Component (Distributable UI)
400
-
401
- ```javascript
402
- // ui-calendar.js
403
- $.component('ui-calendar', (props, { select }) => {
404
- const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
405
- const currentDate = props.date() ? new Date(props.date()) : new Date();
406
-
407
- return html`
408
- <style>
409
- /* These styles are ENCAPSULATED - won't affect the page */
410
- .calendar {
411
- font-family: system-ui, -apple-system, sans-serif;
412
- background: white;
413
- border-radius: 12px;
414
- padding: 20px;
415
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
416
- width: 320px;
417
- }
418
- .header {
419
- display: flex;
420
- justify-content: space-between;
421
- align-items: center;
422
- margin-bottom: 20px;
423
- }
424
- .month {
425
- font-size: 1.2rem;
426
- font-weight: 600;
427
- color: #2c3e50;
428
- }
429
- .nav-btn {
430
- background: none;
431
- border: none;
432
- font-size: 1.2rem;
433
- cursor: pointer;
434
- padding: 4px 12px;
435
- border-radius: 6px;
436
- transition: background 0.2s;
437
- }
438
- .nav-btn:hover {
439
- background: #f0f0f0;
440
- }
441
- .weekdays {
442
- display: grid;
443
- grid-template-columns: repeat(7, 1fr);
444
- text-align: center;
445
- font-weight: 500;
446
- color: #7f8c8d;
447
- margin-bottom: 10px;
448
- font-size: 0.85rem;
449
- }
450
- .days {
451
- display: grid;
452
- grid-template-columns: repeat(7, 1fr);
453
- gap: 4px;
454
- }
455
- .day {
456
- aspect-ratio: 1;
457
- display: flex;
458
- align-items: center;
459
- justify-content: center;
460
- cursor: pointer;
461
- border-radius: 50%;
462
- transition: all 0.2s;
463
- font-size: 0.9rem;
464
- }
465
- .day:hover {
466
- background: #e3f2fd;
467
- }
468
- .day.selected {
469
- background: #2196f3;
470
- color: white;
471
- font-weight: 500;
472
- }
473
- .day.today {
474
- border: 2px solid #2196f3;
475
- font-weight: 600;
476
- }
477
- .day.other-month {
478
- color: #bdc3c7;
479
- }
480
- </style>
481
-
482
- <div class="calendar">
483
- <div class="header">
484
- <button class="nav-btn" @click=${() => handlePrevMonth()}>&#8592;</button>
485
- <span class="month">${currentDate.toLocaleString('default', { month: 'long', year: 'numeric' })}</span>
486
- <button class="nav-btn" @click=${() => handleNextMonth()}>&#8594;</button>
487
- </div>
488
-
489
- <div class="weekdays">
490
- ${days.map(day => html`<span>${day}</span>`)}
491
- </div>
492
-
493
- <div class="days">
494
- ${generateDays(currentDate).map(day => html`
495
- <div
496
- class="day ${day.classes}"
497
- @click=${() => selectDate(day.date)}
498
- >
499
- ${day.number}
500
- </div>
501
- `)}
502
- </div>
503
- </div>
504
- `;
505
- }, ['date'], true); // true = use Shadow DOM
506
- ```
507
-
508
- **Usage - anywhere, anytime, looks identical:**
509
- ```html
510
- <!-- Same calendar, same styles, in ANY website -->
511
- <ui-calendar date="2024-03-15"></ui-calendar>
512
- ```
513
-
514
- ##### Example: Third-party Chat Widget
515
-
516
- ```javascript
517
- // chat-widget.js
518
- $.component('chat-widget', (props, { select }) => {
519
- return html`
520
- <style>
521
- /* Completely isolated - won't affect host page */
522
- :host {
523
- all: initial; /* Reset all styles */
524
- display: block;
525
- }
526
- .chat-container {
527
- position: fixed;
528
- bottom: 20px;
529
- right: 20px;
530
- width: 320px;
531
- height: 480px;
532
- background: white;
533
- border-radius: 16px;
534
- box-shadow: 0 8px 30px rgba(0,0,0,0.2);
535
- display: flex;
536
- flex-direction: column;
537
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
538
- z-index: 2147483647; /* Max z-index */
539
- }
540
- .header {
541
- padding: 16px;
542
- background: #075e54;
543
- color: white;
544
- border-radius: 16px 16px 0 0;
545
- display: flex;
546
- align-items: center;
547
- gap: 12px;
548
- }
549
- .avatar {
550
- width: 40px;
551
- height: 40px;
552
- background: #128c7e;
553
- border-radius: 50%;
554
- display: flex;
555
- align-items: center;
556
- justify-content: center;
557
- font-weight: bold;
558
- font-size: 1.2rem;
559
- }
560
- .title {
561
- font-weight: 600;
562
- }
563
- .subtitle {
564
- font-size: 0.8rem;
565
- opacity: 0.8;
566
- }
567
- .messages {
568
- flex: 1;
569
- padding: 16px;
570
- overflow-y: auto;
571
- background: #e5ddd5;
572
- display: flex;
573
- flex-direction: column;
574
- gap: 8px;
575
- }
576
- .message {
577
- max-width: 80%;
578
- padding: 8px 12px;
579
- border-radius: 12px;
580
- font-size: 0.9rem;
581
- word-wrap: break-word;
582
- }
583
- .message.received {
584
- background: white;
585
- align-self: flex-start;
586
- border-bottom-left-radius: 4px;
587
- }
588
- .message.sent {
589
- background: #dcf8c6;
590
- align-self: flex-end;
591
- border-bottom-right-radius: 4px;
592
- }
593
- .footer {
594
- padding: 12px;
595
- background: #f0f0f0;
596
- border-radius: 0 0 16px 16px;
597
- display: flex;
598
- gap: 8px;
599
- }
600
- .input {
601
- flex: 1;
602
- padding: 8px 12px;
603
- border: none;
604
- border-radius: 20px;
605
- outline: none;
606
- font-size: 0.9rem;
607
- }
608
- .send-btn {
609
- width: 40px;
610
- height: 40px;
611
- border: none;
612
- border-radius: 50%;
613
- background: #075e54;
614
- color: white;
615
- cursor: pointer;
616
- display: flex;
617
- align-items: center;
618
- justify-content: center;
619
- transition: background 0.2s;
620
- }
621
- .send-btn:hover {
622
- background: #128c7e;
623
- }
624
- .send-btn:disabled {
625
- opacity: 0.5;
626
- cursor: not-allowed;
627
- }
628
- </style>
629
-
630
- <div class="chat-container">
631
- <div class="header">
632
- <div class="avatar">💬</div>
633
- <div>
634
- <div class="title">Support Chat</div>
635
- <div class="subtitle">Online</div>
636
- </div>
637
- </div>
638
-
639
- <div class="messages" id="messageContainer">
640
- ${props.messages().map(msg => html`
641
- <div class="message ${msg.type}">${msg.text}</div>
642
- `)}
643
- </div>
644
-
645
- <div class="footer">
646
- <input
647
- type="text"
648
- class="input"
649
- placeholder="Type a message..."
650
- :value=${props.currentMessage}
651
- @keydown.enter=${() => sendMessage()}
652
- />
653
- <button
654
- class="send-btn"
655
- ?disabled=${!props.currentMessage()}
656
- @click=${() => sendMessage()}
657
- >
658
-
659
- </button>
660
- </div>
661
- </div>
662
- `;
663
- }, ['messages', 'currentMessage'], true);
664
- ```
665
-
666
- **Usage - embed in ANY website:**
667
- ```html
668
- <chat-widget .messages=${chatHistory} .currentMessage=${newMessage}></chat-widget>
669
- ```
670
-
671
- ---
672
-
673
- ### 🎯 **Quick Decision Guide**
674
-
675
- | Use Light DOM (`false`) when... | Use Shadow DOM (`true`) when... |
676
- |--------------------------------|-------------------------------|
677
- | ✅ Component is part of your main app | ✅ Building a UI library for others |
678
- | ✅ Using global CSS (Tailwind, Bootstrap) | ✅ Creating embeddable widgets |
679
- | ✅ Need to inherit theme variables | ✅ Styles must be pixel-perfect everywhere |
680
- | ✅ Working with existing design system | ✅ Component has complex, specific styles |
681
- | ✅ Quick prototyping | ✅ Distributing to different projects |
682
- | ✅ Form elements that should match site | ✅ Need style isolation/encapsulation |
683
-
684
- ### 💡 **Pro Tips**
685
-
686
- 1. **Light DOM components** are great for app-specific UI that should feel "native" to your site
687
- 2. **Shadow DOM components** are perfect for reusable "products" that must look identical everywhere
688
- 3. You can mix both in the same app - choose per component based on needs
689
- 4. Shadow DOM also provides DOM isolation - great for complex widgets
690
-
691
- ```javascript
692
- // Mix and match in the same app!
693
- $.component('app-header', setup, ['title']); // Light DOM
694
- $.component('user-menu', setup, ['items']); // Light DOM
695
- $.component('chat-widget', setup, ['messages'], true); // Shadow DOM
696
- $.component('data-grid', setup, ['columns', 'data'], true); // Shadow DOM
697
- ```
698
-
699
- ---
700
-
701
- ### `$.fetch(url, data, [loading])` - Fetch
702
-
703
- Simple fetch wrapper with automatic JSON handling and optional loading signal.
704
-
705
- ```javascript
706
- import { $ } from 'sigpro';
707
-
708
- const loading = $(false);
709
-
710
- async function loadUser(id) {
711
- const data = await $.fetch(`/api/users/${id}`, null, loading);
712
- if (data) userData(data);
713
- }
714
-
715
- // In your UI
716
- html`<div>${() => loading() ? 'Loading...' : userData()?.name}</div>`;
717
- ```
718
-
719
- **Parameters:**
720
- - `url`: Endpoint URL
721
- - `data`: Data to send (auto JSON.stringify'd)
722
- - `loading`: Optional signal function to track loading state
723
-
724
- **Returns:** `Promise<Object|null>` - Parsed JSON response or null on error
725
-
726
- ---
727
-
728
- ### `$.storage(key, initialValue, [storage])` - Persistent Signal
729
-
730
- Signal that automatically syncs with localStorage or sessionStorage.
731
-
732
- ```javascript
733
- import { $ } from 'sigpro';
734
-
735
- // Automatically saves to localStorage
736
- const theme = $.storage('theme', 'light');
737
- const user = $.storage('user', null);
738
-
739
- theme('dark'); // Saved to localStorage
740
- // Page refresh... theme() returns 'dark'
741
-
742
- // Use sessionStorage instead
743
- const tempData = $.storage('temp', {}, sessionStorage);
744
- ```
745
-
746
- **Parameters:**
747
- - `key`: Storage key name
748
- - `initialValue`: Default value if none stored
749
- - `storage`: Storage type (default: `localStorage`, options: `sessionStorage`)
750
-
751
- **Returns:** Signal function that persists to storage on changes
752
-
753
- ---
754
-
755
- ### `$.router(routes)` - Hash-Based Router
756
-
757
- Creates a simple, powerful hash-based router for Single Page Applications (SPAs) with **automatic page cleanup** and **zero configuration**. Built on native browser APIs - no dependencies, no complex setup.
758
-
759
- ### Why Hash-Based?
760
-
761
- Hash routing (`#/about`) works **everywhere** - no server configuration needed. It's perfect for:
762
- - Static sites and SPAs
763
- - GitHub Pages, Netlify, any static hosting
764
- - Local development without a server
765
- - Projects that need to work immediately
766
-
767
- ### Basic Usage
768
-
769
- ```javascript
770
- import { $, html } from 'sigpro';
771
- import HomePage from './pages/HomePage.js';
772
- import AboutPage from './pages/AboutPage.js';
773
- import UserPage from './pages/UserPage.js';
774
- import NotFound from './pages/NotFound.js';
775
-
776
- // Define your routes
777
- const routes = [
778
- { path: '/', component: () => HomePage() },
779
- { path: '/about', component: () => AboutPage() },
780
- { path: '/users/:id', component: (params) => UserPage(params) },
781
- { path: /^\/posts\/(?<id>\d+)$/, component: (params) => PostPage(params) },
782
- ];
783
-
784
- // Create and mount the router
785
- const router = $.router(routes);
786
- document.body.appendChild(router);
787
- ```
788
-
789
- ---
790
-
791
- ### 📋 Route Definition
792
-
793
- Each route is an object with two properties:
794
-
795
- | Property | Type | Description |
796
- |----------|------|-------------|
797
- | `path` | `string` or `RegExp` | Route pattern to match |
798
- | `component` | `Function` | Function that returns page content (receives `params`) |
799
-
800
- #### String Paths (Simple Routes)
801
-
802
- ```javascript
803
- { path: '/', component: () => HomePage() }
804
- { path: '/about', component: () => AboutPage() }
805
- { path: '/contact', component: () => ContactPage() }
806
- { path: '/users/:id', component: (params) => UserPage(params) } // With parameter
807
- ```
808
-
809
- String paths support:
810
- - **Static segments**: `/about`, `/contact`, `/products`
811
- - **Named parameters**: `:id`, `:slug`, `:username` (captured in `params`)
812
-
813
- #### RegExp Paths (Advanced Routing)
814
-
815
- ```javascript
816
- // Match numeric IDs only
817
- { path: /^\/users\/(?<id>\d+)$/, component: (params) => UserPage(params) }
818
-
819
- // Match product slugs (letters, numbers, hyphens)
820
- { path: /^\/products\/(?<slug>[a-z0-9-]+)$/, component: (params) => ProductPage(params) }
821
-
822
- // Match blog posts by year/month
823
- { path: /^\/blog\/(?<year>\d{4})\/(?<month>\d{2})$/, component: (params) => BlogArchive(params) }
824
-
825
- // Match optional language prefix
826
- { path: /^\/(?<lang>en|es|fr)?\/?about$/, component: (params) => AboutPage(params) }
827
- ```
828
-
829
- RegExp gives you **full control** over route matching with named capture groups.
830
-
831
- ---
832
-
833
- ## 🧭 `$.router(routes)` - Simple Router with Parameters
834
-
835
- Creates a hash-based router with support for `:param` parameters. Automatically cleans up pages when navigating away.
836
-
837
- ### 📋 Route Parameters (Human-Friendly)
838
-
839
- ```javascript
840
- const routes = [
841
- { path: '/', component: HomePage },
842
- { path: '/about', component: AboutPage },
843
- { path: '/user/:id', component: UserPage }, // /user/42 → { id: '42' }
844
- { path: '/user/:id/posts', component: UserPostsPage }, // /user/42/posts → { id: '42' }
845
- { path: '/user/:id/posts/:pid', component: PostPage }, // /user/42/posts/123 → { id: '42', pid: '123' }
846
- { path: '/search/:query/page/:num', component: SearchPage }, // /search/js/page/2 → { query: 'js', num: '2' }
847
- ];
848
- ```
849
-
850
- ### 🎯 Accessing Parameters in Pages
851
-
852
- Parameters are automatically extracted and passed to your page component:
853
-
854
- ```javascript
855
- // pages/UserPage.js
856
- import { $, html } from 'sigpro';
857
-
858
- export default (params) => $.page(() => {
859
- // /user/42 → params = { id: '42' }
860
- const userId = params.id;
861
- const userData = $(null);
862
-
863
- return html`
864
- <div>
865
- <h1>User Profile: ${userId}</h1>
866
- <p>Loading user data...</p>
867
- </div>
868
- `;
869
- });
870
-
871
- // pages/PostPage.js
872
- export default (params) => $.page(() => {
873
- // /user/42/posts/123 → params = { id: '42', pid: '123' }
874
- const { id, pid } = params;
875
-
876
- return html`
877
- <div>
878
- <h1>Post ${pid} from user ${id}</h1>
879
- </div>
880
- `;
881
- });
882
- ```
883
-
884
- ### 🧭 Navigation
885
-
886
- ```javascript
887
- // Programmatic navigation
888
- $.router.go('/user/42');
889
- $.router.go('/search/javascript/page/2');
890
- $.router.go('about'); // Same as '/about' (auto-adds leading slash)
891
-
892
- // Link navigation (in templates)
893
- html`
894
- <nav>
895
- <a href="#/">Home</a>
896
- <a href="#/user/42">Profile</a>
897
- <button @click=${() => $.router.go('/contact')}>Contact</button>
898
- </nav>
899
- `;
900
- ```
901
-
902
- ### 🔄 Automatic Page Cleanup
903
-
904
- ```javascript
905
- export default (params) => $.page(({ onUnmount }) => {
906
- // Set up interval
907
- const interval = setInterval(() => {
908
- fetchData(params.id);
909
- }, 5000);
910
-
911
- // Auto-cleaned when navigating away
912
- onUnmount(() => clearInterval(interval));
913
-
914
- return html`<div>Page content</div>`;
915
- });
916
- ```
917
-
918
- ### 📦 Usage in Templates
919
-
920
- ```javascript
921
- import { $, html } from 'sigpro';
922
- import HomePage from './pages/Home.js';
923
- import UserPage from './pages/User.js';
924
-
925
- const routes = [
926
- { path: '/', component: HomePage },
927
- { path: '/user/:id', component: UserPage },
928
- ];
929
-
930
- // Mount router directly in your template
931
- const App = () => html`
932
- <div class="app">
933
- <header>My App</header>
934
- <main>
935
- ${$.router(routes)} <!-- Router renders here -->
936
- </main>
937
- </div>
938
- `;
939
-
940
- document.body.appendChild(App());
941
- ```
942
-
943
- ### 🎯 API Reference
944
-
945
- #### `$.router(routes)`
946
- - **routes**: `Array<{path: string, component: Function}>` - Route configurations with `:param` support
947
- - **Returns**: `HTMLDivElement` - Container that renders the current page
948
-
949
- #### `$.router.go(path)`
950
- - **path**: `string` - Route path (automatically adds leading slash)
951
-
952
- ### 💡 Pro Tips
953
-
954
- 1. **Order matters** - Define more specific routes first:
955
- ```javascript
956
- [
957
- { path: '/user/:id/edit', component: EditUser }, // More specific first
958
- { path: '/user/:id', component: ViewUser }, // Then generic
959
- ]
960
- ```
961
-
962
- 2. **Cleanup is automatic** - All effects, intervals, and event listeners in `$.page` are cleaned up
963
-
964
- 3. **Zero config** - Just define routes and use them
965
-
966
- ---
967
-
968
- ### `html` - Template Literal Tag
969
-
970
- Creates reactive DOM fragments using template literals.
971
-
972
- #### Basic Usage
973
-
974
- ```javascript
975
- import { $, html } from 'sigpro';
976
-
977
- const count = $(0);
978
-
979
- const fragment = html`
980
- <div>
981
- <h1>Count: ${count}</h1>
982
- <button @click=${() => count(c => c + 1)}>+</button>
983
- </div>
984
- `;
985
- ```
986
-
987
- #### Directive Reference
988
-
989
- | Directive | Example | Description |
990
- |-----------|---------|-------------|
991
- | `@event` | `@click=${handler}` | Event listener |
992
- | `:property` | `:value=${signal}` | Two-way binding |
993
- | `?attribute` | `?disabled=${signal}` | Boolean attribute |
994
- | `.property` | `.scrollTop=${signal}` | Property binding |
995
-
996
- **Two-way binding example:**
997
- ```javascript
998
- const text = $('');
999
-
1000
- html`
1001
- <input :value=${text} />
1002
- <p>You typed: ${text}</p>
1003
- `;
1004
- ```
1005
-
1006
- ## 📝 License
1007
-
1008
- MIT © natxocc
1
+ Blazing fast, zero-overhead, vanilla JS renderer with atomic reactivity.
2
+
3
+ # `SigPro`
4
+
5
+ [![npm version](https://img.shields.io/npm/v/sigpro.svg)](https://www.npmjs.com/package/sigpro)
6
+ ![js size](https://img.shields.io/badge/js_size-2.8_kB_brotli-blue)
7
+ [![license](https://img.shields.io/npm/l/sigpro)](https://github.com/natxocc/sigpro/blob/main/LICENSE)
8
+
9
+ [**Explore the Docs →**](https://sigpro.natxocc.com/#/)
10
+
11
+ ---
12
+
13
+ ## Why SigPro?
14
+
15
+ After years of building within closed ecosystems like **React, Vue, or Svelte**—spending countless hours mastering their internal rules and extra development time on abstractions—we must face an inevitable truth: all those complex "wrappers" must ultimately be translated back into Pure JavaScript for the browser to understand.
16
+
17
+ That extra development time and the cognitive load of "learning the framework" instead of "learning the language" is exactly what **SigPro** eliminates. If the final destination is always JS, why not use a Pure JS-based system that drastically simplifies coding with a readable, vanilla, and remarkably fast architecture?
18
+
19
+ * **Atomic Precision:** Powered by a *Signal-based* architecture. State is bound directly to DOM nodeswhen a value changes, **only that specific node updates**.
20
+ * **Zero-Hydration Bottlenecks:** No 100KB bundles or complex build steps. SigPro is pure, optimized JavaScript tailored for the browser's native engine.
21
+ * **Pure Vanilla JS Performance:** High-octane performance without the need for transpilers or heavy transformations. It runs natively in the browser just as well as it does in complex build pipelines.
22
+ * **Build-Tool Agnostic:** Total freedom. Use it with **Vite, Webpack, or Rollup** for enterprise projects, or simply import it via a **`<script>` tag** for rapid prototyping. No tooling required.
23
+ * **Vite-Powered DX:** First-class Vite support with **file-based routing** out of the box. The official `sigpro/vite` plugin automatically scans your `src/pages` directory and generates reactive routes—no manual route configuration needed.
24
+ * **Zero-Scale Bloat:** Unlike other frameworks where the bundle grows exponentially, SigPro's footprint remains **flat and predictable**. You only pay for the code you write.
25
+ * **Premium DX (Developer Experience):** Forget boilerplate imports. SigPro injects an elegant, functional syntax (`div()`, `button()`, `span()`) directly into your scope for a **"Zero-Import"** workflow.
26
+ * **Fully Loaded:** Built-in Hash Routing, native **`localStorage` persistence**, and automatic lifecycle management (cleanups) included in less than 2KB.
27
+ * **Tree-Shakable:** Optimized for modern bundlers. Import only what you use, or load the full engine for rapid prototyping.
28
+
29
+ -----
30
+
31
+ ## Real-World Benchmarks
32
+
33
+ SigPro isn't just "fast on paper." In the industry-standard **JS Framework Benchmark**, it consistently outperforms the most popular libraries by operating at near-native speeds with almost zero memory overhead.
34
+
35
+ ### Execution Speed (CPU)
36
+ *Lower is better. Measured in milliseconds (ms).*
37
+
38
+ | Benchmark Test | **SigPro** | SolidJS | Vue 3 | React 18 |
39
+ | :--- | :--- | :--- | :--- | :--- |
40
+ | **Surgical Update** (10th row) | **46.8ms** | ~48ms | ~75ms | ~158ms |
41
+ | **Direct Selection** (on click) | **17.5ms** | ~18ms | ~32ms | ~65ms |
42
+ | **Initial Render** (1k rows) | **~35ms** | ~32ms | ~45ms | ~70ms |
43
+
44
+ ### Memory Footprint
45
+ *Lower is better. Measured in Megabytes (MB) after 1k rows.*
46
+
47
+ | Metric | **SigPro** | Vanilla JS | Svelte | React |
48
+ | :--- | :--- | :--- | :--- | :--- |
49
+ | **Ready Memory** (Idle) | **1.05 MB** | 1.01 MB | ~2.8 MB | ~10.4 MB |
50
+ | **Run Memory** (1k rows) | **4.90 MB** | 4.25 MB | ~10.2 MB | ~28.5 MB |
51
+
52
+ > **The Verdict:** SigPro delivers **Vanilla-like memory consumption** while maintaining **Surgical reactivity** that rivals (and often beats) SolidJS in granular updates.
53
+
54
+ -----
55
+
56
+ ## Elegance in Action
57
+
58
+ Create reactive, persistent components with a syntax that feels like Vanilla JS, but works like magic:
59
+
60
+ ```html
61
+ <div id="app"></div>
62
+ ```
63
+
64
+ ```javascript
65
+ import { $, mount } from "sigpro";
66
+
67
+ const Counter = () => {
68
+ // Simple signal
69
+ const value = $(100);
70
+ // One-line persistence: state survives page reloads automatically
71
+ const count = $(0, "user-counter-pref");
72
+ // Computed: automatically updated when count() or value() changes
73
+ const doubleValue = $(()=> value() * count());
74
+
75
+ // Create fast HTML with pure JS
76
+ return div({ class: "card" }, [
77
+ h1(() => `Count: ${count()}, Reference: ${value()}, Double x Ref: ${doubleValue()}`),
78
+ p("Atomic updates. Zero re-renders of the parent tree."),
79
+ button({ onclick: () => count(c => c + 1)}, "Increment +1")
80
+ ]);
81
+ };
82
+
83
+ mount(Counter, "#app");
84
+ ```
85
+
86
+ -----
87
+
88
+ ## Performance Without Compromise
89
+
90
+ | Feature | **SigPro** | React / Vue | Svelte |
91
+ | :--- | :--- | :--- | :--- |
92
+ | **Payload (Gzipped)** | **<3KB** | ~30KB - 50KB | ~5KB (Compiled Runtime) |
93
+ | **State Logic** | **Atomic Signals** | Virtual DOM Diffing | Compiler Dirty Bits |
94
+ | **Update Speed** | **Direct Node Access** | Component Re-render | Block Reconciliation |
95
+ | **Native Persistence** | **Included ($)** | Requires Plugins | Manual |
96
+ | **Dependencies** | **Zero** | Many | Build Toolchain |
97
+ | **Lifecycle Mgmt** | **Automatic (Cleanup Root)** | Manual / Hook-based | Manual / Hook-based |
98
+ | **Routing** | **Reactive Hash (Router) + File-based (Vite)** | Virtual Router (External) | File-based / External |
99
+ | **Learning Curve** | **Zero (Vanilla JS)** | Steep (JSX/Templates) | Medium (Directives) |
100
+
101
+ -----
102
+
103
+ ## Scalable Architecture with Vite
104
+
105
+ SigPro scales from micro-widgets to full enterprise dashboards. With the official Vite plugin, routing becomes effortless:
106
+
107
+ ```text
108
+ src/
109
+ ├── 📂 pages/ # File-based routing (auto-scanned)
110
+ │ ├── index.js # /
111
+ │ ├── about.js # → /about
112
+ │ ├── blog/
113
+ │ │ ├── index.js # /blog
114
+ │ │ └── [slug].js # → /blog/:slug
115
+ │ └── docs/
116
+ │ └── [...all].js # /docs/*
117
+ ├── 📂 components/ # Reusable components
118
+ └── 📄 main.js # App Entry & Mounting
119
+ ```
120
+
121
+ **Vite Plugin Setup:**
122
+ ```javascript
123
+ // vite.config.js
124
+ import { defineConfig } from 'vite';
125
+ import { sigproRouter } from 'sigpro/router';
126
+
127
+ export default defineConfig({
128
+ plugins: [sigproRouter()]
129
+ });
130
+ ```
131
+
132
+ The plugin automatically:
133
+ - Scans your `src/pages` directory recursively
134
+ - Generates route definitions from file paths
135
+ - Supports dynamic segments (`[param]`) and catch-all routes (`[...param]`)
136
+ - Provides automatic 404 fallback
137
+ - Enables lazy-loading out of the box
138
+
139
+ -----
140
+
141
+ ## Quick Start
142
+
143
+ ```bash
144
+ npm install sigpro
145
+ ```
146
+
147
+ **With Vite (Recommended for larger apps):**
148
+ ```bash
149
+ npm create vite@latest my-app -- --template vanilla
150
+ cd my-app
151
+ npm install sigpro
152
+ ```
153
+
154
+ **Or without build tools:**
155
+ ```html
156
+ <script src="https://unpkg.com/sigpro"></script>
157
+ ```
158
+
159
+ -----
160
+
161
+ ## License
162
+
163
+ MIT © 2026 **SigPro Team**.
164
+ *Engineered for speed, designed for clarity, built for the modern web.*