Resource Binding

A resource binding declares a site-wide CSS selector query and exposes the matching elements as a named variable for use by templates, constraints, triggers, and other server-side processors.

When to reach for it

Use resource bindings to pull data from across the site graph into a single document. Templates render the bound elements, constraints validate invariants against them, and triggers access context through them.

Namespace declaration

Declare the Pagelove Resource namespace on any element:

<html xmlns:r="https://pagelove.org/Binding/CSS">

The prefix can be any valid XML prefix (r, resource, etc.). Server-side processing must also be enabled by declaring the https://pagelove.org/1.0 namespace:

<html xmlns:p="https://pagelove.org/1.0"
      xmlns:r="https://pagelove.org/Binding/CSS">

Attribute form

Add a namespaced attribute to any element. The attribute name is the variable name. The attribute value is a CSS selector evaluated across the entire site graph.

r:<name>="<css-selector>"

The result is a collection of matching elements.

Semantics

Property Behaviour
Scope Site-wide — selectors operate across all documents
Result type HTML elements, not deserialized records
Evaluation At processing time, not cached
Mutability Read-only — mutation is performed via HTTP methods

Binding placement

Bindings may appear on any element. The declaring element determines scope:

Multiple bindings may appear on the same element.

Examples

Template rendering

The following document binds all schema.org/Person items to a variable named contacts and renders them with a Liquid template:

<!doctype html>
<html lang="en"
      xmlns:r="https://pagelove.org/Binding/CSS"
      xmlns:p="https://pagelove.org/1.0">

<head>
    <title>Contacts List</title>
</head>

<body r:contacts="[itemtype='http://schema.org/Person']"
      p:template="text/liquid">
    <ul>
        {% for contact in contacts %}
        <li>{{ contact.name }}</li>
        {% endfor %}
    </ul>
</body>
</html>

The selector [itemtype='http://schema.org/Person'] evaluates across all documents. Matching elements bind to contacts. The template iterates over contacts to render the list.

Graph constraint

Resource bindings supply the variables that constraint expressions operate on:

<body xmlns:r="https://pagelove.org/Binding/CSS"
      r:users="[itemtype*=User]"
      r:admins="[itemtype*=User]:has([itemprop=role]:value-equals('admin'))">

  <div itemscope itemtype="https://pagelove.org/Constraint">
    <code itemprop="constraint">size(admins) >= 1</code>
    <span itemprop="message">At least one admin user must exist</span>
  </div>

</body>

Trigger context

Triggers use resource bindings to access data for transformations:

<div xmlns:r="https://pagelove.org/Binding/CSS"
     r:auth="[itemtype*=Request] [itemprop=auth]"
     r:apiKey="[itemprop=webhook-api-key]"
     itemscope
     itemtype="https://pagelove.org/Trigger">

    <meta itemprop="selector" content="[itemtype*=Order]">
    <meta itemprop="method" content="PUT">

    <code itemprop="transformation">
      {
        headers: {
          'Authorization': 'Bearer ' + apiKey
        },
        body: {
          purchaser: auth.claims.email
        }
      }
    </code>

    <div itemprop="action" itemscope itemtype="https://pagelove.org/HTTPRequest">
      <meta itemprop="url" content="https://api.example.com/orders">
    </div>
</div>

The transformation expression accesses auth and apiKey bindings directly by name.

Note — binding the Request Document. r:auth="[itemtype*=Request] …" sources data from the Request Document, a transient, selector-addressable document representing the current request. The binding resolves during page composition, and any page that reads a request-document fragment is served Cache-Control: private. If you only need a field (not a selector-addressable fragment), the request object is also available directly in expression bindings and Liquid templates — request.auth.claims.* / request.auth.username (see Sessel syntax → Authenticated identity in composition).

Security

Resource bindings are evaluated server-side during composition and query the entire site graph without per-request authorization. A binding reads matching elements from every document on the host, regardless of whether the current request would be authorized to fetch those documents directly. Authorization rules govern which requests may reach which paths — they do not restrict what a resource binding may read.

The trust boundary is therefore who may author a composed page: because a page's bindings can surface any content stored on the host, treat authorship of a composed page as equivalent to read access over the whole host. Do not use a resource binding to pull data onto a page whose authors should not be able to see that data.

Error cases

Condition Result
Invalid CSS selector Request fails during document processing
Selector matches no elements Empty collection (not an error)
Selector matches elements in other documents All matching elements are returned — bindings are not filtered by per-request authorization (see Security)

See also