# RealPlus Web Components — Usage Guide

> Generated from the component source. The structural facts (tags, attributes, slots) are
> extracted by the Custom Elements Manifest analyzer and cannot drift from what ships; the
> prose and examples are curated. See `manifest.json` for the machine-readable version.

## What this is

A library of presentational `rp-*` custom elements an LLM drops into report HTML. They are
dependency-free native web components: paste the small runtime + the components you use
inline (see **Setup**), write the tags, and they render the polished, on-brand result. The
components are pure presentation — they never fetch data or own report logic; you supply
the data as **attributes and nested children** (there is no JavaScript-property API).

## Machine-readable schema

Every tag, its attributes (with accepted enum values + defaults), slots, and carrier child
elements are exported as a schema you can read instead of
guessing — it is generated from the component source and cannot drift from what ships:

- `manifest.json` — the curated, compact schema (this guide is generated from it). Start here.
- `custom-elements.json` — the raw Custom Elements Manifest from the analyzer.

The per-component reference under **Components** below is that same schema rendered as prose.

## Setup

There are **no external scripts or stylesheets** — everything is pasted inline, so a report
renders inside a previewer whose security policy blocks every external origin. You paste
three things, all available under `examples/components/`:

1. The runtime prelude `rp-base.js` (once) — defines the tiny `el()`/`RpElement` runtime.
2. The components you use (`rp-card.js`, `rp-table.js`, …) — each a self-contained block
   ending in `customElements.define(...)`. Order among them does not matter.
3. The styles — `tokens.css` + `base.css` (once) plus each used component's `.css`.

Put 1+2 in one plain `<script>` (NOT `type="module"`), and 3 in one `<style>`:

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>html,body{margin:0}</style>
    <style>/* paste: tokens.css, base.css, then each used component's .css */</style>
  </head>
  <body>
    <script>/* paste: rp-base.js, then rp-report.js, rp-card.js, … */</script>
    <rp-report>
      <!-- your report here -->
    </rp-report>
  </body>
</html>
```

`examples/self-contained.html` is a complete worked file assembled exactly this way — open
it to copy the whole pattern. `examples/components/HOW-TO.md` is the step-by-step guide.

## Theming & colors

All color (and spacing, type, radius) is driven by `--rp-*` CSS custom properties defined
in `tokens.css` (the design-token file you paste). **Open it to see every variable.** It
has two tiers:

- **Primitives** — raw color ramps (`--rp-accent-600: #1570ef`, `--rp-gray-800`, …). The
  paint palette; components never read these directly.
- **Semantic roles** — what components actually read; they point at primitives and carry
  meaning: `--rp-fg-primary` (body text), `--rp-fg-brand` / `--rp-bg-brand` /
  `--rp-bg-brand-subtle` / `--rp-border-brand` (the accent), `--rp-bg-primary` (page), etc.

Change a role once and everything using it shifts together, consistently. Two ways to
recolor, broadest to most surgical:

1. **Recolor the brand for a whole report** — set the semantic brand roles on a scope (the
   `rp-report` is the natural place); everything beneath re-skins:

   ```html
   <rp-report style="--rp-fg-brand:#b91c1c; --rp-bg-brand:#b91c1c; --rp-bg-brand-subtle:#fef2f2; --rp-border-brand:#ef4444;"> … </rp-report>
   ```

2. **Override one role** — change a single purpose, e.g. the page background:

   ```html
   <rp-report style="--rp-bg-primary:#faf7f2;"> … </rp-report>
   ```

> Prefer overriding **semantic roles** over primitives. A semantic role declared on `:root`
> resolves against `:root`, so re-pointing only a primitive at a lower scope may not flow
> through — set the role you want directly.

## Composition model

Two ways to configure a component, usable together:

- **Nested children (slots)** — *what pieces appear and where*. Place an `rp-media`, an
  `rp-fields`, an `rp-chip` inside an `rp-card`.
- **Attributes** — *how pieces are arranged*: density, columns, alignment, variant.

Every component degrades gracefully: missing/partial/unknown input falls back to a safe
default and never breaks the layout. Unknown attribute values are ignored.

## Authoring pattern: data first, then render

**For reports with repeated content — several listing cards, many table rows — define the
data first as plain JavaScript, then render it into `rp-*` markup in a separate step.** Do
not hand-write each repeated card or row. Put a `<script>` in the report that:

1. declares the data (an array of items, plus an optional block for content that repeats
   across items), then
2. maps each item through small render functions that return `rp-*` markup, and inserts the
   result into the page.

**The data shape is entirely up to you — there is no predefined schema.** Invent whatever
fields, names, and nesting the specific request needs (a listings report, a financials
summary, a comparison — each has its own natural shape). The components read the values you
hand them; they impose no field set of their own. The example below names its array
`LISTINGS` with listing fields only because that is what *that* report is about — yours can
look completely different.

This keeps content and layout separate: you edit the data to change *what* the report says,
and edit the render functions to change *how* it looks — without touching the data. Every
repeated item comes out structurally identical, and a missing field degrades gracefully
because the components handle blanks. Reserve hand-written tags for one-off chrome (the
masthead, the footer). **See the worked report under "Example reports" below — it is built
exactly this way.** For one-off content with no repetition, writing the tags directly is fine.

## Components

The **content & shell** set carries the report: the page, cards, fields, media, tables,
chips, and footer. The **furniture & layout** set is the surrounding prose, mastheads,
contact blocks, and spacing primitives — use them so a report is just the pasted runtime +
`rp-*` tags with no hand-written CSS.

### Content & shell

#### `<rp-report>`

The report canvas: gray backdrop + a centered white US-Letter page. Wrap the whole report in it so plain markup (headings, prose, links) is styled on-brand and everything sits on one sheet.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `width` | `default` \| `wide` \| `full` | — |  |

#### `<rp-card>`

A self-contained block for one subject. Fill its header / media / body / footer regions with other components or content via `slot="…"`; unslotted children fall into the body.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `density` | `comfortable` \| `compact` | — |  |
| `variant` | `default` \| `elevated` \| `outline` | — |  |
| `media-position` | `leading` \| `trailing` | — |  |
| `align` | `start` \| `center` | — |  |

#### `<rp-fields>`

A labeled label/value list, optionally grouped. Supply rows as `rp-field` children.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `columns` | `1` \| `2` \| `3` | — |  |
| `label-position` | `above` \| `left` | — |  |
| `density` | `comfortable` \| `compact` | — |  |

**Slots:** default content — rp-field children whose rows render in document order.

**Child `<rp-field>`** — the value, if no value attribute

| Attribute | Notes |
|-----------|-------|
| `label` | Field label |
| `value` | Field value (or use text content) |
| `group` | Optional group heading |
| `empty` | Placeholder when value is empty (default —) |

#### `<rp-media>`

A media gallery for a subject — images, floor plans, video/tour links. Supply items as `rp-media-item` children.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `variant` | `single-image` \| `small-slider` \| `big-slider` \| `image-grid` | — |  |

**Slots:** default content — rp-media-item children whose tiles supply the viewer.

**Child `<rp-media-item>`** — caption, if no caption attribute

| Attribute | Notes |
|-----------|-------|
| `src` | Image URL |
| `kind` | `photo` `floor-plan` `video` `tour` |
| `portrait` | Boolean — portrait orientation |
| `caption` | Caption text |
| `href` | Link URL (video/tour) |
| `thumbnail` | Thumbnail URL override |

#### `<rp-table>`

A scannable grid, one row per record. Declare `rp-column`s and `rp-row`/`rp-cell`s. A cell may hold rich markup (e.g. an rp-chip).

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `density` | `comfortable` \| `compact` | — |  |

**Slots:** default content — rp-column children (column defs) and rp-row children (one rp-cell per value).

**Child `<rp-column>`** — 

| Attribute | Notes |
|-----------|-------|
| `key` | Column key (matches rp-cell key) |
| `header` | Header label |
| `align` | `left` `center` `right` |
| `width` | CSS width or fr unit (e.g. `2fr`, `120px`) |

**Child `<rp-row>`** — contains rp-cell children

| Attribute | Notes |
|-----------|-------|
| `group` | Optional group heading for this row |

**Child `<rp-cell>`** — cell content — plain text OR rich markup like an rp-chip

| Attribute | Notes |
|-----------|-------|
| `key` | Column key |
| `value` | Cell text (or use child content) |

#### `<rp-chip>`

A small labeled pill for a short status or label. Drop it anywhere — a table cell, a card header, a fields value. The tone is author-chosen; the chip never infers meaning from its text.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `tone` | `neutral` \| `brand` \| `ai` \| `active` \| `pending` \| `sold` \| `expired` \| `future` \| `off-market` | — |  |
| `size` | `md` \| `sm` | — |  |

#### `<rp-footer>`

A centered branded report footer. Drop it at the bottom of a report (inside rp-report it pins to the bottom of the page).

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `variant` | `text` \| `logo` \| `both` | — |  |

### Furniture & layout

#### `<rp-report-header>`

The masthead at the top of a report: a `title` on the left and a right-hand aside (agent / preparer / date — its light-DOM children) above a divider. `eyebrow` adds a small brand line.

**Slots:** default content — the right-hand aside content (agent / preparer / date lines).

#### `<rp-contact>`

An agent / preparer contact block: a caption, name + role, and labeled contact methods via the `office` / `mobile` / `email` attributes (email auto-links).

#### `<rp-heading>`

A heading at one of four type-scale levels (h1–h4). Generic — the LLM picks the `level`; it carries no domain meaning.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `level` | `1` \| `2` \| `3` \| `4` | — |  |

#### `<rp-text>`

A run of text at a chosen `tone` and `size`. A paragraph by default, or `inline` to flow within a line. Use it for prose, captions, and labels instead of bare `<p>`/`<span>`.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `tone` | `primary` \| `secondary` \| `tertiary` \| `brand` | — |  |
| `size` | `body` \| `small` \| `lead` \| `h4` | — |  |
| `weight` | `regular` \| `medium` \| `bold` | — |  |

**Slots:** default content — the text content (text or inline markup).

#### `<rp-eyebrow>`

A small uppercase label that captions the block beneath it (a section kicker, a brand line). `tone` is tertiary (muted) or brand (accent).

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `tone` | `tertiary` \| `brand` | — |  |

#### `<rp-stack>`

A vertical stack of its children with a consistent `gap`. A layout primitive — no chrome, just spacing.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `gap` | `xs` \| `sm` \| `md` \| `lg` | — |  |
| `align` | `start` \| `center` \| `end` | — |  |

#### `<rp-split>`

Two regions side by side, wrapping to stacked on narrow widths. Children opt into a side with `slot="start"` / `slot="end"`. `ratio` skews the widths; `justify` pins the two to the edges.

| Attribute | Values / type | Default | Notes |
|-----------|---------------|---------|-------|
| `ratio` | `even` \| `start-wide` \| `end-wide` \| `start-narrow` \| `end-narrow` | — |  |
| `align` | `start` \| `center` \| `baseline` | — |  |

**Slots:** `slot="start"` — the leading region (default for unmarked children); `slot="end"` — the trailing region.

#### `<rp-list>`

A list of rows, each a term with an optional right-aligned meta value (a transit stop and its distance). Rows come from `rp-list-item` children.

**Slots:** default content — rp-list-item children supply the rows.

**Child `<rp-list-item>`** — the term, if no term attribute

| Attribute | Notes |
|-----------|-------|
| `term` | Row term (or use text content) |
| `meta` | Optional right-aligned meta value |

## Worked examples

These are the canonical compositions the gallery renders end to end.

### Single-listing detail card

```html
<rp-report>
  <rp-card variant="elevated" eyebrow="Flatiron District" title="245 Park Avenue South, 4B">
    <span slot="header"><rp-chip tone="active">Active</rp-chip></span>
    <rp-media slot="media" variant="big-slider">
      <rp-media-item src="https://…/1.jpg" caption="Living room"></rp-media-item>
      <rp-media-item src="https://…/2.jpg" kind="floor-plan"></rp-media-item>
    </rp-media>
    <rp-fields slot="body" columns="3">
      <rp-field label="Price" value="$3,850,000"></rp-field>
      <rp-field label="Beds" value="2"></rp-field>
      <rp-field label="Baths" value="2"></rp-field>
    </rp-fields>
  </rp-card>
  <rp-footer></rp-footer>
</rp-report>
```

### Multi-listing comparison / open-house grid

```html
<rp-report width="wide">
  <h2>Comparable listings</h2>
  <rp-table density="compact" zebra>
    <rp-column key="address" header="Address" width="2fr"></rp-column>
    <rp-column key="price" header="Price" align="right"></rp-column>
    <rp-column key="status" header="Status" align="center"></rp-column>
    <rp-row>
      <rp-cell key="address">245 Park Ave S, 4B</rp-cell>
      <rp-cell key="price">$3,850,000</rp-cell>
      <rp-cell key="status"><rp-chip tone="active">Active</rp-chip></rp-cell>
    </rp-row>
  </rp-table>
  <rp-footer variant="both"></rp-footer>
</rp-report>
```

### Report masthead + furniture (zero hand-written CSS)

The furniture & layout components carry the prose and chrome, so an LLM-authored report
needs no hand-written CSS beyond the pasted component styles — just `rp-*` tags.

```html
<rp-report>
  <rp-report-header title="Full Report w/Photo">
    <rp-text size="small" tone="tertiary">Jordan Avery</rp-text>
    <rp-text size="small" tone="tertiary">jordan.avery@example.com · M: (212) 555-0147</rp-text>
  </rp-report-header>
  <rp-card variant="outline" eyebrow="Chelsea, 10011" title="1. 154 Ninth Avenue">
    <rp-media slot="media" variant="single-image">
      <rp-media-item src="https://…/facade.jpg" caption="Façade"></rp-media-item>
    </rp-media>
    <rp-stack slot="header" gap="xs">
      <rp-chip tone="active">Active</rp-chip>
      <rp-text inline size="h4" weight="bold" tone="brand">$10,000,000</rp-text>
    </rp-stack>
    <rp-fields slot="body" columns="2" label-position="left" dividers>
      <rp-field label="Price" value="$10,000,000"></rp-field>
      <rp-field label="Units" value="3"></rp-field>
    </rp-fields>
    <rp-text slot="body" size="small">A fully gut-renovated mixed-use building in Chelsea…</rp-text>
  </rp-card>
  <rp-contact name="Jordan Avery" role="Licensed Associate" office="(212) 555-0100" email="jordan.avery@example.com"></rp-contact>
  <rp-footer></rp-footer>
</rp-report>
```

## Example reports

Complete, runnable reports built only from these components, served next to this guide,
with the runtime + styles inlined (no external loads). Fetch one to see a full report end to
end, including the **data-first authoring pattern** (a data array + a render step) above.

- [`examples/self-contained.html`](./examples/self-contained.html) — the minimal worked
  file: prelude + components + styles inlined, then a short report. Copy this skeleton.
- [`examples/detail.html`](./examples/detail.html) — a multi-listing detail report: one
  card per listing (header, photo + facts, description, rent roll, financials, transit,
  schools), all generated from a single `LISTINGS` data array.
