@gemboss/website-ui 0.12.0 → 0.14.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.
@@ -188,6 +188,29 @@
188
188
  .gw-badge--danger { background: var(--gw-danger-soft); color: var(--gw-ink); }
189
189
  .gw-badge--danger::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: var(--gw-radius-chip); background: var(--gw-danger); }
190
190
 
191
+ /* Status chip (0.14.0): gemboss.ai's "pills", a state inside a drawn screen of the product (Live, a test, Early access), as
192
+ the GemBoss app draws it: a rounded rectangle (the action radius), 28px, 12px/500 words, neutral by default. --success is
193
+ live (its words in success's text form), --accent a test, --early neutral with the dot in the accent. The dot is an
194
+ optional `.gw-status__dot` before the words. A label that names a thing (New, Recommended) is a .gw-badge. */
195
+ .gw-status {
196
+ display: inline-flex;
197
+ align-items: center;
198
+ gap: 6px;
199
+ min-height: 28px;
200
+ padding: 6px 8px;
201
+ border-radius: var(--gw-radius-action);
202
+ background: color-mix(in srgb, var(--gw-ink) 8%, transparent);
203
+ color: var(--gw-ink-2);
204
+ font-size: 12px;
205
+ font-weight: 500;
206
+ line-height: 16px;
207
+ white-space: nowrap;
208
+ }
209
+ .gw-status--success { background: var(--gw-success-soft); color: var(--gw-success-strong); }
210
+ .gw-status--accent { background: var(--gw-accent-soft); color: var(--gw-accent-strong); }
211
+ .gw-status__dot { width: 6px; height: 6px; border-radius: var(--gw-radius-chip); background: currentColor; }
212
+ .gw-status--early .gw-status__dot { background: var(--gw-accent); }
213
+
191
214
  /* cards sit: white, 1px hairline, the card radius of the site's profile (12, 20 in soft), the dashboard's resting
192
215
  shadow (Chris 24/10/2026: one language with the app); no hover lift. A soft card is a tint and carries no shadow. */
193
216
  .gw-card {
@@ -221,35 +244,48 @@
221
244
  :where(.gw-card, .gw-feature) p { font-size: 16px; color: var(--gw-muted); }
222
245
 
223
246
  /* a framed screen: a browser or app window around a screenshot or a drawn mock-up */
247
+ /* 0.14.0: gemboss.ai's frame, 1:1: it floats (the raised shadow), 13px at 20px lines; the bar 40px with 12px sides, three
248
+ 8px dots 6px apart, the words 16px after them and 8px between the bar's parts, one line, cut when too long; bold words in
249
+ the bar in ink. */
224
250
  .gw-window {
251
+ position: relative;
225
252
  overflow: hidden;
226
253
  border: 1px solid var(--gw-line);
227
254
  border-radius: var(--gw-radius-card);
228
255
  background: var(--gw-surface);
229
256
  color: var(--gw-ink);
230
- box-shadow: var(--gw-shadow);
257
+ box-shadow: var(--gw-shadow-raised);
231
258
  font-size: 13px;
232
- line-height: 1.5;
259
+ line-height: 20px;
233
260
  }
234
261
  .gw-window__bar {
235
262
  display: flex;
236
263
  align-items: center;
237
- gap: 12px;
264
+ gap: 8px;
238
265
  min-height: 40px;
239
- padding: 8px 16px;
266
+ padding: 0 12px;
240
267
  border-bottom: 1px solid var(--gw-line);
241
268
  color: var(--gw-muted);
242
269
  font-size: 12px;
270
+ white-space: nowrap;
271
+ overflow: hidden;
243
272
  }
244
273
  .gw-window__bar::before {
245
274
  content: "";
246
275
  flex: none;
247
- width: 36px;
276
+ width: 8px;
248
277
  height: 8px;
249
- background:
250
- radial-gradient(circle at 4px 4px, var(--gw-line-strong) 4px, transparent 4px),
251
- radial-gradient(circle at 18px 4px, var(--gw-line-strong) 4px, transparent 4px),
252
- radial-gradient(circle at 32px 4px, var(--gw-line-strong) 4px, transparent 4px);
278
+ margin-right: 36px;
279
+ border-radius: var(--gw-radius-chip);
280
+ background: var(--gw-line-strong);
281
+ box-shadow: 14px 0 0 var(--gw-line-strong), 28px 0 0 var(--gw-line-strong);
282
+ }
283
+ .gw-window__bar > :first-child { min-width: 0; overflow: hidden; }
284
+ .gw-window__bar b { color: var(--gw-ink); font-weight: 600; }
285
+ /* up to 560px the bar's words wrap rather than being cut: the bar grows, its parts at the top (gemboss.ai) */
286
+ @media (max-width: 560px) {
287
+ .gw-window__bar { align-items: flex-start; padding-block: 8px; white-space: normal; }
288
+ .gw-window__bar > :first-child { overflow: visible; }
253
289
  }
254
290
  .gw-window__body { padding: 16px; }
255
291
  .gw-window__body:has(> img:only-child) { padding: 0; }
@@ -266,24 +302,43 @@
266
302
  }
267
303
  .gw-window__body:has(> img:only-child) + .gw-window__caption { padding-top: 12px; }
268
304
 
305
+ /* figure (0.13.0, Gemians): a photo and its caption where a section shows one outside the prose (beside words in a
306
+ split, under a hero's actions): the picture at the card radius, the caption 14px, muted, 12px under it, as a figure
307
+ in .gw-prose. A screen of the product goes in .gw-window instead. */
308
+ .gw-figure > :is(img, video, picture) { display: block; width: 100%; height: auto; border-radius: var(--gw-radius-card); }
309
+ .gw-figure > picture > img { display: block; width: 100%; height: auto; border-radius: inherit; }
310
+ .gw-figure > figcaption { margin-top: 12px; color: var(--gw-muted); font-size: 14px; line-height: 1.5; }
311
+
269
312
  /* icon: 20px inline stroke SVG (1.7 to 2px stroke, currentColor) */
270
313
  .gw-icon { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 20px; height: 20px; }
271
314
  .gw-icon svg { width: 20px; height: 20px; }
272
315
 
273
316
  /* list with check marks */
274
- .gw-list--check { display: grid; gap: 12px; padding: 0; list-style: none; }
275
- .gw-list--check > li { position: relative; padding-left: 24px; }
317
+ /* 0.14.0: gemboss.ai's, 1:1: 14px at 1.5 lines, the words 28px in (a 20px column for the mark, 8px after it); the mark a
318
+ 16px tile on the accent's tint, 4px down, with the tick in the accent (the tick follows the theme and the brand). The mark
319
+ is out of the flow, so an item may hold words and links side by side. */
320
+ .gw-list--check { display: grid; gap: 12px; padding: 0; list-style: none; font-size: 14px; line-height: 1.5; }
321
+ .gw-list--check > li { position: relative; padding-left: 28px; }
276
322
  .gw-list--check > li::before {
277
323
  content: "";
278
324
  position: absolute;
279
325
  left: 0;
280
- top: 6px;
281
- top: calc((1lh - 16px) / 2);
326
+ top: 4px;
327
+ width: 16px;
328
+ height: 16px;
329
+ border-radius: var(--gw-radius-4);
330
+ background: var(--gw-accent-soft);
331
+ }
332
+ .gw-list--check > li::after {
333
+ content: "";
334
+ position: absolute;
335
+ left: 0;
336
+ top: 4px;
282
337
  width: 16px;
283
338
  height: 16px;
284
339
  background: var(--gw-accent);
285
- -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4l2.9 2.8L12.5 5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
286
- mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4l2.9 2.8L12.5 5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
340
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.2l2.6 2.5L12 5.3' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
341
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.2l2.6 2.5L12 5.3' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
287
342
  }
288
343
 
289
344
  /* table: the wrapper scrolls inside its own box on phones, the page never does */
@@ -306,10 +361,12 @@
306
361
  .gw-table--compare thead th:not(:first-child) { text-align: center; }
307
362
  .gw-table--compare tbody th { position: sticky; left: 0; z-index: 1; background: var(--gw-surface); min-width: 160px; }
308
363
 
309
- /* FAQ: native details/summary, no script */
364
+ /* FAQ: native details/summary, no script. 0.14.0: gemboss.ai's, 1:1 (the question in the brand face alone, at the line
365
+ height around it; the + is the face's own glyph in the accent, turned 45 degrees when open; the answer 33em wide,
366
+ rising in as the item opens) */
310
367
  .gw-faq { display: grid; gap: 12px; }
311
368
  .gw-faq__item {
312
- padding: 0 24px;
369
+ padding: 2px 24px;
313
370
  border: 1px solid var(--gw-line);
314
371
  border-radius: var(--gw-radius-card);
315
372
  background: var(--gw-surface);
@@ -322,25 +379,28 @@
322
379
  gap: 16px;
323
380
  min-height: 44px;
324
381
  padding: 20px 0;
382
+ font-family: var(--gw-font-action);
325
383
  font-size: 18px;
326
384
  font-weight: 600;
327
- line-height: 1.4;
328
385
  list-style: none;
329
386
  cursor: pointer;
330
387
  }
331
388
  .gw-faq__item > summary::-webkit-details-marker { display: none; }
332
389
  .gw-faq__item > summary::after {
333
- content: "";
334
- flex: none;
335
- width: 16px;
336
- height: 16px;
337
- background:
338
- linear-gradient(var(--gw-accent), var(--gw-accent)) center / 16px 2px no-repeat,
339
- linear-gradient(var(--gw-accent), var(--gw-accent)) center / 2px 16px no-repeat;
340
- transition: transform 0.2s var(--gw-ease);
390
+ content: "+";
391
+ color: var(--gw-accent);
392
+ font-size: 24px;
393
+ font-weight: 400;
394
+ transition: transform 0.2s ease;
341
395
  }
342
396
  .gw-faq__item[open] > summary::after { transform: rotate(45deg); }
343
- .gw-faq__item > :not(summary) { margin: 0 0 20px; max-width: 68ch; color: var(--gw-muted); font-size: 16px; }
397
+ .gw-faq__item > :not(summary) { margin: 0 0 20px; color: var(--gw-muted); font-size: 16px; }
398
+ .gw-faq__item > p { max-width: 33em; }
399
+ @keyframes gw-faq-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
400
+ .gw-faq__item[open] > :not(summary) { animation: gw-faq-in 0.28s ease; }
401
+ @media (prefers-reduced-motion: reduce) { .gw-faq__item[open] > :not(summary) { animation: none; } }
402
+ /* phones: the rows tighten (gemboss.ai), never under the 44px a finger needs */
403
+ @media (max-width: 560px) { .gw-faq__item > summary { padding-block: 4px; } }
344
404
 
345
405
  /* touch: every control is at least 44px tall. A button up to 768px (0.12.0, as gemboss.ai: by the width, not the
346
406
  pointer), its label wrapping (and tighter) from 480px down in the page's <main>, so a long one never pushes the page
package/css/header.css CHANGED
@@ -703,3 +703,16 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
703
703
  :root:not([data-theme="light"]) .gw-theme-toggle__sun { display: block; }
704
704
  :root:not([data-theme="light"]) .gw-theme-toggle__moon { display: none; }
705
705
  }
706
+
707
+ /* ---- 0.13.0: the brand's logo as one drawing (Gemians): the mark and the wordmark in one file, in place of the mark and
708
+ the name, 26px tall, its width the file's. Its alt is the brand's name. A logo drawn for dark grounds only (a white
709
+ wordmark) comes as a second file, .gw-brand__logo--dark, right after the first: it shows in dark (the system's or the
710
+ toggle's) and the first one hides, the toggle icons' rule. One file that reads on both: no --dark. */
711
+ .gw-brand__logo { display: block; flex: none; width: auto; height: 26px; }
712
+ .gw-brand__logo--dark { display: none; }
713
+ :root[data-theme="dark"] .gw-brand__logo--dark { display: block; }
714
+ :root[data-theme="dark"] .gw-brand__logo:has(+ .gw-brand__logo--dark) { display: none; }
715
+ @media (prefers-color-scheme: dark) {
716
+ :root:not([data-theme="light"]) .gw-brand__logo--dark { display: block; }
717
+ :root:not([data-theme="light"]) .gw-brand__logo:has(+ .gw-brand__logo--dark) { display: none; }
718
+ }
package/css/ratings.css CHANGED
@@ -26,7 +26,6 @@
26
26
  display: grid;
27
27
  grid-template-columns: minmax(0, 1fr);
28
28
  gap: 16px;
29
- margin: 0;
30
29
  padding: 0;
31
30
  list-style: none;
32
31
  }
package/css/sections.css CHANGED
@@ -13,7 +13,7 @@
13
13
  radial-gradient(420px 320px at 52% 118%, color-mix(in srgb, var(--gw-wash-mint) 10%, transparent), transparent 62%);
14
14
  }
15
15
  .gw-hero h1 .gw-dim { display: block; }
16
- .gw-hero > .gw-wrap > .gw-window { margin-top: 48px; }
16
+ .gw-hero > .gw-wrap > :is(.gw-window, .gw-figure) { margin-top: 48px; }
17
17
  .gw-hero--center { text-align: center; }
18
18
  .gw-hero--center > .gw-wrap > * { margin-inline: auto; }
19
19
  .gw-hero--center .gw-actions { justify-content: center; }
@@ -46,7 +46,7 @@
46
46
  .gw-logos img { width: auto; height: 28px; }
47
47
 
48
48
  /* stats: three or four facts, each with its source */
49
- .gw-stats { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; margin: 0; padding: 0; list-style: none; }
49
+ .gw-stats { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 0; list-style: none; }
50
50
  .gw-stats__value { font-size: var(--gw-display-h2); font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; font-variant-numeric: tabular-nums; }
51
51
  .gw-stats__label { margin-top: 8px; max-width: 28ch; color: var(--gw-muted); font-size: 14px; }
52
52
  .gw-stats__source { margin-top: 8px; color: var(--gw-muted); font-size: 12px; }
@@ -79,7 +79,6 @@
79
79
  display: grid;
80
80
  grid-template-columns: minmax(0, 1fr);
81
81
  gap: 32px 24px;
82
- margin: 0;
83
82
  padding: 0;
84
83
  list-style: none;
85
84
  counter-reset: gw-step;
package/css/tokens.css CHANGED
@@ -126,6 +126,7 @@
126
126
  --gw-on-accent: var(--gw-on-accent-light);
127
127
  --gw-success: #007f5f;
128
128
  --gw-success-soft: #e3f1df;
129
+ --gw-success-strong: #0a6a4c;
129
130
  --gw-warning: #9a6700;
130
131
  --gw-warning-soft: #fbf1dc;
131
132
  --gw-danger: #b42318;
@@ -158,6 +159,7 @@
158
159
  --gw-on-accent: var(--gw-on-accent-dark);
159
160
  --gw-success: #34c38f;
160
161
  --gw-success-soft: #132a22;
162
+ --gw-success-strong: #34c38f;
161
163
  --gw-warning: #e0b341;
162
164
  --gw-warning-soft: #2b2410;
163
165
  --gw-danger: #ff6b5a;
@@ -193,6 +195,7 @@
193
195
  --gw-on-accent: var(--gw-on-accent-dark);
194
196
  --gw-success: #34c38f;
195
197
  --gw-success-soft: #132a22;
198
+ --gw-success-strong: #34c38f;
196
199
  --gw-warning: #e0b341;
197
200
  --gw-warning-soft: #2b2410;
198
201
  --gw-danger: #ff6b5a;
@@ -227,6 +230,7 @@
227
230
  --gw-on-accent: var(--gw-on-accent-light);
228
231
  --gw-success: #007f5f;
229
232
  --gw-success-soft: #e3f1df;
233
+ --gw-success-strong: #0a6a4c;
230
234
  --gw-warning: #9a6700;
231
235
  --gw-warning-soft: #fbf1dc;
232
236
  --gw-danger: #b42318;
@@ -259,6 +263,7 @@
259
263
  --gw-on-accent: var(--gw-on-accent-dark);
260
264
  --gw-success: #34c38f;
261
265
  --gw-success-soft: #132a22;
266
+ --gw-success-strong: #34c38f;
262
267
  --gw-warning: #e0b341;
263
268
  --gw-warning-soft: #2b2410;
264
269
  --gw-danger: #ff6b5a;
package/demo/header.html CHANGED
@@ -44,7 +44,7 @@
44
44
  <body class="gw-page">
45
45
  <a class="gw-skip" href="#main">Skip to content</a>
46
46
 
47
- <div class="gw-announce" role="region" aria-label="Announcement"><span class="gw-announce__tag">NEW</span> The GemBoss community is live. <a href="https://community.gemboss.ai">Join founders building brand-true stores &rarr;</a></div>
47
+ <div class="gw-announce" role="region" aria-label="Announcement"><span class="gw-announce__tag">New</span> The GemBoss community is live. <a href="https://community.gemboss.ai">Join founders building brand-true stores &rarr;</a></div>
48
48
  <header class="gw-header" data-gw-header>
49
49
  <div class="gw-header__bar">
50
50
  <a class="gw-brand" href="https://gemboss.ai/" data-gw-show="brand:gemboss"><svg class="gw-brand__mark" viewBox="0 0 100 116" aria-hidden="true"><polygon points="26,14 74,14 66,50 34,50" style="fill:var(--gw-accent)"/><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="9" stroke-linejoin="round"/><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linejoin="round"/></svg><span class="gw-brand__name">GemBoss</span></a><a class="gw-brand" href="header.html#gempages" data-gw-show="brand:gempages" hidden><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>
@@ -214,6 +214,22 @@ small text on a tint).
214
214
 
215
215
  Don't: make a badge clickable; write a sentence in it; use success or warning for anything but state.
216
216
 
217
+ ### Status chip `.gw-status`
218
+
219
+ 0.14.0, gemboss.ai's "pills": a state inside a drawn screen of the product, as the GemBoss app draws it (Live, A/B test,
220
+ Early access). A rounded rectangle at the action radius (8px; 12px in `soft`), 28px tall, 12px/500 words, neutral by
221
+ default (ink at 8%). `--success` is live (its words in `--gw-success-strong`, 5.6:1 on the tint), `--accent` a test,
222
+ `--early` neutral with the dot in the accent. The dot is an optional `<span class="gw-status__dot">` before the words.
223
+
224
+ ```html
225
+ <span class="gw-status gw-status--success"><span class="gw-status__dot"></span>Live</span>
226
+ <span class="gw-status gw-status--accent">A/B test</span>
227
+ <span class="gw-status gw-status--early"><span class="gw-status__dot"></span>Early access</span>
228
+ ```
229
+
230
+ A badge (`.gw-badge`, a pill) labels a thing on the page itself (New, Recommended); a status chip shows the state of
231
+ something inside a screen. Don't: a status chip outside a window or a drawn screen; a sentence in it; a click handler.
232
+
217
233
  ### Cards `.gw-card`
218
234
 
219
235
  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
@@ -256,6 +272,11 @@ A framed screen: `__bar` (three dots and where the screen lives), `__body` (16px
256
272
  `<img>`) and `__caption`. Use it around real product screenshots, or around a drawn mock-up built in the site's CSS with
257
273
  the tokens.
258
274
 
275
+ 0.14.0, gemboss.ai's frame 1:1: it floats (`--gw-shadow-raised`), 13px words at 20px lines. The bar is 40px with 12px
276
+ sides: three 8px dots, then where the screen lives 16px after them (12px, muted, one line, cut when too long; `<b>` words
277
+ in ink), then anything else 8px apart (a status chip, an action of the mock-up, which keep the window's ink). Up to 560px
278
+ the bar's words wrap instead and the bar grows, its parts at the top.
279
+
259
280
  `__caption` says what the numbers in the window are: 12px, muted, at the end of the window, the last child after
260
281
  `__body` (a `<figcaption>` when the window is a `<figure>`). Under a drawn mock-up whose numbers are made up it says
261
282
  **"Illustrative data."**, in exactly these words: vi "Dữ liệu minh hoạ.", zh "示意数据。". Add a second sentence only when
@@ -274,6 +295,24 @@ the mock-up shows a demo brand ("Vital Glow is a GemBoss demo brand."). Under re
274
295
  Don't: put interactive controls inside a mock-up; show made-up numbers without the caption; write the caption in
275
296
  other words ("Sample", "Demo numbers") or leave it in English on a translated page.
276
297
 
298
+ ### Figure `.gw-figure`
299
+
300
+ 0.13.0 (Gemians): a photo and its caption outside the prose, where a section shows one: beside the words of a split,
301
+ under a hero's actions (48px below, as a window). The picture takes the card radius; the `<figcaption>` is 14px,
302
+ muted, 12px under it, the look of a figure inside `.gw-prose`. A screen of the product goes in `.gw-window`; a photo
303
+ of people, a place or a product goes here.
304
+
305
+ ```html
306
+ <figure class="gw-figure">
307
+ <img src="/img/team-2025.webp" alt="The team together in the office, 2025" width="1000" height="471" loading="lazy">
308
+ <figcaption>The GemCommerce team, 2025.</figcaption>
309
+ </figure>
310
+ ```
311
+
312
+ Don't: a photo of people without their permission, or of another brand without its; a caption that repeats the alt
313
+ word for word (the alt says what is in the picture, the caption says what it is); a frame, a shadow or a radius of
314
+ your own.
315
+
277
316
  ### Icon `.gw-icon`
278
317
 
279
318
  A 20px box for an inline stroke SVG (1.7–2px stroke, `currentColor`, `aria-hidden="true"`). Inside `.gw-feature` it
@@ -287,7 +326,10 @@ Don't: emoji as icons; filled multi-colour icons; icon-only buttons without `ari
287
326
 
288
327
  ### Check list `.gw-list--check`
289
328
 
290
- A `<ul>` whose items are led by an accent check mark.
329
+ A `<ul>` whose items are led by a check mark: 0.14.0, gemboss.ai's, 1:1: 14px at 1.5 lines, the words 28px in, the mark a
330
+ 16px tile on the accent's tint (4px corners, 4px down) with the tick in the accent, so it follows the theme and the brand.
331
+ An item may hold words and links side by side (a plain link in an item gets the link style). An item with its own
332
+ vertical padding moves the mark with it: set `top` on its `::before` and `::after`.
291
333
 
292
334
  ```html
293
335
  <ul class="gw-list--check"><li>Checkout stays on Shopify</li><li>No overage fees</li></ul>
@@ -314,7 +356,10 @@ Don't: put the class on the `<table>` itself; tables for layout; colour cells to
314
356
 
315
357
  ### FAQ `.gw-faq`
316
358
 
317
- Native `<details class="gw-faq__item">` with a `<summary>`, no script. The + turns into × when open.
359
+ Native `<details class="gw-faq__item">` with a `<summary>`, no script. 0.14.0, gemboss.ai's 1:1: the question 18px/600 in
360
+ the brand face alone, the + the face's own glyph in the accent, turned 45 degrees into × when open; the answer muted,
361
+ 16px, a paragraph at most 33em, rising in as it opens (not under reduced motion). On phones (up to 560px) the rows
362
+ tighten, never under 44px.
318
363
 
319
364
  ```html
320
365
  <div class="gw-faq">
@@ -341,6 +386,7 @@ level. A brand changes what it fills in and the tokens (colours, the Stage, the
341
386
  | `.gw-header--float` | with `.gw-header`, a brand's header profile: from 1081px 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 up to 1080px; solid under `prefers-reduced-transparency` |
342
387
  | `.gw-header__bar` | the row inside: the page's wrap + 12px (1132px on GemBoss), 24px inside (6px up to 640px), 32px between its parts |
343
388
  | `.gw-brand`, `__mark`, `__name` | logo (34px, its corners clipped at 8px) and name, linking home; a 44px row up to 768px |
389
+ | `.gw-brand__logo`, `--dark` | 0.13.0: a brand's logo drawn as one file (mark and wordmark), 26px tall, in place of `__mark` and `__name`, `alt` the brand's name; `--dark` a second file shown in dark only, right after the first (docs/theming.md) |
344
390
  | `.gw-nav`, `__list`, `__item`, `__trigger`, `__chev`, `__link` | the desktop nav (from 1081px), 14px weight 500, 0.005em tighter; `__trigger` is a section's link that opens its mega panel (its 12px `__chev` turns while open), `__link` a plain link; the current page (`aria-current="page"`) or section (`aria-current` on a trigger) in the accent's strong ink, weight 600 |
345
391
  | `.gw-mega` (`--gw-mega-w`, `--gw-mega-pv`, `--gw-mega-cols`), `__body`, `__grid`, `__col`, `__heading`, `__link`, `__aside`, `__card`, `__art`, `__card-text`, `__go`, `__foot`, `__foot-start`, `__foot-end`, `__cta`, `__new` | 0.11.0, gemboss.ai's mega menu: a panel on one surface that morphs between panels (the popover radius, raised shadow), a caret at the open name, a pill under the hovered name, a highlight under the hovered row; columns of rows (a name and one line), the pictures beside them (`__aside`: the panel's own `__card`, a row's preview from its `<template>`), each picture on the Stage (`__art`: the deep iris ground, `--gw-stage-*`); an optional strip under it. The page behind dims and blurs (`.gw-scrim`). Script: `docs/markup-contract.md` |
346
392
  | `.gw-header__actions` | Sign in, a second text action, Start free, the menu button (4px apart; the button 8px further) |
package/docs/theming.md CHANGED
@@ -3,8 +3,9 @@
3
3
  A GemCommerce brand (GemPages, Gemians…) uses the kit as it is and chooses six things, **and nothing else**:
4
4
 
5
5
  1. **the accent variables** (eight, light and dark),
6
- 2. **the wash hues** (only if the brand has its own),
7
- 3. **the logo** (and the favicon),
6
+ 2. **the wash hues** (only if the brand has its own), and the colours of its **announcement bar** and **Stage** (0.13.0,
7
+ only if it uses them and wants its own),
8
+ 3. **the logo** (and the favicon): a mark beside the name, or one drawing of both (0.13.0),
8
9
  4. **the action colour mode**: `data-gw-action="ink|accent"` on `<html>`,
9
10
  5. **the radius profile**: `data-gw-radius="tight|soft"` on `<html>`,
10
11
  6. **the header style**: the flat bar, or `.gw-header--float`.
@@ -46,9 +47,30 @@ brand's page lights in the brand's own colour, in light, in dark and in night ba
46
47
  takes the corners of the element it lights, so both radius profiles hold. A brand does not choose its colour, its
47
48
  strength or its size (they are tokens), and never a second colour.
48
49
 
49
- The logo and the favicon are files, not variables: the `<img class="gw-brand__mark">` (26×26, SVG preferred, with
50
- `alt=""` because the name is written next to it) and the `<span class="gw-brand__name">` in the header and footer
51
- partials, and `<link rel="icon">` in the page head.
50
+ The logo and the favicon are files, not variables, in the header and footer partials and `<link rel="icon">` in the
51
+ page head. The logo is one of two shapes:
52
+
53
+ - **a mark beside the name** (GemBoss): `<img class="gw-brand__mark">` (34×34, SVG preferred, `alt=""` because the name
54
+ is written next to it) and `<span class="gw-brand__name">`;
55
+ - **one drawing of both** (0.13.0, Gemians: the mark and the wordmark in one file): `<img class="gw-brand__logo">`, 26px
56
+ tall, its width the file's, its `alt` the brand's name. A logo that only works on a dark ground (a white wordmark)
57
+ comes with a second file in ink for the light theme, or the other way round: the file for light grounds first, then
58
+ the file for dark grounds as `class="gw-brand__logo gw-brand__logo--dark"`. The kit shows one per theme (the system's
59
+ setting or the toggle). Keep the brand's own drawing: recolour the wordmark, never redraw it in the kit's type.
60
+
61
+ ```html
62
+ <a class="gw-brand" href="/"><img class="gw-brand__logo" src="/assets/img/logo.png" alt="Gemians" width="150" height="26"><img class="gw-brand__logo gw-brand__logo--dark" src="/assets/img/logo-on-dark.png" alt="Gemians" width="150" height="26"></a>
63
+ ```
64
+
65
+ `check` reports a logo without its name in `alt`, without `width` and `height`, or a dark file without the light one
66
+ right before it (`brand-markup`).
67
+
68
+ **The announcement bar and the Stage** (0.13.0). The bar over the header (`--gw-announce-*`, `system.json`
69
+ `color.announce`) and the Stage the mega menu's pictures sit on (`--gw-stage-*`, `color.stage`) are GemBoss's iris by
70
+ default. A brand that shows them may set its own in the same `:root` block: the bar's `bg`, `ink`, `line`, `tag`,
71
+ `tag-ink`, `link`, `link-line`; the Stage's `glow`, `glow-2`, `top`, `bottom`, `dot`, `ink`. Both stay the same in
72
+ light and dark. `check` computes the bar's words and link on its ground and the Stage's words on both ends of its
73
+ gradient, and fails below 4.5:1 (`brand-contrast`).
52
74
 
53
75
  The footer (0.7.0, `templates/footer.html`) is the same for every brand; a brand fills it. Its family row lists the
54
76
  brand's siblings (a GemPages site: GemBoss, GemX, GemUpsells), its Ask AI row asks about the brand, and its wordmark
@@ -106,8 +128,9 @@ button filled from site CSS (`accent-button`).
106
128
 
107
129
  `check` also reports: colours outside the tokens, a redefined kit token (`token-redefined`, including the radius and
108
130
  action tokens), overrides of `.gw-*` classes, an unknown profile or one set on another element than `<html>`
109
- (`profile-unknown`), `.gw-header--float` anywhere but the header itself (`header-float-markup`), and, on a page with
110
- `data-gw-action="accent"`, accent button words below 4.5:1 (`action-contrast`).
131
+ (`profile-unknown`), `.gw-header--float` anywhere but the header itself (`header-float-markup`), on a page with
132
+ `data-gw-action="accent"`, accent button words below 4.5:1 (`action-contrast`), a brand's own announcement bar or
133
+ Stage with words below 4.5:1 (`brand-contrast`), and a one-file logo without its markup (`brand-markup`).
111
134
 
112
135
  ## A full example
113
136
 
@@ -136,7 +159,7 @@ action tokens), overrides of `.gw-*` classes, an unknown profile or one set on a
136
159
  …
137
160
  <header class="gw-header gw-header--float" data-gw-header>
138
161
  <div class="gw-wrap gw-header__bar">
139
- <a class="gw-brand" href="/"><img class="gw-brand__mark" src="/logo-mark.svg" alt="" width="26" height="26"><span class="gw-brand__name">GemPages</span></a>
162
+ <a class="gw-brand" href="/"><img class="gw-brand__mark" src="/logo-mark.svg" alt="" width="34" height="34"><span class="gw-brand__name">GemPages</span></a>
140
163
  ```
141
164
 
142
165
  ## Check the contrast before you ship
@@ -145,8 +168,10 @@ The accent family is read as text: the kit draws links, step numbers, active men
145
168
  `--gw-accent-strong`, and the label square, focus ring, check marks and the featured plan's hairline in `--gw-accent`.
146
169
  So:
147
170
 
148
- 1. **Accent text on white at least 4.5:1**: `--gw-accent-light` and `--gw-accent-strong-light` on `#ffffff`.
149
- Check `--gw-accent-strong-light` on `--gw-bg-soft` (`#f2f3f5`) too, where links sit in soft sections.
171
+ 1. **Accent text on white at least 4.5:1**: `--gw-accent-strong-light` on `#ffffff` and on `--gw-bg-soft`
172
+ (`#f2f3f5`), where links sit in soft sections. The plain `--gw-accent-light` is never text (icons, marks, the focus
173
+ ring, the featured plan's hairline): **3:1** on `#ffffff` is enough (WCAG 1.4.11), so a gold or a light green can
174
+ stay bright there (0.13.0; this line asked 4.5:1 of it before, which no gold passes).
150
175
  2. `--gw-accent-strong-light` on `--gw-accent-soft-light`: at least 4.5:1 (the accent badge, the active chip and the
151
176
  chosen option of a segmented control).
152
177
  3. `--gw-accent-dark` and `--gw-accent-strong-dark` on `#0d0d12` (dark page) and on `#0c0e14` (night band): at
@@ -158,7 +183,9 @@ So:
158
183
  build does the same for its own colours.
159
184
 
160
185
  Any WCAG contrast checker works (paste the two hex values). A bright brand colour (yellow, light green, cyan) almost
161
- never passes on white: darken it for `-light` and keep the bright one for `-dark`. If you cannot get 4.5:1 without
186
+ never passes as text on white: darken it for `-strong-light`, keep it near the brand for `-light` (3:1) and bright
187
+ for `-dark`. A colour that needs dark words on it (gold, yellow) cannot be the button's fill and its text colour at
188
+ once: keep the ink button (Gemians does). If you cannot get 4.5:1 without
162
189
  changing the brand colour beyond recognition, keep the kit's accent and use the brand colour only in the logo and
163
190
  illustrations, and keep the ink button.
164
191
 
package/js/website.js CHANGED
@@ -1,9 +1,9 @@
1
- /*! @gemboss/website-ui 0.12.0 website.js | GENERATED by scripts/build.mjs from js/website.mjs (the source, with its comments): edit that file | spec: docs/markup-contract.md | load with defer */
1
+ /*! @gemboss/website-ui 0.14.0 website.js | GENERATED by scripts/build.mjs from js/website.mjs (the source, with its comments): edit that file | spec: docs/markup-contract.md | load with defer */
2
2
  (function () {
3
3
  'use strict';
4
4
  if (window.gwWebsite) return;
5
5
 
6
- var version = '0.12.0';
6
+ var version = '0.14.0';
7
7
  var api = { version: version, init: init, destroy: destroy };
8
8
  var doc, root, mine, fine, sys, wide, desk, mid, live = [], queued = false;
9
9
  var still, lights;
package/js/website.mjs CHANGED
@@ -6,7 +6,7 @@
6
6
  // the export line and the // comments, plus auto-init): edit this file, then run the build. Keep comments on lines of
7
7
  // their own, so the build can leave them out.
8
8
 
9
- var version = '0.12.0';
9
+ var version = '0.14.0';
10
10
  var api = { version: version, init: init, destroy: destroy };
11
11
  // Page state. Set up by the first init(), never at import: a server can import this file.
12
12
  var doc, root, mine, fine, sys, wide, desk, mid, live = [], queued = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemboss/website-ui",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "The GemBoss website kit: tokens, header, footer, sections and the rules (AGENTS.md) that GemCommerce public websites share, plus a CLI that checks a site, reports kit gaps and upgrades.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/system.json CHANGED
@@ -21,6 +21,7 @@
21
21
  "on-accent": "#ffffff",
22
22
  "success": "#007f5f",
23
23
  "success-soft": "#e3f1df",
24
+ "success-strong": "#0a6a4c",
24
25
  "warning": "#9a6700",
25
26
  "warning-soft": "#fbf1dc",
26
27
  "danger": "#b42318",
@@ -51,6 +52,7 @@
51
52
  "on-accent": "#0d0d12",
52
53
  "success": "#34c38f",
53
54
  "success-soft": "#132a22",
55
+ "success-strong": "#34c38f",
54
56
  "warning": "#e0b341",
55
57
  "warning-soft": "#2b2410",
56
58
  "danger": "#ff6b5a",
@@ -91,7 +93,7 @@
91
93
  "roles": {
92
94
  "action": "The thing you click: ink, or the brand's accent when the site chose the accent action profile (profiles.action).",
93
95
  "accent": "Links, focus rings, active state, a chart line, the 8px label square; the primary button only through the accent action profile. Never body text, never words in a heading, never a section band.",
94
- "success/warning/danger": "State only. Their -soft tints are grounds: a tip or a warning callout, a success or an error badge, a field marked invalid. danger (0.6.0, gap #28) is the dashboard's --gemboss-danger family: the words of a field's error and the fill of a destructive button (on-danger on it).",
96
+ "success/warning/danger": "State only. success-strong (0.14.0) is success's text form, the words of a status chip on its tint (gemboss.ai's emerald ink, 5.6:1). Their -soft tints are grounds: a tip or a warning callout, a success or an error badge, a field marked invalid. danger (0.6.0, gap #28) is the dashboard's --gemboss-danger family: the words of a field's error and the fill of a destructive button (on-danger on it).",
95
97
  "line-control": "The border of a form control (a field, a checkbox, a radio), 0.6.0: it must reach 3:1 on bg, bg-soft and surface, light and dark, where line and line-strong are hairlines that do not. Like the dashboard's --gemboss-field-line.",
96
98
  "wash": "The hero wash and illustrations only.",
97
99
  "scrim": "The veil over the page while a mega menu is open (0.11.0): a dim and a 4px blur, as gemboss.ai.",
@@ -134,7 +134,7 @@
134
134
  <div class="gw-wrap">
135
135
  <div class="gw-footer__grid">
136
136
  <div class="gw-footer__brand">
137
- <a class="gw-brand" href="/"><img class="gw-brand__mark" src="[/logo.svg]" alt="" width="26" height="26"><span class="gw-brand__name">[Brand]</span></a>
137
+ <a class="gw-brand" href="/"><img class="gw-brand__mark" src="[/logo.svg]" alt="" width="34" height="34"><span class="gw-brand__name">[Brand]</span></a>
138
138
  <p>[One sentence on what the brand does for the reader.]</p>
139
139
  </div>
140
140
  <nav class="gw-footer__col" aria-label="[Column heading]">
@@ -11,6 +11,8 @@
11
11
  role="status" line ("Sent. We'll reply to name@example.com.").
12
12
  - Consent boxes start unchecked, say exactly what the reader gets, and are never required to get something else.
13
13
  - One field and its button (a newsletter, "email me the guide"): the .gw-form-inline row, see templates/footer.html.
14
+ As a page's first screen (a contact page, 0.13.0): the heading is the page's one <h1>, drawn at the section size
15
+ (<h1 class="gw-h2">), and the page needs no hero above it.
14
16
  Limits: three fields, one .gw-btn; no field that the team does not read.
15
17
  -->
16
18
  <section class="gw-section" aria-labelledby="[form-h]">
@@ -3,7 +3,8 @@
3
3
  Variants: .gw-hero--center (centred, for short promises), .gw-hero--compact (subpages: less height, h2-sized h1).
4
4
  Add .gw-band--night for a dark hero (it counts as one of the page's three night bands).
5
5
  Limits: a short label, a two-part headline (muted first sentence, ink second), at most two lines under it,
6
- one .gw-btn and one secondary action. The optional window holds a real product screen (img with alt, width, height)
6
+ one .gw-btn and one secondary action. Instead of the window, a photo: <figure class="gw-figure"> with its caption.
7
+ The optional window holds a real product screen (img with alt, width, height)
7
8
  or a drawn mock-up. Numbers in it that are not real get the caption "Illustrative data." (vi: "Dữ liệu minh hoạ.",
8
9
  zh: "示意数据。"); real ones get their source there. Never tuck the hero under the header.
9
10
  -->
@@ -2,7 +2,7 @@
2
2
  Split: one idea told in words beside a figure (a real screen, or a drawn mock-up of the product).
3
3
  .gw-split--reverse puts the figure on the left on wide screens; on phones the words always come first.
4
4
  Alternate --reverse between consecutive splits. Limits: label, h2, at most two lines, a check list of three to
5
- five items, one action (a link or one button). The figure is a .gw-window or a <figure> with alt text; numbers in
5
+ five items, one action (a link or one button). The figure is a .gw-window (a screen) or a <figure class="gw-figure"> (a photo, with alt text and a caption); numbers in
6
6
  a window that are not real get the caption "Illustrative data.", real ones their source.
7
7
  -->
8
8
  <section class="gw-section">
@@ -0,0 +1,23 @@
1
+ // Every static HTML page, served byte for byte from the built file and prerendered at build time, so each page is a
2
+ // file on the CDN. A more specific route (app/blog/[slug]/page.js…) wins over this catch-all. Unknown URL: 404.html, 404.
3
+ // Written by `gemboss-website init --site` (@gemboss/website-ui); the site owns it from here.
4
+ import { readFile } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ import { ROOT, pages } from "../../lib/pages.mjs";
7
+
8
+ export const dynamic = "force-static";
9
+ export const dynamicParams = true;
10
+
11
+ export function generateStaticParams() {
12
+ return [...pages().keys()].map((route) => ({ path: route === "/" ? [] : route.slice(1).split("/") }));
13
+ }
14
+
15
+ const HTML = { "content-type": "text/html; charset=utf-8" };
16
+
17
+ export async function GET(_req, { params }) {
18
+ const { path } = await params;
19
+ const route = "/" + (path || []).join("/");
20
+ const file = pages().get(route);
21
+ if (file) return new Response(await readFile(join(/*turbopackIgnore: true*/ ROOT, file)), { status: 200, headers: HTML });
22
+ return new Response(await readFile(join(/*turbopackIgnore: true*/ ROOT, "404.html")), { status: 404, headers: HTML });
23
+ }
@@ -0,0 +1,3 @@
1
+ /* Stopgaps for what @gemboss/website-ui does not have yet (AGENTS.md section 9). Each: classes prefixed kitgap-, the
2
+ kit's tokens and scales only, never a .gw-* override, and a comment naming its gap (gap #N, from
3
+ npx gemboss-website gap). npx gemboss-website upgrade says when the kit ships it: then delete the stopgap. */
@@ -0,0 +1,16 @@
1
+ /* The site's own CSS, linked after the kit (website.css). Two jobs only (AGENTS.md sections 7 and 8):
2
+ 1. the brand's choices, in ONE :root block: its accent family and wash, and if it has its own, its announcement bar
3
+ and Stage colours (docs/theming.md). Delete the lines the brand keeps from the kit; GemBoss sets none.
4
+ 2. what the kit cannot know: an illustration, a drawn mock-up of the product. Tokens and scales only, classes with
5
+ the site's own prefix, never .gw-*. Something reusable the kit lacks is a gap: kit-gaps.css. */
6
+ :root {
7
+ /* --gw-accent-light: #5661f5; the accent on light pages: links' marks, focus ring, label square, check marks */
8
+ /* --gw-accent-strong-light: #3a44de; accent text: 4.5:1 on --gw-bg and --gw-bg-soft */
9
+ /* --gw-accent-soft-light: #eef0fe; its tint */
10
+ /* --gw-on-accent-light: #ffffff; words on it (the button, under data-gw-action="accent") */
11
+ /* --gw-accent-dark: #7c84ff; the same four in dark mode and night bands */
12
+ /* --gw-accent-strong-dark: #a3a9ff; */
13
+ /* --gw-accent-soft-dark: #1f2140; */
14
+ /* --gw-on-accent-dark: #0d0d12; */
15
+ /* --gw-wash-pink: #ff6f5e; --gw-wash-violet: #7c8af7; --gw-wash-sky: #43b0ff; --gw-wash-mint: #1fcf9f; */
16
+ }