@autono/open-pages 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +108 -0
  3. package/bin.js +2 -0
  4. package/dist/build-TP72kiw7.js +35 -0
  5. package/dist/cli/bin.d.ts +1 -0
  6. package/dist/cli/bin.js +100 -0
  7. package/dist/config-C9or4m4J.d.ts +23 -0
  8. package/dist/config-L0IbktJ0.js +3135 -0
  9. package/dist/design-C13iz9_4.js +35 -0
  10. package/dist/dev-I2IyEh5W.js +97 -0
  11. package/dist/export-B-kBRWB1.js +4 -0
  12. package/dist/export-ic28osQP.js +131 -0
  13. package/dist/index.d.ts +56 -0
  14. package/dist/index.js +3 -0
  15. package/dist/locale/index.d.ts +24 -0
  16. package/dist/locale/index.js +881 -0
  17. package/dist/open-pages-plugin-D_DN_zjh.js +632 -0
  18. package/dist/preview-BrDuvgAN.js +21 -0
  19. package/dist/sync-Be_hxSOF.js +139 -0
  20. package/dist/sync-lKEXyPOh.js +3 -0
  21. package/dist/types-C5NCdDT-.d.ts +219 -0
  22. package/dist/update-CV62NhBT.js +37 -0
  23. package/dist/update-package-Cxt57pz7.js +152 -0
  24. package/dist/version-B6AiiGvT.js +26 -0
  25. package/dist/version-CeffgdfZ.js +3 -0
  26. package/dist/vite/index.d.ts +14 -0
  27. package/dist/vite/index.js +7 -0
  28. package/env.d.ts +59 -0
  29. package/package.json +106 -0
  30. package/skills/apply-comments/SKILL.md +86 -0
  31. package/skills/create-page/SKILL.md +92 -0
  32. package/skills/create-theme/SKILL.md +223 -0
  33. package/skills/current-page/SKILL.md +106 -0
  34. package/skills/page-authoring/SKILL.md +148 -0
  35. package/skills/page-authoring/references/assets-and-fonts.md +85 -0
  36. package/skills/page-authoring/references/html-pages.md +65 -0
  37. package/skills/page-authoring/references/interactivity.md +77 -0
  38. package/skills/page-authoring/references/layout-and-responsive.md +71 -0
  39. package/skills/page-authoring/references/typography-and-color.md +60 -0
  40. package/src/app/app.tsx +45 -0
  41. package/src/app/components/asset-view.tsx +1665 -0
  42. package/src/app/components/command/command-menu.tsx +227 -0
  43. package/src/app/components/command/command.tsx +142 -0
  44. package/src/app/components/command/home-command-menu.tsx +96 -0
  45. package/src/app/components/icon-tooltip.tsx +34 -0
  46. package/src/app/components/language-toggle.tsx +46 -0
  47. package/src/app/components/sidebar/folder-item.tsx +269 -0
  48. package/src/app/components/sidebar/icon-picker.tsx +61 -0
  49. package/src/app/components/sidebar/sidebar-footer.tsx +139 -0
  50. package/src/app/components/sidebar/sidebar.tsx +302 -0
  51. package/src/app/components/theme-toggle.tsx +66 -0
  52. package/src/app/components/themes/theme-detail.tsx +226 -0
  53. package/src/app/components/themes/themes-gallery.tsx +104 -0
  54. package/src/app/components/ui/badge.tsx +48 -0
  55. package/src/app/components/ui/button.tsx +93 -0
  56. package/src/app/components/ui/card.tsx +92 -0
  57. package/src/app/components/ui/context-menu.tsx +247 -0
  58. package/src/app/components/ui/dialog.tsx +155 -0
  59. package/src/app/components/ui/dropdown-menu.tsx +267 -0
  60. package/src/app/components/ui/input.tsx +25 -0
  61. package/src/app/components/ui/label.tsx +21 -0
  62. package/src/app/components/ui/popover.tsx +82 -0
  63. package/src/app/components/ui/progress.tsx +33 -0
  64. package/src/app/components/ui/scroll-area.tsx +53 -0
  65. package/src/app/components/ui/select.tsx +195 -0
  66. package/src/app/components/ui/separator.tsx +26 -0
  67. package/src/app/components/ui/slider.tsx +68 -0
  68. package/src/app/components/ui/sonner.tsx +48 -0
  69. package/src/app/components/ui/tabs.tsx +79 -0
  70. package/src/app/components/ui/textarea.tsx +22 -0
  71. package/src/app/components/ui/toggle-group.tsx +84 -0
  72. package/src/app/components/ui/toggle.tsx +45 -0
  73. package/src/app/components/ui/tooltip.tsx +75 -0
  74. package/src/app/favicon.ico +0 -0
  75. package/src/app/frame/inspect.ts +140 -0
  76. package/src/app/frame/main.tsx +91 -0
  77. package/src/app/frame.html +12 -0
  78. package/src/app/index.html +13 -0
  79. package/src/app/lib/asset-filter.test.ts +176 -0
  80. package/src/app/lib/asset-filter.ts +80 -0
  81. package/src/app/lib/assets.ts +257 -0
  82. package/src/app/lib/design.ts +58 -0
  83. package/src/app/lib/folders.ts +239 -0
  84. package/src/app/lib/frame.ts +38 -0
  85. package/src/app/lib/locale-store.ts +67 -0
  86. package/src/app/lib/page-thumb.tsx +46 -0
  87. package/src/app/lib/pages.ts +28 -0
  88. package/src/app/lib/sdk.ts +43 -0
  89. package/src/app/lib/themes.ts +22 -0
  90. package/src/app/lib/use-locale.ts +8 -0
  91. package/src/app/lib/use-page-module.ts +48 -0
  92. package/src/app/lib/use-page-titles.ts +29 -0
  93. package/src/app/lib/use-restart-server.ts +78 -0
  94. package/src/app/lib/utils.test.ts +25 -0
  95. package/src/app/lib/utils.ts +6 -0
  96. package/src/app/main.tsx +14 -0
  97. package/src/app/routes/assets.tsx +9 -0
  98. package/src/app/routes/home-shell.tsx +252 -0
  99. package/src/app/routes/home.tsx +846 -0
  100. package/src/app/routes/page.tsx +329 -0
  101. package/src/app/routes/themes.tsx +34 -0
  102. package/src/app/styles.css +405 -0
  103. package/src/app/virtual.d.ts +53 -0
  104. package/src/locale/en.ts +218 -0
  105. package/src/locale/format.ts +12 -0
  106. package/src/locale/index.ts +6 -0
  107. package/src/locale/ja.ts +221 -0
  108. package/src/locale/types.ts +215 -0
  109. package/src/locale/zh-cn.ts +218 -0
  110. package/src/locale/zh-tw.ts +218 -0
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: page-authoring
3
+ description: Technical reference for writing or editing open-pages pages — file contract, Tailwind via `className`, layout and responsive breakpoints, web type scale, interactivity with hooks and state, assets and fonts, plain `index.html` pages, and the self-review checklist. Consult this whenever you are about to write or modify any file under `pages/<id>/`, including from inside the `create-page` or `apply-comments` workflows, or for any ad-hoc page edit. Triggers on phrases like "edit the page", "fix the layout", "change the palette", "add a section", "make it responsive", "add a form", "investigate the page framework", "how do pages work here".
4
+ ---
5
+
6
+ # Authoring open-pages pages
7
+
8
+ This skill is the **technical reference** for everything that happens inside `pages/<id>/`. It does not own a workflow:
9
+
10
+ - `create-page` owns "build a new page" — it asks the user scoping questions, then delegates the *how* to this skill.
11
+ - `apply-comments` owns "process inspector markers" — it finds markers and applies edits, but the edits themselves follow the rules here.
12
+ - `current-page` resolves deictic references ("this page", "this element") to a concrete `pageId` + selection. Consult it **first** when the user references the current page without naming it, then come back here for how to edit it.
13
+ - Any ad-hoc page edit (manual tweak, one-off fix) should also consult this skill before touching the file.
14
+
15
+ A page here is a **real web page**: one React component rendered into a real browser document. The workspace previews it live in an iframe, the inspector maps clicks back to source lines, and `open-pages export` turns it into a static folder you can deploy anywhere. Your job is a good web page; the runtime's job is preview, comments, and export.
16
+
17
+ ## Topic references
18
+
19
+ Details live under `references/` in this skill. **Read the relevant file before using the feature**:
20
+
21
+ | Topic | Read before | File |
22
+ | --- | --- | --- |
23
+ | Layout + responsive | any multi-column layout, nav, hero, grid; anything that must work on mobile | `references/layout-and-responsive.md` |
24
+ | Typography + color | picking a type scale or palette, dark backgrounds, contrast | `references/typography-and-color.md` |
25
+ | Interactivity | state, forms, tabs, toggles, anything with an event handler | `references/interactivity.md` |
26
+ | Assets + fonts | images, icons, custom fonts, Google Fonts | `references/assets-and-fonts.md` |
27
+ | Plain HTML pages | a page authored as `index.html` instead of React | `references/html-pages.md` |
28
+
29
+ ## Hard rules
30
+
31
+ - Put the page under `pages/<kebab-case-id>/`.
32
+ - Entry is `pages/<id>/index.tsx` (or `pages/<id>/index.html` for a plain HTML page — see `references/html-pages.md`).
33
+ - Do **not** touch `package.json`, `open-pages.config.ts`, or other pages.
34
+ - Do not add dependencies. Only `react`, `react-dom`, and `@autono/open-pages` (types) are available, plus browser APIs.
35
+ - A page is `index.tsx` plus, optionally, `components/*.tsx`, `styles.css`, and `assets/` for its images and fonts — all inside `pages/<id>/`. Shared assets live in the root `assets/` folder and import via `@assets/...`. No `README.md`, no config files.
36
+ - Style with Tailwind utilities on `className`. Tailwind is preconfigured and scans `pages/` and `themes/`; there is nothing to set up.
37
+
38
+ ## File contract
39
+
40
+ ```tsx
41
+ // pages/<id>/index.tsx
42
+ import type { PageMeta } from '@autono/open-pages';
43
+ import { useState } from 'react';
44
+
45
+ export const meta: PageMeta = {
46
+ title: 'Meridian — Launch',
47
+ description: 'Meridian turns your analytics into weekly decisions.',
48
+ createdAt: '2026-08-28T12:00:00.000Z',
49
+ };
50
+
51
+ export default function Launch() {
52
+ const [open, setOpen] = useState(false);
53
+ return (
54
+ <main className="min-h-screen bg-white text-slate-900 antialiased">
55
+ {/* sections */}
56
+ </main>
57
+ );
58
+ }
59
+ ```
60
+
61
+ - `export default` is **one zero-prop React component** — the whole page. It owns the viewport: set the page background and text color on the root element (`min-h-screen bg-… text-…`).
62
+ - `meta.title` (optional) becomes the browser tab title and the workspace card label. Default is the folder name.
63
+ - `meta.description` (optional) becomes `<meta name="description">` in the exported HTML.
64
+ - `meta.theme` (optional) marks the page as built from a theme under `themes/`. The id must match a `<id>.md` basename. Omit if not derived from a registered theme.
65
+ - `meta.createdAt` is an **ISO 8601 string literal** set once when the page is scaffolded — **immediately before writing the file, run `node -e "console.log(new Date().toISOString())"` via Bash and paste the exact output**. Must stay a plain string literal (the framework reads it via regex, never by evaluating the module).
66
+ - Hooks, state, event handlers, `fetch`, `window`, client-side routing: all allowed. This is a browser. See `references/interactivity.md` for the constraints that still apply (no `window` at module top level, StrictMode double-invokes effects).
67
+
68
+ ## Styling: Tailwind on `className`
69
+
70
+ Write the HTML you already know — `main`, `header`, `nav`, `section`, `h1`–`h3`, `p`, `ul`/`li`, `button`, `a`, `form`, `table` — styled with **Tailwind v4 utilities via `className`**:
71
+
72
+ ```tsx
73
+ <section className="mx-auto max-w-5xl px-6 py-20">
74
+ <h2 className="text-3xl font-bold tracking-tight">Pricing</h2>
75
+ <p className="mt-3 max-w-xl text-lg text-slate-600">Simple plans that grow with you.</p>
76
+ </section>
77
+ ```
78
+
79
+ - `className`, never a `tw` prop. Arbitrary values are fine (`text-[15px]`, `bg-[#0b0b10]`, `max-w-[72ch]`).
80
+ - Responsive variants are the default tool: `grid sm:grid-cols-2 lg:grid-cols-3`. Mobile-first — unprefixed utilities are the mobile layout.
81
+ - State variants for interactive elements: `hover:`, `focus-visible:`, `disabled:`, `aria-pressed:`.
82
+ - Use inline `style={{ … }}` only for values Tailwind cannot express (computed positions, CSS variables from data).
83
+ - A page may import its own stylesheet (`import './styles.css'`) for keyframes, complex selectors, or a font `@import`. Keep it small; utilities first.
84
+ - Preflight (Tailwind's reset) is applied inside the page, so headings and buttons start unstyled — set sizes and weights explicitly.
85
+
86
+ ## Web type scale and color
87
+
88
+ | Element | Size |
89
+ | --- | --- |
90
+ | Hero heading | 48–72px (`text-5xl`–`text-7xl`), tight leading + tracking |
91
+ | Section heading | 24–36px (`text-2xl`–`text-4xl`) |
92
+ | Body | 16–18px (`text-base`–`text-lg`), `leading-relaxed` |
93
+ | Secondary / captions | 13–14px (`text-sm`), muted color |
94
+ | Labels, eyebrows | 11–12px (`text-xs`), `uppercase tracking-widest` |
95
+
96
+ - One page = one palette: a background, a text color, a muted text color, one accent, one border tint. Hold it for the whole page.
97
+ - Contrast: body text must pass 4.5:1 against its background; muted text on dark backgrounds is `text-white/60`, not `text-white/30`.
98
+ - Details in `references/typography-and-color.md`.
99
+
100
+ ## Data rows vs designed repeats
101
+
102
+ - **Repeated data belongs in a `.map` over a data array** — pricing tiers, feature lists, testimonials pulled from a typed const at the top of the file; keep the row JSX in the map body. A comment on any row means "this row template".
103
+ - **Designed repeats that differ (hero vs. secondary CTA, three distinct feature panels with custom art) are explicit instances** of a small helper component defined in the same file or under `components/`. The inspector targets source JSX; explicit instances give each block its own address, so "make the middle one green" is one edit, not three.
104
+
105
+ ## Editing an existing page
106
+
107
+ Locate the section first instead of reading the whole file:
108
+
109
+ ```bash
110
+ grep -n '<section\|<h[12]\|<header\|<footer' pages/<id>/index.tsx
111
+ ```
112
+
113
+ Landmarks and headings anchor sections; read the target range with `offset` + `limit`. Read the whole file when auditing palette or restructuring.
114
+
115
+ ## Themes
116
+
117
+ If `themes/<id>.md` exists and the page is meant to follow it, **the theme file overrides the defaults in this skill** — its palette, typography, and fixed components are authoritative. Read the theme before applying anything else here. Themes are produced by the `create-theme` skill.
118
+
119
+ ## Runtime behavior you get for free
120
+
121
+ - Home lists every folder under `pages/`; cards show a live, scaled-down preview of the real page.
122
+ - The page view at `http://localhost:5173/p/<id>` renders the page in an iframe with **Desktop / Tablet (820px) / Mobile (390px)** viewport toggles, a reload button, and **Open** (the page by itself in a new tab).
123
+ - **Inspect mode** (toolbar button or `i`): the user clicks any element, sees its tag and source line, and can leave a comment that lands in your source as an `@page-comment` marker (see `apply-comments`). The current selection is always in `node_modules/.open-pages/current.json` (see `current-page`).
124
+ - Hot reload: save any file under `pages/<id>/` and the preview updates in place.
125
+ - `open-pages export <id>` builds `export/<id>/` — `index.html` plus hashed assets with relative URLs. Drop the folder on Netlify, Vercel, Cloudflare Pages, GitHub Pages, or S3.
126
+
127
+ ## Self-review before finishing
128
+
129
+ - [ ] `pages/<id>/index.tsx` default-exports **one** component; `meta` has `title` + fresh `createdAt` literal.
130
+ - [ ] Root element sets `min-h-screen`, background, and text color; the page has a `<main>` landmark.
131
+ - [ ] Preview it at `http://localhost:5173/p/<id>` (or ask the user to). No error banner, no console errors.
132
+ - [ ] Check the **Mobile** viewport: no horizontal overflow, nav collapses or wraps, grids stack, text sizes step down.
133
+ - [ ] Every `className` is Tailwind v4 the scanner will see (literal strings, no runtime-concatenated utility names).
134
+ - [ ] Interactive elements are real `<button>`/`<a>`/`<input>` elements with visible `focus-visible:` styles and labels; images have `alt`.
135
+ - [ ] One coherent palette and type scale across the page; contrast holds on dark sections.
136
+ - [ ] Designed repeats are explicit component instances; data lists are a `.map` over a typed const.
137
+ - [ ] No `window`/`document` access at module top level; effects clean up.
138
+ - [ ] Nothing outside `pages/<id>/` was edited.
139
+
140
+ ## Anti-patterns
141
+
142
+ - ❌ `tw` props, `pageOptions`, fixed-size "page" `<div>`s, or any document/slide thinking. This is a scrolling web page.
143
+ - ❌ Desktop-only layouts: absolute pixel positioning, fixed widths on the root, `grid-cols-3` with no `sm:` fallback.
144
+ - ❌ Building class names at runtime (`` `text-${size}` ``) — Tailwind only generates utilities it can read literally. Map to full class strings instead.
145
+ - ❌ `<div onClick>` where a `<button>` belongs; `<a>` without `href`; icon buttons without `aria-label`.
146
+ - ❌ Tiny typography (11px body). Web body is 16px+.
147
+ - ❌ Global CSS resets or `body {}` rules in `styles.css` that fight Preflight — style the root element instead.
148
+ - ❌ Installing packages, editing `package.json`/config/other pages, adding a `README.md` to the page folder.
@@ -0,0 +1,85 @@
1
+ # Assets and fonts
2
+
3
+ Both images and fonts follow the same rule: put the file under
4
+ `pages/<id>/assets/`, import it, and reference the imported value. Vite
5
+ resolves the import to a URL in the preview and to a hashed file in the
6
+ export.
7
+
8
+ ## Images
9
+
10
+ ```tsx
11
+ import hero from './assets/hero.png';
12
+
13
+ <img src={hero} alt="Dashboard showing the weekly brief" width={1200} height={720} className="w-full rounded-xl" />
14
+ ```
15
+
16
+ - **Always give `width` and `height`** (intrinsic pixels) so the browser
17
+ reserves space and the layout does not shift while loading. Let CSS
18
+ (`w-full h-auto`) size it visually.
19
+ - **Always give `alt`**: descriptive for content images, `alt=""` for purely
20
+ decorative ones.
21
+ - Below-the-fold images: `loading="lazy"`. The hero image: eager, and keep it
22
+ reasonably sized (≤ 1600px wide, WebP or JPEG).
23
+ - Page-local images live in `pages/<id>/assets/`; images shared across pages
24
+ live in the root `assets/` folder and import via `@assets/...`.
25
+ - Inline `<svg>` for logos, icons, and simple illustrations — crisp at any
26
+ size and styleable with `fill-current`/`stroke-current`. Icon-only buttons
27
+ need `aria-label`.
28
+ - Remote images (`https://…`) work but make the export depend on that host.
29
+ Prefer local files; ask the user for their assets rather than inventing
30
+ stock imagery.
31
+
32
+ ## Fonts
33
+
34
+ The default is the system font stack — fast, no files, always correct.
35
+ Register a font only when the user names one or supplies files.
36
+
37
+ ### Google Fonts
38
+
39
+ Render the link tags at the top of the page component:
40
+
41
+ ```tsx
42
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
43
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
44
+ <link
45
+ rel="stylesheet"
46
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
47
+ />
48
+ ```
49
+
50
+ Then use it: `className="font-[Inter,ui-sans-serif,system-ui,sans-serif]"` on
51
+ the root element, or in the page's `styles.css`:
52
+
53
+ ```css
54
+ @import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600&display=swap');
55
+ ```
56
+
57
+ ### Self-hosted
58
+
59
+ ```css
60
+ /* pages/<id>/styles.css */
61
+ @font-face {
62
+ font-family: 'Satoshi';
63
+ src: url('./assets/Satoshi-Variable.woff2') format('woff2');
64
+ font-weight: 300 900;
65
+ font-display: swap;
66
+ }
67
+ ```
68
+
69
+ `import './styles.css'` in `index.tsx`, then `font-[Satoshi,sans-serif]`.
70
+ Always include a fallback family.
71
+
72
+ ## Favicons and `<head>`
73
+
74
+ - `meta.title` and `meta.description` are written into the exported
75
+ `<head>`. Other head tags (favicon, Open Graph) are not supported yet; note
76
+ this to the user if they ask for them.
77
+
78
+ ## Anti-patterns
79
+
80
+ - ❌ `<img>` without `alt`, or without `width`/`height`.
81
+ - ❌ Multi-megapixel PNG screenshots as hero images.
82
+ - ❌ `fontFamily` naming a font that was never loaded — it silently falls
83
+ back and the page looks nothing like the theme.
84
+ - ❌ Loading four weights of two families for one heading.
85
+ - ❌ Hotlinking images from someone else's site.
@@ -0,0 +1,65 @@
1
+ # Plain HTML pages
2
+
3
+ A page folder may hold an `index.html` instead of a React module. The
4
+ workspace serves it as-is (through Vite, so scripts and styles are
5
+ processed), the home card previews it live, and `open-pages export <id>`
6
+ builds it into a static folder like any other page.
7
+
8
+ ## When to use it
9
+
10
+ - The user hands you existing HTML/CSS/JS to host or tweak.
11
+ - A tiny page where React is overhead: a one-screen announcement, a redirect
12
+ page, an embed target.
13
+ - Prototypes that intentionally avoid a framework.
14
+
15
+ For anything with real layout, state, or reuse, prefer `index.tsx`.
16
+
17
+ ## Contract
18
+
19
+ ```
20
+ pages/<id>/
21
+ index.html entry — must contain <title>
22
+ style.css optional, referenced with a relative href
23
+ main.js optional, referenced with a relative src (type="module" recommended)
24
+ assets/ images, fonts
25
+ ```
26
+
27
+ ```html
28
+ <!doctype html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8" />
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
33
+ <title>Launch week</title>
34
+ <link rel="stylesheet" href="./style.css" />
35
+ </head>
36
+ <body>
37
+ <main>…</main>
38
+ <script type="module" src="./main.js"></script>
39
+ </body>
40
+ </html>
41
+ ```
42
+
43
+ - Reference siblings with **relative** URLs (`./style.css`, `./assets/logo.svg`,
44
+ `main.js`). Absolute paths (`/style.css`) do not resolve in the preview or
45
+ the export.
46
+ - `<title>` is the card label in the workspace. There is no `meta` export;
47
+ the folder name is the page id.
48
+ - If both `index.tsx` and `index.html` exist, the React entry wins and the
49
+ HTML is treated as a plain asset.
50
+ - Tailwind is **not** wired into HTML pages. Write CSS, or use the React
51
+ entry when you want utilities.
52
+
53
+ ## What you do not get
54
+
55
+ - No inspector, no click-to-comment, no `@page-comment` markers. Feedback on
56
+ an HTML page comes as plain requests; apply them by editing the file.
57
+ - No hot module replacement for the HTML itself — the preview reloads the
58
+ frame on save. CSS and JS changes still hot-update.
59
+
60
+ ## Anti-patterns
61
+
62
+ - ❌ Absolute or root-relative asset paths.
63
+ - ❌ Inline `<script>` blobs of application logic — put them in `main.js`.
64
+ - ❌ Reaching for `index.html` when the request is "a landing page with a
65
+ pricing toggle". That is a React page.
@@ -0,0 +1,77 @@
1
+ # Interactivity
2
+
3
+ Pages are real React 18 components running in the browser, wrapped in
4
+ `StrictMode`. Everything React can do is available; the constraints below
5
+ keep pages preview-safe, export-safe, and inspectable.
6
+
7
+ ## State
8
+
9
+ ```tsx
10
+ const [tab, setTab] = useState<'monthly' | 'yearly'>('monthly');
11
+
12
+ <button
13
+ type="button"
14
+ onClick={() => setTab('yearly')}
15
+ aria-pressed={tab === 'yearly'}
16
+ className={tab === 'yearly' ? 'bg-slate-900 text-white' : 'text-slate-600'}
17
+ >
18
+ Yearly
19
+ </button>
20
+ ```
21
+
22
+ - Keep state local and small: tabs, toggles, accordions, filters, form values.
23
+ - Derive, do not duplicate: compute filtered lists with `useMemo` (or inline)
24
+ from the source array and the filter state.
25
+ - Toggle/tab buttons carry `aria-pressed` or `role="tablist"` semantics;
26
+ disclosure buttons carry `aria-expanded` and `aria-controls`.
27
+
28
+ ## Forms
29
+
30
+ - Real `<form>` with `onSubmit={(e) => { e.preventDefault(); … }}`.
31
+ - Every input has a `<label htmlFor>` (or `aria-label`), a `name`, and a
32
+ sensible `type`/`inputMode`/`autoComplete`.
33
+ - No backend exists in the workspace or the export. Either mirror submitted
34
+ values into local state ("Thanks, we'll be in touch"), or point the form at
35
+ a URL the user supplies (`action="https://formspree.io/…" method="post"`).
36
+ Never invent an endpoint.
37
+
38
+ ## Network and browser APIs
39
+
40
+ - `fetch`, `localStorage`, `IntersectionObserver`, `matchMedia` are fine —
41
+ inside effects or handlers, with loading and error states.
42
+ - Assume the exported page may run from `file://` or a static host with no
43
+ API: gate network features behind a URL the user supplied, and render
44
+ something meaningful without it.
45
+ - Access `window`/`document` only inside `useEffect` or event handlers.
46
+ Module-top-level `window.innerWidth` breaks the module import and the
47
+ export build.
48
+
49
+ ## Effects and StrictMode
50
+
51
+ - Effects run twice in development (StrictMode). Every subscription,
52
+ observer, timer, and listener must be cleaned up in the effect's return.
53
+ - No side effects in render. No `Date.now()` / `Math.random()` in render for
54
+ layout-affecting values; seed them in state via a lazy initializer.
55
+
56
+ ## Progressive enhancement
57
+
58
+ - Content must be visible with JavaScript disabled or before hydration: no
59
+ empty shells that only fill from an effect.
60
+ - Hover-only affordances need a keyboard/touch equivalent.
61
+ - Animations: `motion-safe:transition-*`; respect `prefers-reduced-motion`.
62
+
63
+ ## Routing inside a page
64
+
65
+ - Simple multi-view pages: a `view` state and conditional rendering, with
66
+ `<a href="#section">` anchors for in-page navigation.
67
+ - Hash routing is acceptable when the user asks for "pages" inside one export
68
+ (`window.location.hash`, read in an effect). Path-based routing needs a
69
+ host with rewrites — mention this to the user before choosing it.
70
+
71
+ ## Anti-patterns
72
+
73
+ - ❌ `useEffect` to compute something derivable from props/state.
74
+ - ❌ Fetching from an endpoint nobody set up.
75
+ - ❌ `document.querySelector` to mutate the DOM React owns.
76
+ - ❌ Global listeners without cleanup; timers that outlive the component.
77
+ - ❌ Interactive `<div>`s. Use `<button>`, `<a>`, `<input>`, `<select>`.
@@ -0,0 +1,71 @@
1
+ # Layout and responsive design
2
+
3
+ The viewer previews every page at three widths — **Desktop** (the full
4
+ viewport), **Tablet** (820px), and **Mobile** (390px). A page is done when all
5
+ three look intentional. Tailwind is mobile-first: unprefixed utilities are the
6
+ phone layout; `sm:` (640px), `md:` (768px), `lg:` (1024px), `xl:` (1280px)
7
+ layer larger layouts on top.
8
+
9
+ ## Containers
10
+
11
+ ```tsx
12
+ <section className="mx-auto max-w-6xl px-6 py-16 sm:py-24">…</section>
13
+ ```
14
+
15
+ - Center content with `mx-auto max-w-*`; keep horizontal padding (`px-6`) so
16
+ text never touches the viewport edge on mobile.
17
+ - Reading columns: `max-w-2xl` / `max-w-[65ch]` for prose. Marketing grids:
18
+ `max-w-5xl`–`max-w-7xl`.
19
+ - Vertical rhythm: `py-16` between sections on mobile, `sm:py-24` on desktop.
20
+ Sections separate by space or a hairline (`border-t border-slate-200`), not
21
+ both.
22
+
23
+ ## Grids and stacks
24
+
25
+ ```tsx
26
+ <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
27
+ {features.map((f) => <FeatureCard key={f.title} {...f} />)}
28
+ </div>
29
+ ```
30
+
31
+ - Default to a single column, add columns at `sm:`/`lg:`.
32
+ - `flex flex-col gap-4 sm:flex-row sm:items-center` for header rows that must
33
+ stack on phones.
34
+ - Use `gap-*`, not margins on children, for spacing inside grids and flex rows.
35
+
36
+ ## Navigation
37
+
38
+ - Desktop nav links: `hidden sm:flex gap-8`. On mobile either show a compact
39
+ set of links, or a `<button aria-expanded>` that toggles a stacked menu with
40
+ `useState`. Never let a nav overflow horizontally.
41
+ - Sticky headers: `sticky top-0 z-10 bg-white/90 backdrop-blur` — give them a
42
+ background so content does not bleed through.
43
+
44
+ ## Hero
45
+
46
+ - Heading `text-4xl sm:text-6xl lg:text-7xl`, `leading-[1.05] tracking-tight`.
47
+ - Constrain the heading (`max-w-3xl`) and the lede (`max-w-xl`) independently.
48
+ - CTA row: `flex flex-col gap-3 sm:flex-row` so buttons stack on mobile.
49
+
50
+ ## Full-viewport pages (dashboards, app UIs)
51
+
52
+ - Root: `min-h-screen` (not `h-screen`) so content can scroll.
53
+ - Sidebars: `hidden lg:block w-64` plus a mobile alternative (top tabs or a
54
+ toggle). Content area: `min-w-0 flex-1` so tables and code blocks can shrink.
55
+ - Wide content (tables, charts) scrolls inside its own `overflow-x-auto`
56
+ wrapper; the page body must never scroll horizontally.
57
+
58
+ ## Checklist
59
+
60
+ - [ ] Mobile: no horizontal scrollbar, every grid stacks, headings shrink.
61
+ - [ ] Tablet: two-column layouts where three would cramp.
62
+ - [ ] Desktop: content is capped by a `max-w-*`, not stretched edge to edge.
63
+ - [ ] Touch targets ≥ 40px tall on mobile (`py-2.5` on buttons and links).
64
+
65
+ ## Anti-patterns
66
+
67
+ - ❌ Fixed pixel widths on layout containers (`w-[1200px]`).
68
+ - ❌ `absolute` positioning for layout (fine for badges, overlays, decoration).
69
+ - ❌ `h-screen` on scrolling pages; `overflow-hidden` on `body`/root.
70
+ - ❌ Grids with no mobile fallback (`grid-cols-4` alone).
71
+ - ❌ Text sized only for desktop (`text-7xl` with no smaller base).
@@ -0,0 +1,60 @@
1
+ # Typography and color
2
+
3
+ ## Type scale (web)
4
+
5
+ | Role | Tailwind | Notes |
6
+ | --- | --- | --- |
7
+ | Hero heading | `text-5xl sm:text-7xl font-bold leading-[1.02] tracking-tight` | one per page |
8
+ | Page / section heading | `text-3xl sm:text-4xl font-bold tracking-tight` | |
9
+ | Subsection | `text-xl font-semibold` | |
10
+ | Lede | `text-lg sm:text-xl text-<muted>` | under the hero heading |
11
+ | Body | `text-base leading-relaxed` (16px) | never smaller for running copy |
12
+ | Secondary | `text-sm text-<muted>` | captions, metadata, table cells |
13
+ | Eyebrow / label | `text-xs font-semibold uppercase tracking-[0.2em]` | above headings |
14
+ | Code | `font-mono text-[0.9em]` with a subtle background | |
15
+
16
+ - Line-height: tight (`leading-[1.05]`–`leading-tight`) for display sizes,
17
+ `leading-relaxed` (1.625) for body.
18
+ - Measure: cap prose at `max-w-[65ch]` / `max-w-2xl`.
19
+ - Numbers in tables and prices: `tabular-nums`.
20
+ - Fonts: the system stack is the default and is fine. Register a display or
21
+ brand font only when the user names one — see `assets-and-fonts.md`.
22
+
23
+ ## Palette structure
24
+
25
+ One page, one palette. Name the roles up front and reuse them literally:
26
+
27
+ | Role | Light example | Dark example |
28
+ | --- | --- | --- |
29
+ | background | `bg-white` | `bg-[#0b0b10]` |
30
+ | surface | `bg-slate-50` / `bg-white` cards with `border-slate-200` | `bg-white/[0.04]` with `border-white/10` |
31
+ | text | `text-slate-900` | `text-white` |
32
+ | muted | `text-slate-600` (never lighter than `slate-500` on white) | `text-white/60` |
33
+ | accent | one hue, one shade for fills (`bg-indigo-600`) and one for text (`text-indigo-600`) | `bg-emerald-400 text-black` |
34
+ | border | `border-slate-200` | `border-white/10` |
35
+
36
+ - Define the palette as repeated utilities or as a small `const` map of class
37
+ strings when a page uses many surfaces. Do not build class names at runtime.
38
+ - Accent is for actions and one highlight per screen. Two accents means no
39
+ accent.
40
+ - Dark pages: prefer near-black (`#0b0b10`, `#111`) over pure black; lift
41
+ surfaces with translucent white (`bg-white/[0.03]`), not grey hex soup.
42
+
43
+ ## Contrast
44
+
45
+ - Body text ≥ 4.5:1 against its background; large headings ≥ 3:1.
46
+ - Muted text on white: `slate-500` is the floor. On dark: `white/60` is the
47
+ floor; `white/40` only for decorative metadata.
48
+ - Accent text on white must be a 600+ shade (`indigo-600`, `emerald-700`).
49
+ `emerald-400` text on white fails.
50
+ - Links inside prose: underline or a clearly different color, not color alone
51
+ at low contrast.
52
+
53
+ ## Anti-patterns
54
+
55
+ - ❌ Body text under 16px, or `text-xs` for anything the user must read.
56
+ - ❌ Three font families. Two is the ceiling (display + body).
57
+ - ❌ Pure black on pure white for long text (`text-slate-900` on white reads
58
+ better); pure white on pure black for dark pages.
59
+ - ❌ Gradient text, drop shadows, and glows on every heading. One flourish, once.
60
+ - ❌ Palette drift: a new grey shade every section.
@@ -0,0 +1,45 @@
1
+ import config from 'virtual:open-pages/config';
2
+ import { BrowserRouter, Route, Routes } from 'react-router-dom';
3
+ import { Toaster } from './components/ui/sonner';
4
+ import { useLocale } from './lib/use-locale';
5
+ import { AssetsPage } from './routes/assets';
6
+ import { Home } from './routes/home';
7
+ import { HomeShell } from './routes/home-shell';
8
+ import { PageView } from './routes/page';
9
+ import { ThemeDetailPage, ThemesGalleryPage } from './routes/themes';
10
+
11
+ export function App() {
12
+ return (
13
+ <BrowserRouter basename={import.meta.env.BASE_URL}>
14
+ <Routes>
15
+ {config.build.showPageBrowser ? (
16
+ <Route element={<HomeShell />}>
17
+ <Route path="/" element={<Home />} />
18
+ <Route path="/themes" element={<ThemesGalleryPage />} />
19
+ <Route path="/themes/:themeId" element={<ThemeDetailPage />} />
20
+ <Route path="/assets" element={<AssetsPage />} />
21
+ </Route>
22
+ ) : (
23
+ <Route path="/" element={<NotFound />} />
24
+ )}
25
+ <Route path="/p/:pageId" element={<PageView />} />
26
+ <Route path="*" element={<NotFound />} />
27
+ </Routes>
28
+ <Toaster />
29
+ </BrowserRouter>
30
+ );
31
+ }
32
+
33
+ function NotFound() {
34
+ const t = useLocale();
35
+ return (
36
+ <div className="grid h-screen place-items-center bg-background px-6 text-center text-foreground">
37
+ <div>
38
+ <p className="folio">{t.notFound.eyebrow}</p>
39
+ <h1 className="mt-2 font-heading text-2xl font-semibold tracking-tight">
40
+ {t.notFound.title}
41
+ </h1>
42
+ </div>
43
+ </div>
44
+ );
45
+ }