SPEC-007
ID:SPEC-007Status:draft

Local Runes — Declarative Rune Extension (v1)

claude/align-sidenav-styling-4MuxV View source
Branches 3
History 3
  1. cf0cfc0
    Content editedby Claude
    Revert "Fix plan site truncation by escaping Markdoc tags in code fences
  2. 703b85a
    Content editedby Claude
    Fix plan site truncation by escaping Markdoc tags in code fences
  3. 6a9de8b
    Created (draft)by Claude
    Migrate planning documents to rune-enhanced Markdoc format

Context

Projects need custom interactive components in content — a playground, a pricing calculator, a product demo. Rather than a {% component %} escape hatch that breaks out of the rune system, local runes extend it. To the content author, a local rune looks identical to a built-in rune. The only difference is where it's defined.

This spec covers v1 runtime support: config declaration, schema generation, content parsing, and component rendering. VS Code language server support is a fast-follow.

v1 Config Surface

Local runes are declared in refrakt.config.json:

{
  "contentDir": "./content",
  "theme": "@refrakt-md/lumina",
  "target": "svelte",
  "runes": {
    "playground": {
      "component": "./src/components/Playground.svelte",
      "description": "Interactive code playground with live preview",
      "attributes": {
        "height": { "type": "number", "default": 600 },
        "example": { "type": "string", "default": "default" },
        "editable": { "type": "boolean", "default": true }
      },
      "children": "render"
    },
    "pricing-calculator": {
      "component": "./src/components/PricingCalculator.svelte",
      "description": "Interactive pricing calculator",
      "attributes": {
        "currency": { "type": "string", "default": "USD" },
        "plans": { "type": "string" }
      }
    }
  }
}

Config Fields

FieldTypeRequiredDefaultPurpose
componentstringYesPath to Svelte component (relative to project root)
descriptionstringNo""Human-readable description (used by language server in fast-follow)
attributesRecord<string, AttrDef>No{}Attribute schema for validation
children"none" | "render"No"none"How children are handled

Attribute Definition

interface AttrDef {
  type: "string" | "number" | "boolean";
  default?: string | number | boolean;
  required?: boolean;
}

Children Modes

"none" (default) — Self-closing tag. No closing tag needed. Component receives only declared attributes.

{% pricing-calculator plans="starter,pro" %}