packages

Give a page a new shape

A package can restyle Leaf, add one element, or carry a related widget family. This tutorial builds the smallest useful package: a reusable callout made from one registry entry and one CSS rule.

Start with two files

Create a package in the project, then check the empty composition:

$ leaf package init packages/callout
$ leaf package check packages/callout

The widget in this tutorial needs only these files:

packages/callout/
  registry.json
  theme.css

A project-wide package conventionally lives at .leaf/. A package kept elsewhere joins a page with --package. In the commands below, PAGE is the page directory to create or update:

$ leaf page init --package packages/callout PAGE

The complete package contract covers user packages, bundled packages, contribution directories, composition order, and replacement rules.

package init also creates empty contribution directories for guidance, browser modules, widgets, and vendor files. The CSS-only widget here leaves them untouched.

To start with behavior instead, name the first element while creating the package:

$ leaf package init packages/risk-notes --widget lf-risk-note

Leaf writes a valid prose-widget registry entry and its one-shot browser module, then checks the complete candidate before changing the package. The tutorial below stays with the smaller CSS-only form so each contract remains visible. A behavior module imports Leaf's package API from ../runtime/widget-api.js; it registers each interactive capability once with commands(). The same live command rows feed keyboard dispatch, help, accessibility shortcuts, and—when decision names the action—the controls shown inline for an Ask.

State the look

A package's theme.css joins Leaf's existing cascade. This package only styles the element it introduces:

lf-callout {
  display: block;
  padding: var(--sp-3);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--field);
  --lf-frame: 1;
}

--lf-frame: 1 tells the shared layout that this rule draws the widget's inset. The opening theme token block lists values such as --accent that a package may override for the whole page. Presentation used by only one page stays in that version's own <style>.

Declare the element

The page will author the callout as ordinary readable markup:

<lf-callout id="migration-warning">
  <strong>Migration window</strong>
  Writes pause for about two minutes.
</lf-callout>

Its complete registry.json entry validates the attributes and tells Leaf that the body is prose. x-example is the specimen an author queries with the entry:

{
  "lf-callout": {
    "description": "A short warning or constraint that belongs in the reading flow.",
    "type": "object",
    "properties": {
      "id": {
        "type": "string",
        "pattern": "^[a-z0-9][a-z0-9-]*$"
      }
    },
    "required": ["id"],
    "additionalProperties": false,
    "x-content": "prose",
    "x-upgrade": false,
    "x-example": "<lf-callout id=\"migration-warning\"><strong>Migration window</strong> Writes pause for about two minutes.</lf-callout>"
  }
}

x-content and x-upgrade are the two Leaf declarations every widget requires. The ordinary JSON Schema fields validate its attributes; description and x-example make the merged registry useful to an author.

The registry-key reference lists every x-* declaration. A CSS-only widget stops here.

Compose and check the page

$ leaf package check packages/callout
$ leaf page init --package packages/callout PAGE
$ jq '.["lf-callout"], .["$keys"]' PAGE/registry.json
$ leaf version check PAGE --render

Re-running page init is the explicit update for an existing page. Pages that are not re-initialized keep their vendored layer unchanged.

Registry keys
The generated reference for declarations such as x-data and x-request.
Package contract
Composition, guidance, browser modules, external data, requests, and validation.
Bundled default package
A complete package containing the widgets used across Leaf pages.