@noidmejs/atomkit
v0.8.0
Published
Headless, SSR-safe, atom-based visual builder with a declarative query language (AQL) + AI bridge. Every atom carries its own styling, data-binding, accessibility, analytics and security as plain data — author by DSL or AI (drag-and-drop editor on the roa
Maintainers
Readme
atomkit
Headless, SSR-safe, atom-based visual builder. Render a JSON document to React — on the server or the client — where every atom carries its own styling, data-binding, accessibility, analytics and security as plain data.
npm i @noidmejs/atomkitThe idea
A page is a tree of nodes. Each node is an atom (or a container of atoms) and is fully described by data — nothing about it is hard-coded in a component:
| Field | What it holds |
|-------|---------------|
| style | typography, colour, gradient, background, size, spacing, border, effects — plus responsive overrides |
| data | where content comes from — a static value, or an API call + JSON path |
| a11y | role, aria-label, aria-hidden, aria-describedby, alt, tabindex, lang |
| meta | tags, analytics tracking, and security / consent gating |
The renderer whitelists style properties and sanitises values (including the dimension props that reach an inline style), routes every URL through a scheme guard, and enforces each node's security rules — so a hand-authored or stale document cannot inject script or CSS, or leak protected content, through the render path.
A document is still trusted input in one respect: an
apidata binding is fetched client-side with the document's own method, headers and body, using the browser'ssame-origincredentials, and is not host-allow-listed —connect-src 'self'does not stop same-origin requests. Render untrusted or AI-generated documents on the server (stripDocument+ SSR), or strip their bindings first. See SECURITY.md.
Quick start
import { Render, defaultAtoms, type BuilderDocument } from '@noidmejs/atomkit';
const doc: BuilderDocument = {
version: 1,
root: [
{
id: 'h', type: 'heading',
props: { text: 'Powering the web from Hyderabad', level: 1 },
style: { color: '#0b1220', fontSize: 'clamp(2rem,5vw,3.4rem)', textAlign: 'center' },
},
{
id: 'cta', type: 'button',
props: { text: 'Get started', href: '/start' },
style: { color: '#fff', gradient: 'linear-gradient(103deg,#005DAB,#E31936)', borderRadius: '999px', paddingX: '22px', paddingY: '12px' },
meta: { analytics: { id: 'cta_top', event: 'cta_click' } },
},
],
};
export default function Page() {
return <Render document={doc} registry={defaultAtoms} />;
}Data binding — static or API
{
id: 'price', type: 'text',
data: { source: { kind: 'api', url: '/api/price', path: 'data.amount' }, bindTo: 'text' },
}Static values render on the server; API-bound nodes render their fallback, then
resolve on the client (your CSP connect-src governs which URLs may be hit).
Security, consent & PII — per atom
{ id: 'internal', type: 'text', props: { text: 'Roadmap' },
meta: { security: { protected: true, roles: ['admin'] } } }
{ id: 'email', type: 'text', props: { text: '[email protected]' },
meta: { security: { pii: true } } } // masked unless the viewer may see PII<Render document={doc} registry={defaultAtoms}
context={{ canViewProtected: true, roles: ['admin'], canViewPii: false, consent: { analytics: true } }} />Custom atoms + your design tokens
import { createBuilder, defaultAtoms } from '@noidmejs/atomkit';
const builder = createBuilder({
atoms: {
...defaultAtoms,
stat: { render: ({ props, style }) => <div style={style}><b>{String(props.value)}</b> {String(props.label)}</div>, label: 'Stat' },
},
tokens: { '--brand': '#005DAB' },
});
<Render document={doc} registry={builder.registry} />Status
v0.5 — the document model + SSR-safe renderer, ~19 atoms (layout / content /
media / disclosure), the AQL query language, the AI bridge, governance-at-egress
(stripDocument), and an a11y lint(). Compile to standalone React with
@noidmejs/atomkit-compiler
and connect to backends with
@noidmejs/atomkit-http.
The visual drag-and-drop editor is on the roadmap (not yet shipped).
Security & governance
See SECURITY.md. In short: no raw HTML / no eval; style + URL
whitelists; per-node protected / roles / pii / consent; PII masked by value
with subtree cascade — every non-structural prop, whatever its name or type, on
built-in and custom atoms alike; stripDocument(doc, ctx) enforces governance at
egress on the server; analytics attributes fail closed and require an explicit
consent.analytics === true. The host owns authentication, the
authoritative consent/role facts (RenderContext), the CSP, and
colour-contrast / focus / target-size. Report vulnerabilities via a GitHub Security
Advisory. Pre-1.0 and not yet independently pen-tested.
MIT © noidmejs
