Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ registry.json
pnpm-lock.yaml
index.html
Components.dc.html
contracts
8 changes: 8 additions & 0 deletions contracts/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Portable interaction contract changelog

## 1.0.0 — 2026-08-30

- Added 14 framework-neutral web primitives.
- Added stable conformance hooks and shared state vocabulary.
- Added 24 mutation-guarded keyboard, focus, no-JavaScript, failure, reduced-motion, forced-colors, target-size, and reflow vectors.
- Added a native-first reference fixture.
19 changes: 19 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Portable web interaction contract v1.0.0

`web-interactions.v1.json` is the framework-neutral semantic and interaction contract owned by `OpenCoven/ui`. `test-vectors.v1.json` is the shared conformance suite. `fixtures/reference.html` demonstrates the native-first baseline without importing React, Tailwind, Base UI, or the specimen app.

Visual identity and token values remain owned by `OpenCoven/brand`. Product state, authority, protocol, and runtime behavior remain owned by the corresponding product repositories.

## Stable hooks

Consumers expose `data-oc-primitive`, `data-oc-part`, and optional `data-oc-state` hooks named by the contract. These are testing semantics, not a styling API, and never replace native attributes such as `href`, `open`, `disabled`, `aria-expanded`, `aria-selected`, or `aria-busy`.

## Conformance

Run every applicable vector with JavaScript enabled and disabled, keyboard-only input, 320 px width, 200% zoom/reflow, reduced motion, and forced colors where supported. Each vector returns `pass`, `fail`, or `unsupported-with-reason`; unsupported is never counted as pass.

Ordinary navigation is not an application menu. Mobile navigation is not a modal. Tabs do not perform remote mutation. Tooltips contain no essential or interactive content. Progress is measured, not decorative. Download links remain browser-native; page JavaScript never owns installer bytes. Dialogs are reserved for true modal tasks. Guided proof remains semantic and complete without canvas, WebGL, drag, or motion.

## Versioning

Patch changes clarify wording without changing behavior. Minor changes add backward-compatible primitives, parts, states, or vectors. Major changes remove/rename hooks or alter required keyboard, focus, failure, or progressive-enhancement behavior. Consumers pin an immutable revision and run the shared vectors before updating.
1 change: 1 addition & 0 deletions contracts/fixtures/reference.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><html lang="en" data-theme="system"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><title>OpenCoven portable interaction reference</title><style>*{box-sizing:border-box}body{margin:0;min-width:20rem;font:1rem/1.55 system-ui}a,button,summary,select,input{min-height:44px}:focus-visible{outline:3px solid CanvasText;outline-offset:3px}header,main{width:min(70rem,calc(100% - 2rem));margin:auto}header{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:1rem 0}nav a{display:inline-flex;align-items:center;padding:.5rem}section{padding:1.25rem 0;border-top:1px solid currentColor}.proof{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem}.enhanced [role=tabpanel][hidden]{display:none}@media(max-width:43rem){.desktop{display:none}.proof{grid-template-columns:1fr}}@media(min-width:43.01rem){.mobile{display:none}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}}</style><script type="module" src="./reference.js"></script></head><body><header><a href="#main" data-oc-part="brand-link">OpenCoven</a><nav class="desktop" aria-label="Primary" data-oc-primitive="global-navigation"><span data-oc-part="primary-links"><a href="#product" aria-current="page">Product</a><a href="#protocol">Protocol</a><a href="#developers">Developers</a><a href="#community">Community</a></span><a href="#start" data-oc-part="primary-action">Start locally</a></nav><details class="mobile" data-oc-primitive="mobile-navigation" data-oc-state="collapsed"><summary data-oc-part="trigger">Menu</summary><nav aria-label="Mobile primary" data-oc-part="panel"><span data-oc-part="links"><a href="#product">Product</a><a href="#protocol">Protocol</a><a href="#developers">Developers</a><a href="#community">Community</a><a href="#start">Start locally</a></span></nav></details></header><main id="main"><h1>Portable interaction reference</h1><section id="start" data-oc-primitive="action"><h2>Actions</h2><a href="#download" data-oc-part="label">Start locally</a> <a href="#protocol">Understand the protocol</a> <button type="button">Quiet operation</button></section><section data-oc-primitive="theme-control"><h2>Theme</h2><label data-oc-part="label" for="theme">Preference</label><select id="theme" data-oc-part="control"><option>system</option><option>light</option><option>dark</option></select></section><section data-oc-primitive="copy-control"><h2>Copy</h2><code data-oc-part="source">npm install -g @opencoven/cli</code> <button hidden type="button" data-oc-part="trigger">Copy</button><span data-oc-part="status" role="status" aria-live="polite" aria-atomic="true"></span></section><section id="product" data-oc-primitive="tabs"><h2>Views</h2><div role="tablist" aria-label="Views" data-oc-part="list"><button role="tab" id="t1" aria-controls="p1" aria-selected="true" tabindex="0" data-oc-part="tab">Overview</button><button role="tab" id="t2" aria-controls="p2" aria-selected="false" tabindex="-1" data-oc-part="tab">Evidence</button></div><section role="tabpanel" id="p1" aria-labelledby="t1" data-oc-part="panel"><h3>Overview</h3><p>Visible without JavaScript.</p></section><section role="tabpanel" id="p2" aria-labelledby="t2" data-oc-part="panel"><h3>Evidence</h3><p>Also visible without JavaScript.</p></section></section><section data-oc-primitive="tooltip"><h2>Help</h2><p>Essential explanation is already visible. <button data-oc-part="trigger" aria-describedby="tip">More</button></p><span id="tip" role="tooltip" data-oc-part="content" hidden>Supplemental detail.</span></section><section data-oc-primitive="status-indicator"><h2>Status</h2><p data-oc-part="label">✓ Verified from a pinned revision</p></section><section data-oc-primitive="progress"><h2>Progress</h2><label data-oc-part="label" for="progress">Verification</label><progress id="progress" data-oc-part="value-or-state" value="2" max="3">2 of 3</progress></section><section id="download" data-oc-primitive="download-chooser"><h2>Download</h2><div data-oc-part="artifact-links"><a href="/download/mac">macOS</a> <a href="/download/windows">Windows</a> <a href="/download/linux">Linux</a></div><p data-oc-part="fallback"><a href="https://github.com/OpenCoven/coven-cave/releases/latest">All release artifacts</a></p></section><section id="protocol" data-oc-primitive="guided-proof"><h2>Identity → authority → execution</h2><fieldset data-oc-part="controls"><legend>Inspect a state</legend><label><input type="radio" name="proof" checked>Identity</label><label><input type="radio" name="proof">Authority</label><label><input type="radio" name="proof">Execution</label></fieldset><p data-oc-part="state-label">All three states remain below.</p><div class="proof" data-oc-part="evidence-region"><article><h3>Identity bound</h3><p>A familiar revision is named.</p></article><article><h3>Authority checked</h3><p>Protected change is permitted, degraded, or rejected.</p></article><article><h3>Execution receipted</h3><p>The runtime records what happened.</p></article></div></section><section><h2>Dialog</h2><a href="#fallback">Read inline</a> or <button data-dialog-open>open modal</button><dialog data-oc-primitive="dialog" aria-labelledby="dialog-title"><h3 id="dialog-title" tabindex="-1" data-oc-part="title">Modal reference</h3><div data-oc-part="content">A true modal contains and restores focus.</div><button data-oc-part="close">Close</button></dialog><div id="fallback"><h3>Inline fallback</h3><p>Available without JavaScript.</p></div></section><section data-oc-primitive="error-surface"><h2 data-oc-part="title">Evidence unavailable</h2><p data-oc-part="description">No verified value was returned, so no zero is shown.</p><p data-oc-part="recovery-or-fallback"><a href="https://github.com/OpenCoven">Inspect source</a></p></section></main></body></html>
11 changes: 11 additions & 0 deletions contracts/fixtures/reference.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/test-vectors.v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":"opencoven.ui-test-vectors/v1","contractVersion":"1.0.0","result":"pass|fail|unsupported-with-reason; unsupported is never pass","vectors":[{"id":"NAV-001","primitive":"global-navigation","modes":["js","no-js"],"steps":["inspect landmarks/links"],"assertions":["one named global nav","brand and Start locally are links","one aria-current when represented","no duplicate focusable nav"],"mutationGuard":"remove label or expose duplicate"},{"id":"NAV-002","primitive":"global-navigation","modes":["js","no-js"],"steps":["Tab forward/back","Enter links"],"assertions":["visible focus","reading order","focus exits both directions","href followed"],"mutationGuard":"preventDefault Tab"},{"id":"MOB-001","primitive":"mobile-navigation","modes":["js"],"steps":["open","Tab inside","Escape"],"assertions":["44×44 trigger","open state synced","not modal/inert","Escape closes and restores trigger"],"mutationGuard":"leave expanded or lose focus"},{"id":"MOB-002","primitive":"mobile-navigation","modes":["no-js"],"steps":["open details if used","traverse links"],"assertions":["all destinations present","Start locally present","no script overlay"],"mutationGuard":"hide panel pending JS"},{"id":"MOB-003","primitive":"mobile-navigation","modes":["js"],"steps":["open","outside click","reopen","activate link"],"assertions":["outside closes","inside link navigates once"],"mutationGuard":"backdrop swallows link"},{"id":"ACT-001","primitive":"action","modes":["js","no-js","320px"],"steps":["inspect/activate each"],"assertions":["a for navigation","button for operation","44×44 targets","one filled action"],"mutationGuard":"clickable div or two filled actions"},{"id":"ACT-002","primitive":"action","modes":["js"],"steps":["activate async twice"],"assertions":["one operation","name retained","busy exposed","visible result/recovery"],"mutationGuard":"duplicate request or blank busy label"},{"id":"THEME-001","primitive":"theme-control","modes":["js"],"steps":["select light/dark/system","reload","change OS"],"assertions":["only three stored values","explicit survives","system tracks OS","pre-paint state matches preference"],"mutationGuard":"two-state toggle"},{"id":"THEME-002","primitive":"theme-control","modes":["no-js","forced-colors"],"steps":["load light/dark/high contrast"],"assertions":["system remains legible","content/navigation complete"],"mutationGuard":"hard-code unusable theme"},{"id":"COPY-001","primitive":"copy-control","modes":["js"],"steps":["grant clipboard","activate"],"assertions":["source equals copy","focus retained","Copied visible 2–4s","one polite announcement"],"mutationGuard":"move focus to toast"},{"id":"COPY-002","primitive":"copy-control","modes":["js","no-js"],"steps":["reject clipboard or disable JS"],"assertions":["failure wording","source selectable","no false success","static source visible"],"mutationGuard":"hide source"},{"id":"TABS-001","primitive":"tabs","modes":["js"],"steps":["ArrowRight","End","Home","Tab"],"assertions":["one selected/tabindex0","keys select correctly","matching panel","Tab exits widget"],"mutationGuard":"all tabs tabindex0"},{"id":"TABS-002","primitive":"tabs","modes":["no-js"],"steps":["inspect source order"],"assertions":["every panel visible","each panel labelled"],"mutationGuard":"ship inactive panels hidden"},{"id":"TIP-001","primitive":"tooltip","modes":["js"],"steps":["focus/Escape","hover/leave"],"assertions":["same supplement","tooltip not focusable","control already named","essential text outside"],"mutationGuard":"put link inside tooltip"},{"id":"STATUS-001","primitive":"status-indicator","modes":["js","no-js","forced-colors"],"steps":["inspect static/dynamic"],"assertions":["visible wording/non-color cue","routine update polite once","unavailable not zero"],"mutationGuard":"colored dot only"},{"id":"PROGRESS-001","primitive":"progress","modes":["js","reduced-motion"],"steps":["inspect determinate/indeterminate/unavailable"],"assertions":["valid measured value","busy wording","no fabricated percent","motion not required"],"mutationGuard":"hard-code fake 73%"},{"id":"DOWN-001","primitive":"download-chooser","modes":["js","no-js"],"steps":["traverse artifacts/fallback"],"assertions":["ordinary hrefs","all alternatives","activation required","page does not fetch bytes"],"mutationGuard":"fetch/blob installer"},{"id":"DOWN-002","primitive":"download-chooser","modes":["js"],"steps":["fail resolver/stream"],"assertions":["direct release fallback or error surface","no endless retry"],"mutationGuard":"swallow failure"},{"id":"DIALOG-001","primitive":"dialog","modes":["js"],"steps":["open","Tab cycle","Escape"],"assertions":["labelled","initial focus inside","focus contained","background inert","focus restored"],"mutationGuard":"restore to body"},{"id":"DIALOG-002","primitive":"dialog","modes":["no-js"],"steps":["inspect fallback"],"assertions":["content inline or ordinary link","nothing essential modal-only"],"mutationGuard":"hide all dialog content"},{"id":"PROOF-001","primitive":"guided-proof","modes":["js","reduced-motion","320px"],"steps":["select three states"],"assertions":["three named states","non-color selection","labelled evidence","no remote mutation","no WebGL requirement"],"mutationGuard":"canvas-only meaning"},{"id":"PROOF-002","primitive":"guided-proof","modes":["no-js"],"steps":["read sequence"],"assertions":["all three visible","headings/evidence for each"],"mutationGuard":"render first only"},{"id":"ERROR-001","primitive":"error-surface","modes":["js","no-js"],"steps":["render busy/error/unavailable","traverse recovery"],"assertions":["heading/description","fallback/next step","routine update keeps focus","recovery follows action"],"mutationGuard":"blank spinner"},{"id":"REFLOW-001","primitive":"action","modes":["320px","200%-zoom","forced-colors"],"steps":["traverse all controls"],"assertions":["44×44 targets","no overlap","no two-dimensional scroll","focus not obscured","system focus visible"],"mutationGuard":"shrink or hide focused target"}]}
Loading
Loading