# Shareout authoring guidelines

Read this before writing a document for Shareout. It is short because the rules are
few, and every one of them is enforced at publish time: a document that breaks them is
rejected or, once published, will not load the offending resource. The CLI's `check`
command runs the same validation locally before you publish.

## 1. What you are producing

A **bundle**: a directory with `index.html` at its root and, optionally, other files
next to it (`data.json`, `chart.svg`, `notes.pdf`, `images/hero.png`). The bundle is
served as-is from a per-document hostname such as `k7f3m2.shareout.page`. There is no
build step, no server-side rendering, and no backend of yours behind it.

- `index.html` is the entry. It must exist at the root of the bundle.
- A single `.html` file with no other assets is also accepted; it becomes `index.html`.
- Everything the page needs must be in the bundle or on one of the allowlisted hosts
  in section 3.

## 2. Relative paths only

Reference bundled files by **relative path**: `data.json`, `images/hero.png`,
`./styles.css`. Never use a root-relative path (`/data.json`, `/images/hero.png`) or a
protocol-relative one (`//example.com/x`).

Why: link-gated documents are served under a path prefix, `/g/<grant>/`, that the
viewer's browser inherits for relative URLs. A root-relative path ignores that prefix
and requests a file that does not exist. The same document may also be served at the
root of its hostname when public, and at `/v/<n>/` for a pinned version. Relative
paths work in every case; root-relative paths work in none of the gated ones.

Do not use `<base href>`. Do not build absolute URLs to your own hostname.

## 3. External resources: the allowlist

Documents render under a content security policy. Only these external origins load;
anything else is blocked silently, with no error visible to the reader.

| Resource | Allowed origins |
|---|---|
| Scripts (`<script src>`) | `https://cdnjs.cloudflare.com`, `https://cdn.jsdelivr.net`, `https://cdn.tailwindcss.com`, `https://code.jquery.com` |
| Stylesheets (`<link rel=stylesheet>`, `@import`) | `https://fonts.googleapis.com` |
| Fonts (`@font-face` `url()`) | `https://fonts.gstatic.com`, bundled files, `data:` |
| Images, media | bundled files, `data:`, `blob:` |
| Network calls (`fetch`, XHR, EventSource, WebSocket) | the document's own origin only |

Rules that follow from the table:

- Prefer no external resources at all. A self-contained page renders identically
  forever; a CDN dependency does not.
- When you do load a library, pin an exact version in the URL and use the UMD or IIFE
  build that defines a global. Put the `<script src>` before the inline script that
  uses it.
- Inline scripts and inline styles are allowed.
- `https://` only. `http://` URLs are rejected. XML namespace identifiers such as
  `xmlns="http://www.w3.org/2000/svg"` on inline SVG are names, not fetches, and are
  exempt.
- Images from other hosts do not load. Inline SVG, bundle the file, or use a `data:` URI.
- Tailwind's play CDN is allowed but adds 300 KB and a flash of unstyled content;
  write plain CSS unless you need it.

## 4. No network, no forms, no frames

- `fetch('data.json')` works and is the right way to load a large bundled dataset.
  `fetch('https://api.example.com/...')` is blocked by policy. There is no way to reach
  a backend from a Shareout document, by design. Put the data in the bundle.
- `<form>` is not allowed and form submission is blocked. Interactive controls
  (buttons, inputs, selects) are fine as long as nothing submits.
- `<iframe>`, `<object>`, `<embed>`, and workers are not allowed. A document cannot
  embed another page; link to it instead.
- Navigation to other sites through `<a href>` works. Use `target="_blank"` with
  `rel="noopener"` for anything external.

## 5. Storage

`localStorage`, `sessionStorage`, and IndexedDB work. Each document has its own origin,
so storage is private to that document, and it is partitioned inside the viewer frame:
what a reader stores in one browser stays there and is not visible to other readers,
to other documents, or to you. Storage can also be empty or throw (private windows,
cleared data, strict browsers), so wrap access in `try`/`catch` and render correctly
with nothing stored. Use it for conveniences like a remembered tab or a collapsed
section, not for anything that must persist.

## 6. Light and dark

Readers arrive in whichever color scheme their system uses. Support both.

- Use the brand's CSS custom properties for every color: `--so-bg`, `--so-fg`,
  `--so-muted`, `--so-surface`, `--so-border`, `--so-accent`, `--so-accent-fg`,
  `--so-link`, `--so-success`, `--so-warning`, `--so-danger`, plus `--so-font-body`,
  `--so-font-heading`, `--so-font-mono`, `--so-radius`, `--so-space`, `--so-measure`.
  `brand.css` defines them for light, redefines them under
  `@media (prefers-color-scheme: dark)`, and honours `data-theme="light"` or
  `data-theme="dark"` on `<html>` if a document wants a manual toggle.
- Do not hard-code colors in your own styles. If you need a tint, derive it:
  `color-mix(in srgb, var(--so-accent) 10%, var(--so-bg))`.
- Give `body` an explicit background from the tokens. A transparent body inherits the
  viewer's ground and looks wrong in one of the two modes.
- Inline SVG charts: put colors on CSS classes that use the tokens, not on `fill`
  attributes with literal values.
- Check both modes before publishing. In most browsers the developer tools can emulate
  `prefers-color-scheme`.

## 7. Title

Put a specific `<title>` in `<head>`, within the first 8 KB of the file. It names the
document in the reader's tab, in link previews, and in the publisher's document list.
"RCA: Dispatch API outage, 2026-08-27" is a title; "Report" and "Untitled" are not.

## 8. Print

Include `@media print` rules. Readers print RCAs, PRDs, and status reports, and export
decks to PDF. The template themes already hide navigation, remove backgrounds, keep
tables and figures on one page where possible, and (for presentations) put one slide
per landscape page. If you add your own components, give them print rules too. Do not
rely on dark-mode colors in print; browsers print the light scheme.

## 9. Size budget

| Limit | Free tier | Owner tier |
|---|---|---|
| Total bundle size | 10 MB | 16 MB |
| Files per bundle | 100 | 255 |
| Single file | 10 MB | 16 MB |

Templates' exemplars are 30 to 60 KB. A typical document with inline CSS and an SVG
chart or two is under 200 KB. If you are near a limit, the cause is almost always
images: resize them to the display size, prefer SVG for diagrams, and use WebP or AVIF
for photographs. `data:` URIs count toward the size of the file that contains them.

## 10. Using a template

Templates are instructions plus a stylesheet plus one complete example. They are not
skeletons to fill in; write the document yourself and make it look like the example.

1. Read the template's `brief.md`: purpose, audience, required sections in order,
   tone, evidence to include, what to avoid.
2. Open `exemplar.html` and read it as a finished document. Note the structure and the
   classes it uses for each section.
3. Take `brand.css` (from the brand you are publishing under; `default` if none) and
   the template's `theme.css`. Inline both in `<head>` as two `<style>` blocks, brand
   first, then theme. Page-specific styles go in a third block after them.
4. Write `index.html` following the brief's section order. Use the theme's classes
   (`.so-doc`, `.so-header`, `.so-meta`, `.so-toc`, `.so-section`, `.so-callout`,
   `.so-table`, `.so-figure`, `.so-footer`, and the template-specific ones the brief
   lists). Do not invent colors; use the tokens.
5. End with the brand footer text from `brand.json` (`footer`) in `.so-footer`. The
   brand's logo is available inline as `logo_svg` if you want it in the header or
   footer.
6. Run the checks in section 12, then publish.

## 11. Accessibility

- One `<h1>`. Headings in order (`h2` under `h1`, `h3` under `h2`) with no skipped
  levels; readers with screen readers navigate by them.
- Every image has `alt` text that says what it shows, or `alt=""` if it is decorative.
  Inline SVG charts get `role="img"` with a `<title>` and a `<desc>` that states the
  numbers, or an adjacent table with the same data.
- Colour is never the only carrier of meaning. Pair status colours with a word or a
  badge; pair chart series with labels or a legend.
- Contrast: the brand tokens meet WCAG AA in both modes when used as intended
  (`--so-fg` on `--so-bg` or `--so-surface`; `--so-accent-fg` on `--so-accent`).
  Do not put `--so-muted` text on `--so-accent` or invent lighter tints for text.
- Anything interactive (slide navigation, collapsible sections, tabs) works with the
  keyboard: focusable, operable with Enter or Space or the arrow keys, with a visible
  focus ring. Use real `<button>` elements, not clickable `<div>`s.
- Tables use `<th>` for headers. Wide tables scroll inside a wrapper with
  `overflow-x: auto`; the page itself never scrolls horizontally.
- Text stays readable at 320 px wide and when zoomed to 200%.
- `<html lang="en">` (or the document's language) is set.

## 12. Pre-publish checklist

- [ ] `index.html` at the root; every other file referenced by a relative path.
- [ ] No `/...` or `//...` in `src`, `href`, `url()`, or `@import`.
- [ ] Every external URL is `https://` and on the allowlist in section 3, with a pinned
      version for scripts.
- [ ] No `fetch`, XHR, or WebSocket to any host but the document's own; no `<form>`,
      `<iframe>`, `<object>`, `<embed>`.
- [ ] Specific `<title>`; `<meta charset>`; viewport meta; `<html lang>`.
- [ ] `brand.css` then `theme.css` inlined; no literal colors in your own styles.
- [ ] Looks right in light and in dark; prints sensibly.
- [ ] Headings in order, alt text present, keyboard works for interactive parts.
- [ ] Under the size and file-count limits for your tier.
- [ ] Brand footer text present.
- [ ] The CLI's `check` command passes with no errors and no warnings you cannot explain.
