@gemboss/website-ui 0.2.0 → 0.4.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.
package/css/header.css CHANGED
@@ -1,5 +1,6 @@
1
1
  /* header: announcement, a flat 64px bar with one hairline, mega menu, mobile menu below 1080px.
2
- Not a floating pill: no radius, no blur, no shadow on the bar. Nothing tucks under it. */
2
+ Not a floating pill: no radius, no blur, no shadow on the bar. Nothing tucks under it. A brand may choose the one
3
+ other shape, .gw-header--float (an inset card from 1080px, at the end of this file). */
3
4
  .gw-announce {
4
5
  padding: 8px var(--gw-gutter);
5
6
  border-bottom: 1px solid var(--gw-line);
@@ -83,9 +84,9 @@
83
84
  text-underline-offset: 8px;
84
85
  }
85
86
 
86
- /* mega menu: a floating card centred under the bar, as on gemboss.ai (Chris 24/10/2026): one hairline, radius 20,
87
- the raised shadow (it is a popover), 32px inside. The bridge (::before) keeps the pointer inside while it crosses
88
- the 8px gap from the trigger. */
87
+ /* mega menu: a floating card centred under the bar, as on gemboss.ai (Chris 24/10/2026): one hairline, the popover
88
+ radius of the site's profile (20, 24 in soft), the raised shadow (it is a popover), 32px inside. The bridge
89
+ (::before) keeps the pointer inside while it crosses the 8px gap from the trigger. */
89
90
  .gw-mega {
90
91
  position: absolute;
91
92
  top: calc(100% + 8px);
@@ -95,7 +96,7 @@
95
96
  overflow-y: auto;
96
97
  transform: translateX(-50%);
97
98
  border: 1px solid var(--gw-line);
98
- border-radius: 20px;
99
+ border-radius: var(--gw-radius-popover);
99
100
  background: var(--gw-surface);
100
101
  box-shadow: var(--gw-shadow-raised);
101
102
  }
@@ -229,3 +230,34 @@ html.is-menu-open .gw-header { position: fixed; top: 0; left: 0; right: 0; }
229
230
  @media (max-width: 768px), (pointer: coarse) {
230
231
  .gw-theme-toggle, .gw-menu-toggle { width: var(--gw-tap-min); height: var(--gw-tap-min); }
231
232
  }
233
+
234
+ /* floating header (0.3.0), a brand's header profile: <header class="gw-header gw-header--float">. The same bar, mega
235
+ menu and script, drawn from 1080px as an inset card: the panel radius of the site's profile (16, 24 in soft), the
236
+ surface at 85% over a 10px blur, one hairline, the raised shadow, a 16px gap above it and when it sticks. It is
237
+ wrap + 2 x 24px + its border wide, so the bar's content lines up with the page's from 1170px. Below 1080px it is
238
+ the flat bar, so the phone menu is unchanged. Solid for a visitor who asks for less transparency, and where the
239
+ browser cannot blur. No other floating or blurred header: it is a profile, not a style to copy. */
240
+ @media (min-width: 1080px) {
241
+ .gw-header--float {
242
+ top: var(--gw-header-gap);
243
+ width: min(100% - 32px, var(--gw-wrap) + 50px);
244
+ margin: var(--gw-header-gap) auto 0;
245
+ border: 1px solid var(--gw-line);
246
+ border-radius: var(--gw-radius-panel);
247
+ background: color-mix(in srgb, var(--gw-surface) var(--gw-header-surface), transparent);
248
+ -webkit-backdrop-filter: blur(var(--gw-header-blur));
249
+ backdrop-filter: blur(var(--gw-header-blur));
250
+ box-shadow: var(--gw-shadow-raised);
251
+ }
252
+ .gw-header--float.is-scrolled { border-color: var(--gw-line-strong); }
253
+ .gw-header--float > .gw-header__bar { width: auto; padding-inline: 24px; }
254
+ .gw-header--float .gw-mega { max-height: calc(100vh - var(--gw-header-height) - var(--gw-header-gap) - 24px); }
255
+ /* an anchor lands below the card, not under it */
256
+ :root:has(.gw-header--float) [id] { scroll-margin-top: calc(var(--gw-header-height) + var(--gw-header-gap) + 18px); }
257
+ }
258
+ @media (min-width: 1080px) and (prefers-reduced-transparency: reduce) {
259
+ .gw-header--float { background: var(--gw-surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
260
+ }
261
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
262
+ .gw-header--float { background: var(--gw-surface); }
263
+ }
package/css/prose.css CHANGED
@@ -59,7 +59,8 @@
59
59
  :where(.gw-prose) > hr { height: 0; border: 0; border-top: 1px solid var(--gw-line); }
60
60
  :where(.gw-prose) :is(sup, sub) { line-height: 0; }
61
61
 
62
- /* code: inline, a tint chip that wraps on a phone; a block scrolls inside its own box */
62
+ /* code: inline, a tint chip that wraps on a phone (radius 6 in every profile: it follows the text, not the shapes);
63
+ a block scrolls inside its own box */
63
64
  :where(.gw-prose) :is(pre, code, kbd, samp) { font-family: var(--gw-font-mono); }
64
65
  :where(.gw-prose) code {
65
66
  padding: 2px 6px;
@@ -87,7 +88,7 @@
87
88
  line-height: 1.6;
88
89
  tab-size: 2;
89
90
  }
90
- :where(.gw-prose) pre code { padding: 0; border-radius: 0; background: none; font-size: inherit; overflow-wrap: normal; }
91
+ :where(.gw-prose) pre code { padding: 0; border-radius: var(--gw-radius-0); background: none; font-size: inherit; overflow-wrap: normal; }
91
92
 
92
93
  /* images and captions (an image inside a .gw-window keeps the window's square edges) */
93
94
  :where(.gw-prose) > :is(img, video),
package/css/tokens.css CHANGED
@@ -46,11 +46,13 @@
46
46
  --gw-radius-12: 12px;
47
47
  --gw-radius-16: 16px;
48
48
  --gw-radius-20: 20px;
49
+ --gw-radius-24: 24px;
49
50
  --gw-radius-999: 999px;
50
51
  --gw-radius-action: 8px;
51
52
  --gw-radius-chip: 999px;
52
53
  --gw-radius-card: 12px;
53
54
  --gw-radius-panel: 16px;
55
+ --gw-radius-popover: 20px;
54
56
  --gw-shadow: 0 1px 2px rgba(31,30,29,.04), 0 2px 6px rgba(31,30,29,.06);
55
57
  --gw-shadow-sm: 0 1px 2px rgba(31,30,29,.04);
56
58
  --gw-shadow-raised: 0 4px 16px rgba(31,30,29,.10);
@@ -58,6 +60,9 @@
58
60
  --gw-wrap-narrow: 780px;
59
61
  --gw-gutter: 22px;
60
62
  --gw-header-height: 64px;
63
+ --gw-header-gap: 16px;
64
+ --gw-header-surface: 85%;
65
+ --gw-header-blur: 10px;
61
66
  --gw-button-height: 40px;
62
67
  --gw-tap-min: 44px;
63
68
  --gw-ease: cubic-bezier(.22,1,.36,1);
@@ -198,3 +203,17 @@
198
203
  --gw-success-soft: #132a22;
199
204
  --gw-warning: #e0b341;
200
205
  }
206
+ /* action profile "accent": data-gw-action="accent" on <html>. In night bands too, where the dark palette applies. */
207
+ :root[data-gw-action="accent"],
208
+ :root[data-gw-action="accent"] :is(.gw-band--night, .gw-showcase) {
209
+ --gw-action: var(--gw-accent);
210
+ --gw-action-hover: var(--gw-accent-strong);
211
+ --gw-on-action: var(--gw-on-accent);
212
+ }
213
+ /* radius profile "soft": data-gw-radius="soft" on <html> */
214
+ :root[data-gw-radius="soft"] {
215
+ --gw-radius-action: 12px;
216
+ --gw-radius-card: 20px;
217
+ --gw-radius-panel: 24px;
218
+ --gw-radius-popover: 24px;
219
+ }
@@ -0,0 +1,199 @@
1
+ <!doctype html>
2
+ <!-- Kit demo: one brand that chose all three profiles (docs/theming.md). The profiles are two attributes on <html> and
3
+ one class on the header; the only CSS of the brand is the accent family below. Sample text, not a brand's copy. -->
4
+ <html lang="en" data-gw-action="accent" data-gw-radius="soft">
5
+ <head>
6
+ <meta charset="utf-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <title>GemPages-style example · website UI kit</title>
9
+ <meta name="description" content="A kit demo with all three brand profiles on: the accent action, soft corners and the floating header, with a GemPages-style blue.">
10
+ <script>try{var t=localStorage.getItem('gw-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;650;700&display=swap">
14
+ <link rel="stylesheet" href="../website.css">
15
+ <style>
16
+ /* The brand's whole CSS: the accent family, light and dark, in one :root block (docs/theming.md).
17
+ Light: #3c67ff fills the primary button, white words on it are 4.58:1 (6.36:1 on the hover #2f52d6).
18
+ #2f52d6 as accent-strong is an EXAMPLE, not confirmed by the GemPages team: accent text needs it, because
19
+ #3c67ff is 4.13:1 on --gw-bg-soft; #2f52d6 is 6.36:1 on white, 5.73:1 on --gw-bg-soft, 5.54:1 on its tint.
20
+ Dark and night bands: #7c9bff is 7.37:1 on the dark page, and #0d0d12 words on it are 7.37:1. */
21
+ :root {
22
+ --gw-accent-light: #3c67ff;
23
+ --gw-accent-strong-light: #2f52d6;
24
+ --gw-accent-soft-light: #eaefff;
25
+ --gw-on-accent-light: #ffffff;
26
+ --gw-accent-dark: #7c9bff;
27
+ --gw-accent-strong-dark: #a9beff;
28
+ --gw-accent-soft-dark: #16213d;
29
+ --gw-on-accent-dark: #0d0d12;
30
+ }
31
+ </style>
32
+ </head>
33
+ <body class="gw-page">
34
+ <a class="gw-skip" href="#main">Skip to content</a>
35
+
36
+ <div class="gw-announce" role="region" aria-label="Announcement">Kit demo: a GemPages-style brand with all three profiles on. Sample text, not a brand's page. <a href="index.html">Back to the full demo</a></div>
37
+ <header class="gw-header gw-header--float" data-gw-header>
38
+ <div class="gw-wrap gw-header__bar">
39
+ <a class="gw-brand" href="brand-profiles.html"><svg class="gw-brand__mark" viewBox="0 0 26 26" aria-hidden="true"><rect width="26" height="26" rx="7" style="fill:var(--gw-accent)"/><path d="M8 9h10M8 13h10M8 17h6" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/></svg><span class="gw-brand__name">Example brand</span></a>
40
+ <nav class="gw-nav" aria-label="Main">
41
+ <ul class="gw-nav__list">
42
+ <li class="gw-nav__item">
43
+ <button class="gw-nav__trigger" type="button" aria-expanded="false" aria-controls="gw-mega-kit" data-gw-mega="gw-mega-kit">The kit</button>
44
+ <div class="gw-mega" id="gw-mega-kit" hidden>
45
+ <div class="gw-wrap gw-mega__grid">
46
+ <div class="gw-mega__col">
47
+ <p class="gw-mega__heading">See it</p>
48
+ <a class="gw-mega__link" href="index.html#components-h"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="7" height="7" rx="1.5"/><rect x="13" y="4" width="7" height="7" rx="1.5"/><rect x="4" y="13" width="7" height="7" rx="1.5"/><rect x="13" y="13" width="7" height="7" rx="1.5"/></svg></span><span><b>Components</b><i>Buttons, chips, cards, tables</i></span></a>
49
+ <a class="gw-mega__link" href="index.html#features-h"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h8M8 16h5"/></svg></span><span><b>Sections</b><i>Hero, features, pricing, FAQ</i></span></a>
50
+ </div>
51
+ <div class="gw-mega__col">
52
+ <p class="gw-mega__heading">Brands</p>
53
+ <a class="gw-mega__link" href="theme-example.html"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><path d="M12 4a8 8 0 0 1 0 16z"/></svg></span><span><b>Accent only</b><i>A brand that changed one colour</i></span></a>
54
+ <a class="gw-mega__link" href="#profiles"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h10M18 7h2M4 17h2M10 17h10"/><circle cx="16" cy="7" r="2"/><circle cx="8" cy="17" r="2"/></svg></span><span><b>All three profiles</b><i>This page</i></span></a>
55
+ </div>
56
+ <a class="gw-mega__feature" href="#profiles"><b>Three named choices</b><i>Action colour, corners and header. Chosen once per site.</i><span class="gw-link gw-link--arrow">See the profiles</span></a>
57
+ </div>
58
+ </div>
59
+ </li>
60
+ <li class="gw-nav__item"><a class="gw-nav__link" href="index.html">Default demo</a></li>
61
+ <li class="gw-nav__item"><a class="gw-nav__link" href="theme-example.html">Accent only</a></li>
62
+ </ul>
63
+ </nav>
64
+ <div class="gw-header__actions">
65
+ <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>
66
+ <a class="gw-btn gw-btn--secondary gw-btn--sm" href="index.html">Default demo</a>
67
+ <a class="gw-btn gw-btn--sm" href="#profiles">See the profiles</a>
68
+ <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>
69
+ </div>
70
+ </div>
71
+ <div class="gw-mobile-menu" id="gw-mobile-menu" hidden>
72
+ <nav class="gw-wrap" aria-label="Main, mobile">
73
+ <details class="gw-mobile-menu__group">
74
+ <summary>The kit</summary>
75
+ <ul class="gw-mobile-menu__list">
76
+ <li><a class="gw-mobile-menu__link" href="index.html#components-h">Components</a></li>
77
+ <li><a class="gw-mobile-menu__link" href="index.html#features-h">Sections</a></li>
78
+ <li><a class="gw-mobile-menu__link" href="#profiles">All three profiles</a></li>
79
+ </ul>
80
+ </details>
81
+ <a class="gw-mobile-menu__top" href="index.html">Default demo</a>
82
+ <a class="gw-mobile-menu__top" href="theme-example.html">Accent only</a>
83
+ <div class="gw-mobile-menu__actions">
84
+ <a class="gw-btn gw-btn--block" href="#profiles">See the profiles</a>
85
+ <a class="gw-btn gw-btn--secondary gw-btn--block" href="index.html">Default demo</a>
86
+ </div>
87
+ </nav>
88
+ </div>
89
+ </header>
90
+
91
+ <main id="main">
92
+ <section class="gw-hero" aria-labelledby="hero-h">
93
+ <div class="gw-hero__wash" aria-hidden="true"></div>
94
+ <div class="gw-wrap">
95
+ <p class="gw-label">Brand profiles</p>
96
+ <h1 id="hero-h"><span class="gw-dim">The same kit, three named choices.</span> Still one family.</h1>
97
+ <p class="gw-lead">This brand fills its primary button with its accent, rounds its corners one step and floats its header as a card. Everything else is the kit as it ships.</p>
98
+ <div class="gw-actions">
99
+ <a class="gw-btn" href="#profiles">See the three profiles</a>
100
+ <a class="gw-btn gw-btn--secondary" href="index.html">Open the default demo</a>
101
+ </div>
102
+ </div>
103
+ </section>
104
+
105
+ <section class="gw-section gw-section--soft" id="profiles" aria-labelledby="profiles-h">
106
+ <div class="gw-wrap">
107
+ <div class="gw-section__head">
108
+ <p class="gw-label">Three knobs</p>
109
+ <h2 id="profiles-h"><span class="gw-dim">Set once for the site.</span> Every component follows.</h2>
110
+ <p class="gw-lead">Each knob is a named profile, not a free value. A brand picks one of each, and the kit does the rest.</p>
111
+ </div>
112
+ <div class="gw-grid--3">
113
+ <div class="gw-card">
114
+ <h3>Action colour</h3>
115
+ <p>The primary button takes the accent. Its label must reach 4.5:1 on the fill in light and dark, or the check fails.</p>
116
+ </div>
117
+ <div class="gw-card">
118
+ <h3>Soft corners</h3>
119
+ <p>Actions 12px, cards 20px, panels 24px. Chips stay pills, and nothing else is allowed in between.</p>
120
+ </div>
121
+ <div class="gw-card">
122
+ <h3>Floating header</h3>
123
+ <p>An inset card over a light blur, from 1080px. The same bar and mega menu; phones keep the flat bar.</p>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </section>
128
+
129
+ <section class="gw-section" aria-labelledby="shapes-h">
130
+ <div class="gw-wrap gw-split">
131
+ <div>
132
+ <p class="gw-label">Radius profiles</p>
133
+ <h2 id="shapes-h">Two sets of corners, nothing in between.</h2>
134
+ <p class="gw-lead">Tight is the default. Soft rounds every action, card and panel by one step.</p>
135
+ <div class="gw-actions">
136
+ <button class="gw-chip" type="button" aria-pressed="false">Tight</button>
137
+ <button class="gw-chip" type="button" aria-pressed="true">Soft</button>
138
+ <span class="gw-badge gw-badge--accent">Chosen on the page</span>
139
+ </div>
140
+ <div class="gw-actions">
141
+ <a class="gw-btn" href="#rules-h">See what stays fixed</a>
142
+ <a class="gw-btn gw-btn--ghost" href="theme-example.html">Accent only</a>
143
+ </div>
144
+ </div>
145
+ <div class="gw-table" role="region" aria-labelledby="shapes-h" tabindex="0">
146
+ <table>
147
+ <thead><tr><th scope="col">Profile</th><th scope="col">Action</th><th scope="col">Card</th><th scope="col">Panel</th></tr></thead>
148
+ <tbody>
149
+ <tr><th scope="row">Tight, the default</th><td>8px</td><td>12px</td><td>16px</td></tr>
150
+ <tr><th scope="row">Soft</th><td>12px</td><td>20px</td><td>24px</td></tr>
151
+ </tbody>
152
+ </table>
153
+ </div>
154
+ </div>
155
+ </section>
156
+
157
+ <section class="gw-section gw-section--soft" aria-labelledby="rules-h">
158
+ <div class="gw-wrap gw-wrap--narrow">
159
+ <div class="gw-section__head">
160
+ <p class="gw-label">Still fixed</p>
161
+ <h2 id="rules-h"><span class="gw-dim">Three knobs, no more.</span> The rest is the kit's.</h2>
162
+ </div>
163
+ <div class="gw-faq">
164
+ <details class="gw-faq__item"><summary>Can a brand choose its own radius?</summary><p>No. It picks tight or soft. A radius of its own, or a token it redefines, is reported by the check.</p></details>
165
+ <details class="gw-faq__item"><summary>Can a heading use the accent?</summary><p>No. Words in a heading stay ink or muted, never the accent or a gradient. Bands are white, soft grey or night, never the accent. Labels stay in sentence case.</p></details>
166
+ <details class="gw-faq__item"><summary>What changes on a phone?</summary><p>Nothing. Below 1080px the floating header is the flat bar, and the menu opens the same way.</p></details>
167
+ <details class="gw-faq__item"><summary>What if a visitor turns transparency off?</summary><p>The header becomes a solid card. A browser that cannot blur gets the same solid card.</p></details>
168
+ </div>
169
+ </div>
170
+ </section>
171
+
172
+ <section class="gw-section gw-band--night" aria-labelledby="cta-h">
173
+ <div class="gw-wrap gw-cta">
174
+ <p class="gw-label">Night band</p>
175
+ <h2 id="cta-h">The accent button keeps its contrast at night.</h2>
176
+ <p class="gw-lead">In a night band and in dark mode it takes the dark accent with dark words, also checked at 4.5:1.</p>
177
+ <div class="gw-actions">
178
+ <a class="gw-btn" href="theme-example.html">Open the accent example</a>
179
+ <a class="gw-btn gw-btn--secondary" href="index.html">Open the default demo</a>
180
+ </div>
181
+ </div>
182
+ </section>
183
+ </main>
184
+
185
+ <footer class="gw-footer">
186
+ <div class="gw-wrap">
187
+ <div class="gw-footer__bottom">
188
+ <span>Website UI kit demo. Sample text.</span>
189
+ <ul aria-label="Demo pages">
190
+ <li><a href="index.html">Default demo</a></li>
191
+ <li><a href="theme-example.html">Accent only</a></li>
192
+ </ul>
193
+ </div>
194
+ </div>
195
+ </footer>
196
+
197
+ <script src="../js/website.js" defer></script>
198
+ </body>
199
+ </html>
@@ -8,6 +8,10 @@ Conventions: kit classes start with `gw-`, variants are `--name`, parts are `__n
8
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
9
  (`AGENTS.md` section 9).
10
10
 
11
+ Radii and the button fill follow the site's brand profiles (`docs/theming.md`): below, "radius 8 / 12 / 16" is the
12
+ default `tight` profile, and `soft` makes them 12 / 20 / 24; "ink fill" is the default `ink` action profile, and
13
+ `data-gw-action="accent"` makes it the accent.
14
+
11
15
  Spacing between the children of text blocks is automatic: inside `.gw-section__head`, the hero, a split column,
12
16
  `.gw-cta`, `.gw-card`, `.gw-feature` and `.gw-plan`, each child is 16px below the previous one, a `.gw-actions` row
13
17
  32px, a `.gw-list--check` 24px. Do not add margins to fix rhythm; reorder or remove content instead.
@@ -28,6 +32,30 @@ Spacing between the children of text blocks is automatic: inside `.gw-section__h
28
32
 
29
33
  Don't remove the skip link, and don't leave out `id="main"`.
30
34
 
35
+ ## Script
36
+
37
+ `js/website.js` (a `<script defer>`) or `js/website.mjs` (an `import`) wires every part that moves: the mega menu,
38
+ the mobile menu, the theme toggle, reveal, the header's scroll state, the current page, the segmented control and
39
+ the busy button. Markup that arrives after the page loaded (a client-side route change, a builder block, a CMS
40
+ preview) is wired by calling `init` on it, as often as needed: an element is wired once.
41
+
42
+ ```js
43
+ // a page with the classic script, after it inserted kit markup
44
+ slot.innerHTML = html;
45
+ gwWebsite.init(slot);
46
+ ```
47
+
48
+ ```js
49
+ // an app: import the ESM entry, call init() in an effect after each route change
50
+ import { init } from '@gemboss/website-ui/website.mjs';
51
+ ```
52
+
53
+ `destroy(root)` takes it back when a framework unmounts. The Next.js recipe, the `data-gw-ready` mark and the
54
+ events (`gw:mega`, `gw:theme`, `gw:segmented`) are in `docs/markup-contract.md`, "Script".
55
+
56
+ Don't: wire a kit part with your own script (a click handler on a trigger, a class toggled by hand), or load two
57
+ copies of the script and expect both to work (the first owns the page).
58
+
31
59
  ## Type and text
32
60
 
33
61
  | class | for |
@@ -86,7 +114,8 @@ Don't: more than three night bands on a page (the closing CTA does not count), o
86
114
 
87
115
  ### Buttons `.gw-btn`
88
116
 
89
- 40px tall, radius 8, 14px/600, sentence case, ink fill. Variants: `--secondary` (hairline, transparent), `--ghost`
117
+ 40px tall, the action radius (8, 12 in `soft`), 14px/600, sentence case, the action colour as fill: ink, or the
118
+ brand's accent with `--gw-on-accent` words under `data-gw-action="accent"` (one colour on the whole site). Variants: `--secondary` (hairline, transparent), `--ghost`
90
119
  (no frame), `--on-night` (light fill for a dark surface that is not a `.gw-band--night`), `--sm` (36px, with a 44px
91
120
  hit area), `--lg` (48px, 16px type), `--block` (full width). On touch screens every button is at least 44px tall and
92
121
  its label may wrap. Inside a night band the default button turns light by itself.
@@ -98,7 +127,8 @@ its label may wrap. Inside a night band the default button turns light by itself
98
127
  </div>
99
128
  ```
100
129
 
101
- Pressed (`:active`): the ink one step darker (the hover ink), and nothing moves; `--secondary` takes the soft tint and
130
+ Pressed (`:active`): the fill one step stronger (the hover fill: the darker ink, or `--gw-accent-strong`), and
131
+ nothing moves; `--secondary` takes the soft tint and
102
132
  `--ghost` the hairline tint.
103
133
 
104
134
  **Loading.** While a `<button>` waits on the network (a form sent, an audit started), set `aria-busy="true"` on it and
@@ -116,7 +146,8 @@ button.setAttribute('aria-busy', 'true'); // the request leaves
116
146
  button.removeAttribute('aria-busy'); // the answer came
117
147
  ```
118
148
 
119
- Don't: an accent, gradient or wash fill; pill-shaped buttons; uppercase; two filled buttons side by side (one
149
+ Don't: a fill of your own (the accent comes only from the action profile, set on `<html>`), a gradient or a wash; a
150
+ raised or inset shadow; pill-shaped buttons; uppercase; two filled buttons side by side (one
120
151
  `.gw-btn` per view, the second is `--secondary` or a link); a `<div>` with a click handler instead of `<a>`/`<button>`;
121
152
  a loading class, your own spinner or `disabled` on a waiting button (`check` reports `busy-button`: `disabled` throws
122
153
  the keyboard focus to the top of the page); a "Loading…" label in place of the real one.
@@ -136,7 +167,7 @@ Don't: use a chip as the page's action; square it; more than about six in a row.
136
167
 
137
168
  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
169
  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
170
+ look is the dashboard's (`@gemboss/ui` `.ui-seg`): one hairline group, the action radius, a hairline between options, the chosen
140
171
  option on the accent tint, so it follows a brand's accent. 40px tall; on touch screens every option is at least 44px
141
172
  and its label may wrap. `--block` stretches it to the row and shares the width equally.
142
173
 
@@ -176,7 +207,7 @@ Don't: make a badge clickable; write a sentence in it; use success or warning fo
176
207
 
177
208
  ### Cards `.gw-card`
178
209
 
179
- A white box with a 1px hairline, radius 12, the resting shadow `--gw-shadow` (as the dashboard's cards), 24px padding
210
+ A white box with a 1px hairline, the card radius (12, 20 in `soft`), the resting shadow `--gw-shadow` (as the dashboard's cards), 24px padding
180
211
  (32px from 768px). It sits: no hover lift. `--soft` is a tint and has no shadow.
181
212
  Variants: `--soft` (on `--gw-bg-soft`), `--link` (the whole card is one `<a>`; the border darkens on hover).
182
213
 
@@ -188,8 +219,8 @@ Variants: `--soft` (on `--gw-bg-soft`), `--link` (the whole card is one `<a>`; t
188
219
  </a>
189
220
  ```
190
221
 
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).
222
+ Don't: put links or buttons inside a `--link` card (one card, one destination); a bigger shadow or a hover lift; a
223
+ radius of your own (cards take the card radius, plans and the quote the panel radius, both from the profile).
193
224
 
194
225
  ### Window `.gw-window`
195
226
 
@@ -238,7 +269,7 @@ Don't: more than five or six items; full paragraphs as items.
238
269
 
239
270
  ### Table `.gw-table`
240
271
 
241
- A wrapper around a plain `<table>`: hairline, radius 12, and it scrolls inside its own box when the table is wider
272
+ A wrapper around a plain `<table>`: hairline, the card radius, and it scrolls inside its own box when the table is wider
242
273
  than the screen. `--compare`: centred values, a 640px minimum width and a sticky first column, for plan comparisons.
243
274
  Give the wrapper `role="region"`, an accessible name and `tabindex="0"` so keyboard users can scroll it.
244
275
 
@@ -275,10 +306,11 @@ Don't: hide content a reader needs to decide (prices, terms) only inside a close
275
306
  |---|---|
276
307
  | `.gw-announce` | optional one-sentence bar above the header, with one link |
277
308
  | `.gw-header` (`data-gw-header`) | the sticky, flat 64px bar with one hairline; `.is-scrolled` darkens the hairline |
309
+ | `.gw-header--float` | with `.gw-header`, a brand's header profile: from 1080px the same bar as an inset card (panel radius, the surface at 85% over a blur, one hairline, the raised shadow, 16px from the top); the flat bar below 1080px; solid under `prefers-reduced-transparency` |
278
310
  | `.gw-header__bar` | the row inside (`.gw-wrap gw-header__bar`) |
279
311
  | `.gw-brand`, `__mark`, `__name` | logo (26px) and name, linking home |
280
312
  | `.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 |
313
+ | `.gw-mega`, `__grid`, `__col`, `__heading`, `__link`, `__icon`, `__feature` | a floating card centred under the bar (the popover radius: 20, 24 in `soft`; raised shadow, as on gemboss.ai): columns of links with an icon and one line each, and at most one feature card |
282
314
  | `.gw-header__actions` | theme toggle, Sign in, Book a demo, Start free, menu button |
283
315
  | `.gw-header__signin` | with `.gw-nav__link`: hidden below 1080px (it moves into the mobile menu) |
284
316
  | `.gw-theme-toggle` | icon button (`data-gw-theme-toggle`), 40px, 44px on touch |
@@ -292,7 +324,8 @@ Don't: hide content a reader needs to decide (prices, terms) only inside a close
292
324
  Header buttons: the secondary one shows from 768px, the primary one from 480px; below that they live in the mobile
293
325
  menu. Full markup: `templates/header.html`.
294
326
 
295
- Don't: a floating, rounded or blurred header; more than five top-level items; a second filled button in the bar; a
327
+ Don't: a floating, rounded or blurred header of your own (the one floating header is `.gw-header--float`, chosen
328
+ for the whole site); more than five top-level items; a second filled button in the bar; a
296
329
  hero pulled up under the header.
297
330
 
298
331
  ## Footer
@@ -361,7 +394,7 @@ The HTML a CMS or a markdown renderer writes needs no classes. Inside `.gw-prose
361
394
  | `code` | a tint chip in `--gw-font-mono` (16px, 14px in `--sm`) that wraps on a phone; in a table it stays whole |
362
395
  | `pre` | a soft box, 14px mono, that scrolls sideways inside itself |
363
396
  | `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 |
397
+ | `img`, `figure`, `figcaption` | images take the card radius; a caption 14px muted, 12px under its figure |
365
398
  | `hr`, `a` | a hairline with 32px of air; links in the accent, underlined |
366
399
 
367
400
  Kit components inside the prose keep their own look (`.gw-table`, `.gw-window`, `.gw-list--check`, `.gw-quote`,
@@ -394,6 +427,18 @@ them in order); a page without `.gw-prose` that styles its articles in site CSS
394
427
  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
428
  the first screen, and don't animate anything but `transform` and `opacity`.
396
429
 
430
+ ## Additions to the markup contract in 0.4.0
431
+
432
+ The script: `init(root)` and `destroy(root)` (`window.gwWebsite`, or `js/website.mjs`), the `data-gw-ready` mark on
433
+ what it wired, and the events `gw:mega` (`{ id, open }`) and `gw:theme` (`{ theme }`) next to `gw:segmented`. No
434
+ markup or class of 0.3.0 changes.
435
+
436
+ ## Additions to the markup contract in 0.3.0
437
+
438
+ The brand profiles: `data-gw-action="ink|accent"` and `data-gw-radius="tight|soft"` on `<html>`, and
439
+ `.gw-header--float` on the header (`docs/theming.md`). Tokens: `--gw-radius-popover` (the mega menu), `--gw-radius-24`,
440
+ `--gw-header-gap`, `--gw-header-surface`, `--gw-header-blur`. No markup of 0.2.0 changes.
441
+
397
442
  ## Additions to the markup contract in 0.2.0
398
443
 
399
444
  The button states (`:active`, `[aria-busy="true"]`), `.gw-segmented` (+ `--block`) with its `data-gw-segmented` /