Reading notes / 001
Give the content room.
A display preference should work beyond a paragraph. This sample includes navigation, form controls and different surfaces, with space to grow when text or spacing increases.
01 / Vanilla HTML
Change the display profile and try the form, cards and second control below. Open the static docs in another tab to see the same selection there.
Flexible layout · Paired surfaces · Native form
Reading notes / 001
A display preference should work beyond a paragraph. This sample includes navigation, form controls and different surfaces, with space to grow when text or spacing increases.
Palette check
Foreground + backgroundVision + High Contrast uses a restrained green palette and stronger borders. Max Contrast switches to black and white. Both define each surface and its text together.
Same controller / A different style
This second widget hides the decorative icon. Its native select stays in sync without moving focus.
The Vision profiles compose wcagPresets.textSpacing with
htmlPresets.nativeControls. The first supplies presentation
tokens; native HTML supplies labels and control behavior. Inspect
config.js and demo.css below.
These are the files behind this example. The tabs support arrow keys, Home and End.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Vanilla HTML — 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="./" aria-current="page">Vanilla HTML</a
><a href="./docs.html">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">01 / Vanilla HTML</p>
<h1>One preference.<br /><em>Across the website.</em></h1>
<p class="intro">
Change the display profile and try the form, cards and second
control below. Open the static docs in another tab to see the same
selection there.
</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="section-head">
<h2>A small reading workspace</h2>
<p>Flexible layout · Paired surfaces · Native form</p>
</div>
<div class="grid">
<article class="card">
<p class="eyebrow">Reading notes / 001</p>
<h2>Give the content room.</h2>
<p>
A display preference should work beyond a paragraph. This sample
includes navigation, form controls and different surfaces, with
space to grow when text or spacing increases.
</p>
<label
>Search the example<input
type="search"
placeholder="A native search field to try"
/></label>
</article>
<article class="card dark">
<p class="eyebrow">Palette check</p>
<span class="tag">Foreground + background</span>
<h2 style="margin-top: 1.5rem">Keep color pairs together.</h2>
<p>
Vision + High Contrast uses a restrained green palette and stronger
borders. Max Contrast switches to black and white. Both define each
surface and its text together.
</p>
<button type="button" id="reset">Reset preferences</button>
</article>
</div>
<section class="sync-row">
<div>
<p class="eyebrow">Same controller / A different style</p>
<h2>Two controls, one preference.</h2>
<p>
This second widget hides the decorative icon. Its native select
stays in sync without moving focus.
</p>
</div>
<div id="secondary"></div>
</section>
<p class="recipe-note">
The Vision profiles compose <code>wcagPresets.textSpacing</code> with
<code>htmlPresets.nativeControls</code>. The first supplies presentation
tokens; native HTML supplies labels and control behavior. Inspect
<strong>config.js</strong> and <strong>demo.css</strong> below.
</p>
</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",
},
);
OpenRegardDisplay.mountDisplayWidget(
document.getElementById("secondary"),
regard,
{
icon: false,
className: "demo-widget",
},
);
document.getElementById("reset").onclick = () => regard.reset();
</script>
</body>
</html>
// 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();
: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;
}
}