Skip to content

Commit d874d26

Browse files
committed
ui: deliver shared redesign system across studio and generated apps
1 parent 91b31f2 commit d874d26

15 files changed

Lines changed: 563 additions & 142 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Execution backlog and current gap-tracking is in `AGENTS.md`.
2525
- Rollout decision doc: `docs/generated-app-test-rollout.md`
2626
- Generated app CI details: `docs/generated-app-ci.md`
2727
- Release acceptance checklist: `docs/release-checklist.md`
28+
- UI redesign system notes: `docs/ui-redesign-system.md`
2829

2930
## Builder CI baseline
3031

docs/generated-app-ci.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ This repo validates generated-app test scaffolds and canonical job-board behavio
3535
- generated contract scaffold execution
3636
- generated UI scaffold execution against live preview
3737
- end-to-end canonical job-board flow
38+
- presence of shared theme token source and network-status UX components in generated output
3839

3940
These checks back the required `integration-local` CI job in this repository.

docs/ui-redesign-system.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# UI Redesign System (Studio + Generated UI)
2+
3+
This repo now uses a shared token source for Studio and generated app UI.
4+
5+
## Shared token source
6+
7+
- Canonical token file: `packages/templates/next-export-ui/src/theme/tokens.json`
8+
- Generated Next UI consumes it via `packages/templates/next-export-ui/src/theme/index.ts`.
9+
- `th studio` loads the same JSON and maps values into CSS custom properties.
10+
11+
## Token groups
12+
13+
- `colors`: background, panel, border, text, primary/accent, success/danger
14+
- `radius`: small/medium/large corner radii
15+
- `spacing`: xs/sm/md/lg/xl rhythm
16+
- `typography`: display/body/mono stacks
17+
- `motion`: fast/base transition timings
18+
19+
## Runtime UX improvements
20+
21+
- Generated UI now exposes explicit wallet network mismatch recovery in `NetworkStatus`.
22+
- Faucet button provides actionable disabled reason when endpoint is unavailable.
23+
- Connect flow uses deployment chain context when available.
24+
25+
## Contribution guidance
26+
27+
- Keep visual primitives token-driven.
28+
- Avoid hardcoded ad-hoc colors/spacings in route components.
29+
- Add or update generated app tests when adding new critical UX surfaces.

packages/cli/src/index.ts

Lines changed: 108 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -272,44 +272,135 @@ function validateStudioFormState(formState: any): {
272272
};
273273
}
274274

275+
type SharedThemeTokens = {
276+
colors: {
277+
bg: string;
278+
bgAlt: string;
279+
panel: string;
280+
panelStrong: string;
281+
border: string;
282+
text: string;
283+
muted: string;
284+
primary: string;
285+
primaryStrong: string;
286+
accent: string;
287+
success: string;
288+
danger: string;
289+
};
290+
radius: { sm: string; md: string; lg: string };
291+
spacing: { xs: string; sm: string; md: string; lg: string; xl: string };
292+
typography: { display: string; body: string; mono: string };
293+
motion: { fast: string; base: string };
294+
};
295+
296+
function defaultSharedThemeTokens(): SharedThemeTokens {
297+
return {
298+
colors: {
299+
bg: '#06122b',
300+
bgAlt: '#0a1f45',
301+
panel: '#0f2958cc',
302+
panelStrong: '#103062',
303+
border: '#7eb8ff55',
304+
text: '#f3f8ff',
305+
muted: '#b6caea',
306+
primary: '#4cb1f7',
307+
primaryStrong: '#1e8fe0',
308+
accent: '#ffc700',
309+
success: '#12c26d',
310+
danger: '#ff5f63'
311+
},
312+
radius: { sm: '10px', md: '14px', lg: '20px' },
313+
spacing: { xs: '6px', sm: '10px', md: '16px', lg: '24px', xl: '36px' },
314+
typography: {
315+
display: '"Montserrat", "Avenir Next", "Segoe UI", sans-serif',
316+
body: '"Inter", "Segoe UI", sans-serif',
317+
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
318+
},
319+
motion: { fast: '120ms', base: '180ms' }
320+
};
321+
}
322+
323+
function loadSharedThemeTokens(): SharedThemeTokens {
324+
try {
325+
const templateDir = resolveNextExportUiTemplateDir();
326+
const tokenPath = path.join(templateDir, 'src', 'theme', 'tokens.json');
327+
if (!fs.existsSync(tokenPath)) return defaultSharedThemeTokens();
328+
return JSON.parse(fs.readFileSync(tokenPath, 'utf-8')) as SharedThemeTokens;
329+
} catch {
330+
return defaultSharedThemeTokens();
331+
}
332+
}
333+
334+
function renderStudioThemeCssVars(tokens: SharedThemeTokens): string {
335+
return [
336+
`--th-bg:${tokens.colors.bg}`,
337+
`--th-bg-alt:${tokens.colors.bgAlt}`,
338+
`--th-panel:${tokens.colors.panel}`,
339+
`--th-panel-strong:${tokens.colors.panelStrong}`,
340+
`--th-border:${tokens.colors.border}`,
341+
`--th-text:${tokens.colors.text}`,
342+
`--th-muted:${tokens.colors.muted}`,
343+
`--th-primary:${tokens.colors.primary}`,
344+
`--th-primary-strong:${tokens.colors.primaryStrong}`,
345+
`--th-accent:${tokens.colors.accent}`,
346+
`--th-success:${tokens.colors.success}`,
347+
`--th-danger:${tokens.colors.danger}`,
348+
`--th-radius-sm:${tokens.radius.sm}`,
349+
`--th-radius-md:${tokens.radius.md}`,
350+
`--th-radius-lg:${tokens.radius.lg}`,
351+
`--th-space-xs:${tokens.spacing.xs}`,
352+
`--th-space-sm:${tokens.spacing.sm}`,
353+
`--th-space-md:${tokens.spacing.md}`,
354+
`--th-space-lg:${tokens.spacing.lg}`,
355+
`--th-space-xl:${tokens.spacing.xl}`,
356+
`--th-font-display:${tokens.typography.display}`,
357+
`--th-font-body:${tokens.typography.body}`,
358+
`--th-font-mono:${tokens.typography.mono}`,
359+
`--th-motion-fast:${tokens.motion.fast}`,
360+
`--th-motion-base:${tokens.motion.base}`
361+
].join(';');
362+
}
363+
275364
function renderStudioHtml(): string {
276365
// Keep this local-first and dependency-free for fast startup in any repo clone.
366+
const themeTokens = loadSharedThemeTokens();
367+
const cssVars = renderStudioThemeCssVars(themeTokens);
277368
return `<!doctype html>
278369
<html lang="en">
279370
<head>
280371
<meta charset="utf-8" />
281372
<meta name="viewport" content="width=device-width, initial-scale=1" />
282373
<title>Token Host Studio (Local)</title>
283374
<style>
284-
:root { color-scheme: light; --bg:#0b1220; --panel:#111a2b; --muted:#9db0cc; --text:#edf3ff; --ok:#2bb673; --err:#f25f5c; --warn:#f2c14e; }
375+
:root { color-scheme: light; ${cssVars}; --ok:var(--th-success); --err:var(--th-danger); --warn:var(--th-accent); }
285376
* { box-sizing: border-box; }
286-
body { margin:0; font-family: ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; background: radial-gradient(circle at 10% 10%, #1f3559, #0b1220 55%); color: var(--text); }
377+
body { margin:0; font-family: var(--th-font-body); background: radial-gradient(circle at 8% 0%, #255bb688, transparent 42%), radial-gradient(circle at 88% 0%, #50b9fa66, transparent 36%), linear-gradient(155deg, var(--th-bg) 20%, var(--th-bg-alt) 100%); color: var(--th-text); }
287378
.wrap { max-width: 1400px; margin: 0 auto; padding: 20px; }
288379
.row { display:grid; grid-template-columns: 1.6fr 1fr; gap: 14px; }
289-
.panel { background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(0,0,0,0.12)); border:1px solid rgba(255,255,255,0.12); border-radius: 14px; padding: 14px; }
290-
.title { margin:0 0 10px 0; font-size: 18px; font-weight: 700; }
291-
.muted { color: var(--muted); font-size: 13px; }
292-
textarea { width:100%; min-height: 120px; border-radius: 10px; border:1px solid rgba(255,255,255,0.18); background: #0a1426; color: #eaf2ff; padding: 10px; font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; font-size: 13px; line-height: 1.35; }
293-
input[type=text], input[type=number], select { width: 100%; border-radius: 8px; border:1px solid rgba(255,255,255,0.18); background:#0a1426; color:#eaf2ff; padding: 8px; }
294-
label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
380+
.panel { background: linear-gradient(180deg, #0f2958cf, #0d234bd4); border:1px solid var(--th-border); border-radius: var(--th-radius-lg); padding: var(--th-space-md); box-shadow: 0 8px 32px #02122f4d; }
381+
.title { margin:0 0 10px 0; font-size: 22px; font-family: var(--th-font-display); font-weight: 700; }
382+
.muted { color: var(--th-muted); font-size: 13px; }
383+
textarea { width:100%; min-height: 120px; border-radius: var(--th-radius-sm); border:1px solid var(--th-border); background: #071b3f; color: var(--th-text); padding: 10px; font-family: var(--th-font-mono); font-size: 13px; line-height: 1.35; }
384+
input[type=text], input[type=number], select { width: 100%; border-radius: var(--th-radius-sm); border:1px solid var(--th-border); background:#071b3f; color:var(--th-text); padding: 8px; }
385+
label { display: block; font-size: 12px; color: var(--th-muted); margin-bottom: 4px; }
295386
.grid2 { display:grid; grid-template-columns: 1fr 1fr; gap:8px; }
296387
.grid3 { display:grid; grid-template-columns: 1fr 1fr 1fr; gap:8px; }
297-
.card { border:1px solid rgba(255,255,255,0.12); border-radius: 10px; padding: 8px; margin-top: 8px; background: rgba(0,0,0,0.2); }
388+
.card { border:1px solid var(--th-border); border-radius: var(--th-radius-sm); padding: 8px; margin-top: 8px; background: #0a2a5888; }
298389
.sectionTitle { font-size: 14px; font-weight: 700; margin-top: 10px; }
299390
.stack { display:flex; flex-direction:column; gap:8px; }
300391
.toolbar { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:10px; }
301392
.configList { display:flex; flex-direction:column; gap:8px; max-height:260px; overflow:auto; }
302-
.configRow { display:grid; grid-template-columns: 1fr auto; gap:8px; align-items:center; border:1px solid rgba(255,255,255,0.12); border-radius:8px; padding:8px; background: rgba(0,0,0,0.2); }
303-
.configPath { font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
304-
button { border:1px solid rgba(255,255,255,0.2); color:#fff; background:#1e3357; border-radius:8px; padding:8px 10px; cursor:pointer; }
305-
button:hover { filter: brightness(1.08); }
393+
.configRow { display:grid; grid-template-columns: 1fr auto; gap:8px; align-items:center; border:1px solid var(--th-border); border-radius: var(--th-radius-sm); padding:8px; background: #0a2a5888; }
394+
.configPath { font-family: var(--th-font-mono); font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
395+
button { border:1px solid var(--th-border); color:var(--th-text); background:#15407f; border-radius: var(--th-radius-sm); padding:8px 10px; cursor:pointer; transition: transform var(--th-motion-fast) ease, background var(--th-motion-base) ease; font-weight: 600; }
396+
button:hover { background:#1a4f9d; }
306397
.pill { display:inline-block; padding: 2px 8px; border-radius:999px; font-size: 12px; border:1px solid transparent; }
307-
.ok { color:#d8ffe9; background: rgba(43,182,115,.2); border-color: rgba(43,182,115,.45);}
308-
.err { color:#ffd6d6; background: rgba(242,95,92,.2); border-color: rgba(242,95,92,.45);}
309-
.warn { color:#fff6d5; background: rgba(242,193,78,.2); border-color: rgba(242,193,78,.45);}
398+
.ok { color:#d8ffe9; background: color-mix(in srgb, var(--th-success) 24%, transparent); border-color: color-mix(in srgb, var(--th-success) 45%, transparent);}
399+
.err { color:#ffd6d6; background: color-mix(in srgb, var(--th-danger) 24%, transparent); border-color: color-mix(in srgb, var(--th-danger) 45%, transparent);}
400+
.warn { color:#fff6d5; background: color-mix(in srgb, var(--th-accent) 24%, transparent); border-color: color-mix(in srgb, var(--th-accent) 45%, transparent);}
310401
ul { margin: 8px 0 0 18px; padding:0; }
311402
li { margin: 2px 0; }
312-
pre { white-space: pre-wrap; word-break: break-word; background:#0a1426; border:1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 10px; max-height: 280px; overflow:auto; }
403+
pre { white-space: pre-wrap; word-break: break-word; background:#071b3f; border:1px solid var(--th-border); border-radius: var(--th-radius-sm); padding: 10px; max-height: 280px; overflow:auto; }
313404
</style>
314405
</head>
315406
<body>

0 commit comments

Comments
 (0)