// Shared UI primitives + tokens for the Unblock Research ecosystem
// Aesthetic: scientific instrument × terminal. Off-white paper, near-black ink,
// acidic chartreuse accent. Inter Tight + JetBrains Mono + Instrument Serif.
const UB = {
paper: '#F2F0E8',
paperDeep: '#E9E6DC',
ink: '#0E0E10',
ink2: '#26252A',
ink3: '#5B5A60',
ink4: '#8A8990',
rule: '#1A1A1C',
rule2: 'rgba(14,14,16,0.12)',
rule3: 'rgba(14,14,16,0.06)',
accent: 'oklch(0.86 0.19 118)', // acidic chartreuse
accentDim: 'oklch(0.86 0.19 118 / 0.18)',
ok: 'oklch(0.72 0.13 155)',
warn: 'oklch(0.78 0.16 70)',
bad: 'oklch(0.62 0.20 25)',
info: 'oklch(0.66 0.13 240)',
serif: '"Instrument Serif", "Times New Roman", serif',
sans: '"Inter Tight", "Inter", system-ui, sans-serif',
mono: '"JetBrains Mono", "SF Mono", ui-monospace, monospace'
};
// ---- Layout: a uniform 1280×800 dapp frame ----------------------------------
// Auto-fits content: if children render taller than 800px, the inner stack is
// scaled down (origin top-left) so nothing spills out of the frame. Reads tweaks
// from window.__UB_TWEAKS for global overrides (density, font scale, dark, accent).
function Dapp({ children, dark: darkProp = false, frameless = false }) {
const innerRef = React.useRef(null);
const [scale, setScale] = React.useState(1);
const [tw, setTw] = React.useState(() => window.__UB_TWEAKS || {});
React.useEffect(() => {
const onTw = () => {
const t = window.__UB_TWEAKS || {};
if (t.accent && t.accent !== UB.accent) {
UB.accent = t.accent;
// recompute accentDim
UB.accentDim = t.accent + '2E'; // ~18% alpha hex suffix
}
setTw({ ...t });
};
window.addEventListener('__ub_tweaks', onTw);
onTw();
return () => window.removeEventListener('__ub_tweaks', onTw);
}, []);
React.useLayoutEffect(() => {
const el = innerRef.current;
if (!el) return;
const measure = () => {
if (tw.fitOverflow === false) { setScale(1); return; }
const sh = el.scrollHeight, sw = el.scrollWidth;
const sy = sh > 800 ? 800 / sh : 1;
const sx = sw > 1280 ? 1280 / sw : 1;
const next = Math.min(sx, sy, 1);
setScale(next < 0.999 ? next : 1);
};
measure();
const ro = new ResizeObserver(measure);
ro.observe(el);
return () => ro.disconnect();
}, [children, tw.fontScale, tw.density, tw.fitOverflow, tw.compactNav]);
const dark = tw.dark != null ? tw.dark : darkProp;
const fontScale = tw.fontScale || 1;
return (
{children}
);
}
// Top chrome: tiny system bar + dapp title bar
function ChromeBar({ dapp, code, status = 'ONLINE', user = 'malcolm.harriot.eth', balance = '12,840 UBR' }) {
return (
UNBLOCK/{dapp}{code}
UB-CHAIN · BLOCK 8,419,206 {status}
GAS 0.00021 UBR