Lucentive Labs · design-tooling
Loupe
ActiveLoupe is a config-driven decision-lock tool for visual or strategic choices. It renders typed options, records locked selections, and exports deterministic Markdown and JSON build briefs for the next human or agent pass.
The wedge
Config in. Live preview. Deterministic brief out.
A visual decision should end as something a build can reproduce, not a paragraph someone has to interpret. Loupe runs the decision through three steps and stops the drift.
Author the config
One typed file lists the decisions: groups of option tiles built from real image crops, palettes, type specimens, motion presets, and layout mocks. A Zod schema validates it.
Lock tiles in a live preview
Click a tile and a sticky preview recomposes from your picks — the same crop engine, theme, and type the real build will use. You are choosing against what you will ship, not a swatch in isolation.
Hand off a deterministic brief
Every lock keeps an export brief in sync: human-readable markdown and a machine-readable JSON of the same decisions. No timestamps, no absolute paths — the same picks always produce the same brief.
The change
From a decision log to a locked artifact
A decision log nobody can rerun
- Direction lives in a doc: prose notes, hex codes, and screenshots that drift from the build.
- “Make it warmer” means something different to the person who wrote it and the agent that reads it.
- Re-litigated next week, because nothing recorded which option won or why.
- The build pass guesses at the crop, the weight, the spacing — and guesses differently each time.
A brief the next pass reproduces
- Direction is a locked artifact: each decision is a tile you can see, click, and compose against the others.
- “Warmer” becomes a specific palette tile with exact colors, captured the moment it is chosen.
- The brief is the record of what won — a human or an agent reads the same ground truth.
- The exported crop, weight, and layout are exact values, so the next pass reproduces them.
What it is
Small surface, exact behavior
Five packages, one contract
Schema, headless core, vanilla DOM renderer, React adapter, and a static-HTML generator. Take only the layer you need; they all read the same config.
Exact crop engine
Specimens carry a normalized crop rectangle (fractions of the intrinsic image). The core computes exact cover math from the rect plus intrinsic dimensions, so the tile, the preview, and the export all show the same pixels.
Bring your own brand
A theme is a flat map of semantic tokens — background, surface, primary, ring, type. Pass it on the config or the component and the whole picker wears your skin. The three examples ship three different brands off the same machinery.
Agent-native contract
The config is a Zod schema, and one call emits its JSON Schema. An agent can author a valid decision lock the same way a person does, and a semantic validator catches missing assets, duplicate ids, and broken preview references before render.
Portable artifact or live React
Generate a portable static artifact, or drop <Loupe/> into a React 19 app. Asset-light configs can be one index.html; referenced assets are copied beside it. Both paths render the same structure, classes, and ARIA from the same source.
@lucentive-labs/loupe-schema
The Zod config contract a human or an agent fills, plus the JSON Schema emitter and a semantic validator.
@lucentive-labs/loupe-core
Framework-free headless core: SSR-safe store, deterministic preview and brief derivations, crop math, ARIA prop-getters. Its only runtime dependency is the schema package.
@lucentive-labs/loupe-dom
The canonical vanilla browser renderer — mount(), renderToString(), the crop engine, and styles.css. No framework.
@lucentive-labs/loupe-react
The React 19 adapter: <Loupe/> and useLoupe(), a thin useSyncExternalStore view over the core store.
@lucentive-labs/loupe-generator
Node-only generate(): emits deterministic index.html with JS and CSS inlined, copying referenced assets beside it when needed.
In code
Author it, render it, hand it off
The config is the contract. A person writes it by hand; an agent writes it from the JSON Schema. Either way, the same file drives the live React adapter and the portable artifact.
1 · Author a typed config. One file lists the groups, the option tiles, and your brand theme. Zod validates it.
// loupe.config.ts
import type { Config } from "@lucentive-labs/loupe-schema";
export const config: Config = {
version: 1,
title: "Northwind · Brand System",
// No image assets in this lock — palette / type specimens only.
assets: {},
// Bring your own brand: semantic tokens, kebab keys, no --loupe- prefix.
theme: {
"color-bg": "#14110e",
"color-primary": "#f6a13c",
"color-ring": "#f6a13c",
"font-sans": "Manrope, system-ui, sans-serif",
},
groups: [
{
id: "color",
title: "Color system",
prompt: "Which palette carries the brand?",
options: [
{
id: "amberInk",
label: "Amber on ink",
caption: "Warm, premium, high-contrast",
recommended: true,
specimen: {
kind: "palette",
colors: ["#14110e", "#f6a13c", "#ffc879", "#f7efe3"],
},
},
{
id: "monoSignal",
label: "Mono + one signal",
specimen: {
kind: "palette",
colors: ["#101216", "#3a4048", "#9aa3ad", "#f6a13c"],
},
},
],
},
],
// What the build pass must never do — travels with the brief.
banned: ["Generic SaaS gradient blobs.", "Cold corporate blue as primary."],
};
2 · Drop the React adapter into an app. <Loupe /> is a thin view over the headless core; import the shared stylesheet once.
// app/decide/page.tsx
"use client";
import { Loupe } from "@lucentive-labs/loupe-react";
import "@lucentive-labs/loupe-dom/styles.css";
import { config } from "./loupe.config";
export default function DecidePage() {
return (
<Loupe
config={config}
// Optional per-mount theme override.
theme={{ "color-primary": "#2fd4c4" }}
// Fires after every lock / clear / reset with the live selections.
onLockChange={(selections) => console.log(selections)}
/>
);
}
3 · Or generate a portable artifact. generate() validates, copies assets, and inlines JS and CSS into index.html. Referenced assets are copied beside it.
// generate.ts — Node, build-time
import { generate } from "@lucentive-labs/loupe-generator";
import { config } from "./loupe.config";
// Validates (structural + semantic), copies referenced assets, and inlines
// JS + CSS into index.html. Throws on an invalid config.
const { htmlPath, assets } = await generate(config, {
outDir: "out/decision-lock",
assetsDir: "design/boards",
});
console.log(`Artifact: ${htmlPath}`);
console.log(`Copied ${assets.length} asset(s) — open the file anywhere.`);
And the agent path: hand a model the JSON Schema, parse what it returns, and run the semantic validator before render.
// agent-authoring.ts — an agent fills the same contract
import {
toJsonSchema,
parseConfig,
validateConfig,
} from "@lucentive-labs/loupe-schema";
// 1. Hand the JSON Schema to the model as the authoring contract.
const schema = toJsonSchema();
// 2. The model returns a config object; parse enforces structure.
const config = parseConfig(modelOutput);
// 3. Semantic checks beyond shape: missing assets, duplicate ids,
// broken preview references. Empty array = ready to render.
const problems = validateConfig(config);
if (problems.length) throw new Error(problems.join("\n"));
Worked examples
One canonical example, two catalogue fixtures
Brand-starter is public and runnable in Loupe's canonical repository. Human-today and ias-control-plane are maintained in the private Labs repository, so they are shown here as catalogue orientation rather than public source links. A fixture demonstrates the tool and is not production-adoption proof.
examples/human-today
Photographic art direction
The image-heavy “Soft Data Body” lab, reproduced from a hand-built decision page as a typed config. Real moodboard crops drive imageCrop, layoutMock, and motion specimens.
Labs catalogue fixture · Private sourceexamples/ias-control-plane
A product surface, not a brand
The decisions a control plane actually makes: how a run is drawn, surface density, telemetry feel, status color. Asset-light by design, so the artifact is one tiny file that reviews fast.
Labs catalogue fixture · Private sourceexamples/brand-starter
A brand system from zero
Color, type, layout rhythm, and motion before any imagery exists. A warm amber-on-ink “Northwind” theme re-skins the same machinery — the template for deciding a system, not a photo.
Canonical Loupe example · View on GitHub →Adoption
Two ways to run it
Ship a portable artifact
Run generate() in a build step and get a portable index.html plus any copied assets. It opens from a file path or static host and carries the decision interface and export brief without a runtime package dependency.
Embed the live React adapter
Mount <Loupe/> inside an existing React 19 app for an always-current lab. It is a thin view over the headless core, so selections, the composed preview, and the export brief stay in sync with the same config the artifact uses. The demo above is this adapter, running here.
Verified use
Publicly observable today
Interactive Loupe demonstration on labs.lucentive.io
Public demo; no production-runtime claim is inferred from it.
Get started
Lock your next visual decision
Read the docs for the install and the config schema, or read the source. MIT-licensed and on GitHub.