@gemboss/website-ui 0.7.0 → 0.9.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 -3
- package/CHANGELOG.md +129 -0
- package/README.md +8 -8
- package/bin/lib/check.mjs +72 -1
- package/css/effects.css +71 -0
- package/css/header.css +176 -0
- package/css/tokens.css +7 -0
- package/demo/header.html +356 -0
- package/demo/index.html +2 -0
- package/demo/spotlight.html +291 -0
- package/docs/components.md +116 -4
- package/docs/markup-contract.md +135 -5
- package/docs/theming.md +9 -2
- package/js/website.js +168 -4
- package/js/website.mjs +208 -4
- package/package.json +1 -1
- package/skill/SKILL.md +6 -0
- package/system.json +13 -1
- package/templates/header.html +34 -6
- package/website.css +256 -1
- package/website.min.css +1 -1
package/css/header.css
CHANGED
|
@@ -261,3 +261,179 @@ html.is-menu-open .gw-header { position: fixed; top: 0; left: 0; right: 0; }
|
|
|
261
261
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
262
262
|
.gw-header--float { background: var(--gw-surface); }
|
|
263
263
|
}
|
|
264
|
+
|
|
265
|
+
/* ---- 0.8.0: the drill-down mobile menu (gap #41) and the language switcher (gap #29) --------------------------------
|
|
266
|
+
Every rule below selects a part 0.7.0 does not have (data-gw-menu="drill", the script's .is-drill, the menu's
|
|
267
|
+
__head, __back, __title, __heading and __icon, .gw-lang), so a 0.7.0 header looks and works exactly as before. */
|
|
268
|
+
|
|
269
|
+
/* The drill-down menu, as gemboss.ai's phone menu (v4, approved 15/10/2026), opt-in on the sheet:
|
|
270
|
+
<div class="gw-mobile-menu" data-gw-menu="drill">. Its look needs only the attribute: top-level rows of 64px with
|
|
271
|
+
24px words and one hairline each, a level's links with an optional icon and line, and the actions in a bar that
|
|
272
|
+
stays at the bottom of the sheet while the list scrolls (the sheet has no bottom padding, so nothing peeks out under
|
|
273
|
+
the bar; nor sideways while a level slides in). Without the script the groups are the 0.7.0 <details> that open in
|
|
274
|
+
place, their chevron pointing down. */
|
|
275
|
+
.gw-mobile-menu[data-gw-menu="drill"] { display: flex; flex-direction: column; overflow-x: hidden; padding-bottom: 0; }
|
|
276
|
+
.gw-mobile-menu[data-gw-menu="drill"] > nav { display: flex; flex: 1 0 auto; flex-direction: column; }
|
|
277
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__group > summary,
|
|
278
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__top { min-height: 64px; font-size: 24px; letter-spacing: -0.01em; }
|
|
279
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__group > summary::after { width: 20px; height: 20px; -webkit-mask-size: 20px; mask-size: 20px; }
|
|
280
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__list { padding: 0; }
|
|
281
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__list .gw-mobile-menu__link {
|
|
282
|
+
gap: 12px;
|
|
283
|
+
min-height: 56px;
|
|
284
|
+
padding-block: 12px;
|
|
285
|
+
border-bottom: 1px solid var(--gw-line);
|
|
286
|
+
color: var(--gw-ink);
|
|
287
|
+
font-weight: 500;
|
|
288
|
+
}
|
|
289
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__link b { display: block; font-size: 16px; font-weight: 500; line-height: 20px; }
|
|
290
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__link i { display: block; margin-top: 2px; color: var(--gw-muted); font-size: 13px; font-style: normal; line-height: 18px; }
|
|
291
|
+
/* a level's optional parts: a muted heading over a run of links, a 32px icon tile before a link's words */
|
|
292
|
+
.gw-mobile-menu__heading { padding: 16px 0 8px; color: var(--gw-muted); font-size: 12px; font-weight: 500; line-height: 1.5; }
|
|
293
|
+
.gw-mobile-menu__icon {
|
|
294
|
+
display: grid;
|
|
295
|
+
place-items: center;
|
|
296
|
+
flex: none;
|
|
297
|
+
width: 32px;
|
|
298
|
+
height: 32px;
|
|
299
|
+
border: 1px solid var(--gw-line);
|
|
300
|
+
border-radius: var(--gw-radius-action);
|
|
301
|
+
color: var(--gw-muted);
|
|
302
|
+
}
|
|
303
|
+
.gw-mobile-menu__icon svg { width: 16px; height: 16px; }
|
|
304
|
+
/* a level's last link, to the section's own page: its row opens the level instead of going there */
|
|
305
|
+
.gw-mobile-menu__all { display: flex; align-items: center; gap: 6px; min-height: 56px; color: var(--gw-accent-strong); font-size: 16px; font-weight: 500; }
|
|
306
|
+
.gw-mobile-menu__all::after { content: "\2192"; }
|
|
307
|
+
/* the action bar: what is not a button (Sign in, the language) on the first line, the last one on the right; the
|
|
308
|
+
buttons side by side under it (gw-btn--lg: 48px, as gemboss.ai) */
|
|
309
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions {
|
|
310
|
+
position: sticky;
|
|
311
|
+
bottom: 0;
|
|
312
|
+
z-index: 1;
|
|
313
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
314
|
+
align-items: center;
|
|
315
|
+
gap: 8px 16px;
|
|
316
|
+
margin-top: auto;
|
|
317
|
+
padding-block: 12px calc(12px + env(safe-area-inset-bottom));
|
|
318
|
+
border-top: 1px solid var(--gw-line);
|
|
319
|
+
background: var(--gw-bg);
|
|
320
|
+
}
|
|
321
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > .gw-btn { grid-row: 2; }
|
|
322
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > :not(.gw-btn) { grid-row: 1; }
|
|
323
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > :not(.gw-btn):last-child { grid-column: 2; justify-self: end; }
|
|
324
|
+
.gw-mobile-menu[data-gw-menu="drill"] .gw-mobile-menu__actions > .gw-mobile-menu__link { color: var(--gw-ink); font-weight: 500; }
|
|
325
|
+
|
|
326
|
+
/* Wired (.is-drill, set by website.js): a group's row points right and opens its level, which shows alone
|
|
327
|
+
(.is-drilled) under its head: Back on the left, the level's title in the middle. The sheet starts where the header
|
|
328
|
+
ends (--gw-menu-top, measured by the script) and the header stays in place, so an announcement bar above it does not
|
|
329
|
+
jump away. */
|
|
330
|
+
.gw-mobile-menu.is-drill { top: var(--gw-menu-top, calc(var(--gw-header-height) + 1px)); }
|
|
331
|
+
html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky; }
|
|
332
|
+
.gw-mobile-menu.is-drill .gw-mobile-menu__group > summary::after { transform: rotate(-90deg); transition: none; }
|
|
333
|
+
.gw-mobile-menu.is-drilled > nav > :not([open], .gw-mobile-menu__actions),
|
|
334
|
+
.gw-mobile-menu.is-drilled .gw-mobile-menu__group[open] > summary { display: none; }
|
|
335
|
+
.gw-mobile-menu__head { display: none; }
|
|
336
|
+
.gw-mobile-menu.is-drilled .gw-mobile-menu__group[open] > .gw-mobile-menu__head {
|
|
337
|
+
display: grid;
|
|
338
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
339
|
+
align-items: center;
|
|
340
|
+
gap: 8px;
|
|
341
|
+
min-height: 56px;
|
|
342
|
+
margin-bottom: 8px;
|
|
343
|
+
border-bottom: 1px solid var(--gw-line);
|
|
344
|
+
}
|
|
345
|
+
.gw-mobile-menu__back {
|
|
346
|
+
display: inline-flex;
|
|
347
|
+
align-items: center;
|
|
348
|
+
justify-self: start;
|
|
349
|
+
gap: 4px;
|
|
350
|
+
min-height: var(--gw-tap-min);
|
|
351
|
+
padding: 0 8px 0 0;
|
|
352
|
+
border: 0;
|
|
353
|
+
background: transparent;
|
|
354
|
+
color: var(--gw-muted);
|
|
355
|
+
font-size: 16px;
|
|
356
|
+
line-height: 1.5;
|
|
357
|
+
}
|
|
358
|
+
.gw-mobile-menu__back:hover { color: var(--gw-ink); }
|
|
359
|
+
.gw-mobile-menu__back::before {
|
|
360
|
+
content: "";
|
|
361
|
+
flex: none;
|
|
362
|
+
width: 16px;
|
|
363
|
+
height: 16px;
|
|
364
|
+
background: currentColor;
|
|
365
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M15 18l-6-6 6-6' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
|
|
366
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M15 18l-6-6 6-6' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
|
|
367
|
+
}
|
|
368
|
+
.gw-mobile-menu__title { color: var(--gw-ink); font-size: 16px; font-weight: 600; letter-spacing: 0; line-height: 1.5; text-align: center; }
|
|
369
|
+
/* a level slides in from the right, the top level back in from the left; under reduced motion they just switch */
|
|
370
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
371
|
+
.gw-mobile-menu.is-drilled .gw-mobile-menu__group[open] { animation: gw-drill-in 0.24s var(--gw-ease); }
|
|
372
|
+
.gw-mobile-menu.is-back > nav > :not(.gw-mobile-menu__actions) { animation: gw-drill-back 0.24s var(--gw-ease); }
|
|
373
|
+
}
|
|
374
|
+
@keyframes gw-drill-in { from { opacity: 0; transform: translateX(32px); } }
|
|
375
|
+
@keyframes gw-drill-back { from { opacity: 0; transform: translateX(-32px); } }
|
|
376
|
+
|
|
377
|
+
/* The language switcher: <div class="gw-lang" data-gw-lang hidden>, a ghost button with the globe and the current
|
|
378
|
+
language's short code, and its list of the other languages, a small popover card (the card radius of the site's
|
|
379
|
+
profile, one hairline, the raised shadow). website.js fills the list from the page's <link rel="alternate" hreflang>
|
|
380
|
+
and takes `hidden` away when there is another language. In the header's actions it shows from 1080px, where the nav
|
|
381
|
+
is; below that a second one sits in the mobile menu's action bar, and its list opens upward. */
|
|
382
|
+
.gw-lang { position: relative; flex: none; }
|
|
383
|
+
.gw-header__actions > .gw-lang { display: none; }
|
|
384
|
+
.gw-lang__btn {
|
|
385
|
+
display: inline-flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
gap: 6px;
|
|
388
|
+
height: 40px;
|
|
389
|
+
padding: 0 12px;
|
|
390
|
+
border: 0;
|
|
391
|
+
border-radius: var(--gw-radius-action);
|
|
392
|
+
background: transparent;
|
|
393
|
+
color: var(--gw-ink-2);
|
|
394
|
+
font-size: 14px;
|
|
395
|
+
font-weight: 500;
|
|
396
|
+
line-height: 1.4;
|
|
397
|
+
white-space: nowrap;
|
|
398
|
+
}
|
|
399
|
+
.gw-lang__btn:hover,
|
|
400
|
+
.gw-lang__btn[aria-expanded="true"] { background: var(--gw-bg-soft); color: var(--gw-ink); }
|
|
401
|
+
.gw-lang__btn svg { flex: none; width: 18px; height: 18px; }
|
|
402
|
+
.gw-lang__menu {
|
|
403
|
+
position: absolute;
|
|
404
|
+
top: calc(100% + 8px);
|
|
405
|
+
right: 0;
|
|
406
|
+
z-index: 1;
|
|
407
|
+
display: grid;
|
|
408
|
+
gap: 2px;
|
|
409
|
+
min-width: 200px;
|
|
410
|
+
margin: 0;
|
|
411
|
+
padding: 8px;
|
|
412
|
+
list-style: none;
|
|
413
|
+
border: 1px solid var(--gw-line);
|
|
414
|
+
border-radius: var(--gw-radius-card);
|
|
415
|
+
background: var(--gw-surface);
|
|
416
|
+
box-shadow: var(--gw-shadow-raised);
|
|
417
|
+
}
|
|
418
|
+
.gw-lang__link {
|
|
419
|
+
display: flex;
|
|
420
|
+
align-items: center;
|
|
421
|
+
min-height: 40px;
|
|
422
|
+
padding: 0 12px;
|
|
423
|
+
border-radius: var(--gw-radius-action);
|
|
424
|
+
color: var(--gw-ink);
|
|
425
|
+
font-size: 14px;
|
|
426
|
+
font-weight: 500;
|
|
427
|
+
white-space: nowrap;
|
|
428
|
+
}
|
|
429
|
+
.gw-lang__link:hover { background: var(--gw-bg-soft); }
|
|
430
|
+
.gw-mobile-menu .gw-lang__btn { padding: 0 8px 0 0; color: var(--gw-muted); font-size: 16px; }
|
|
431
|
+
.gw-mobile-menu .gw-lang__btn:hover,
|
|
432
|
+
.gw-mobile-menu .gw-lang__btn[aria-expanded="true"] { background: transparent; color: var(--gw-ink); }
|
|
433
|
+
.gw-mobile-menu .gw-lang__menu { top: auto; right: auto; bottom: calc(100% + 8px); left: 0; }
|
|
434
|
+
@media (max-width: 768px), (pointer: coarse) {
|
|
435
|
+
.gw-lang__btn, .gw-lang__link { min-height: var(--gw-tap-min); }
|
|
436
|
+
}
|
|
437
|
+
@media (min-width: 1080px) {
|
|
438
|
+
.gw-header__actions > .gw-lang { display: block; }
|
|
439
|
+
}
|
package/css/tokens.css
CHANGED
|
@@ -69,6 +69,13 @@
|
|
|
69
69
|
--gw-ease: cubic-bezier(.22,1,.36,1);
|
|
70
70
|
--gw-reveal-ms: 700ms;
|
|
71
71
|
--gw-spin-ms: 700ms;
|
|
72
|
+
--gw-spot-radius: 260px;
|
|
73
|
+
--gw-spot-radius-action: 120px;
|
|
74
|
+
--gw-spot-strength: 22%;
|
|
75
|
+
--gw-spot-strength-action: 38%;
|
|
76
|
+
--gw-spot-strength-stage: 40%;
|
|
77
|
+
--gw-spot-ms: 550ms;
|
|
78
|
+
--gw-spot-ease: cubic-bezier(.16,1,.3,1);
|
|
72
79
|
--gw-wash-pink: #ff6f5e;
|
|
73
80
|
--gw-wash-violet: #7c8af7;
|
|
74
81
|
--gw-wash-sky: #43b0ff;
|