@obinexusltd/obix-template-html
v0.1.0
Published
OBIX Heart UX — vanilla HTML/CSS/JS starter. No build step, no framework.
Readme
obix-template-html
OBIX Heart UX — Vanilla HTML · CSS · JavaScript starter
No build tools · No framework · No compilation · Justnpx serve .
What this is
A zero-dependency browser template that implements the core OBIX Heart UX patterns in plain HTML, CSS, and JavaScript. You get:
- OBIX Controller → Control → Controllee architecture in vanilla JS
- #NoGhosting compliance policy running on every state change
- WCAG 2.1 AA accessible components (48px touch targets, focus rings, ARIA)
- jfix.scss-inspired design tokens as CSS custom properties
- Session-persistent state via
sessionStorage - Full OBIX component documentation in
docs/
Quick start
# No install needed — just serve the folder
npx serve .
# Or any static server works:
npx http-server .
python -m http.server 8080Then open http://localhost:3000 (or whatever port serve picks).
That's it. No npm install, no build step, no compiler.
Folder structure
obix-template-html/
├── index.html ← App shell, semantic HTML, ARIA landmarks
├── styles.css ← Design tokens + all component styles
├── app.js ← State, actions, policies, renderer, event wiring
├── docs/
│ ├── OBIX_COMPONENT_DOCUMENTATION_PART1.md ← Philosophy, DOP model, 30 components overview
│ ├── OBIX_COMPONENT_DOCUMENTATION_PART2.md ← Full component API reference
│ ├── OBIX_COMPONENT_DOCUMENTATION_PART3.md ← Integration patterns & examples
│ └── OBIX_COMPONENT_DOCUMENTATION_PART4.md ← WCAG 2.1 AA compliance guide
└── README.mdHow the OBIX patterns work in vanilla JS
Controller → Control → Controllee
OBIX uses a three-layer model. In vanilla JS it maps directly to:
| Layer | OBIX role | In this template |
|---|---|---|
| Controller | Captures intent | <form> submit, button click events |
| Control | Computes transition | createItem(), setItemStatus(), deleteItem() in app.js |
| Controllee | Applies output | render() — writes to the DOM |
Data-Oriented Programming (DOP)
State is a plain object — no classes, no proxies, no framework reactivity:
const state = {
items: {}, // id → Item record
filter: 'all',
nextId: 1,
};Actions are pure functions that mutate state then call persist():
function createItem({ title, description, priority }) {
const id = `item-${state.nextId++}`;
state.items[id] = { id, title, priority, status: 'open', assignedTo: [], ... };
persist();
return id;
}The renderer reads state and rewrites the DOM — deterministic, no diffing:
function render() {
const items = Object.values(state.items);
document.getElementById('stat-total').textContent = items.length;
// ...
}#NoGhosting policy
validateCompliance() runs after every render and surfaces violations:
function validateCompliance() {
const violations = [];
for (const item of Object.values(state.items)) {
if (item.status === 'completed') continue;
// Every open item must have an owner
if (item.assignedTo.length === 0) {
violations.push({
severity: item.priority === 'critical' ? 'critical' : 'warning',
description: `"${item.title}" has no assignee (#NoGhosting)`,
});
}
}
return violations;
}Add your own rules the same way — return a violation object to surface it in the UI, return nothing to pass.
Extending the template
Add a new field to items
- Add the
<input>to the form inindex.html - Read the value in the
submithandler inapp.js - Store it in
createItem()→state.items[id] - Render it in
renderItemCard(item)
Add a policy
// In validateCompliance(), add a new check:
if (item.priority === 'critical' && item.assignedTo.length === 0) {
violations.push({
severity: 'critical',
description: `Critical item "${item.title}" must be assigned immediately`,
});
}Add a new status
- Add the status string to the
ORDERmap inrender() - Add a
STATUS_BADGEentry inapp.js - Add an action button in
renderItemCard()
Persist to a real backend
Replace persist() / hydrate() with fetch() calls to your API.
The state shape is already JSON-serialisable:
async function persist() {
await fetch('/api/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(state.items),
});
}Design system (CSS tokens)
All visual decisions live in :root custom properties in styles.css.
Override any token to retheme the entire app:
:root {
--clr-accent: #00b4d8; /* change the primary colour */
--clr-bg: #ffffff; /* switch to a light theme */
--clr-text: #1a1a2e;
--clr-surface: #f4f4f8;
}Key tokens:
| Token | Default | Purpose |
|---|---|---|
| --clr-accent | #6c63ff | Primary / interactive colour |
| --clr-bg | #0f0f11 | Page background |
| --clr-surface | #1a1a1f | Card / panel background |
| --clr-danger | #ef4444 | Errors, critical badges |
| --clr-success | #3ecf8e | Done state |
| --touch-min | 48px | WCAG 2.5.5 minimum touch target |
| --focus-ring | 0 0 0 3px accent | jfix focus policy |
Accessibility
Every interactive element meets WCAG 2.1 AA:
- Touch targets: all buttons/inputs ≥ 48 × 48 px (
--touch-min) - Focus indicators: all focusable elements show a 3px accent ring on
:focus-visible - Skip link: keyboard users can jump to
#main-content - ARIA live regions: item list and violation list update are announced to screen readers
- Semantic HTML:
<header>,<main>,<footer>,<section>witharia-label - Colour contrast: all text ≥ 4.5:1 against background
- Reduced motion: transitions disabled via
@media (prefers-reduced-motion)
See docs/OBIX_COMPONENT_DOCUMENTATION_PART4.md for the full WCAG 2.1 AA
compliance guide.
Connecting to obix-template-node
This template is designed as the browser front-end for obix-template-node.
To connect them, replace persist() / hydrate() with calls to the REST API:
// In app.js — swap sessionStorage for the Node API:
const API = 'http://localhost:3000/api';
async function hydrate() {
const res = await fetch(`${API}/items`);
const { data } = await res.json();
data.forEach(item => { state.items[item.id] = item; });
render();
}
async function persist() {
// items are persisted individually via createItem / setItemStatus
}
async function createItem({ title, description, priority }) {
const res = await fetch(`${API}/items`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, description, priority, createdBy: 'browser' }),
});
const { data } = await res.json();
state.items[data.id] = data;
render();
}OBINexus lineage
This template is part of the OBINexus Heart UX mono-repo
(@obinexusltd/obix). It implements the same DOP model and #NoGhosting
governance policy used across all OBINexus toolchain components — no riftlang,
no nlink, no polybuild required. Pure browser, pure standards.
Maintainer: Nnamdi Michael Okpala [email protected]
License: MIT
