@shing.wong/sure-ui
v0.3.0
Published
UI component library for sure-factor generated interfaces — forms, data tables, modals, and CRUD layouts with accessibility and theme support.
Maintainers
Readme
sure-ui
Runtime UI library for sure-factor generated interfaces. Seven preset themes (Nord, Forest, Dracula, Dark, Positronic, vision-system, vision-atkinson) matching sure-factor's semantic class conventions, plus a notification system with four display modes. Drop in a theme, call showNotification, and go.
npm package name:
@shing.wong/sure-ui(published scoped — the unscopedsure-uiname is owned by an unrelated publisher). The repo and directory remainsure-ui.
import { nord } from '@shing.wong/sure-ui'
const style = document.createElement('style')
style.textContent = nord
document.head.appendChild(style)Why sure-ui?
| Problem | How sure-ui solves it |
|---------|--------------------------|
| Generated code has no styles | sure-factor generates semantic HTML (.sure-form, .sure-table, .sure-crud). sure-ui provides production CSS for all of them — no design work needed. |
| Hopping between UI libraries | Seven cohesive themes — five everyday palettes plus two WCAG-AAA vision themes. Switch by changing one import. |
| Scattered notification patterns | Four modes from one function: inline (field-level), toast (pop-up), statusBar (banner), sidePanel (error list). Consistent API, consistent styling. |
| Accessibility afterthought | Themes include focus rings (:focus-visible), aria-invalid styling, semantic color contrast, and proper font-size units. |
| Theme lock-in | Themes are plain CSS strings — inject at runtime, swap without rebuilding. No CSS-in-JS, no bundler config, no framework dependency. |
Themes
| Export | Character |
|---|---|
| nord | Cool professional |
| forest | Warm organic |
| dracula | Dark modern |
| dark | Neutral dark with generic aliases |
| positronic | Positronic brand — dark default, generic tokens plus pp-* helper classes (see below) |
| vision-system | Vision / low vision — WCAG-AAA light palette, Verdana-first system stack at 125% type |
| vision-atkinson | Vision / low vision — the same AAA palette on vendored Atkinson Hyperlegible (OFL; serve the two .woff2 files from /fonts/, copies live in fonts/) |
Typography contract
Every theme declares four variables in its :root and ships the rules that
apply them — a runtime swap can never drop the base typography:
| Var | Meaning |
|---|---|
| --font-body | Body font stack |
| --font-mono | Monospace stack for code/pre |
| --font-size | html font size as a percentage of the browser default (so user preferences still multiply through) |
| --line-height | Body leading |
Consumption rules (html { font-size: var(--font-size, 100%) },
body { font-family: var(--font-body); line-height: var(--line-height, 1.6) })
are embedded in every theme. Consumers that extract only :root blocks —
like the positronic landing page — still get every knob, which is why all
theme settings must live in :root as variables.
Mobile & touch
Every theme appends one shared block (src/styles/mobile.ts, exported as
mobileLayer), so all seven carry it byte-identical — asserted in
index.test.ts, not maintained by hand. It is colour-free: layout and hit
targets only, so shipping it changes no contrast maths and no :root knob.
| Media query | Detects | Does |
|---|---|---|
| max-width: 640px | phones holding fixed-width chrome | forms, the side panel, dialogs and toasts stop overflowing the viewport; action rows wrap; table padding tightens |
| hover: none | touch has no hover | reveals the two hover-only affordances (message actions, session actions), which are otherwise unreachable |
| pointer: coarse | fingers are not cursors | 44px targets for buttons, inputs and nav items; larger dialog resize handles |
Following the system light/dark scheme is deliberately the consumer's call —
prefers-color-scheme is a behaviour, not a palette. The recommended
pattern (what the positronic landing page runs): store the mode, not the
theme, and let auto map it live.
const scheme = matchMedia('(prefers-color-scheme: dark)')
const AUTO = 'auto', DEF = 'positronic'
let mode = localStorage.getItem('theme') || AUTO
const theme = () => mode === AUTO ? (scheme.matches ? 'dark' : DEF) : mode
const paint = () => {
const t = theme()
if (t === DEF) delete document.documentElement.dataset.theme
else document.documentElement.dataset.theme = t
localStorage.setItem('theme', mode)
}
scheme.addEventListener('change', () => mode === AUTO && paint())Contrast is measured, not promised
index.test.ts asserts, for every theme: body text, muted text and
links at ≥ 4.5:1 (WCAG AA) against both the page and card surfaces, and
borders at ≥ 3:1 (WCAG 1.4.11). The two vision themes go further:
≥ 7:1 (AAA) on all text pairs plus ≥ 112.5% type. The vision pair
shares one palette and differs only in --font-body, so a reader compares
fonts, not colours.
positronic theme
Usage identical to other themes:
import { positronic } from '@shing.wong/sure-ui'
const style = document.createElement('style')
style.textContent = positronic
document.head.appendChild(style)Tokens: --bg, --surface, --text, --muted, --border, --accent, --error, --success, --warn, --highlight (all aliased to --positronic-* internals). Helper classes available out of the box:
| Class | Purpose |
|---|---|
| .pp-wrap | Centered max-width page container |
| .pp-btn / .pp-btn--ghost | Primary / outline buttons |
| .pp-card | Elevated surface card |
| .pp-badge / --ok / --warn / --beta | Status pill |
| pre, code | Monospace code blocks on the surface token |
How it compares
| | sure-ui | Tailwind UI | shadcn/ui | Bootstrap |
|---|---|---|---|---|
| Generated code ready | ✅ .sure-form, .sure-table, etc. | ❌ Custom classes needed | ❌ Custom components | ❌ |
| Preset themes | ✅ 7 (incl. 2 AAA vision) | ✅ Unlimited (custom) | ⚠️ Single (copy-to-project) | ✅ 5 built-in |
| Theme injection | ✅ CSS string, runtime swap | ❌ Build-time only | ⚠️ CSS variables | ⚠️ SASS vars |
| Notification modes | ✅ inline, toast, statusBar, sidePanel | ❌ | ⚠️ Sonner (toast only) | ⚠️ Toast only |
| Bundle size | ~4 KB CSS + ~2 KB JS | ~300 KB CSS | ~40 KB per component | ~150 KB CSS + JS |
| Framework | Agnostic (CSS strings) | Tailwind | React | Agnostic |
| Peer dependencies | None | Tailwind | React | None |
Installation
npm install @shing.wong/sure-uiZero peer dependencies. Works with any framework (React, Vue, Svelte, vanilla JS).
Themes
Seven preset themes matching sure-factor's BEM-style generated classes.
import { nord, forest, dracula, themes } from '@shing.wong/sure-ui'
import type { ThemeName } from '@shing.wong/sure-ui'
// Inject a theme at runtime:
const style = document.createElement('style')
style.textContent = nord
document.head.appendChild(style)
// Or swap themes dynamically:
function setTheme(name: ThemeName) {
document.querySelector(':root')!.classList.remove('theme-nord', 'theme-forest', 'theme-dracula')
document.querySelector(':root')!.classList.add(`theme-${name}`)
}
// Each theme is available individually or via the themes map:
console.log(themes.nord === nord) // true
console.log(Object.keys(themes)) // ['nord', 'forest', 'dracula']Theme Palettes
| Theme | Background | Accent | Vibe | Best for | |-------|-----------|--------|------|----------| | Nord | Light cool (#eceff4) | Blue (#5e81ac) | Clean, professional | Dashboards, admin panels | | Forest | Warm cream (#f4f1ea) | Green (#3d6b3c) | Earthy, organic | Content sites, SaaS | | Dracula | Dark (#282a36) | Purple (#bd93f9) | Modern, dark mode | Developer tools, terminals |
Styled Components
Each theme provides CSS for these semantic class conventions (matching sure-factor output):
| Component | Class prefix | Elements |
|-----------|-------------|----------|
| Form | .sure-form | __field, __label, input/select/textarea, __error, __help |
| Modal | .sure-modal__form | __field, __label, input/select/textarea, __error, __help |
| Table | .sure-table | __header, __cell, __error, __help |
| CRUD | .sure-crud | __field, __label, __error, __help |
| Search | .sure-search | __input, __label, __error, __help |
| Buttons | .btn-primary | Accent background, hover, focus ring |
| Buttons | .btn-secondary | Ghost style with border |
Plus form states: input:focus, [aria-invalid="true"], input:disabled.
Notifications
Four notification modes in one consistent API. Levels: error, success, info.
import { showNotification, clearNotifications } from '@shing.wong/sure-ui'
// Inline — field-level error, appended to the field container
showNotification({ id: '1', message: 'Required', level: 'error', mode: 'inline', field: 'email' })
// Toast — top-right popup, auto-dismiss
showNotification({ id: '2', message: 'Saved!', level: 'success', mode: 'toast' })
// Status bar — top banner, auto-dismiss
showNotification({ id: '3', message: 'Server error', level: 'error', mode: 'statusBar' })
// Side panel — right-side error list, click to scroll to field
showNotification({ id: '4', message: 'Email is invalid', level: 'error', mode: 'sidePanel', field: 'email' })Notification API
showNotification({
id: string // unique identifier
message: string // display text
level: 'error' | 'success' | 'info'
mode: 'inline' | 'toast' | 'statusBar' | 'sidePanel'
field?: string // scroll target (inline/toast/sidePanel)
duration?: number // auto-dismiss ms (default: 4000)
})
clearNotifications(mode?) // remove all, or filter by modeNotification behavior
| Mode | Position | Auto-dismiss | Click action |
|------|----------|-------------|--------------|
| inline | Inside field container | No | — |
| toast | Top-right fixed | Yes (4s) | Scrolls to field |
| statusBar | Top of page (banner) | Yes (4s) | — |
| sidePanel | Right-side panel | No | Scrolls to field |
Architecture
┌──────────────────────────────────────────────┐
│ sure-ui │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Themes │ │ Notifications │ │
│ │ ┌──────────┐ │ │ ┌────────────────┐ │ │
│ │ │ Nord │ │ │ │ inline │ │ │
│ │ │ Forest │ │ │ │ toast │ │ │
│ │ │ Dracula │ │ │ │ statusBar │ │ │
│ │ └──────────┘ │ │ │ sidePanel │ │ │
│ │ ~4 KB CSS │ │ └────────────────┘ │ │
│ └──────────────┘ │ │ ~2 KB JS (runtime)│ │
│ │ └────────────────────┘ │
│ │ │
│ Output: CSS │ Output: DOM elements │
│ strings │ with BEM classes │
└─────────┬──────────┴────────────┬─────────────┘
│ │
│ sure-factor generated HTML
▼ ▼
<style>.sure-form…</style> <div class="toast">Usage with sure-factor
sure-ui is designed to pair with sure-factor. The generated code uses class conventions that sure-ui themes style out of the box.
import { generateForm } from 'sure-factor'
import { nord } from '@shing.wong/sure-ui'
import { showNotification } from '@shing.wong/sure-ui'
// Inject theme
const style = document.createElement('style')
style.textContent = nord
document.head.appendChild(style)
// Generate form HTML (from sure-factor)
const html = generateForm({ /* field configs */ })
document.body.innerHTML = html
// Wire up notifications
document.querySelector('form')?.addEventListener('submit', async (e) => {
e.preventDefault()
try {
await api.submit(formData)
showNotification({ id: 'save', message: 'Saved!', level: 'success', mode: 'toast' })
} catch (err) {
showNotification({ id: 'err', message: err.message, level: 'error', mode: 'inline', field: 'email' })
}
})Agent usage
AI coding assistants can use sure-ui to theme generated interfaces:
// Prompt: "Theme this sure-factor form with the Dracula theme"
import { dracula } from '@shing.wong/sure-ui'
const style = document.createElement('style')
style.textContent = dracula
document.head.appendChild(style)
// Prompt: "Add toast notifications on form submit"
import { showNotification } from '@shing.wong/sure-ui'
form.addEventListener('submit', async () => {
showNotification({ id: 'ok', message: 'Success', level: 'success', mode: 'toast' })
})Markdown Rendering
Render LLM responses with .sure-markdown class. Supports tables, code blocks, lists, headings, blockquotes, and links — all themed via CSS variables.
<div class="sure-markdown">
<h2>Electron</h2>
<p>An <strong>electron</strong> carries a <strong>negative charge</strong>.</p>
<table>
<tr><th>Feature</th><th>Value</th></tr>
<tr><td>Charge</td><td>Negative (−)</td></tr>
</table>
<pre><code>const e = new Electron()</code></pre>
</div>| Class | Purpose |
|-------|---------|
| .sure-markdown | Root container |
| .sure-markdown h2, h3, h4 | Headings |
| .sure-markdown table | Tables with bordered cells |
| .sure-markdown pre code | Code blocks |
| .sure-markdown ul li | Lists |
| .sure-markdown blockquote | Blockquotes |
| .sure-markdown a | Links |
Auth Forms
Login, register, and password-reset forms using .sure-auth__* CSS classes. Styled consistently across all three themes:
<form class="sure-auth__form">
<div class="sure-auth__header">
<h1>Sign In</h1>
</div>
<div class="sure-auth__field">
<label class="sure-auth__label">Email</label>
<input class="sure-auth__input" type="email">
</div>
<div class="sure-auth__field">
<label class="sure-auth__label">Password</label>
<input class="sure-auth__input" type="password">
</div>
<button class="sure-auth__btn">Sign In</button>
<div class="sure-auth__divider">or continue with</div>
<button class="sure-auth__btn--social">Sign in with Google</button>
<div class="sure-auth__footer">
<a href="#">Create an account</a>
</div>
</form>| Class | Purpose |
|-------|---------|
| .sure-auth__form | Form container |
| .sure-auth__input | Text/password/email input |
| .sure-auth__input--error | Input with validation error |
| .sure-auth__btn | Primary submit button |
| .sure-auth__btn--social | OAuth provider button |
| .sure-auth__divider | "or" separator with line |
| .sure-auth__error | Error message text |
| .sure-auth__alert | Info/success/error banner |
Development
git clone [email protected]:ShingWong/sure-ui.git
cd sure-ui
npm install
npm run build
npm test # 13 tests
npm run lint # tsc --noEmit