{
  "directives": [
    {
      "name": "state",
      "pattern": false,
      "priority": 0,
      "valueType": "object",
      "valueDescription": "JS object literal",
      "requiresValue": true,
      "companions": [
        {
          "name": "persist",
          "type": "boolean",
          "description": "No.JS: Persist state to localStorage"
        },
        {
          "name": "persist-key",
          "type": "string",
          "description": "No.JS: Custom localStorage key"
        },
        {
          "name": "persist-fields",
          "type": "string",
          "description": "No.JS: Comma-separated list of state fields to persist selectively"
        }
      ],
      "documentation": "Declares reactive state on the element. The value is evaluated as a JavaScript object literal.\n\n**Example:**\n```html\n<div state=\"{ count: 0, name: 'World' }\">\n```\n\n**Companions:** `persist`, `persist-key`, `persist-fields`",
      "category": "state"
    },
    {
      "name": "store",
      "pattern": false,
      "priority": 0,
      "valueType": "string",
      "valueDescription": "Store name string",
      "requiresValue": true,
      "companions": [
        {
          "name": "value",
          "type": "expression",
          "description": "No.JS: Initial store data as JS object literal"
        }
      ],
      "documentation": "Declares a named global store accessible via `$store.name`.\n\n**Example:**\n```html\n<div store=\"cart\" value=\"{ items: [], total: 0 }\">\n```\n\n**Companion:** `value`",
      "category": "state"
    },
    {
      "name": "computed",
      "pattern": false,
      "priority": 2,
      "valueType": "string",
      "valueDescription": "Property name string",
      "requiresValue": true,
      "companions": [
        {
          "name": "expr",
          "type": "expression",
          "description": "No.JS: Expression to evaluate"
        }
      ],
      "documentation": "Declares a computed property that auto-updates when dependencies change.\n\n**Example:**\n```html\n<div computed=\"fullName\" expr=\"firstName + ' ' + lastName\">\n```\n\n**Companion:** `expr`",
      "category": "state"
    },
    {
      "name": "watch",
      "pattern": false,
      "priority": 2,
      "valueType": "expression",
      "valueDescription": "Expression to watch",
      "requiresValue": true,
      "companions": [
        {
          "name": "on:change",
          "type": "statement",
          "description": "No.JS: Handler executed on change; receives $old, $new"
        }
      ],
      "documentation": "Watches an expression for changes and runs a handler.\n\n**Example:**\n```html\n<div watch=\"count\" on:change=\"console.log($old, $new)\">\n```\n\n**Companion:** `on:change`\n**Handler variables:** `$old`, `$new`\n\n**Limitation:** On form controls (`input`/`textarea`/`select`), avoid combining `watch` with an explicit `on:change` handler — both claim the change event and may conflict.",
      "category": "state"
    },
    {
      "name": "get",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "as",
          "type": "identifier",
          "description": "No.JS: Variable name for response data"
        },
        {
          "name": "loading",
          "type": "templateId",
          "description": "No.JS: Template ID shown while loading"
        },
        {
          "name": "error",
          "type": "templateId",
          "description": "No.JS: Template ID shown on error"
        },
        {
          "name": "empty",
          "type": "templateId",
          "description": "No.JS: Template ID shown when response is empty"
        },
        {
          "name": "success",
          "type": "templateId",
          "description": "No.JS: Template ID shown on success"
        },
        {
          "name": "then",
          "type": "expression",
          "description": "No.JS: Expression executed on success"
        },
        {
          "name": "redirect",
          "type": "string",
          "description": "No.JS: Path to redirect after success"
        },
        {
          "name": "confirm",
          "type": "string",
          "description": "No.JS: Confirmation message before request"
        },
        {
          "name": "refresh",
          "type": "number",
          "description": "No.JS: Auto-refresh interval in ms"
        },
        {
          "name": "cached",
          "type": "boolean",
          "description": "No.JS: Enable response caching"
        },
        {
          "name": "body",
          "type": "expression",
          "description": "No.JS: Request body expression"
        },
        {
          "name": "headers",
          "type": "expression",
          "description": "No.JS: Custom request headers object"
        },
        {
          "name": "var",
          "type": "identifier",
          "description": "No.JS: Variable name for additional data"
        },
        {
          "name": "into",
          "type": "string",
          "description": "No.JS: Store name to put response into"
        },
        {
          "name": "retry",
          "type": "number",
          "description": "No.JS: Number of retry attempts"
        },
        {
          "name": "retry-delay",
          "type": "number",
          "description": "No.JS: Delay between retries in ms"
        },
        {
          "name": "params",
          "type": "expression",
          "description": "No.JS: Query parameters object"
        },
        {
          "name": "debounce",
          "type": "number",
          "description": "No.JS: Debounce delay in ms"
        },
        {
          "name": "skeleton",
          "type": "string",
          "description": "No.JS: ID (without #) of a DOM element to hide while loading and restore on response. Prevents CLS — element must start visible in HTML."
        },
        {
          "name": "get-trigger",
          "type": "enum",
          "description": "No.JS: Controls when the GET request fires",
          "values": [
            "load",
            "visible",
            "hover",
            "none",
            "scroll",
            "button"
          ]
        },
        {
          "name": "get-trigger-label",
          "type": "string",
          "description": "No.JS: Label text for the auto-generated Load More button (get-trigger=\"button\")"
        },
        {
          "name": "get-insert",
          "type": "enum",
          "description": "No.JS: How fetched content is inserted into the container",
          "values": [
            "append",
            "prepend"
          ]
        },
        {
          "name": "get-page",
          "type": "number",
          "description": "No.JS: Initial page number for offset-based pagination (auto-increments)"
        },
        {
          "name": "get-cursor",
          "type": "boolean",
          "description": "No.JS: Enable cursor-based pagination (mutually exclusive with get-page)"
        },
        {
          "name": "get-cursor-field",
          "type": "string",
          "description": "No.JS: Dot-notation path to cursor value in JSON response (e.g. 'meta.nextCursor')"
        },
        {
          "name": "get-threshold",
          "type": "string",
          "description": "No.JS: IntersectionObserver rootMargin for scroll/visible triggers (e.g. '200px')"
        }
      ],
      "documentation": "Performs an HTTP GET request and makes response data available.\n\n**Example:**\n```html\n<div get=\"/api/users\" as=\"users\" loading=\"spinner-tpl\">\n  <li each=\"user in users\" bind=\"user.name\">\n</div>\n```\n\n**Companions:** `as`, `loading`, `error`, `empty`, `success`, `then`, `redirect`, `confirm`, `refresh`, `cached`, `body`, `headers`, `var`, `into`, `retry`, `retry-delay`, `params`, `debounce`, `skeleton`, `get-trigger`, `get-trigger-label`, `get-insert`, `get-page`, `get-cursor`, `get-cursor-field`, `get-threshold`",
      "category": "http"
    },
    {
      "name": "post",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression",
      "requiresValue": true,
      "companionsSameAs": "get",
      "companions": [],
      "documentation": "Performs an HTTP POST request. Same companions as `get`.\n\n**Example:**\n```html\n<form post=\"/api/users\" body=\"{ name, email }\" as=\"result\">\n```",
      "category": "http"
    },
    {
      "name": "put",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression",
      "requiresValue": true,
      "companionsSameAs": "get",
      "companions": [],
      "documentation": "Performs an HTTP PUT request. Same companions as `get`.",
      "category": "http"
    },
    {
      "name": "patch",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression",
      "requiresValue": true,
      "companionsSameAs": "get",
      "companions": [],
      "documentation": "Performs an HTTP PATCH request. Same companions as `get`.",
      "category": "http"
    },
    {
      "name": "delete",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression",
      "requiresValue": true,
      "companionsSameAs": "get",
      "companions": [],
      "documentation": "Performs an HTTP DELETE request. Same companions as `get`.",
      "category": "http"
    },
    {
      "name": "sse",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression (supports {expr} interpolation)",
      "requiresValue": true,
      "companions": [
        {
          "name": "as",
          "type": "identifier",
          "description": "No.JS: Variable name for received data (default: \"data\")"
        },
        {
          "name": "sse-event",
          "type": "string",
          "description": "No.JS: Named SSE event to listen for (default: \"message\")"
        },
        {
          "name": "sse-insert",
          "type": "enum",
          "description": "No.JS: How incoming events are stored — replace overwrites, append/prepend accumulate into an array",
          "values": [
            "replace",
            "append",
            "prepend"
          ]
        },
        {
          "name": "sse-limit",
          "type": "number",
          "description": "No.JS: Maximum items to keep when using sse-insert append/prepend (prevents unbounded growth)"
        },
        {
          "name": "sse-credentials",
          "type": "boolean",
          "description": "No.JS: Send credentials (cookies) with the EventSource connection"
        },
        {
          "name": "into",
          "type": "string",
          "description": "No.JS: Store name to dual-write received data into"
        },
        {
          "name": "error",
          "type": "templateId",
          "description": "No.JS: Template ID shown when the SSE connection closes with an error"
        },
        {
          "name": "then",
          "type": "expression",
          "description": "No.JS: Expression executed on each received event ($event = parsed data)"
        }
      ],
      "documentation": "Opens a Server-Sent Events (EventSource) connection to the given URL. Incoming events are parsed as JSON (with raw-string fallback) and stored in the context variable named by `as`.\n\n**Example:**\n```html\n<div sse=\"/events/notifications\" as=\"notification\">\n  <p bind=\"notification.message\"></p>\n</div>\n```\n\n**Feed (append mode):**\n```html\n<ul sse=\"/events/feed\" as=\"items\" sse-insert=\"append\" sse-limit=\"50\">\n  <li each=\"item in items\" bind=\"item.text\"></li>\n</ul>\n```\n\n**Context key:** `$sse` — `{ connecting, open, error }` booleans for connection state.\n\n**Companions:** `as`, `sse-event`, `sse-insert`, `sse-limit`, `sse-credentials`, `into`, `error`, `then`",
      "category": "http"
    },
    {
      "name": "query",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "URL string or expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "as",
          "type": "identifier",
          "description": "No.JS: Variable name for response data"
        },
        {
          "name": "loading",
          "type": "templateId",
          "description": "No.JS: Template ID shown while loading"
        },
        {
          "name": "error",
          "type": "templateId",
          "description": "No.JS: Template ID shown on error"
        },
        {
          "name": "empty",
          "type": "templateId",
          "description": "No.JS: Template ID shown when response is empty"
        },
        {
          "name": "success",
          "type": "templateId",
          "description": "No.JS: Template ID shown on success"
        },
        {
          "name": "then",
          "type": "expression",
          "description": "No.JS: Expression executed on success"
        },
        {
          "name": "redirect",
          "type": "string",
          "description": "No.JS: Path to redirect after success"
        },
        {
          "name": "confirm",
          "type": "string",
          "description": "No.JS: Confirmation message before request"
        },
        {
          "name": "refresh",
          "type": "number",
          "description": "No.JS: Auto-refresh interval in ms"
        },
        {
          "name": "cached",
          "type": "boolean",
          "description": "No.JS: Enable response caching"
        },
        {
          "name": "body",
          "type": "expression",
          "description": "No.JS: Request body expression"
        },
        {
          "name": "headers",
          "type": "expression",
          "description": "No.JS: Custom request headers object"
        },
        {
          "name": "var",
          "type": "identifier",
          "description": "No.JS: Variable name for additional data"
        },
        {
          "name": "into",
          "type": "string",
          "description": "No.JS: Store name to put response into"
        },
        {
          "name": "retry",
          "type": "number",
          "description": "No.JS: Number of retry attempts"
        },
        {
          "name": "retry-delay",
          "type": "number",
          "description": "No.JS: Delay between retries in ms"
        },
        {
          "name": "params",
          "type": "expression",
          "description": "No.JS: Query parameters object"
        },
        {
          "name": "debounce",
          "type": "number",
          "description": "No.JS: Debounce delay in ms"
        },
        {
          "name": "skeleton",
          "type": "string",
          "description": "No.JS: ID (without #) of a DOM element to hide while loading and restore on response. Prevents CLS — element must start visible in HTML."
        },
        {
          "name": "query-trigger",
          "type": "enum",
          "description": "No.JS: Controls when the QUERY request fires",
          "values": [
            "load",
            "visible",
            "hover",
            "none",
            "scroll",
            "button"
          ]
        },
        {
          "name": "query-trigger-label",
          "type": "string",
          "description": "No.JS: Label text for the auto-generated Load More button (query-trigger=\"button\")"
        },
        {
          "name": "query-threshold",
          "type": "string",
          "description": "No.JS: IntersectionObserver rootMargin for scroll/visible triggers (e.g. '200px')"
        }
      ],
      "documentation": "Performs an HTTP QUERY request (RFC 10008) and makes response data available. QUERY is a **safe, idempotent, cacheable** method that carries a request body — semantically a read (like `get`) but able to send a structured payload (like `post`). On non-form elements it auto-fires and supports trigger modes; on `<form>` it intercepts submit. It is cacheable and CSRF-exempt.\n\n**Example:**\n```html\n<form query=\"/orders\" body=\"{ status, minTotal }\" as=\"result\">\n  <input model=\"status\">\n  <input model=\"minTotal\" type=\"number\">\n</form>\n```\n\n**Companions:** `as`, `loading`, `error`, `empty`, `success`, `then`, `redirect`, `confirm`, `refresh`, `cached`, `body`, `headers`, `var`, `into`, `retry`, `retry-delay`, `params`, `debounce`, `skeleton`, `query-trigger`, `query-trigger-label`, `query-threshold`",
      "category": "http"
    },
    {
      "name": "bind",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression",
      "requiresValue": true,
      "companions": [],
      "documentation": "Binds element's text content to a JS expression. Updates reactively.\n\n**Example:**\n```html\n<span bind=\"user.name\">\n<p bind=\"items.length + ' items'\">\n```\n\n**Limitation:** Do not use `bind` and `t` on the same element — both write text content and the last-processed directive wins silently.",
      "category": "binding"
    },
    {
      "name": "bind-html",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression (HTML string)",
      "requiresValue": true,
      "companions": [],
      "documentation": "Binds element's innerHTML to a JS expression.\n\n**Example:**\n```html\n<div bind-html=\"richContent\">\n```",
      "category": "binding"
    },
    {
      "name": "model",
      "pattern": false,
      "priority": 20,
      "valueType": "path",
      "valueDescription": "Variable path",
      "requiresValue": true,
      "companions": [],
      "documentation": "Two-way data binding for form elements (INPUT, SELECT, TEXTAREA).\n\n**Example:**\n```html\n<input model=\"username\">\n<select model=\"selectedOption\">\n<textarea model=\"message\">\n```\n\n**Limitation:** Do not use `model` and `bind-value` on the same element — both create two-way bindings with duplicate listeners that may conflict.",
      "category": "binding"
    },
    {
      "name": "if",
      "pattern": false,
      "priority": 10,
      "valueType": "expression",
      "valueDescription": "JS boolean expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "then",
          "type": "templateId",
          "description": "No.JS: Template ID to render when true"
        },
        {
          "name": "else",
          "type": "templateId",
          "description": "No.JS: Template ID to render when false"
        },
        {
          "name": "animate-enter",
          "type": "animation",
          "description": "No.JS: Enter animation name"
        },
        {
          "name": "animate",
          "type": "animation",
          "description": "No.JS: Enter animation name (alias)"
        },
        {
          "name": "animate-leave",
          "type": "animation",
          "description": "No.JS: Leave animation name"
        },
        {
          "name": "transition",
          "type": "string",
          "description": "No.JS: CSS transition name"
        },
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        }
      ],
      "documentation": "Conditionally renders the element. Removes from DOM when expression is false.\n\n**Example:**\n```html\n<div if=\"isLoggedIn\">Welcome!</div>\n<div else-if=\"isGuest\">Hello Guest</div>\n<div else>Please log in</div>\n```\n\n**Companions:** `then`, `else`, `animate-enter`, `animate-leave`, `transition`, `animate-duration`\n\n**Limitation:** Do not place `if` and a loop (`each`/`foreach`/`for`) on the same element — the condition cannot filter individual items. Use the loop's `filter` attribute or wrap the loop in a container with `if`.",
      "category": "conditional"
    },
    {
      "name": "else-if",
      "pattern": false,
      "priority": 10,
      "valueType": "expression",
      "valueDescription": "JS boolean expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "then",
          "type": "templateId",
          "description": "No.JS: Template ID to render when true"
        }
      ],
      "documentation": "Conditional branch after `if`. Must follow a sibling with `if` or `else-if`.\n\n**Example:**\n```html\n<div else-if=\"isAdmin\">Admin panel</div>\n```\n\n**Companion:** `then`",
      "category": "conditional"
    },
    {
      "name": "else",
      "pattern": false,
      "priority": 10,
      "valueType": "none",
      "valueDescription": "No value needed",
      "requiresValue": false,
      "companions": [
        {
          "name": "then",
          "type": "templateId",
          "description": "No.JS: Template ID to render"
        }
      ],
      "documentation": "Fallback branch after `if`/`else-if`. Must follow a sibling with `if` or `else-if`.\n\n**After conditional:**\n```html\n<div if=\"isLoggedIn\">Welcome!</div>\n<div else>Please log in</div>\n```\n\n**On loop elements** (`each`/`foreach`/`for`), `else=\"templateId\"` is the empty-state companion — the referenced template (bare id or `#id`) renders when the list is empty, null/undefined, or not an array:\n```html\n<li each=\"item in items\" else=\"no-items-tpl\" bind=\"item.name\"></li>\n<template id=\"no-items-tpl\"><p>No items found</p></template>\n```\n\nA bare `else` with no preceding `if`/`else-if` sibling is invalid — No.JS logs a console warning.\n\n**Companion:** `then`",
      "category": "conditional"
    },
    {
      "name": "show",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS boolean expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "animate-enter",
          "type": "animation",
          "description": "No.JS: Enter animation name"
        },
        {
          "name": "animate",
          "type": "animation",
          "description": "No.JS: Enter animation name (alias)"
        },
        {
          "name": "animate-leave",
          "type": "animation",
          "description": "No.JS: Leave animation name"
        },
        {
          "name": "transition",
          "type": "string",
          "description": "No.JS: CSS transition name"
        },
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        }
      ],
      "documentation": "Toggles element visibility via CSS `display`. Element stays in DOM.\n\n**Example:**\n```html\n<div show=\"isVisible\">Visible when true</div>\n```\n\n**Companions:** `animate-enter`, `animate-leave`, `transition`, `animate-duration`",
      "category": "conditional"
    },
    {
      "name": "hide",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS boolean expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "animate-enter",
          "type": "animation",
          "description": "No.JS: Enter animation name"
        },
        {
          "name": "animate",
          "type": "animation",
          "description": "No.JS: Enter animation name (alias)"
        },
        {
          "name": "animate-leave",
          "type": "animation",
          "description": "No.JS: Leave animation name"
        },
        {
          "name": "transition",
          "type": "string",
          "description": "No.JS: CSS transition name"
        },
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        }
      ],
      "documentation": "Opposite of `show`. Hides element via CSS `display: none` when true.\n\n**Example:**\n```html\n<div hide=\"isLoading\">Content</div>\n```",
      "category": "conditional"
    },
    {
      "name": "switch",
      "pattern": false,
      "priority": 10,
      "valueType": "expression",
      "valueDescription": "JS expression",
      "requiresValue": true,
      "companions": [],
      "documentation": "Switch statement. Children use `case`, `default`, and `then` attributes.\n\n**Example:**\n```html\n<div switch=\"role\">\n  <p case=\"admin\">Admin panel</p>\n  <p case=\"user\">Dashboard</p>\n  <p default>Guest view</p>\n</div>\n```",
      "category": "conditional"
    },
    {
      "name": "case",
      "pattern": false,
      "priority": 10,
      "valueType": "string",
      "valueDescription": "Value to match against parent switch expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "then",
          "type": "templateId",
          "description": "No.JS: Template ID to render"
        }
      ],
      "documentation": "Switch case. Used inside a `switch` container.\n\n**Example:**\n```html\n<p case=\"admin\">Admin panel</p>\n<p case=\"editor\" then=\"editor-tpl\"></p>\n```\n\n**Limitation:** Do not combine `case` (or `default`) with a loop directive (`each`/`foreach`/`for`) on the same element — the switch logic becomes inert and all branches render.",
      "category": "conditional"
    },
    {
      "name": "default",
      "pattern": false,
      "priority": 10,
      "valueType": "none",
      "valueDescription": "No value needed",
      "requiresValue": false,
      "companions": [
        {
          "name": "then",
          "type": "templateId",
          "description": "No.JS: Template ID to render"
        }
      ],
      "documentation": "Default case in a `switch` block. Renders when no `case` matches.\n\n**Example:**\n```html\n<p default>Guest view</p>\n```\n\n**Limitation:** Do not combine `default` (or `case`) with a loop directive (`each`/`foreach`/`for`) on the same element — the switch logic becomes inert and all branches render.",
      "category": "conditional"
    },
    {
      "name": "each",
      "pattern": false,
      "priority": 10,
      "valueType": "iterable",
      "valueDescription": "\"item in list\" syntax",
      "requiresValue": true,
      "companions": [
        {
          "name": "index",
          "type": "identifier",
          "description": "No.JS: Custom index variable name"
        },
        {
          "name": "filter",
          "type": "expression",
          "description": "No.JS: Filter expression"
        },
        {
          "name": "sort",
          "type": "string",
          "description": "No.JS: Property to sort by"
        },
        {
          "name": "limit",
          "type": "number",
          "description": "No.JS: Max items to show"
        },
        {
          "name": "offset",
          "type": "number",
          "description": "No.JS: Number of items to skip"
        },
        {
          "name": "template",
          "type": "templateId",
          "description": "No.JS: Template ID for item rendering"
        },
        {
          "name": "else",
          "type": "templateId",
          "description": "No.JS: Template ID (bare id or #id) to render when the list is empty, null/undefined, or not an array"
        },
        {
          "name": "key",
          "type": "expression",
          "description": "No.JS: Unique key expression for DOM optimization"
        },
        {
          "name": "animate-enter",
          "type": "animation",
          "description": "No.JS: Enter animation name"
        },
        {
          "name": "animate",
          "type": "animation",
          "description": "No.JS: Enter animation name (alias)"
        },
        {
          "name": "animate-leave",
          "type": "animation",
          "description": "No.JS: Leave animation name"
        },
        {
          "name": "animate-stagger",
          "type": "number",
          "description": "No.JS: Stagger delay between items in ms"
        },
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        }
      ],
      "documentation": "Alias of `foreach`. Self-repeating loop directive — the element with `each` IS the template that gets cloned for each item. Uses `\"item in list\"` syntax. The element is removed from the DOM and replaced by comment markers; clones are inserted between them as siblings.\n\n**Example:**\n```html\n<ul>\n  <li each=\"user in users\" bind=\"user.name\"></li>\n</ul>\n```\n\n**With filter/sort:**\n```html\n<li each=\"item in products\" filter=\"item.price > 10\" sort=\"name\" limit=\"5\" bind=\"item.name\"></li>\n```\n\n**Empty list fallback (else=\"templateId\"):**\n```html\n<li each=\"item in items\" else=\"noItemsTpl\" bind=\"item.name\"></li>\n<template id=\"noItemsTpl\"><li>No items found</li></template>\n```\nThe `else` template (bare id or `#id`) renders when the list is empty (`[]`) or null/undefined/not an array.\n\n**Loop context variables:** `$index`, `$count`, `$first`, `$last`, `$even`, `$odd`\n\n**Companions:** `index`, `filter`, `sort`, `limit`, `offset`, `template`, `else`, `key`, `animate-enter`, `animate-leave`, `animate-stagger`, `animate-duration`",
      "category": "loop"
    },
    {
      "name": "foreach",
      "pattern": false,
      "priority": 10,
      "valueType": "iterable",
      "valueDescription": "\"item in list\" syntax",
      "requiresValue": true,
      "companions": [
        {
          "name": "index",
          "type": "identifier",
          "description": "No.JS: Custom index variable name"
        },
        {
          "name": "filter",
          "type": "expression",
          "description": "No.JS: Filter expression"
        },
        {
          "name": "sort",
          "type": "string",
          "description": "No.JS: Property to sort by"
        },
        {
          "name": "limit",
          "type": "number",
          "description": "No.JS: Max items to show"
        },
        {
          "name": "offset",
          "type": "number",
          "description": "No.JS: Number of items to skip"
        },
        {
          "name": "template",
          "type": "templateId",
          "description": "No.JS: Template ID for item rendering"
        },
        {
          "name": "else",
          "type": "templateId",
          "description": "No.JS: Template ID (bare id or #id) to render when the list is empty, null/undefined, or not an array"
        },
        {
          "name": "key",
          "type": "expression",
          "description": "No.JS: Unique key expression for DOM optimization"
        },
        {
          "name": "animate-enter",
          "type": "animation",
          "description": "No.JS: Enter animation name"
        },
        {
          "name": "animate",
          "type": "animation",
          "description": "No.JS: Enter animation name (alias)"
        },
        {
          "name": "animate-leave",
          "type": "animation",
          "description": "No.JS: Leave animation name"
        },
        {
          "name": "animate-stagger",
          "type": "number",
          "description": "No.JS: Stagger delay between items in ms"
        },
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        }
      ],
      "documentation": "Primary self-repeating loop directive — the element with `foreach` IS the template that gets cloned for each item. Uses `\"item in list\"` syntax. The element is removed from the DOM and replaced by comment markers; clones are inserted between them as siblings. Also supports **external templates** via the `template` companion. Aliases: `each`, `for`.\n\n**Self-repeating (recommended):**\n```html\n<ul>\n  <li foreach=\"user in users\" bind=\"user.name\"></li>\n</ul>\n```\n\n**With filter/sort:**\n```html\n<li foreach=\"item in products\" filter=\"item.price > 10\" sort=\"name\" limit=\"5\" bind=\"item.name\"></li>\n```\n\n**External template:**\n```html\n<li foreach=\"item in products\" template=\"item-tpl\"></li>\n```\n\n**Empty list fallback (else=\"templateId\"):**\n```html\n<li foreach=\"item in items\" else=\"noItemsTpl\" bind=\"item.name\"></li>\n<template id=\"noItemsTpl\"><li>No items found</li></template>\n```\nThe `else` template (bare id or `#id`) renders when the list is empty (`[]`) or null/undefined/not an array.\n\n**Loop context variables:** `$index`, `$count`, `$first`, `$last`, `$even`, `$odd`\n\n**Companions:** `index`, `filter`, `sort`, `limit`, `offset`, `template`, `else`, `key`, animation attrs",
      "category": "loop"
    },
    {
      "name": "for",
      "pattern": false,
      "priority": 10,
      "valueType": "iterable",
      "valueDescription": "\"item in list\" syntax",
      "requiresValue": true,
      "companions": [
        {
          "name": "index",
          "type": "identifier",
          "description": "No.JS: Custom index variable name"
        },
        {
          "name": "filter",
          "type": "expression",
          "description": "No.JS: Filter expression"
        },
        {
          "name": "sort",
          "type": "string",
          "description": "No.JS: Property to sort by"
        },
        {
          "name": "limit",
          "type": "number",
          "description": "No.JS: Max items to show"
        },
        {
          "name": "offset",
          "type": "number",
          "description": "No.JS: Number of items to skip"
        },
        {
          "name": "template",
          "type": "templateId",
          "description": "No.JS: Template ID for item rendering"
        },
        {
          "name": "else",
          "type": "templateId",
          "description": "No.JS: Template ID (bare id or #id) to render when the list is empty, null/undefined, or not an array"
        },
        {
          "name": "key",
          "type": "expression",
          "description": "No.JS: Unique key expression for DOM optimization"
        },
        {
          "name": "animate-enter",
          "type": "animation",
          "description": "No.JS: Enter animation name"
        },
        {
          "name": "animate",
          "type": "animation",
          "description": "No.JS: Enter animation name (alias)"
        },
        {
          "name": "animate-leave",
          "type": "animation",
          "description": "No.JS: Leave animation name"
        },
        {
          "name": "animate-stagger",
          "type": "number",
          "description": "No.JS: Stagger delay between items in ms"
        },
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        }
      ],
      "documentation": "Alias of `foreach`. Self-repeating loop directive — the element with `for` IS the template that gets cloned for each item. Uses `\"item in list\"` syntax. The element is removed from the DOM and replaced by comment markers; clones are inserted between them as siblings.\n\n**Example:**\n```html\n<ul>\n  <li for=\"user in users\" bind=\"user.name\"></li>\n</ul>\n```\n\n**With filter/sort:**\n```html\n<li for=\"item in products\" filter=\"item.price > 10\" sort=\"name\" limit=\"5\" bind=\"item.name\"></li>\n```\n\n**Empty list fallback (else=\"templateId\"):**\n```html\n<li for=\"item in items\" else=\"noItemsTpl\" bind=\"item.name\"></li>\n<template id=\"noItemsTpl\"><li>No items found</li></template>\n```\nThe `else` template (bare id or `#id`) renders when the list is empty (`[]`) or null/undefined/not an array.\n\n**Loop context variables:** `$index`, `$count`, `$first`, `$last`, `$even`, `$odd`\n\n**Companions:** `index`, `filter`, `sort`, `limit`, `offset`, `template`, `else`, `key`, `animate-enter`, `animate-leave`, `animate-stagger`, `animate-duration`",
      "category": "loop"
    },
    {
      "name": "trigger",
      "pattern": false,
      "priority": 20,
      "valueType": "string",
      "valueDescription": "Custom event name",
      "requiresValue": true,
      "companions": [
        {
          "name": "trigger-data",
          "type": "expression",
          "description": "No.JS: Event detail data expression"
        }
      ],
      "documentation": "Dispatches a custom event.\n\n**Example:**\n```html\n<button on:click=\"count++\" trigger=\"countChanged\" trigger-data=\"count\">\n```\n\n**Companion:** `trigger-data`",
      "category": "event"
    },
    {
      "name": "ref",
      "pattern": false,
      "priority": 5,
      "valueType": "identifier",
      "valueDescription": "Reference name",
      "requiresValue": true,
      "companions": [],
      "documentation": "Declares a reference to this element, accessible via `$refs.name`.\n\n**Example:**\n```html\n<input ref=\"emailInput\">\n<!-- Access: $refs.emailInput.value -->\n```\n\n**Limitation:** On a looped element, every clone re-registers the same ref name — `$refs.name` will point to the last clone only.",
      "category": "reference"
    },
    {
      "name": "use",
      "pattern": false,
      "priority": 10,
      "valueType": "templateId",
      "valueDescription": "Template ID",
      "requiresValue": true,
      "companions": [
        {
          "name": "var-*",
          "type": "expression",
          "description": "No.JS: Slot parameter (pattern: var-name=\"expr\")"
        }
      ],
      "documentation": "Inserts a `<template>` by ID, optionally passing slot parameters.\n\n**Example:**\n```html\n<template id=\"greeting\">\n  <h1 bind=\"name\"></h1>\n</template>\n<div use=\"greeting\" var-name=\"'World'\">\n```\n\n**Companions:** `var-*` (slot parameters)",
      "category": "template"
    },
    {
      "name": "call",
      "pattern": false,
      "priority": 20,
      "valueType": "url",
      "valueDescription": "URL string (supports interpolation)",
      "requiresValue": true,
      "companions": [
        {
          "name": "method",
          "type": "string",
          "description": "No.JS: HTTP method (default: get)"
        },
        {
          "name": "as",
          "type": "identifier",
          "description": "No.JS: Variable name for response data (default: \"data\")"
        },
        {
          "name": "loading",
          "type": "templateId",
          "description": "No.JS: Template ID shown while loading"
        },
        {
          "name": "into",
          "type": "string",
          "description": "No.JS: Store name for response"
        },
        {
          "name": "success",
          "type": "templateId",
          "description": "No.JS: Template ID on success"
        },
        {
          "name": "error",
          "type": "templateId",
          "description": "No.JS: Template ID on error"
        },
        {
          "name": "then",
          "type": "expression",
          "description": "No.JS: Expression on success"
        },
        {
          "name": "redirect",
          "type": "string",
          "description": "No.JS: Path to redirect after success"
        },
        {
          "name": "confirm",
          "type": "string",
          "description": "No.JS: Confirmation message"
        },
        {
          "name": "body",
          "type": "expression",
          "description": "No.JS: Request body expression"
        },
        {
          "name": "headers",
          "type": "expression",
          "description": "No.JS: Custom request headers object"
        }
      ],
      "documentation": "HTTP call triggered by user action.\n\n**Example:**\n```html\n<button call=\"/api/delete/1\" method=\"delete\" confirm=\"Are you sure?\" loading=\"spinner-tpl\">\n```\n\n**Companions:** `method`, `as`, `loading`, `into`, `success`, `error`, `then`, `redirect`, `confirm`, `body`, `headers`",
      "category": "http"
    },
    {
      "name": "validate",
      "pattern": false,
      "priority": 30,
      "valueType": "string",
      "valueDescription": "Validator rule string (pipe-separated)",
      "requiresValue": true,
      "plugin": "@no-js-dev/nojs-elements",
      "companions": [
        {
          "name": "error",
          "type": "string",
          "description": "No.JS: Error message or template ID (prefix with #)"
        },
        {
          "name": "error-*",
          "type": "string",
          "description": "No.JS: Per-rule error message or template ID (e.g. error-required, error-email)"
        },
        {
          "name": "error-class",
          "type": "string",
          "description": "No.JS: CSS class(es) applied to invalid fields"
        },
        {
          "name": "validate-on",
          "type": "string",
          "description": "No.JS: Validation trigger events (space-separated: input, blur, focusout, submit)"
        },
        {
          "name": "validate-if",
          "type": "expression",
          "description": "No.JS: Conditional expression; field is only validated when truthy"
        },
        {
          "name": "as",
          "type": "identifier",
          "description": "No.JS: Expose per-field validation state as a context variable"
        },
        {
          "name": "success",
          "type": "templateId",
          "description": "No.JS: Success template for form"
        }
      ],
      "documentation": "Form validation with pristine-aware errors.\n\n**On a form:** creates `$form` context with `valid`, `dirty`, `touched`, `pending`, `errors`, `values`, `fields`, `firstError`, `errorCount`, `reset()`.\n\n**On a field:** standalone validation with error template.\n\n**Example:**\n```html\n<form validate=\"\" error-class=\"is-invalid\" validate-on=\"input blur\">\n  <input name=\"email\" validate=\"required|email\" error-required=\"Email is required\" />\n  <span if=\"$form.errors.email\" bind=\"$form.errors.email\"></span>\n</form>\n```\n\n**Built-in validators:** `required`, `email`, `url`, `min`, `max`, `custom`\n\n**Companions:** `error`, `error-*`, `error-class`, `validate-on`, `validate-if`, `as`, `success`",
      "category": "form"
    },
    {
      "name": "error-boundary",
      "pattern": false,
      "priority": 1,
      "valueType": "templateId",
      "valueDescription": "Fallback template ID",
      "requiresValue": true,
      "companions": [
        {
          "name": "on:error",
          "type": "expression",
          "description": "No.JS: Handler for the nojs:error CustomEvent dispatched when the boundary catches an error"
        }
      ],
      "documentation": "Wraps content with error handling. Shows fallback template on error.\n\nWhen an error is caught, dispatches a `nojs:error` CustomEvent on the boundary element. `$event.detail` contains `message` (string) and `error` (original Error).\n\n**Example:**\n```html\n<div error-boundary=\"error-tpl\"\n     on:error=\"console.log($event.detail.message)\">\n  <!-- content that might throw -->\n</div>\n```\n\n**Companion:** `on:error`",
      "category": "error"
    },
    {
      "name": "t",
      "pattern": false,
      "priority": 20,
      "valueType": "string",
      "valueDescription": "Translation key",
      "requiresValue": true,
      "companions": [
        {
          "name": "t-html",
          "type": "boolean",
          "description": "No.JS: Render translation as sanitized HTML (via _sanitizeHtml()) instead of plain text"
        }
      ],
      "documentation": "i18n translation. Value is the translation key.\n\n**Example:**\n```html\n<span t=\"greeting\" t-name=\"World\">\n```\n\n**Expression proxy:** Use `$i18n.[path]` in expressions to access translations as reactive dot-notation properties — e.g. `bind=\"$i18n.labels.email\"`. Fully reactive: locale switches update bindings in real-time.\n\n**Companions:** `t-*` (interpolation parameters), `t-html`\n\nInterpolation params are passed as `t-{param}` attributes.\n\n**Limitation:** Do not use `t` and `bind` on the same element — both write text content and the last-processed directive wins silently.",
      "category": "i18n"
    },
    {
      "name": "i18n-ns",
      "pattern": false,
      "priority": 1,
      "valueType": "string",
      "valueDescription": "Namespace path",
      "requiresValue": false,
      "companions": [],
      "documentation": "Sets i18n namespace for child elements. When used without a value on `route-view`, the namespace is auto-detected from the template name.\n\n**Example:**\n```html\n<div i18n-ns=\"dashboard\">\n  <span t=\"title\"><!-- resolves dashboard.title --></span>\n</div>\n\n<!-- Auto-detect namespace from route template -->\n<main route-view src=\"templates/\" i18n-ns></main>\n```",
      "category": "i18n"
    },
    {
      "name": "drag",
      "pattern": false,
      "priority": 15,
      "valueType": "expression",
      "valueDescription": "Expression for dragged item value",
      "requiresValue": true,
      "plugin": "@no-js-dev/nojs-elements",
      "companions": [
        {
          "name": "drag-type",
          "type": "string",
          "description": "No.JS: Type identifier for the dragged item"
        },
        {
          "name": "drag-effect",
          "type": "string",
          "description": "No.JS: Drag effect (copy, move, link, none)"
        },
        {
          "name": "drag-handle",
          "type": "string",
          "description": "No.JS: CSS selector for drag handle"
        },
        {
          "name": "drag-image",
          "type": "string",
          "description": "No.JS: CSS selector for custom drag image"
        },
        {
          "name": "drag-image-offset",
          "type": "string",
          "description": "No.JS: Offset for drag image (x,y)"
        },
        {
          "name": "drag-disabled",
          "type": "expression",
          "description": "No.JS: Expression to disable dragging"
        },
        {
          "name": "drag-class",
          "type": "string",
          "description": "No.JS: CSS class while dragging"
        },
        {
          "name": "drag-ghost-class",
          "type": "string",
          "description": "No.JS: CSS class for ghost element"
        },
        {
          "name": "drag-group",
          "type": "string",
          "description": "No.JS: Group name for multi-select"
        }
      ],
      "documentation": "Makes element draggable.\n\n**Example:**\n```html\n<div drag=\"item\" drag-type=\"card\" drag-class=\"dragging\">\n```\n\n**Companions:** `drag-type`, `drag-effect`, `drag-handle`, `drag-image`, `drag-image-offset`, `drag-disabled`, `drag-class`, `drag-ghost-class`, `drag-group`",
      "category": "dnd"
    },
    {
      "name": "drop",
      "pattern": false,
      "priority": 15,
      "valueType": "statement",
      "valueDescription": "Statement to execute on drop",
      "requiresValue": true,
      "plugin": "@no-js-dev/nojs-elements",
      "companions": [
        {
          "name": "drop-accept",
          "type": "string",
          "description": "No.JS: Accepted drag types (comma-separated)"
        },
        {
          "name": "drop-effect",
          "type": "string",
          "description": "No.JS: Drop effect (copy, move, link, none)"
        },
        {
          "name": "drop-class",
          "type": "string",
          "description": "No.JS: CSS class on valid dragover"
        },
        {
          "name": "drop-reject-class",
          "type": "string",
          "description": "No.JS: CSS class on invalid dragover"
        },
        {
          "name": "drop-disabled",
          "type": "expression",
          "description": "No.JS: Expression to disable dropping"
        },
        {
          "name": "drop-max",
          "type": "expression",
          "description": "No.JS: Max items accepted"
        },
        {
          "name": "drop-sort",
          "type": "string",
          "description": "No.JS: Sort direction (vertical, horizontal, grid)"
        },
        {
          "name": "drop-placeholder",
          "type": "string",
          "description": "No.JS: Placeholder HTML/tag"
        },
        {
          "name": "drop-placeholder-class",
          "type": "string",
          "description": "No.JS: CSS class for placeholder"
        }
      ],
      "documentation": "Makes element a drop target.\n\n**Example:**\n```html\n<div drop=\"items.push($drag)\" drop-accept=\"card\" drop-class=\"over\">\n```\n\n**Handler variables:** `$drag`, `$dragType`, `$dragEffect`, `$dropIndex`, `$source`, `$target`, `$el`",
      "category": "dnd"
    },
    {
      "name": "drag-list",
      "pattern": false,
      "priority": 10,
      "valueType": "expression",
      "valueDescription": "List path expression",
      "requiresValue": true,
      "plugin": "@no-js-dev/nojs-elements",
      "companions": [
        {
          "name": "template",
          "type": "templateId",
          "description": "No.JS: Template ID for items"
        },
        {
          "name": "drag-list-key",
          "type": "string",
          "description": "No.JS: Unique key property"
        },
        {
          "name": "drag-list-item",
          "type": "string",
          "description": "No.JS: Item variable name in template"
        },
        {
          "name": "drop-sort",
          "type": "string",
          "description": "No.JS: Sort direction"
        },
        {
          "name": "drag-type",
          "type": "string",
          "description": "No.JS: Type identifier"
        },
        {
          "name": "drop-accept",
          "type": "string",
          "description": "No.JS: Accepted drag types"
        },
        {
          "name": "drag-list-copy",
          "type": "boolean",
          "description": "No.JS: Copy instead of move"
        },
        {
          "name": "drag-list-remove",
          "type": "boolean",
          "description": "No.JS: Remove from source after drag"
        },
        {
          "name": "drag-disabled",
          "type": "expression",
          "description": "No.JS: Disable dragging"
        },
        {
          "name": "drop-disabled",
          "type": "expression",
          "description": "No.JS: Disable dropping"
        },
        {
          "name": "drop-max",
          "type": "expression",
          "description": "No.JS: Max items"
        },
        {
          "name": "drop-placeholder",
          "type": "string",
          "description": "No.JS: Placeholder HTML/tag"
        },
        {
          "name": "drop-placeholder-class",
          "type": "string",
          "description": "No.JS: Placeholder CSS class"
        },
        {
          "name": "drag-class",
          "type": "string",
          "description": "No.JS: CSS class while dragging"
        },
        {
          "name": "drop-class",
          "type": "string",
          "description": "No.JS: CSS class on valid dragover"
        },
        {
          "name": "drop-reject-class",
          "type": "string",
          "description": "No.JS: CSS class on invalid dragover"
        },
        {
          "name": "drop-settle-class",
          "type": "string",
          "description": "No.JS: CSS class during settle animation"
        },
        {
          "name": "drop-empty-class",
          "type": "string",
          "description": "No.JS: CSS class when list is empty"
        }
      ],
      "documentation": "Sortable list with drag and drop support.\n\n**Example:**\n```html\n<ul drag-list=\"items\" template=\"item-tpl\" drag-type=\"card\">\n```",
      "category": "dnd"
    },
    {
      "name": "drag-multiple",
      "pattern": false,
      "priority": 16,
      "valueType": "none",
      "valueDescription": "No value needed",
      "requiresValue": false,
      "plugin": "@no-js-dev/nojs-elements",
      "companions": [
        {
          "name": "drag-group",
          "type": "string",
          "description": "No.JS: Group name (required)"
        },
        {
          "name": "drag-multiple-class",
          "type": "string",
          "description": "No.JS: CSS class for selected items"
        }
      ],
      "documentation": "Enables multi-select drag on `drag` elements.\n\n**Example:**\n```html\n<div drag=\"item\" drag-multiple drag-group=\"cards\">\n```\n\n**Companions:** `drag-group` (required), `drag-multiple-class`",
      "category": "dnd"
    },
    {
      "name": "base",
      "pattern": false,
      "priority": 1,
      "valueType": "url",
      "valueDescription": "Base URL string",
      "requiresValue": true,
      "companions": [],
      "documentation": "Sets the base URL for all descendant fetch directives (`get`, `post`, `put`, `patch`, `delete`, `query`, `call`).\n\n**Example:**\n```html\n<div base=\"https://api.example.com/v1\">\n  <div get=\"/users\" as=\"users\"><!-- fetches https://api.example.com/v1/users --></div>\n</div>\n```",
      "category": "http"
    },
    {
      "name": "route",
      "pattern": false,
      "priority": 15,
      "valueType": "string",
      "valueDescription": "Route path pattern or \"*\" for catch-all wildcard",
      "requiresValue": true,
      "companions": [
        {
          "name": "route-active",
          "type": "string",
          "description": "No.JS: CSS class added when route is active (default: 'active')"
        },
        {
          "name": "route-active-exact",
          "type": "string",
          "description": "No.JS: CSS class added only on exact path match"
        },
        {
          "name": "lazy",
          "type": "string",
          "description": "No.JS: Loading strategy for route template (priority or ondemand)"
        },
        {
          "name": "outlet",
          "type": "string",
          "description": "No.JS: Named route-view outlet target (default: 'default')"
        },
        {
          "name": "page-title",
          "type": "expression",
          "description": "No.JS: Sets document.title on each navigation. Value is a JS expression; use single-quoted strings: page-title=\"'About | Site'\". Can access $route and $store."
        },
        {
          "name": "page-description",
          "type": "expression",
          "description": "No.JS: Creates/updates <meta name=\"description\"> on each navigation. Value is a JS expression."
        },
        {
          "name": "page-canonical",
          "type": "expression",
          "description": "No.JS: Creates/updates <link rel=\"canonical\"> on each navigation. Value is a JS expression."
        },
        {
          "name": "page-jsonld",
          "type": "string",
          "description": "No.JS: Creates/updates <script type=\"application/ld+json\" data-nojs> on each navigation. Value is a JSON string with {placeholder} interpolation."
        }
      ],
      "documentation": "Turns an `<a>` element into a route link that navigates without page reload. Also used on `<template>` elements to define routes.\n\nUse `route=\"*\"` on a `<template>` to create a catch-all wildcard route for 404 pages. The wildcard matches when no other route matches the current path. Use `$route.matched` (boolean) to check whether an explicit route matched (`true`) or a wildcard/fallback is rendering (`false`).\n\n**Route head attributes** — update `<head>` on every navigation:\n- `page-title=\"'Title | Site'\"` — sets `document.title`\n- `page-description=\"product.description\"` — updates `<meta name=\"description\">`\n- `page-canonical=\"'/products/' + $route.params.id\"` — updates `<link rel=\"canonical\">`\n- `page-jsonld='{\"@type\":\"Product\"}` — updates JSON-LD structured data\n\n**Example:**\n```html\n<a route=\"/about\">About</a>\n<template route=\"/dashboard\" lazy=\"ondemand\" outlet=\"sidebar\">\n<template route=\"/products/:id\"\n          page-title=\"'Product ' + $route.params.id + ' | Store'\"\n          page-description=\"'View product detail'\"\n          page-canonical=\"'/products/' + $route.params.id\">\n\n<!-- Catch-all 404 -->\n<template route=\"*\">\n  <h1>404</h1>\n  <p>Path <code bind=\"$route.path\"></code> not found.</p>\n</template>\n```\n\n**Companions:** `route-active`, `route-active-exact`, `lazy`, `outlet`, `page-title`, `page-description`, `page-canonical`, `page-jsonld`",
      "category": "routing"
    },
    {
      "name": "route-view",
      "pattern": false,
      "priority": 1,
      "valueType": "string",
      "valueDescription": "Outlet name (optional, empty for default outlet)",
      "requiresValue": false,
      "companions": [
        {
          "name": "src",
          "type": "string",
          "description": "No.JS: File-based routing folder path"
        },
        {
          "name": "ext",
          "type": "string",
          "description": "No.JS: Template file extension (default: .html)"
        },
        {
          "name": "route-index",
          "type": "string",
          "description": "No.JS: Index template name (default: 'index')"
        },
        {
          "name": "i18n-ns",
          "type": "string",
          "description": "No.JS: i18n namespace for route templates"
        },
        {
          "name": "transition",
          "type": "string",
          "description": "No.JS: View Transition API preset or custom name for route changes. Built-in presets: slide, fade, scale, none."
        }
      ],
      "documentation": "Route rendering outlet. Displays the template matching the current route.\n\nUses the **View Transition API** by default for route transitions (configurable via `router.viewTransition`). The `transition` attribute accepts built-in presets or a custom name.\n\n**Built-in presets:**\n- `slide` — horizontal slide between old and new content\n- `fade` — crossfade between old and new content\n- `scale` — scale down old content, scale up new content\n- `none` — disable transition animation\n\nCSS uses `::view-transition-old(route-content)` / `::view-transition-new(route-content)` pseudo-elements.\n\n**Example:**\n```html\n<div route-view src=\"pages\" transition=\"slide\"></div>\n\n<!-- Named outlet -->\n<div route-view=\"sidebar\" src=\"panels\" transition=\"fade\"></div>\n```\n\n**Config:** `router.viewTransition` (default: `true`)\n\n**Companions:** `src`, `ext`, `route-index`, `i18n-ns`, `transition`",
      "category": "routing"
    },
    {
      "name": "guard",
      "pattern": false,
      "priority": 5,
      "valueType": "expression",
      "valueDescription": "JS boolean expression",
      "requiresValue": true,
      "companions": [
        {
          "name": "redirect",
          "type": "string",
          "description": "No.JS: Path to redirect to when guard fails"
        }
      ],
      "documentation": "Route guard that prevents navigation when the expression is falsy.\n\n**Example:**\n```html\n<template route=\"/admin\" guard=\"isLoggedIn\" redirect=\"/login\">\n  <h1>Admin Panel</h1>\n</template>\n```\n\n**Companion:** `redirect`",
      "category": "routing"
    },
    {
      "name": "include",
      "pattern": false,
      "priority": 1,
      "valueType": "templateId",
      "valueDescription": "Template ID to clone",
      "requiresValue": true,
      "companions": [],
      "documentation": "Synchronously clones an inline template into the current position. Used on `<template>` elements.\n\n**Example:**\n```html\n<template id=\"header\">\n  <h1>My App</h1>\n</template>\n\n<template include=\"header\"></template>\n```",
      "category": "template"
    },
    {
      "name": "page-title",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression evaluating to a string",
      "requiresValue": true,
      "companions": [],
      "documentation": "Updates `document.title` reactively. Place on a `<div hidden>` element for non-routing pages. For SPA routes prefer the `page-title` attribute on `<template route>` instead.\n\n**Example:**\n```html\n<div hidden page-title=\"product.name + ' | My Store'\"></div>\n<!-- Static -->\n<div hidden page-title=\"'About Us | My Store'\"></div>\n```\n\nValue is a No.JS expression — use single-quoted strings inside the double-quoted HTML attribute.",
      "category": "head"
    },
    {
      "name": "page-description",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression evaluating to a string",
      "requiresValue": true,
      "companions": [],
      "documentation": "Creates or updates `<meta name=\"description\" content=\"...\">` in `<head>`. Place on a `<div hidden>` element. Reactive — re-applies on state changes.\n\n**Example:**\n```html\n<div hidden page-description=\"product.description\"></div>\n```",
      "category": "head"
    },
    {
      "name": "page-canonical",
      "pattern": false,
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression evaluating to a URL string",
      "requiresValue": true,
      "companions": [],
      "documentation": "Creates or updates `<link rel=\"canonical\" href=\"...\">` in `<head>`. Place on a `<div hidden>` element. Reactive — re-applies on state changes.\n\n**Example:**\n```html\n<div hidden page-canonical=\"'/products/' + product.slug\"></div>\n```",
      "category": "head"
    },
    {
      "name": "page-jsonld",
      "pattern": false,
      "priority": 20,
      "valueType": "template",
      "valueDescription": "JSON-LD template in element body with {placeholder} expressions",
      "requiresValue": false,
      "companions": [],
      "documentation": "Creates or updates `<script type=\"application/ld+json\" data-nojs>` in `<head>`. Unlike other head directives, the JSON-LD template is written as the **element body** (not the attribute value). Use `{expression}` placeholders for dynamic values. Place on a `<div hidden>` element.\n\n**Example:**\n```html\n<div hidden page-jsonld>\n  {\n    \"@context\": \"https://schema.org\",\n    \"@type\": \"Product\",\n    \"name\": \"{product.name}\",\n    \"offers\": { \"@type\": \"Offer\", \"price\": \"{product.price}\" }\n  }\n</div>\n```\n\nThe `data-nojs` marker distinguishes this tag from hand-written JSON-LD. Structural JSON braces are not interpreted as placeholders — only `{variable}` patterns matching an identifier are.",
      "category": "head"
    },
    {
      "name": "animate",
      "pattern": false,
      "priority": 15,
      "valueType": "animation",
      "valueDescription": "Animation name",
      "requiresValue": false,
      "companions": [
        {
          "name": "animate-duration",
          "type": "number",
          "description": "No.JS: Animation duration in ms"
        },
        {
          "name": "transition",
          "type": "string",
          "description": "No.JS: CSS transition name"
        }
      ],
      "documentation": "Standalone enter animation directive. Plays an enter animation when the element appears in the DOM (e.g. cloned error templates, static markup). Skipped when `if`, `show`, or `hide` is present on the same element — those directives handle animation themselves.\n\n**Example:**\n```html\n<div animate=\"fadeIn\">Animated on appear</div>\n<div animate=\"bounceIn\" animate-duration=\"500\">Slow bounce</div>\n<div animate transition=\"slide\">CSS transition on appear</div>\n```\n\nThe value can be an animation name or left empty when using `animate-enter` or `transition` instead.\n\n**Companions:** `animate-duration`, `transition`",
      "category": "animation"
    }
  ],
  "patterns": [
    {
      "name": "bind-*",
      "prefix": "bind-",
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression",
      "requiresValue": true,
      "documentation": "Dynamically binds an HTML attribute to a JS expression.\n\n**Example:**\n```html\n<a bind-href=\"url\">Link</a>\n<img bind-src=\"imageUrl\">\n```\n\n**Boolean attributes:** `disabled`, `readonly`, `checked`, `selected`, `hidden`, `required`\n\n**Common targets:** `href`, `src`, `class`, `title`, `alt`, `placeholder`, `action`, `value`",
      "commonTargets": [
        "href",
        "src",
        "class",
        "title",
        "alt",
        "placeholder",
        "action",
        "value",
        "disabled",
        "readonly",
        "checked",
        "selected",
        "hidden",
        "required"
      ],
      "category": "binding"
    },
    {
      "name": "class-*",
      "prefix": "class-",
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS boolean expression (or object/array for map/list)",
      "requiresValue": true,
      "documentation": "Conditionally applies CSS class(es).\n\n**Example:**\n```html\n<div class-active=\"isActive\">\n<div class-map=\"{ active: isActive, hidden: !show }\">\n<div class-list=\"['base', isActive ? 'active' : '']\">\n```\n\n**Sub-behaviors:** `class-map` (object → classes), `class-list` (array → classes)",
      "subBehaviors": [
        "class-map",
        "class-list"
      ],
      "category": "styling"
    },
    {
      "name": "style-*",
      "prefix": "style-",
      "priority": 20,
      "valueType": "expression",
      "valueDescription": "JS expression (CSS value)",
      "requiresValue": true,
      "documentation": "Dynamically sets a CSS style property. Attribute name is kebab-case, converted to camelCase.\n\n**Example:**\n```html\n<div style-color=\"textColor\">\n<div style-font-size=\"fontSize + 'px'\">\n<div style-map=\"{ color: textColor, fontSize: size + 'px' }\">\n```\n\n**Sub-behavior:** `style-map` (object → styles)",
      "subBehaviors": [
        "style-map"
      ],
      "category": "styling"
    },
    {
      "name": "on:*",
      "prefix": "on:",
      "priority": 20,
      "valueType": "statement",
      "valueDescription": "JS statement(s)",
      "requiresValue": true,
      "documentation": "Listens for DOM events and executes JS statement(s).\n\n**Example:**\n```html\n<button on:click=\"count++\">Increment</button>\n<form on:submit.prevent=\"handleSubmit()\">\n<input on:keydown.enter=\"search()\">\n```\n\n**Modifiers:** `.prevent`, `.stop`, `.once`, `.self`, `.debounce`, `.throttle`\n**Key modifiers:** `.enter`, `.escape`, `.tab`, `.space`, `.delete`, `.backspace`, `.up`, `.down`, `.left`, `.right`, `.ctrl`, `.alt`, `.shift`, `.meta`\n\n**Handler variables:** `$event`, `$el`",
      "commonEvents": [
        "click",
        "submit",
        "input",
        "change",
        "focus",
        "blur",
        "keydown",
        "keyup",
        "keypress",
        "mouseenter",
        "mouseleave",
        "mouseover",
        "mouseout",
        "mousedown",
        "mouseup",
        "dblclick",
        "contextmenu",
        "wheel",
        "scroll",
        "resize",
        "load",
        "error",
        "touchstart",
        "touchend",
        "touchmove"
      ],
      "modifiers": {
        "behavioral": [
          "prevent",
          "stop",
          "once",
          "self"
        ],
        "timing": [
          "debounce",
          "throttle"
        ],
        "key": [
          "enter",
          "escape",
          "tab",
          "space",
          "delete",
          "backspace",
          "up",
          "down",
          "left",
          "right",
          "ctrl",
          "alt",
          "shift",
          "meta"
        ]
      },
      "category": "event"
    }
  ],
  "lifecycleEvents": [
    "mounted",
    "init",
    "updated",
    "unmounted",
    "error"
  ],
  "contextKeys": [
    "$watch",
    "$notify",
    "$set",
    "$parent",
    "$refs",
    "$store",
    "$route",
    "$router",
    "$i18n",
    "$form",
    "$sse",
    "$el",
    "$event",
    "$error",
    "$rule"
  ],
  "loopContextVars": [
    "$index",
    "$count",
    "$first",
    "$last",
    "$even",
    "$odd"
  ],
  "eventHandlerVars": [
    "$event",
    "$el"
  ],
  "watchHandlerVars": [
    "$old",
    "$new"
  ],
  "dropHandlerVars": [
    "$drag",
    "$dragType",
    "$dragEffect",
    "$dropIndex",
    "$source",
    "$target",
    "$el"
  ],
  "animations": [
    "fadeIn",
    "fadeOut",
    "fadeInUp",
    "fadeInDown",
    "fadeOutUp",
    "fadeOutDown",
    "slideInLeft",
    "slideInRight",
    "slideOutLeft",
    "slideOutRight",
    "zoomIn",
    "zoomOut",
    "bounceIn",
    "bounceOut"
  ]
}
