--- name: nojs metadata: version: 1.11.0 description: Expert-level knowledge of the No.JS HTML-first reactive framework for building dynamic web applications using only HTML attributes. Use this skill whenever the user mentions No.JS, NoJS, "no javascript framework", HTML-first framework, or is writing HTML with reactive attributes like bind, state, get, each, on:click, model, route, store, computed, watch, if/else, show/hide, foreach, validate, animate, drag, drop, t (i18n), class-*, style-*, or bind-*. Also use when the user asks about declarative HTML frameworks, zero-JS frameworks, or wants to build a web app without writing JavaScript. Even if the user doesn't mention No.JS by name, activate this skill when you see HTML attributes that match No.JS directive patterns. --- # NoJS-Skill No.JS is an HTML-first reactive framework with zero dependencies that replaces JavaScript with declarative HTML attributes. CSP-compliant via a custom expression parser (no eval). Include one `

``` No `app.mount()`, no `createApp()`, no build step. It just works. ## When to use Use this skill when: - The user mentions **No.JS**, **NoJS**, or the **HTML-first reactive framework** - The user is writing HTML with No.JS directive attributes (`bind`, `state`, `get`, `each`, `on:click`, `model`, `route`, `store`, `foreach`, `validate`, `animate`, `drag`, `drop`, `t`, `class-*`, `style-*`, `bind-*`) - The user asks about **declarative HTML frameworks** or wants to build a **web app without writing JavaScript** - The user needs to **scaffold**, **validate**, or **debug** No.JS templates - You see HTML attributes that match No.JS directive patterns, even without explicit mention ## Instructions ### 1. Understand the framework architecture No.JS works by walking the DOM on `DOMContentLoaded`, matching HTML attributes to directives, and executing them by priority: | Priority | Directives | Purpose | |----------|-----------|---------| | 0 | `state`, `store` | Initialize reactive data first | | 1 | `get`, `post`, `put`, `patch`, `delete`, `error-boundary`, `i18n-ns`, `page-title`, `page-description`, `page-canonical`, `page-jsonld` | Fetch data, error/i18n setup, head management | | 2 | `computed`, `watch` | Derive values and observe changes | | 5 | `ref` | Element references | | 10 | `if`, `else-if`, `else`, `switch`, `each`, `foreach`, `use`, `drag-list` | Structural (add/remove DOM) | | 15 | `drag`, `drop` | Drag and drop setup | | 16 | `drag-multiple` | Multi-select drag | | 20 | `bind`, `bind-*`, `bind-html`, `model`, `class-*`, `style-*`, `on:*`, `show`, `hide`, `t`, `call`, `trigger`, `page-title`, `page-description`, `page-canonical`, `page-jsonld` | Rendering, events, i18n, actions, head management | | 30 | `validate` | Form validation side effects | Data lives in Proxy-backed reactive contexts that inherit from parent elements (like lexical scoping). When data changes, every bound element updates automatically. ### 2. Know the core directives **Data Fetching** - `get="/url"` with `as="varName"`, `loading`, `error`, `empty`, `success`, `refresh`, `cached`, `into`, `debounce`, `headers`, `params`, `skeleton` (CLS-prevention: hides an existing DOM element during fetch). URLs support interpolation: `get="/users/{userId}"` re-fetches reactively. Mutation verbs: `post`, `put`, `patch`, `delete`. Static `get=` URLs automatically get a `` hint injected at init time; cross-origin URLs also get ``. Route templates with `src=` get `` at router startup. **State** - `state="{ key: value }"` (local), `store="name"` (global via `$store.name`), `computed="name" expr="expr"`, `watch="prop" on:change="handler"`, `persist`/`persist-fields`. Note: stores created via `NoJS.config({ stores })` won't be overwritten by later `
` with the same name. Use `NoJS.notify()` after mutating stores from JavaScript outside expressions. **Binding** - `bind="expr"` (text), `bind-html="expr"` (sanitized HTML), `bind-*="expr"` (any attribute), `model="prop"` (two-way for form inputs). **Conditionals** - `if`/`then`/`else`, `else-if`, `show`/`hide` (CSS toggle), `switch`/`case`/`default`. **Loops** - `each="item in items"`, `foreach="item" from="items"` (with `filter`, `sort`, `limit`, `offset`, `key`). Loop vars: `$index`, `$count`, `$first`, `$last`, `$even`, `$odd`. **Events** - `on:click="expr"` with modifiers `.prevent`, `.stop`, `.once`, `.self`, `.debounce.300`, `.throttle.100`. Key mods: `.enter`, `.escape`, `.tab`, `.space`, `.delete`, `.backspace`, `.up`, `.down`, `.left`, `.right`, `.ctrl`, `.alt`, `.shift`, `.meta`. Lifecycle: `on:init`, `on:mounted`, `on:updated`, `on:unmounted`. Vars: `$event`, `$el`. **Styling** - `class-active="cond"`, `class-map="{ ... }"`, `style-color="expr"`, `style-map="{ ... }"`. **Forms** - `
` with `$form` context (`valid`, `dirty`, `submitting`, `errors`, `firstError`, `reset()`). Field rules: `validate="required,email,min:5"`. Per-field state: `$form.fields.email.valid`. Triggers: `validate-on="blur"`. Conditional: `validate-if="expr"`. Auto-disables submit buttons when `$form.submitting`. Custom: `NoJS.validator('name', fn)`. **Routing** - ``, `