@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.
- package/AGENTS.md +18 -5
- package/CHANGELOG.md +65 -0
- package/README.md +6 -3
- package/bin/gemboss-website.mjs +13 -1
- package/bin/lib/build.mjs +101 -0
- package/bin/lib/check.mjs +47 -3
- package/bin/lib/init.mjs +93 -3
- package/css/article.css +1 -1
- package/css/components.css +87 -27
- package/css/header.css +13 -0
- package/css/ratings.css +0 -1
- package/css/sections.css +2 -3
- package/css/tokens.css +5 -0
- package/demo/header.html +1 -1
- package/docs/components.md +48 -2
- package/docs/theming.md +38 -11
- package/js/website.js +2 -2
- package/js/website.mjs +1 -1
- package/package.json +1 -1
- package/system.json +3 -1
- package/templates/page.html +1 -1
- package/templates/sections/form.html +2 -0
- package/templates/sections/hero.html +2 -1
- package/templates/sections/split.html +1 -1
- package/templates/site/app/[[...path]]/route.js +23 -0
- package/templates/site/assets/kit-gaps.css +3 -0
- package/templates/site/assets/site.css +16 -0
- package/templates/site/gitignore +4 -0
- package/templates/site/lib/pages.mjs +41 -0
- package/templates/site/next.config.mjs +15 -0
- package/templates/site/stage-public.mjs +49 -0
- package/templates/site/vercel.json +5 -0
- package/website.css +109 -33
- package/website.min.css +1 -1
package/css/components.css
CHANGED
|
@@ -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:
|
|
259
|
+
line-height: 20px;
|
|
233
260
|
}
|
|
234
261
|
.gw-window__bar {
|
|
235
262
|
display: flex;
|
|
236
263
|
align-items: center;
|
|
237
|
-
gap:
|
|
264
|
+
gap: 8px;
|
|
238
265
|
min-height: 40px;
|
|
239
|
-
padding:
|
|
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:
|
|
276
|
+
width: 8px;
|
|
248
277
|
height: 8px;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
.
|
|
275
|
-
|
|
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:
|
|
281
|
-
|
|
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='
|
|
286
|
-
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='
|
|
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:
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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;
|
|
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
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;
|
|
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">
|
|
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 →</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>
|
package/docs/components.md
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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`),
|
|
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="
|
|
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-
|
|
149
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.",
|
package/templates/page.html
CHANGED
|
@@ -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="
|
|
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.
|
|
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
|
+
}
|