@gemboss/website-ui 0.0.0-stage → 0.2.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 (44) hide show
  1. package/AGENTS.md +191 -0
  2. package/CHANGELOG.md +47 -0
  3. package/README.md +118 -2
  4. package/bin/gemboss-website.mjs +78 -0
  5. package/bin/lib/check.mjs +695 -0
  6. package/bin/lib/gap.mjs +189 -0
  7. package/bin/lib/init.mjs +99 -0
  8. package/bin/lib/upgrade.mjs +155 -0
  9. package/bin/lib/util.mjs +147 -0
  10. package/css/base.css +107 -0
  11. package/css/components.css +324 -0
  12. package/css/footer.css +53 -0
  13. package/css/header.css +231 -0
  14. package/css/layout.css +59 -0
  15. package/css/prose.css +119 -0
  16. package/css/sections.css +173 -0
  17. package/css/tokens.css +200 -0
  18. package/demo/index.html +703 -0
  19. package/demo/theme-example.html +133 -0
  20. package/docs/components.md +407 -0
  21. package/docs/markup-contract.md +148 -0
  22. package/docs/philosophy.md +69 -0
  23. package/docs/theming.md +96 -0
  24. package/js/website.js +186 -0
  25. package/package.json +50 -4
  26. package/skill/SKILL.md +49 -0
  27. package/system.json +122 -0
  28. package/templates/footer.html +40 -0
  29. package/templates/header.html +60 -0
  30. package/templates/page.html +134 -0
  31. package/templates/sections/cta.html +15 -0
  32. package/templates/sections/faq.html +24 -0
  33. package/templates/sections/features.html +32 -0
  34. package/templates/sections/hero.html +27 -0
  35. package/templates/sections/logos.html +15 -0
  36. package/templates/sections/pricing.html +51 -0
  37. package/templates/sections/prose.html +36 -0
  38. package/templates/sections/quote.html +13 -0
  39. package/templates/sections/showcase.html +25 -0
  40. package/templates/sections/split.html +27 -0
  41. package/templates/sections/stats.html +26 -0
  42. package/templates/sections/steps.html +17 -0
  43. package/website.css +1274 -0
  44. package/website.min.css +1 -0
@@ -0,0 +1,133 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Theme example · website UI kit</title>
7
+ <meta name="description" content="The same kit markup with a GemPages-style accent: only the --gw-accent variables change.">
8
+ <script>try{var t=localStorage.getItem('gw-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;650;700&display=swap">
12
+ <link rel="stylesheet" href="../website.css">
13
+ <style>
14
+ /* The whole brand theme: the accent family, light and dark, in one :root block (docs/theming.md).
15
+ Contrast checked: #1e5eff on #ffffff is 5.1:1, #6b98ff on #0d0d12 is 7.0:1. */
16
+ :root {
17
+ --gw-accent-light: #1e5eff;
18
+ --gw-accent-strong-light: #1547d1;
19
+ --gw-accent-soft-light: #eaf0ff;
20
+ --gw-accent-dark: #6b98ff;
21
+ --gw-accent-strong-dark: #a8c0ff;
22
+ --gw-accent-soft-dark: #16213d;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body class="gw-page">
27
+ <a class="gw-skip" href="#main">Skip to content</a>
28
+
29
+ <div class="gw-announce" role="region" aria-label="Announcement">Theme example: the markup of the GemBoss demo, with only the accent changed. <a href="index.html">Back to the full demo</a></div>
30
+ <header class="gw-header" data-gw-header>
31
+ <div class="gw-wrap gw-header__bar">
32
+ <a class="gw-brand" href="index.html"><svg class="gw-brand__mark" viewBox="0 0 26 26" aria-hidden="true"><rect width="26" height="26" rx="6" style="fill:var(--gw-accent)"/><path d="M8 8h10M8 13h10M8 18h6" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/></svg><span class="gw-brand__name">GemPages</span></a>
33
+ <nav class="gw-nav" aria-label="Main">
34
+ <ul class="gw-nav__list">
35
+ <li class="gw-nav__item"><a class="gw-nav__link" href="https://gemboss.ai/gempages" aria-current="page">Page builder</a></li>
36
+ <li class="gw-nav__item"><a class="gw-nav__link" href="https://gemboss.ai/gemthemes">Section library</a></li>
37
+ <li class="gw-nav__item"><a class="gw-nav__link" href="https://gemboss.ai/pricing">Pricing</a></li>
38
+ </ul>
39
+ </nav>
40
+ <div class="gw-header__actions">
41
+ <button class="gw-theme-toggle" type="button" data-gw-theme-toggle aria-label="Switch to dark theme"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="8"/><path d="M12 4a8 8 0 0 1 0 16z" fill="currentColor"/></svg></button>
42
+ <a class="gw-nav__link gw-header__signin" href="https://admin.gemboss.ai/login">Sign in</a>
43
+ <a class="gw-btn gw-btn--secondary gw-btn--sm" href="https://gemboss.ai/contact">Book a demo</a>
44
+ <a class="gw-btn gw-btn--sm" href="https://admin.gemboss.ai/signup">Start free</a>
45
+ <button class="gw-menu-toggle" type="button" aria-expanded="false" aria-controls="gw-mobile-menu" data-gw-menu-toggle aria-label="Open menu"><svg class="gw-menu-toggle__open" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg><svg class="gw-menu-toggle__close" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
46
+ </div>
47
+ </div>
48
+ <div class="gw-mobile-menu" id="gw-mobile-menu" hidden>
49
+ <nav class="gw-wrap" aria-label="Main, mobile">
50
+ <a class="gw-mobile-menu__top" href="https://gemboss.ai/gempages" aria-current="page">Page builder</a>
51
+ <a class="gw-mobile-menu__top" href="https://gemboss.ai/gemthemes">Section library</a>
52
+ <a class="gw-mobile-menu__top" href="https://gemboss.ai/pricing">Pricing</a>
53
+ <div class="gw-mobile-menu__actions">
54
+ <a class="gw-btn gw-btn--block" href="https://admin.gemboss.ai/signup">Start free</a>
55
+ <a class="gw-btn gw-btn--secondary gw-btn--block" href="https://gemboss.ai/contact">Book a demo</a>
56
+ <a class="gw-mobile-menu__link" href="https://admin.gemboss.ai/login">Sign in</a>
57
+ </div>
58
+ </nav>
59
+ </div>
60
+ </header>
61
+
62
+ <main id="main">
63
+ <section class="gw-hero" aria-labelledby="hero-h">
64
+ <div class="gw-hero__wash" aria-hidden="true"></div>
65
+ <div class="gw-wrap">
66
+ <p class="gw-label">Page builder</p>
67
+ <h1 id="hero-h"><span class="gw-dim">Describe it. Watch the page change.</span> Ship it.</h1>
68
+ <p class="gw-lead">The page builder in GemBoss is a chat beside your live page: ask for a section, a headline or a photo, and watch it change. Nothing reaches shoppers until you press Publish live.</p>
69
+ <div class="gw-actions">
70
+ <a class="gw-btn" href="https://admin.gemboss.ai/signup">Start free</a>
71
+ <a class="gw-btn gw-btn--secondary" href="https://gemboss.ai/contact">Book a demo</a>
72
+ </div>
73
+ </div>
74
+ </section>
75
+
76
+ <section class="gw-section gw-section--soft" aria-labelledby="how-h">
77
+ <div class="gw-wrap">
78
+ <div class="gw-section__head">
79
+ <p class="gw-label">How a change happens</p>
80
+ <h2 id="how-h"><span class="gw-dim">Ask, watch, publish.</span> You are the one who ships.</h2>
81
+ <div class="gw-actions">
82
+ <button class="gw-chip" type="button" aria-pressed="true">New section</button>
83
+ <button class="gw-chip" type="button" aria-pressed="false">Copy</button>
84
+ <button class="gw-chip" type="button" aria-pressed="false">Media</button>
85
+ <span class="gw-badge gw-badge--accent">Draft, not live yet</span>
86
+ </div>
87
+ </div>
88
+ <div class="gw-grid--3">
89
+ <div class="gw-card">
90
+ <h3>Ask</h3>
91
+ <p>Type it in plain words, or click a section and ask about that one.</p>
92
+ </div>
93
+ <div class="gw-card">
94
+ <h3>Watch</h3>
95
+ <p>It edits the page in front of you and checks how it looks.</p>
96
+ </div>
97
+ <a class="gw-card gw-card--link" href="https://gemboss.ai/gempages">
98
+ <h3>Publish</h3>
99
+ <p>Changes stay a draft until you press Publish live. Undo any step.</p>
100
+ <span class="gw-link gw-link--arrow">See the page builder</span>
101
+ </a>
102
+ </div>
103
+ </div>
104
+ </section>
105
+
106
+ <section class="gw-section gw-band--night" aria-labelledby="cta-h">
107
+ <div class="gw-wrap gw-cta">
108
+ <p class="gw-label">One engine underneath</p>
109
+ <h2 id="cta-h">Looks like your store. Because it is built from it.</h2>
110
+ <p class="gw-lead">Every page uses your storefront's sections and brand settings. Read the <a href="https://gemboss.ai/docs">docs</a> for the MCP tools.</p>
111
+ <div class="gw-actions">
112
+ <a class="gw-btn" href="https://admin.gemboss.ai/signup">Start free</a>
113
+ <a class="gw-btn gw-btn--secondary" href="https://gemboss.ai/contact">Book a demo</a>
114
+ </div>
115
+ </div>
116
+ </section>
117
+ </main>
118
+
119
+ <footer class="gw-footer">
120
+ <div class="gw-wrap">
121
+ <div class="gw-footer__bottom">
122
+ <span>© 2026 GemBoss. All rights reserved.</span>
123
+ <ul aria-label="Legal">
124
+ <li><a href="https://gemboss.ai/privacy">Privacy</a></li>
125
+ <li><a href="https://gemboss.ai/terms">Terms</a></li>
126
+ </ul>
127
+ </div>
128
+ </div>
129
+ </footer>
130
+
131
+ <script src="../js/website.js" defer></script>
132
+ </body>
133
+ </html>
@@ -0,0 +1,407 @@
1
+ # Components and sections
2
+
3
+ Every class in `website.css`, what it is for, its variants, a minimal example and what not to do with it. Open
4
+ `demo/index.html` to see all of them on one page, and start pages from `templates/`. The markup that the script
5
+ (`js/website.js`) relies on is fixed in `docs/markup-contract.md`; this file follows it.
6
+
7
+ Conventions: kit classes start with `gw-`, variants are `--name`, parts are `__name`, states set by the script are
8
+ `is-*`. Never style a `.gw-*` class in your site's CSS: if the kit's version is wrong for you, that is a gap
9
+ (`AGENTS.md` section 9).
10
+
11
+ Spacing between the children of text blocks is automatic: inside `.gw-section__head`, the hero, a split column,
12
+ `.gw-cta`, `.gw-card`, `.gw-feature` and `.gw-plan`, each child is 16px below the previous one, a `.gw-actions` row
13
+ 32px, a `.gw-list--check` 24px. Do not add margins to fix rhythm; reorder or remove content instead.
14
+
15
+ ## Page
16
+
17
+ | class | for |
18
+ |---|---|
19
+ | `.gw-page` | on `<body>`: the footer stays at the bottom of a short page |
20
+ | `.gw-skip` | the first element in `<body>`: "Skip to content", visible only on keyboard focus |
21
+
22
+ ```html
23
+ <body class="gw-page">
24
+ <a class="gw-skip" href="#main">Skip to content</a>
25
+ …
26
+ <main id="main">…</main>
27
+ ```
28
+
29
+ Don't remove the skip link, and don't leave out `id="main"`.
30
+
31
+ ## Type and text
32
+
33
+ | class | for |
34
+ |---|---|
35
+ | `h1`, `h2` (and `.gw-h1`, `.gw-h2`) | display sizes: h1 `clamp(40px, 4.6vw, 68px)`, h2 `clamp(32px, 3.4vw, 52px)`, weight 600, tracking −0.03em |
36
+ | `h3`, `h4` (and `.gw-h3`, `.gw-h4`) | 24px / 650 and 20px / 600 |
37
+ | `.gw-dim` | the muted first sentence of the house headline shape |
38
+ | `.gw-label` | the label above a heading: 13px, muted, led by the 8px accent square |
39
+ | `.gw-lead` | the one or two lines under a heading (18px, 20px in the hero, muted, max 60ch) |
40
+ | `.gw-note` | a short reassurance under actions (14px, muted) |
41
+ | `.gw-link` | an inline link in the accent, underlined |
42
+ | `.gw-link--arrow` | a standalone "go there" link with a → that moves 2px on hover |
43
+
44
+ ```html
45
+ <p class="gw-label">How it works</p>
46
+ <h2><span class="gw-dim">Give it a product.</span> Watch the engine work.</h2>
47
+ <p class="gw-lead">One or two lines, no more.</p>
48
+ <a class="gw-link gw-link--arrow" href="/funnel">See the funnel builder</a>
49
+ ```
50
+
51
+ Plain links inside `p`, `dd`, `td`, `figcaption` and check-list items get the link style without a class.
52
+ `.gw-h1`…`.gw-h4` exist so the visual size can differ from the heading level: keep one `<h1>` per page and headings in
53
+ order, then choose the look.
54
+
55
+ Don't: uppercase or letter-spaced labels; a label longer than a few words; the accent as body text; "Learn more" or
56
+ "Click here" as the whole link text; more than two lines under a heading.
57
+
58
+ ## Layout
59
+
60
+ | class | for |
61
+ |---|---|
62
+ | `.gw-wrap` | content width: 1120px max with 22px gutters |
63
+ | `.gw-wrap--narrow` | 780px max, for FAQ and long reading; use alone or with `.gw-wrap` |
64
+ | `.gw-section` | a section's vertical rhythm (`--gw-space-section`) |
65
+ | `.gw-section--soft` | on `--gw-bg-soft`, to separate a section without a line |
66
+ | `.gw-section--tight` | less padding (40px, 64px from 768px), for proof strips and follow-on tables |
67
+ | `.gw-section__head` | label + h2 + lead, max 660px; 40–48px above the content |
68
+ | `.gw-section__head--center` | the same, centred |
69
+ | `.gw-grid`, `.gw-grid--2`, `.gw-grid--3`, `.gw-grid--4` | equal columns: one on phones; 2 and 3 from 768px; 4 is 2 from 480px and 4 from 1080px |
70
+ | `.gw-actions` | a row of actions that wraps; on phones the hero and CTA buttons go full width |
71
+ | `.gw-band--night` | a dark band: the dark palette on the night ground, in both themes |
72
+
73
+ ```html
74
+ <section class="gw-section gw-section--soft">
75
+ <div class="gw-wrap">
76
+ <div class="gw-section__head">…</div>
77
+ <div class="gw-grid--3">…</div>
78
+ </div>
79
+ </section>
80
+ ```
81
+
82
+ Don't: more than three night bands on a page (the closing CTA does not count), or two in a row; a grey or cream page background (white or
83
+ `--gw-bg-soft` only); a grid with a single item (use a narrower wrap instead); fixed widths in px on content.
84
+
85
+ ## Components
86
+
87
+ ### Buttons `.gw-btn`
88
+
89
+ 40px tall, radius 8, 14px/600, sentence case, ink fill. Variants: `--secondary` (hairline, transparent), `--ghost`
90
+ (no frame), `--on-night` (light fill for a dark surface that is not a `.gw-band--night`), `--sm` (36px, with a 44px
91
+ hit area), `--lg` (48px, 16px type), `--block` (full width). On touch screens every button is at least 44px tall and
92
+ its label may wrap. Inside a night band the default button turns light by itself.
93
+
94
+ ```html
95
+ <div class="gw-actions">
96
+ <a class="gw-btn" href="/signup">Start free</a>
97
+ <a class="gw-btn gw-btn--secondary" href="/contact">Book a demo</a>
98
+ </div>
99
+ ```
100
+
101
+ Pressed (`:active`): the ink one step darker (the hover ink), and nothing moves; `--secondary` takes the soft tint and
102
+ `--ghost` the hairline tint.
103
+
104
+ **Loading.** While a `<button>` waits on the network (a form sent, an audit started), set `aria-busy="true"` on it and
105
+ remove it when the answer comes. The label stays in the markup, so the button keeps its width and its accessible name,
106
+ but it is not painted: a 16px ring in the button's own ink turns in its place, and under `prefers-reduced-motion` it is
107
+ a still ellipsis. `website.js` ignores presses on a busy button, by mouse or keyboard, so nothing is sent twice. When
108
+ the wait ends, say what happened in words (a `.gw-note` or a `role="status"` line next to it).
109
+
110
+ ```html
111
+ <button class="gw-btn" type="submit" aria-busy="true">Send the request</button>
112
+ ```
113
+
114
+ ```js
115
+ button.setAttribute('aria-busy', 'true'); // the request leaves
116
+ button.removeAttribute('aria-busy'); // the answer came
117
+ ```
118
+
119
+ Don't: an accent, gradient or wash fill; pill-shaped buttons; uppercase; two filled buttons side by side (one
120
+ `.gw-btn` per view, the second is `--secondary` or a link); a `<div>` with a click handler instead of `<a>`/`<button>`;
121
+ a loading class, your own spinner or `disabled` on a waiting button (`check` reports `busy-button`: `disabled` throws
122
+ the keyboard focus to the top of the page); a "Loading…" label in place of the real one.
123
+
124
+ ### Chips `.gw-chip`
125
+
126
+ A small selectable token, a pill. Active: `.is-active`, `aria-pressed="true"` or `aria-selected="true"` (accent
127
+ hairline on accent-soft). As a `<button>`, `<a>` or `<label>` it is 44px tall on touch screens.
128
+
129
+ ```html
130
+ <button class="gw-chip" type="button" aria-pressed="true">Serum</button>
131
+ ```
132
+
133
+ Don't: use a chip as the page's action; square it; more than about six in a row.
134
+
135
+ ### Segmented control `.gw-segmented`
136
+
137
+ One choice out of two to four, shown as one group: Monthly / Yearly on a pricing page, Run it yourself / We run it for
138
+ you. It is a radio group: one Tab stop, the arrow keys (and Home, End) move and choose, a click or Space chooses. The
139
+ look is the dashboard's (`@gemboss/ui` `.ui-seg`): one hairline group, radius 8, a hairline between options, the chosen
140
+ option on the accent tint, so it follows a brand's accent. 40px tall; on touch screens every option is at least 44px
141
+ and its label may wrap. `--block` stretches it to the row and shares the width equally.
142
+
143
+ What an option shows: any element with `data-gw-show="group:value"` (the group's `data-gw-segmented` name, the option's
144
+ `value`) is hidden unless that option is chosen. Put every version in the markup and `hidden` on all but the chosen
145
+ one's: without the script the page reads with the default choice, with it `website.js` swaps them. An element may
146
+ belong to several options (`data-gw-show="billing:monthly billing:quarterly"`). A choice also fires `gw:segmented` on
147
+ the group (it bubbles; `detail` is `{ name, value }`) for the page's own script, for example to move a "Recommended"
148
+ badge.
149
+
150
+ ```html
151
+ <div class="gw-segmented" role="radiogroup" aria-label="Billing period" data-gw-segmented="billing">
152
+ <button type="button" role="radio" aria-checked="true" value="monthly">Monthly</button>
153
+ <button type="button" role="radio" aria-checked="false" value="yearly">Yearly –20%</button>
154
+ </div>
155
+ <p class="gw-plan__price"><span data-gw-show="billing:monthly">$49</span><span data-gw-show="billing:yearly" hidden>$39</span> <small>/ month</small></p>
156
+ ```
157
+
158
+ Labels: one to three words, as short in every language as in English (gemboss.ai: "Tháng / Năm –20%", "按月 / 按年
159
+ –20%"). The chosen label does not change weight, so nothing moves. A discount in a label is the one billing gives.
160
+
161
+ Don't: links dressed as a segmented control (to go to another page, use links); panels of one page that the reader
162
+ should be able to see side by side; more than four options (use chips); a choice that changes nothing visible; two
163
+ groups with the same name on one page; the old `role="group"` with `aria-pressed` buttons (`check` reports
164
+ `segmented-markup`).
165
+
166
+ ### Badges `.gw-badge`
167
+
168
+ Small status, a pill, 12px. Variants: `--accent` (a test, the recommended plan), `--success` and `--warning` (state:
169
+ a coloured dot, ink words, because the state hues fail 4.5:1 as small text on a tint).
170
+
171
+ ```html
172
+ <span class="gw-badge gw-badge--success">Live</span>
173
+ ```
174
+
175
+ Don't: make a badge clickable; write a sentence in it; use success or warning for anything but state.
176
+
177
+ ### Cards `.gw-card`
178
+
179
+ A white box with a 1px hairline, radius 12, the resting shadow `--gw-shadow` (as the dashboard's cards), 24px padding
180
+ (32px from 768px). It sits: no hover lift. `--soft` is a tint and has no shadow.
181
+ Variants: `--soft` (on `--gw-bg-soft`), `--link` (the whole card is one `<a>`; the border darkens on hover).
182
+
183
+ ```html
184
+ <a class="gw-card gw-card--link" href="/mcp">
185
+ <h3>Open to your team and agents</h3>
186
+ <p>100+ MCP tools, so your team, your agency or your own AI can drive the engine.</p>
187
+ <span class="gw-link gw-link--arrow">See MCP and agents</span>
188
+ </a>
189
+ ```
190
+
191
+ Don't: put links or buttons inside a `--link` card (one card, one destination); a bigger shadow or a hover lift; radius
192
+ other than 12 (16 is for large panels: plans, quote).
193
+
194
+ ### Window `.gw-window`
195
+
196
+ A framed screen: `__bar` (three dots and where the screen lives), `__body` (16px padding, none around a single
197
+ `<img>`) and `__caption`. Use it around real product screenshots, or around a drawn mock-up built in the site's CSS with
198
+ the tokens.
199
+
200
+ `__caption` says what the numbers in the window are: 12px, muted, at the end of the window, the last child after
201
+ `__body` (a `<figcaption>` when the window is a `<figure>`). Under a drawn mock-up whose numbers are made up it says
202
+ **"Illustrative data."**, in exactly these words: vi "Dữ liệu minh hoạ.", zh "示意数据。". Add a second sentence only when
203
+ the mock-up shows a demo brand ("Vital Glow is a GemBoss demo brand."). Under real numbers it names their source
204
+ ("Measured on a live GemBoss store, 10 Oct 2026."). `check` warns about a drawn window with numbers and no caption
205
+ (`illustrative-caption`).
206
+
207
+ ```html
208
+ <div class="gw-window">
209
+ <div class="gw-window__bar">Funnels / Radiance Serum</div>
210
+ <div class="gw-window__body"><img src="/img/funnels.webp" alt="The funnel list with four steps" width="1200" height="750"></div>
211
+ <p class="gw-window__caption">Illustrative data. Vital Glow is a GemBoss demo brand.</p>
212
+ </div>
213
+ ```
214
+
215
+ Don't: put interactive controls inside a mock-up; show made-up numbers without the caption; write the caption in
216
+ other words ("Sample", "Demo numbers") or leave it in English on a translated page.
217
+
218
+ ### Icon `.gw-icon`
219
+
220
+ A 20px box for an inline stroke SVG (1.7–2px stroke, `currentColor`, `aria-hidden="true"`). Inside `.gw-feature` it
221
+ becomes a 40px tile with a hairline.
222
+
223
+ ```html
224
+ <span class="gw-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">…</svg></span>
225
+ ```
226
+
227
+ Don't: emoji as icons; filled multi-colour icons; icon-only buttons without `aria-label`.
228
+
229
+ ### Check list `.gw-list--check`
230
+
231
+ A `<ul>` whose items are led by an accent check mark.
232
+
233
+ ```html
234
+ <ul class="gw-list--check"><li>Checkout stays on Shopify</li><li>No overage fees</li></ul>
235
+ ```
236
+
237
+ Don't: more than five or six items; full paragraphs as items.
238
+
239
+ ### Table `.gw-table`
240
+
241
+ A wrapper around a plain `<table>`: hairline, radius 12, and it scrolls inside its own box when the table is wider
242
+ than the screen. `--compare`: centred values, a 640px minimum width and a sticky first column, for plan comparisons.
243
+ Give the wrapper `role="region"`, an accessible name and `tabindex="0"` so keyboard users can scroll it.
244
+
245
+ ```html
246
+ <div class="gw-table gw-table--compare" role="region" aria-labelledby="compare-h" tabindex="0">
247
+ <table>
248
+ <thead><tr><th scope="col">Plan</th><th scope="col">Starter</th></tr></thead>
249
+ <tbody><tr><th scope="row">Price a month</th><td>$49</td></tr></tbody>
250
+ </table>
251
+ </div>
252
+ ```
253
+
254
+ Don't: put the class on the `<table>` itself; tables for layout; colour cells to show "yes".
255
+
256
+ ### FAQ `.gw-faq`
257
+
258
+ Native `<details class="gw-faq__item">` with a `<summary>`, no script. The + turns into × when open.
259
+
260
+ ```html
261
+ <div class="gw-faq">
262
+ <details class="gw-faq__item"><summary>Does GemBoss run my ads?</summary><p>No. …</p></details>
263
+ </div>
264
+ ```
265
+
266
+ Don't: hide content a reader needs to decide (prices, terms) only inside a closed answer; more than eight questions.
267
+
268
+ ### Not in the kit yet
269
+
270
+ `.gw-tabs` does not exist yet. If a page needs tabs, build a stopgap (`kitgap-tabs`) and report the gap.
271
+
272
+ ## Header
273
+
274
+ | class | for |
275
+ |---|---|
276
+ | `.gw-announce` | optional one-sentence bar above the header, with one link |
277
+ | `.gw-header` (`data-gw-header`) | the sticky, flat 64px bar with one hairline; `.is-scrolled` darkens the hairline |
278
+ | `.gw-header__bar` | the row inside (`.gw-wrap gw-header__bar`) |
279
+ | `.gw-brand`, `__mark`, `__name` | logo (26px) and name, linking home |
280
+ | `.gw-nav`, `__list`, `__item`, `__trigger`, `__link` | the desktop nav (from 1080px); `__trigger` opens a mega panel, `__link` is a plain link; `aria-current="page"` underlines it in the accent |
281
+ | `.gw-mega`, `__grid`, `__col`, `__heading`, `__link`, `__icon`, `__feature` | a floating card centred under the bar (radius 20, raised shadow, as on gemboss.ai): columns of links with an icon and one line each, and at most one feature card |
282
+ | `.gw-header__actions` | theme toggle, Sign in, Book a demo, Start free, menu button |
283
+ | `.gw-header__signin` | with `.gw-nav__link`: hidden below 1080px (it moves into the mobile menu) |
284
+ | `.gw-theme-toggle` | icon button (`data-gw-theme-toggle`), 40px, 44px on touch |
285
+ | `.gw-menu-toggle`, `__open`, `__close` | the menu button below 1080px; it shows `__open` or `__close` from `aria-expanded` |
286
+ | `.gw-mobile-menu` | the full-height sheet under the bar (`hidden` until opened); `html.is-menu-open` locks the page |
287
+ | `.gw-mobile-menu__group` | a `<details>` group with a `<summary>` row |
288
+ | `.gw-mobile-menu__list`, `__link` | the links inside a group, 44px rows |
289
+ | `.gw-mobile-menu__top` | a top-level row without a group (Pricing) |
290
+ | `.gw-mobile-menu__actions` | the buttons and Sign in at the end of the sheet |
291
+
292
+ Header buttons: the secondary one shows from 768px, the primary one from 480px; below that they live in the mobile
293
+ menu. Full markup: `templates/header.html`.
294
+
295
+ Don't: a floating, rounded or blurred header; more than five top-level items; a second filled button in the bar; a
296
+ hero pulled up under the header.
297
+
298
+ ## Footer
299
+
300
+ | class | for |
301
+ |---|---|
302
+ | `.gw-footer` | the footer: one hairline on top, white like the page |
303
+ | `.gw-footer__grid` | brand block and link columns: 2 columns on phones, 3 from 768px, one row from 1080px |
304
+ | `.gw-footer__brand` | the brand and one sentence, full width on phones |
305
+ | `.gw-footer__col`, `__heading` | a `<nav>` column: a sentence-case heading and a `<ul>` of links |
306
+ | `.gw-footer__bottom` | copyright, legal links, social links; alone (no grid above), it drops its top rule |
307
+
308
+ Full markup: `templates/footer.html`. Don't: uppercase column headings; a newsletter form that stores nothing.
309
+
310
+ ## Sections
311
+
312
+ | class | for |
313
+ |---|---|
314
+ | `.gw-hero` | the first screen and the only `<h1>`; `.gw-dim` in its h1 sits on its own line |
315
+ | `.gw-hero__wash` | an empty `aria-hidden` div, first in the hero: the soft colour wash (the one place for wash hues) |
316
+ | `.gw-hero--center`, `--compact` | centred; less height and an h2-sized h1 for subpages |
317
+ | `.gw-logos` | a proof strip: a short line and a `<ul>` of logos (28px tall) or wordmarks |
318
+ | `.gw-stats`, `__item`, `__value`, `__label`, `__source` | three or four facts, each with its source |
319
+ | `.gw-features` + `.gw-feature` | with a `.gw-grid--*`: icon, title, one or two lines |
320
+ | `.gw-split`, `--reverse` | words beside a figure; two columns from 768px, words first on phones |
321
+ | `.gw-showcase` | a night band with one large `.gw-window` and two or three `.gw-feature` points |
322
+ | `.gw-steps` | an `<ol>` of three or four steps, numbered by the list |
323
+ | `.gw-quote` | one real testimonial: `<figure>`, `<blockquote>`, `<figcaption>` with name, role, company |
324
+ | `.gw-pricing` + `.gw-plan`, `--featured`, `__price` | plan cards; the featured one has an accent hairline and its badge sits on the top edge |
325
+ | `.gw-cta` | the closing call to action, centred; put it in a night band or a soft section |
326
+
327
+ ```html
328
+ <section class="gw-section">
329
+ <div class="gw-wrap">
330
+ <ul class="gw-stats">
331
+ <li class="gw-stats__item">
332
+ <p class="gw-stats__value">2.1 s</p>
333
+ <p class="gw-stats__label">Largest Contentful Paint on mobile</p>
334
+ <p class="gw-stats__source">Live GemBoss store, simulated 4G, median of 8 Lighthouse runs, 10 Oct 2026</p>
335
+ </li>
336
+ </ul>
337
+ </div>
338
+ </section>
339
+ ```
340
+
341
+ One snippet per section, with its limits in a comment, is in `templates/sections/`.
342
+
343
+ Don't: a number without a source; logos or quotes you do not have permission for; a filled button on every plan
344
+ (only the featured one); a hero without a real first line of content above the fold on a 390px phone.
345
+
346
+ ## Prose
347
+
348
+ | class | for |
349
+ |---|---|
350
+ | `.gw-prose` | a page that is read rather than scanned (a blog post, a docs page, terms, privacy): the article's own HTML inside it, 18px body, 68ch measure |
351
+ | `.gw-prose--sm` | the same at a 16px body, for docs and legal pages |
352
+
353
+ The HTML a CMS or a markdown renderer writes needs no classes. Inside `.gw-prose`:
354
+
355
+ | element | look |
356
+ |---|---|
357
+ | `h1` | the article title, at the section-headline size (`--gw-display-h2`) |
358
+ | `h2`, `h3`, `h4` | 30, 24, 20px (24, 20, 18px in `--sm`), weight 650; 48, 40, 32px of air above, 12px below |
359
+ | `p`, `ul`, `ol` | 20px between blocks (16px in `--sm`); bullets and numbers with muted markers, 8px between items |
360
+ | `blockquote` | a 3px rule on its left, muted words (the dashboard's reader quote) |
361
+ | `code` | a tint chip in `--gw-font-mono` (16px, 14px in `--sm`) that wraps on a phone; in a table it stays whole |
362
+ | `pre` | a soft box, 14px mono, that scrolls sideways inside itself |
363
+ | `table` | as a direct child: the `.gw-table` look, and it scrolls inside its own box when it is wider than the column |
364
+ | `img`, `figure`, `figcaption` | images radius 12; a caption 14px muted, 12px under its figure |
365
+ | `hr`, `a` | a hairline with 32px of air; links in the accent, underlined |
366
+
367
+ Kit components inside the prose keep their own look (`.gw-table`, `.gw-window`, `.gw-list--check`, `.gw-quote`,
368
+ `.gw-btn`). Prefer the `.gw-table` wrapper (`role="region"`, a name, `tabindex="0"`) when you write the markup: a
369
+ keyboard can scroll it.
370
+
371
+ ```html
372
+ <section class="gw-section">
373
+ <div class="gw-wrap gw-wrap--narrow">
374
+ <article class="gw-prose">
375
+ <h1>Why the kit looks the way it does</h1>
376
+ <p>Each rule came from measuring real pages, not from taste alone.</p>
377
+ <h2>One language for the site and the product</h2>
378
+ <p>A visitor crosses from the website into the product in one click. …</p>
379
+ <ul><li>…</li></ul>
380
+ </article>
381
+ </div>
382
+ </section>
383
+ ```
384
+
385
+ Don't: marketing sections inside the prose (end the article, then the section); headings chosen for their size (keep
386
+ them in order); a page without `.gw-prose` that styles its articles in site CSS (that was the stopgap of gap #46).
387
+
388
+ ## Motion
389
+
390
+ | class | for |
391
+ |---|---|
392
+ | `.gw-reveal` | fades and rises 24px into place when 15% visible (the script adds `.is-in`); shown anyway after 2.5s, and still under `prefers-reduced-motion` |
393
+
394
+ Put it on a block (a grid, a list, a window), not on each card in a grid. Don't use it on the hero or anything in
395
+ the first screen, and don't animate anything but `transform` and `opacity`.
396
+
397
+ ## Additions to the markup contract in 0.2.0
398
+
399
+ The button states (`:active`, `[aria-busy="true"]`), `.gw-segmented` (+ `--block`) with its `data-gw-segmented` /
400
+ `data-gw-show` hooks, `.gw-window__caption` and `.gw-prose` (+ `--sm`). Two tokens: `--gw-font-mono` and `--gw-spin-ms`.
401
+
402
+ ## Additions to the markup contract in 0.1.0
403
+
404
+ The contract names the classes the script relies on; these CSS-only helpers were added with it: `.gw-page`
405
+ (sticky footer), `.gw-h1`…`.gw-h4`, `.gw-lead`, `.gw-note`, `.gw-actions`, `.gw-footer__brand`,
406
+ `.gw-stats__source`, `.gw-plan__price`, `.gw-menu-toggle__open` / `__close`, and the mobile menu parts
407
+ `__group`, `__list`, `__link`, `__top`, `__actions`. `.gw-table` is the scrolling wrapper around a `<table>`.