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