open regard. Module 01 / Display / Runnable examples

02 / Static HTML

A separate page.
The same settings.

This independent HTML document reads the shared preference before its content paints. There is no app router or React runtime on this page.

01 / Initialization

Independent documents can agree

Use the same origin, storage key, stable profile IDs and styles in your application and documentation. Load the browser bundle, optional preset bundle and configuration synchronously in the head.

<script src="/assets/openregard.js?v=f38755b255"></script>
<script src="/assets/presets.js?v=f38755b255"></script>
<script src="/assets/display-config.js?v=f38755b255"></script>

02 / WCAG-informed presentation

Connect tokens to your CSS.

The Vision profile uses the text-spacing override values from WCAG 1.4.12. Your CSS consumes the line height, paragraph, letter and word spacing tokens. These values are an override recipe, not a required default design.

  1. Compose the optional presets into a profile’s tokens.
  2. Use the custom properties in headings, body text, navigation and controls.
  3. Check the real page at 320 CSS pixels, with enlarged text and your users’ settings.

Vision + High Contrast also uses paired palette tokens whose solid text pairs exceed 7:1. Other colors, imagery and component states still need their own checks.

03 / WHATWG HTML integration

Keep the native behavior.

The widget uses a real label, select and options. The native-controls recipe uses platform color tokens; it cannot create semantics. Use labeled inputs, real links and buttons in the surrounding page as well.

A token preset does not establish accessibility conformance. See the standards and token preset guide for the distinction and sources.

Source code

These are the files behind this example. The tabs support arrow keys, Home and End.

docs.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Static documentation — Open Regard Display</title>
    <!-- Self-host synchronous scripts so the saved profile applies before paint. -->
    <script src="../dist/display.browser.js"></script>
    <script src="../dist/presets.browser.js"></script>
    <script src="./config.js"></script>
    <link rel="stylesheet" href="../dist/style.css" />
    <link rel="stylesheet" href="./demo.css" />
    <link rel="icon" href="./mark.svg" type="image/svg+xml" />
  </head>
  <body>
    <a class="skip" href="#main">Skip to content</a>
    <header class="masthead shell">
      <a class="brand" href="https://openregard.org/"
        ><img src="./mark.svg" alt="" />open regard.</a
      >
      <span class="meta">Module 01 / Display / Runnable examples</span>
    </header>
    <nav class="example-nav shell" aria-label="Example navigation">
      <a href="./">Vanilla HTML</a
      ><a href="./docs.html" aria-current="page">Static docs</a
      ><a href="./react.html">React</a
      ><a href="https://openregard.org/docs/">Library documentation ↗</a>
    </nav>
    <main id="main" class="shell">
      <section class="hero">
        <div>
          <p class="eyebrow">02 / Static HTML</p>
          <h1>A separate page.<br /><em>The same settings.</em></h1>
          <p class="intro">
            This independent HTML document reads the shared preference before
            its content paints. There is no app router or React runtime on this
            page.
          </p>
        </div>
        <aside class="control-card" aria-label="Display settings">
          <p class="eyebrow">Try a display profile</p>
          <div id="controls"></div>
        </aside>
      </section>

      <div class="doc-layout">
        <nav class="doc-index" aria-label="Example document sections">
          <a href="#initialize">01 / Initialize</a
          ><a href="#tokens">02 / Apply tokens</a
          ><a href="#html">03 / Use native HTML</a>
        </nav>
        <article class="doc-article">
          <section id="initialize">
            <p class="eyebrow">01 / Initialization</p>
            <h2>Independent documents can agree</h2>
            <p>
              Use the same origin, storage key, stable profile IDs and styles in
              your application and documentation. Load the browser bundle,
              optional preset bundle and configuration synchronously in the
              head.
            </p>
            <pre
              class="code-note"
            ><code>&lt;script src="/assets/openregard.js"&gt;&lt;/script&gt;
&lt;script src="/assets/presets.js"&gt;&lt;/script&gt;
&lt;script src="/assets/display-config.js"&gt;&lt;/script&gt;</code></pre>
          </section>
          <section id="tokens">
            <p class="eyebrow">02 / WCAG-informed presentation</p>
            <h2>Connect tokens to your CSS.</h2>
            <p>
              The Vision profile uses the text-spacing override values from WCAG
              1.4.12. Your CSS consumes the line height, paragraph, letter and
              word spacing tokens. These values are an override recipe, not a
              required default design.
            </p>
            <ol class="steps">
              <li>
                Compose the optional presets into a profile’s
                <code>tokens</code>.
              </li>
              <li>
                Use the custom properties in headings, body text, navigation and
                controls.
              </li>
              <li>
                Check the real page at 320 CSS pixels, with enlarged text and
                your users’ settings.
              </li>
            </ol>
            <p>
              Vision + High Contrast also uses paired palette tokens whose solid
              text pairs exceed 7:1. Other colors, imagery and component states
              still need their own checks.
            </p>
          </section>
          <section id="html">
            <p class="eyebrow">03 / WHATWG HTML integration</p>
            <h2>Keep the native behavior.</h2>
            <p>
              The widget uses a real label, select and options. The
              native-controls recipe uses platform color tokens; it cannot
              create semantics. Use labeled inputs, real links and buttons in
              the surrounding page as well.
            </p>
            <p class="recipe-note">
              A token preset does not establish accessibility conformance. See
              the
              <a href="https://openregard.org/docs/#standards-and-token-presets"
                >standards and token preset guide</a
              >
              for the distinction and sources.
            </p>
          </section>
        </article>
      </div>

      
    </main>
    <footer class="shell">
      <p>
        Open Regard / Display. Profiles and page styles are configured by the
        site. Test your integration with your content and users.
      </p>
      <a href="https://github.com/inferal-oss/openregard">View the project ↗</a>
    </footer>
    <script>
      OpenRegardDisplay.mountDisplayWidget(
        document.getElementById("controls"),
        regard,
        {
          className: "demo-widget",
        },
      );
    </script>
  </body>
</html>
config.js
// Optional token recipes. Profile names, composition and site styling are yours.
const { wcagPresets, htmlPresets } = OpenRegardPresets;
window.regardConfig = {
  version: 1,
  defaultProfile: "standard",
  storageKey: "openregard-example",
  controlLabel: "Reading and display preferences",
  profiles: [
    {
      id: "standard",
      label: "Standard",
      description: "The original site presentation.",
    },
    {
      id: "roomy",
      label: "Vision",
      description: "Larger text with the WCAG text-spacing override values.",
      tokens: {
        ...wcagPresets.textSpacing,
        ...htmlPresets.nativeControls,
        "--regard-font-size": "125%",
      },
    },
    {
      id: "vision-contrast",
      label: "Vision + High Contrast",
      description:
        "Larger, spaced text with a restrained palette and stronger borders.",
      tokens: {
        ...wcagPresets.textSpacing,
        ...wcagPresets.contrastLight,
        ...htmlPresets.nativeControls,
        "--regard-font-size": "125%",
      },
    },
    {
      id: "strong",
      label: "Max Contrast",
      description:
        "Larger text, generous spacing and black-and-white surfaces.",
      tokens: {
        ...wcagPresets.textSpacing,
        ...htmlPresets.nativeControls,
        "--regard-font-size": "150%",
        "--regard-page-ink": "#000",
        "--regard-page-paper": "#fff",
        "--regard-panel-ink": "#fff",
        "--regard-panel-paper": "#000",
        "--regard-border-color": "#000",
      },
    },
  ],
};
window.regard = OpenRegardDisplay.createDisplay(window.regardConfig);
window.stopRegard = window.regard.start();
demo.css
:root {
  font-size: var(--regard-font-size, 100%);
  --ink: var(--regard-page-ink, #203b32);
  --paper: var(--regard-page-paper, #f4f5ee);
  --muted: var(--regard-page-ink, #52645b);
  --line: var(--regard-border-color, #c5cec0);
  --panel-paper: var(--regard-panel-paper, #203b32);
  --panel-ink: var(--regard-panel-ink, #f4f7ee);
  --surface: var(--regard-page-paper, #fffef8);
  --accent: var(--regard-page-paper, #d8ebbb);
  color: var(--ink);
  background: var(--paper);
  font-family:
    ui-sans-serif,
    system-ui,
    -apple-system,
    sans-serif;
  line-height: var(--regard-line-height, 1.65);
  letter-spacing: var(--regard-letter-spacing, normal);
  word-spacing: var(--regard-word-spacing, normal);
}
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  overflow-wrap: anywhere;
}
.shell {
  width: min(100% - 3rem, 76rem);
  margin-inline: auto;
}
a {
  color: inherit;
  text-underline-offset: 0.25em;
}
:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 4px;
}
.skip {
  position: absolute;
  transform: translateY(-150%);
  background: Canvas;
  color: CanvasText;
  padding: 0.7em;
  z-index: 5;
}
.skip:focus {
  transform: none;
}
.masthead {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  align-items: center;
  padding-block: 1.5rem;
}
.brand {
  display: inline-flex;
  gap: 0.65em;
  align-items: center;
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: -0.04em;
  text-decoration: none;
}
.brand img {
  width: 2rem;
  height: 2rem;
}
.masthead .meta {
  font-size: 0.75rem;
  color: var(--muted);
}
.example-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.5rem;
  padding-block: 0.8rem;
  border-block: 1px solid var(--line);
  font-size: 0.85rem;
}
.example-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75em;
  text-decoration: none;
}
.example-nav [aria-current] {
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.55em;
}
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  padding-block: 3.5rem;
  align-items: start;
}
.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.7rem;
  font-weight: 700;
}
h1 {
  font-size: clamp(2.4rem, 5vw, 4.4rem);
  line-height: 1.1;
  letter-spacing: -0.055em;
  margin: 0.8rem 0 1.5rem;
  font-weight: 600;
}
h1 em {
  font-family: Georgia, serif;
  font-weight: 400;
}
h2 {
  font-size: clamp(1.5rem, 3vw, 2.2rem);
  line-height: 1.2;
  letter-spacing: -0.035em;
  font-weight: 600;
  margin-top: 0;
}
h3 {
  line-height: 1.3;
  font-size: 1.1rem;
}
p {
  margin-block: 0 1em;
  margin-block-end: var(--regard-paragraph-spacing, 1em);
}
.intro {
  font-size: 1.05rem;
  color: var(--muted);
  max-width: 39rem;
}
.control-card {
  padding: 1.5rem;
  border: 1px solid var(--line);
  border-radius: 0.8rem;
  background: var(--surface);
  box-shadow: 0 8px 0 var(--accent);
  min-width: 0;
}
.control-card .eyebrow {
  margin-bottom: 1rem;
}
/* The same public styling hooks work on React and vanilla widgets. */
.demo-widget {
  --openregard-control-radius: 0.4rem;
  --openregard-control-padding: 0.75em;
  --openregard-gap: 0.6rem;
  --openregard-focus-color: var(--ink);
}
.demo-widget .openregard-label {
  flex: 1;
  font-size: 0.8rem;
  font-weight: 600;
}
.demo-widget .openregard-select {
  width: 100%;
}
.demo-widget .openregard-description {
  font-size: 0.78rem;
  margin-top: 0.8rem;
  color: var(--muted);
}
.section-head {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  margin-block: 1rem 1.5rem;
}
.section-head h2 {
  margin: 0;
}
.section-head p {
  font-size: 0.8rem;
  color: var(--muted);
  margin: 0;
}
.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.25rem;
}
.card {
  border: 1px solid var(--line);
  border-radius: 0.7rem;
  padding: clamp(1.1rem, 3vw, 2rem);
  background: var(--surface);
  min-width: 0;
}
.card .eyebrow {
  margin-bottom: 1.4rem;
}
.dark {
  background: var(--panel-paper);
  color: var(--panel-ink);
  border-color: currentColor;
}
.dark p {
  color: inherit;
}
.large-number {
  font-family: Georgia, serif;
  font-size: 3.5rem;
  line-height: 1.2;
}
.tag {
  display: inline-block;
  border: 1px solid currentColor;
  border-radius: 2rem;
  padding: 0.2em 0.7em;
  font-size: 0.7rem;
}
label:not(.openregard) {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
}
input,
button {
  box-sizing: border-box;
  font: inherit;
  max-width: 100%;
  min-width: 0;
}
input {
  display: block;
  width: 100%;
  margin-top: 0.5em;
  padding: 0.75em;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 0.35rem;
}
button {
  cursor: pointer;
  padding: 0.65em 0.9em;
  background: Canvas;
  color: CanvasText;
  border: 1px solid currentColor;
  border-radius: 0.35rem;
}
.sync-row {
  margin-block: 1.5rem 3rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 2rem;
  align-items: center;
  padding: 2rem;
  background: var(--accent);
  border: 1px solid var(--line);
  border-radius: 0.7rem;
}
.sync-row p {
  font-size: 0.85rem;
}
.doc-layout {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
  gap: 3rem;
}
.doc-index {
  border-top: 1px solid var(--line);
  padding-top: 1rem;
  font-size: 0.8rem;
}
.doc-index a {
  display: block;
  padding-block: 0.6em;
}
.doc-article {
  min-width: 0;
  max-width: 48rem;
}
.doc-article section {
  padding-block: 1rem 2rem;
}
.steps {
  padding-inline-start: 1.5rem;
}
.steps li {
  padding-inline-start: 0.4em;
  margin-block: 1em;
}
code {
  font-size: 0.88em;
  overflow-wrap: anywhere;
}
.code-note {
  padding: 1.5rem;
  background: var(--panel-paper);
  color: var(--panel-ink);
  border-radius: 0.5rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.7;
}
.recipe-note {
  border-left: 3px solid currentColor;
  padding-inline-start: 1rem;
  font-size: 0.85rem;
}
footer {
  border-top: 1px solid var(--line);
  padding-block: 2rem;
  margin-top: 3rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  font-size: 0.8rem;
  color: var(--muted);
}
footer p {
  margin: 0;
  max-width: 40rem;
}
/* Spacing overrides also reach typography with authored heading/tracking values. */
:root:not([data-regard-profile="standard"])
  :is(h1, h2, h3, p, a, label, span, select, button, li) {
  line-height: var(--regard-line-height, 1.65);
  letter-spacing: var(--regard-letter-spacing, normal);
  word-spacing: var(--regard-word-spacing, normal);
}
@media (max-width: 850px) {
  .hero,
  .doc-layout {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
  .hero {
    padding-block: 2rem 3rem;
  }
  .doc-index {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
  }
}
@media (max-width: 600px) {
  .shell {
    width: calc(100% - 2rem);
  }
  .grid,
  .sync-row {
    grid-template-columns: 1fr;
  }
  .sync-row {
    padding: 1.25rem;
    gap: 1rem;
  }
  .masthead {
    padding-block: 1rem;
  }
}
@media (forced-colors: active) {
  .dark,
  .code-note {
    background: Canvas;
    color: CanvasText;
  }
  .control-card {
    box-shadow: none;
  }
}