@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.
@@ -3,8 +3,8 @@
3
3
  The full class list, including the CSS-only helpers added while building 0.1.0 (`.gw-h1`–`.gw-h4`, `.gw-lead`,
4
4
  `.gw-note`, `.gw-actions`, `.gw-stats__source`, `.gw-plan__price`, the mobile menu parts…), is `docs/components.md`.
5
5
 
6
- The CSS (`css/*.css`), the script (`js/website.js`) and the templates (`templates/`) agree on exactly this. Change it
7
- here first, then in all three.
6
+ The CSS (`css/*.css`), the script (`js/website.mjs`, and `js/website.js` built from it) and the templates
7
+ (`templates/`) agree on exactly this. Change it here first, then in all three.
8
8
 
9
9
  Prefixes: kit classes `gw-`, kit CSS variables `--gw-`, state classes `is-` (set by the script), hooks `data-gw-*`.
10
10
  BEM-ish: `.gw-block`, `.gw-block__part`, `.gw-block--variant`.
@@ -12,20 +12,96 @@ BEM-ish: `.gw-block`, `.gw-block__part`, `.gw-block--variant`.
12
12
  ## Page
13
13
 
14
14
  ```html
15
- <html lang="en" data-theme?="light|dark"> <!-- absent = follow the system; the toggle sets it -->
15
+ <html lang="en" data-theme?="light|dark" data-gw-action?="accent" data-gw-radius?="soft">
16
+ <!-- data-theme absent = follow the system; the toggle sets it. data-gw-action, data-gw-radius: brand profiles (0.3.0),
17
+ absent = ink, tight -->
16
18
  <body class="gw-page">
17
19
  <a class="gw-skip" href="#main">Skip to content</a>
18
20
  <div class="gw-announce" role="region" aria-label="Announcement">…<a href="…">…</a></div> <!-- optional -->
19
21
  <header class="gw-header" data-gw-header> … </header>
20
22
  <main id="main"> sections </main>
21
23
  <footer class="gw-footer"> … </footer>
22
- <script src="…/website.js" defer></script>
24
+ <script src="…/website.js" defer></script> <!-- or the ESM entry, see "Script" below -->
25
+ ```
26
+
27
+ ## Script (0.4.0)
28
+
29
+ One script wires every part below: the mega menu, the mobile menu, the theme toggle, reveal, the header's scroll
30
+ state, the current page, the segmented control and the busy button. It comes in two entries built from one source
31
+ (`js/website.mjs`; `scripts/build.mjs` writes `js/website.js` from it and `--check` fails when they differ):
32
+
33
+ | entry | file | how it starts |
34
+ |---|---|---|
35
+ | classic | `js/website.js` (`@gemboss/website-ui/website.js`) | `<script src="…/website.js" defer>`: wires the page on `DOMContentLoaded` by itself, then `window.gwWebsite` |
36
+ | ESM | `js/website.mjs` (`@gemboss/website-ui/website.mjs`) | `import { init, destroy, version } from '@gemboss/website-ui/website.mjs'`: importing does nothing (safe on a server); the app calls `init()` |
37
+
38
+ - **`init(root = document)`** wires the kit markup under `root`, `root` included. It is idempotent: an element is
39
+ wired once, so call it again after every client-side navigation or insert, and only the new elements are wired.
40
+ Called on a fragment, it wires that fragment and nothing else. It also lets go of elements removed from the page
41
+ since the last call, and moves the script's own `aria-current` mark to the new path. The listeners that belong
42
+ to the page (Escape, the outside click, the busy-button guard, the system theme, the scroll) are installed once.
43
+ - **`destroy(root = document)`** takes back what `init` wired under `root`: its listeners and its marks. An open
44
+ mega menu or mobile menu is closed first, so an unmounted menu cannot leave the page locked.
45
+ - **`version`**: `'0.4.0'`. The classic script exposes the three as `window.gwWebsite`.
46
+ - **`data-gw-ready`**: on each wired element, the parts wired on it (`mega`, `menu`, `theme`, `reveal`, `header`,
47
+ `segmented`). It is a mark for people and tests, not a switch: markup copied from a live page, attribute and all,
48
+ is still wired.
49
+ - **One copy per page.** The first copy that runs owns the page (`window.gwWebsite`); another one (the classic
50
+ script next to a bundle, two bundles) hands its calls to it, so nothing is wired twice.
51
+ - Never wire a kit part with the site's own script (a click handler on `.gw-nav__trigger`, a class toggled by hand):
52
+ insert the kit's markup, then call `init` on it.
53
+
54
+ ### After inserting markup
55
+
56
+ A Next.js app (App Router): one client component in the root layout, an effect on each route change. Effects run
57
+ after hydration, so the server's markup is never changed before React reads it.
58
+
59
+ ```jsx
60
+ // app/kit-script.jsx
61
+ 'use client';
62
+ import { useEffect } from 'react';
63
+ import { usePathname } from 'next/navigation';
64
+ import { init } from '@gemboss/website-ui/website.mjs';
65
+
66
+ export default function KitScript() {
67
+ const pathname = usePathname();
68
+ useEffect(() => { init(); }, [pathname]);
69
+ return null;
70
+ }
71
+ // app/layout.js: <body className="gw-page">{children}<KitScript /></body>
72
+ ```
73
+
74
+ A component that mounts and unmounts kit markup on its own (a dialog, a part that streams in later):
75
+ `useEffect(() => { const el = ref.current; init(el); return () => destroy(el); }, [])`, on a wrapper with
76
+ `style={{ display: 'contents' }}` so it does not change the layout.
77
+
78
+ A page that inserts HTML (a GemPages block rendered after load, a CMS preview), with the classic script:
79
+
80
+ ```js
81
+ var slot = document.getElementById('pricing-slot');
82
+ slot.innerHTML = html; // kit markup: a header, a segmented control, sections
83
+ window.gwWebsite.init(slot); // wires what is new in it; the rest of the page is left alone
84
+ ```
85
+
86
+ ### Events
87
+
88
+ The script tells the page's own code what changed. Every event is a `CustomEvent` that bubbles, so one listener on
89
+ `document` hears them all; none is fired by `init`.
90
+
91
+ | event | target | `detail` | fires when |
92
+ |---|---|---|---|
93
+ | `gw:mega` | the trigger (`[data-gw-mega]`) | `{ id, open }`: the panel's `id`, `true` when it opened | a panel opens or closes: click, hover, Escape, a click outside, focus leaving, a link inside, `destroy` of an open one. Moving to another menu fires a close, then an open |
94
+ | `gw:theme` | `<html>` | `{ theme: 'light' \| 'dark' }`: the theme the page now shows | a press on a theme toggle; also a change of the system's scheme while the page follows it (no `data-theme`) |
95
+ | `gw:segmented` | the group (`[data-gw-segmented]`) | `{ name, value }`: the group's name, the chosen option's `value` | the reader chooses an option (click, arrows, Home, End) |
96
+
97
+ ```js
98
+ document.addEventListener('gw:theme', function (e) { chart.setTheme(e.detail.theme); });
23
99
  ```
24
100
 
25
101
  ## Header
26
102
 
27
103
  ```html
28
- <header class="gw-header" data-gw-header>
104
+ <header class="gw-header gw-header--float?" data-gw-header> <!-- --float: a brand's header profile (0.3.0) -->
29
105
  <div class="gw-wrap gw-header__bar">
30
106
  <a class="gw-brand" href="/"><img class="gw-brand__mark" src="…" alt="" width="26" height="26"><span class="gw-brand__name">GemBoss</span></a>
31
107
  <nav class="gw-nav" aria-label="Main">
@@ -59,23 +135,35 @@ BEM-ish: `.gw-block`, `.gw-block__part`, `.gw-block--variant`.
59
135
  Script behaviour:
60
136
  - **Mega menu**: click on a trigger toggles its panel (`hidden` removed, `aria-expanded="true"`, `.is-open` on the
61
137
  `.gw-nav__item`). Hover opens on fine pointers after 80ms and closes 160ms after leaving item and panel. Only one
62
- open at a time. `Escape` closes and returns focus to the trigger. A click outside closes. Arrow keys are not required.
138
+ open at a time on the page, across every header `init` wired. `Escape` closes and returns focus to the trigger. A
139
+ click outside closes. Arrow keys are not required. Each change fires `gw:mega`.
63
140
  - **Mobile menu** (below 1080px the `.gw-nav` and sign-in hide, `.gw-menu-toggle` shows): the toggle flips `hidden`
64
- on `#gw-mobile-menu`, `aria-expanded`, `.is-menu-open` on `<html>` (the CSS locks scroll), and its `aria-label`
65
- (Open menu / Close menu). `Escape` closes. Resizing above 1080px closes it.
141
+ on the menu its `aria-controls` names (`#gw-mobile-menu`), `aria-expanded`, `.is-menu-open` on `<html>` (the CSS
142
+ locks scroll), and its `aria-label` (Open menu / Close menu). `Escape` closes. Resizing above 1080px closes it.
66
143
  - **Labels in other languages**: the script never writes English over a translated page. Give the toggles their
67
144
  words with `data-gw-label-open` / `data-gw-label-close` (menu toggle) and `data-gw-label-dark` /
68
145
  `data-gw-label-light` (theme toggle); without them it keeps the markup's own `aria-label`, then falls back to English.
69
146
  - **Theme toggle**: cycles light ↔ dark by setting `data-theme` on `<html>`, stores the choice in `localStorage`
70
- (`gw-theme`, every access in try/catch), updates `aria-label`. On load, a stored choice is applied before paint by a
71
- one-line inline script in `<head>` that the template carries (`templates/page.html`).
72
- - **Reveal**: elements with `.gw-reveal` get `.is-in` when 15% visible (IntersectionObserver, once). Failsafe: after
73
- 2.5s, or if IntersectionObserver is missing, all get `.is-in`. Under `prefers-reduced-motion` the CSS shows them
74
- without motion.
147
+ (`gw-theme`, every access in try/catch), updates `aria-label` on every toggle, fires `gw:theme`. On load, a stored
148
+ choice is applied before paint by a one-line inline script in `<head>` that the template carries
149
+ (`templates/page.html`).
150
+ - **Reveal**: elements with `.gw-reveal` get `.is-in` when 15% visible (IntersectionObserver, once). Failsafe: 2.5s
151
+ after the `init` that found them, or at once if IntersectionObserver is missing, they get `.is-in`. Under
152
+ `prefers-reduced-motion` the CSS shows them without motion.
75
153
  - **Header**: `.is-scrolled` on `[data-gw-header]` once the page scrolled more than 8px (the CSS may use it for the
76
- hairline only; the bar does not change height or colour).
77
- - **Current page**: the script marks `aria-current="page"` on `.gw-nav__link` / `.gw-mega__link` whose `href`
78
- pathname equals `location.pathname` (when the markup did not already).
154
+ hairline only; the bar does not change height or colour). The floating header needs nothing more from the script:
155
+ it is the same element with one more class.
156
+ - **Current page**: the script marks `aria-current="page"` on `.gw-nav__link` / `.gw-mega__link` and the mobile
157
+ menu's links whose `href` pathname equals `location.pathname` (when the markup did not already). Each `init` takes
158
+ its own marks back first, so after a client-side navigation they follow the new path; a mark in the markup stays.
159
+
160
+ ## Brand profiles (0.3.0)
161
+
162
+ - `data-gw-action` and `data-gw-radius` sit on `<html>` only, with a name from `system.json` `profiles` (`ink` or
163
+ `accent`, `tight` or `soft`); absent means the first. The CSS reads them (`css/tokens.css`), the script does not.
164
+ - `.gw-header--float` sits on the `<header class="gw-header">` itself, next to `gw-header`.
165
+ - `check` reports any other name or place (`profile-unknown`, `header-float-markup`) and, with `accent`, words on the
166
+ button below 4.5:1 (`action-contrast`).
79
167
 
80
168
  ## Segmented control (0.2.0)
81
169
 
@@ -112,14 +200,14 @@ Script behaviour:
112
200
 
113
201
  | class | what |
114
202
  |---|---|
115
- | `.gw-btn` (+ `--secondary`, `--ghost`, `--on-night`, `--sm`, `--lg`, `--block`) | actions; rectangle 8px, ink fill by default; `:active` pressed, `[aria-busy="true"]` loading (script above) |
203
+ | `.gw-btn` (+ `--secondary`, `--ghost`, `--on-night`, `--sm`, `--lg`, `--block`) | actions; a rectangle with the action radius, the action colour as fill (ink, or the accent under `data-gw-action="accent"`); `:active` pressed, `[aria-busy="true"]` loading (script above) |
116
204
  | `.gw-link` (+ `--arrow`) | inline text link in the accent; `--arrow` adds → that moves 2px on hover |
117
205
  | `.gw-label` | the label above a heading: 13px muted, 8px accent square before it |
118
206
  | `.gw-dim` | the muted first sentence inside a headline |
119
207
  | `.gw-chip` (+ `.is-active`) | selectable token, pill |
120
208
  | `.gw-segmented` (+ `--block`) | one choice out of two to four (script above) |
121
209
  | `.gw-badge` (+ `--accent`, `--success`, `--warning`) | small status, pill |
122
- | `.gw-card` (+ `--soft`, `--link`) | white box, 1px hairline, radius 12; `--link` makes the whole card an `<a>` |
210
+ | `.gw-card` (+ `--soft`, `--link`) | white box, 1px hairline, the card radius; `--link` makes the whole card an `<a>` |
123
211
  | `.gw-window` (+ `__bar`, `__body`, `__caption`) | a framed screenshot/mock-up, like a browser or app window; `__caption` (last child) says what its numbers are: "Illustrative data." or their source |
124
212
  | `.gw-icon` | 20px inline stroke SVG wrapper |
125
213
  | `.gw-list--check` | list with check marks |
@@ -7,26 +7,31 @@ spirit. Each rule came from measuring real pages, not from taste alone.
7
7
 
8
8
  A visitor crosses from the website into the product in one click. If the two disagree (a cream site and a white app,
9
9
  a purple button outside and a black one inside), the seam is exactly where trust is decided. So the website uses the
10
- product's language: white canvas, ink actions, one accent, hairlines, almost no shadow.
10
+ product's language: white canvas, the product's action colour, one accent, hairlines, almost no shadow.
11
11
 
12
- ## The thing you click is ink
12
+ ## The thing you click is one colour: the product's
13
13
 
14
- A page has one thing it wants you to do. Near-black is the strongest mark on white and stays readable in every
15
- light; the accent colour is louder but weaker as an instruction, and a page full of accent-coloured buttons has no
16
- hierarchy left. The accent marks where you are and what is interactive in text: links, focus, the active item, the
17
- small square before a label.
14
+ A page has one thing it wants you to do, and the button that does it looks the same on every page. For GemBoss that
15
+ is near-black ink, as in its dashboard: the strongest mark on white, readable in every light. Since 0.3.0 a brand
16
+ whose product clicks in its accent (GemPages' product uses blue primary buttons) may do the same on its site, with
17
+ the `accent` action profile: the site and the product still speak one language. It is a mode, not a colour picked
18
+ per button; its words must reach 4.5:1 on it, and it never gains a shadow or a gradient. Everywhere else the accent
19
+ marks where you are and what is interactive in text: links, focus, the active item, the small square before a
20
+ label. It never colours the words of a heading or a whole section band: that makes a page louder, not clearer.
18
21
 
19
22
  ## Shapes carry meaning
20
23
 
21
24
  Rectangles act, pills choose. When every button is a rectangle and every chip is a pill, people stop reading the
22
- shape and read the words. A radius scale with seven steps means two cards side by side never differ by 2px, which is
23
- the kind of drift that makes a page feel assembled rather than designed.
25
+ shape and read the words. A site has one set of corners, a named profile (`tight` for GemBoss, `soft` for GemPages,
26
+ as in each product), so two cards side by side never differ by 2px, which is the kind of drift that makes a page feel
27
+ assembled rather than designed. Profiles, not free values: a brand can look like itself without inventing a radius.
24
28
 
25
29
  ## Separation by line and space, light only at rest
26
30
 
27
31
  A shadow says "this floats above". Cards on a page do not float; they sit on it. A 1px hairline and the dashboard's
28
32
  resting shadow (two faint pixels) separate a card and keep the page calm; nothing lifts on hover. Larger light is
29
- spent where it means something: a popover such as the mega menu, an illustration that lights itself.
33
+ spent where it means something: a popover such as the mega menu, the floating header a brand may choose (it does
34
+ float), an illustration that lights itself.
30
35
 
31
36
  ## Scales instead of eyeballing
32
37
 
package/docs/theming.md CHANGED
@@ -1,27 +1,37 @@
1
1
  # Theming for a brand other than GemBoss
2
2
 
3
- A GemCommerce brand (GemPages, Gemians…) uses the kit as it is and changes four things: **its accent colour**, **the
4
- wash hues**, **its logo** and **its favicon**. Everything else stays the kit's: the shapes, the scales, the ink
5
- buttons, the white canvas, the type. That is what makes the sites look like one family, and it is why a site built
6
- by someone with no design training still looks careful.
3
+ A GemCommerce brand (GemPages, Gemians…) uses the kit as it is and chooses six things, **and nothing else**:
7
4
 
8
- Working example: `demo/theme-example.html` (the demo's hero, buttons and cards with a GemPages-style blue).
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),
8
+ 4. **the action colour mode**: `data-gw-action="ink|accent"` on `<html>`,
9
+ 5. **the radius profile**: `data-gw-radius="tight|soft"` on `<html>`,
10
+ 6. **the header style**: the flat bar, or `.gw-header--float`.
9
11
 
10
- ## What you may override
12
+ The last three are **named profiles**, not values: a brand picks one of a short list, once for the whole site, and
13
+ every component follows. Everything else stays the kit's: the neutrals, the type, the spacing, the shadows, the
14
+ widths. That is what makes the sites look like one family, and why a site built by someone with no design training
15
+ still looks careful. `npx gemboss-website check` reports anything outside this set.
16
+
17
+ Working examples: `demo/theme-example.html` (the accent only) and `demo/brand-profiles.html` (a GemPages-style brand:
18
+ the accent, all three profiles and the floating header).
19
+
20
+ ## 1 to 3. Accent, wash, logo
11
21
 
12
22
  Put these in the site's own CSS (`site.css`, linked **after** `website.css`), in **one `:root {}` block**, and nowhere
13
23
  else.
14
24
 
15
25
  | variable | what it is | GemBoss value |
16
26
  |---|---|---|
17
- | `--gw-accent-light` | the accent on light pages: links, focus ring, active item, label square, chart line | `#5661f5` |
18
- | `--gw-accent-strong-light` | the accent on hover and as text on `--gw-accent-soft` | `#3a44de` |
27
+ | `--gw-accent-light` | the accent on light pages: links, focus ring, active item, label square, chart line; the primary button's fill under `data-gw-action="accent"` | `#5661f5` |
28
+ | `--gw-accent-strong-light` | accent text (links, the active item), and the accent button's hover and pressed fill | `#3a44de` |
19
29
  | `--gw-accent-soft-light` | the accent's tint: active chip, chosen segmented option, accent badge, icon hover | `#eef0fe` |
20
- | `--gw-on-accent-light` | text on a solid accent (rare; the kit never fills a button with it) | `#ffffff` |
30
+ | `--gw-on-accent-light` | words on a solid accent: the accent button's label | `#ffffff` |
21
31
  | `--gw-accent-dark` | the accent in dark mode **and inside night bands** | `#7c84ff` |
22
- | `--gw-accent-strong-dark` | its hover / strong form | `#a3a9ff` |
32
+ | `--gw-accent-strong-dark` | its text and hover form | `#a3a9ff` |
23
33
  | `--gw-accent-soft-dark` | its tint | `#1f2140` |
24
- | `--gw-on-accent-dark` | text on it | `#0d0d12` |
34
+ | `--gw-on-accent-dark` | words on it | `#0d0d12` |
25
35
  | `--gw-wash-pink`, `--gw-wash-violet`, `--gw-wash-sky`, `--gw-wash-mint` | the secondary hues of the hero wash and illustrations | `#ff6f5e`, `#7c8af7`, `#43b0ff`, `#1fcf9f` |
26
36
 
27
37
  Why the `-light` / `-dark` names and not `--gw-accent` itself: the kit sets `--gw-accent` from these in four places
@@ -33,45 +43,86 @@ The logo and the favicon are files, not variables: the `<img class="gw-brand__ma
33
43
  `alt=""` because the name is written next to it) and the `<span class="gw-brand__name">` in the header and footer
34
44
  partials, and `<link rel="icon">` in the page head.
35
45
 
46
+ ## 4 to 6. The three profiles
47
+
48
+ Chosen once, in the page skeleton (the layout or partial every page shares). The first of each is the default and
49
+ needs no markup, so a GemBoss page, and every page written for 0.2.0, stays exactly as it was.
50
+
51
+ ```html
52
+ <html lang="en" data-gw-action="accent" data-gw-radius="soft">
53
+ …
54
+ <header class="gw-header gw-header--float" data-gw-header> …the same header markup… </header>
55
+ ```
56
+
57
+ | knob | profiles | what changes |
58
+ |---|---|---|
59
+ | action colour, `data-gw-action` on `<html>` | `ink` (GemBoss, default), `accent` | `accent`: the primary `.gw-btn` is filled with `--gw-accent`, its words are `--gw-on-accent`, hover and pressed are `--gw-accent-strong`. In dark mode and night bands it takes the `-dark` family. Secondary, ghost and on-night buttons do not change. |
60
+ | radius, `data-gw-radius` on `<html>` | `tight` (GemBoss, default): 8 action, 12 card, 16 panel, 20 mega menu; `soft` (GemPages): 12, 20, 24, 24 | every button, control, card, window, table, FAQ, plan, quote and the mega menu. Chips and badges stay pills. |
61
+ | header, a class on the `<header>` | flat (default), `.gw-header--float` | from 1080px the bar becomes an inset card: the panel radius of the profile (16 tight, 24 soft), the surface at 85% over a 10px blur, one hairline, the raised shadow, 16px from the top. Same bar, same mega menu, same script; below 1080px it is the flat bar, so the phone menu is unchanged. Solid for a visitor who asks for less transparency, and in a browser that cannot blur. |
62
+
63
+ Why the action colour is a mode and not a colour of its own: the button stays one colour on the whole site, and that
64
+ colour is a family the brand already checked for contrast. "Site and product speak one language": GemBoss's dashboard
65
+ clicks in ink, so gemboss.ai does; GemPages' product uses blue primary buttons, so a GemPages site may too. Still no
66
+ raised or inset shadow on a button, no gradient, no wash.
67
+
68
+ Why the header uses the panel radius: it is the largest piece of chrome on the page, so it follows the profile with
69
+ the panels; in `tight` that is the 16px the header was designed with.
70
+
36
71
  ## What you may not change
37
72
 
38
73
  - **Neutrals**: `--gw-bg`, `--gw-bg-soft`, `--gw-surface`, `--gw-ink`, `--gw-ink-2`, `--gw-muted`, `--gw-line`,
39
74
  `--gw-line-strong`, and the night colours. No cream or grey page, no brand-coloured ink.
40
- - **The action**: `--gw-action`, `--gw-action-hover`, `--gw-on-action`. Buttons stay ink. Your accent never fills a
41
- button, a section or a header.
75
+ - **The action tokens**: `--gw-action`, `--gw-action-hover`, `--gw-on-action`. Choose the `accent` profile instead;
76
+ a colour set here is not checked and does not follow dark mode or night bands.
42
77
  - **State**: `--gw-success`, `--gw-warning` and their tints. Green and amber mean state on every GemCommerce site.
43
- - **Scales and shapes**: type sizes and weights, spacing, radii, shadows, widths, the header height, the
44
- font family. No rounder buttons, no bigger shadows, no other font.
78
+ - **Scales and shapes**: type sizes and weights, spacing, the radius tokens (`--gw-radius-*`: choose a profile), shadows,
79
+ widths, the header height, the font family. No pill buttons, no bigger shadows, no other font.
45
80
  - **Kit classes**: never restyle a `.gw-*` class. If the kit cannot do what your brand needs, that is a gap
46
81
  (`AGENTS.md` section 9), not a theme.
47
82
 
48
- `npx gemboss-website check` reports colours outside the tokens, overrides of `.gw-*` classes and accent-filled
49
- buttons.
83
+ ### Still forbidden with every profile
84
+
85
+ - **Gradient or accent-coloured words in a heading.** A heading is ink, with a muted first sentence (`.gw-dim`).
86
+ - **Accent-coloured section bands.** A section is white, `--gw-bg-soft` or a night band; the accent only lights a
87
+ night band's top edge.
88
+ - **All-caps labels.** Labels are sentence case, muted, led by the 8px accent square.
89
+
90
+ These make a page louder without making it clearer, and `check` reports the last one (`uppercase-label`) and any
91
+ button filled from site CSS (`accent-button`).
92
+
93
+ `check` also reports: colours outside the tokens, a redefined kit token (`token-redefined`, including the radius and
94
+ action tokens), overrides of `.gw-*` classes, an unknown profile or one set on another element than `<html>`
95
+ (`profile-unknown`), `.gw-header--float` anywhere but the header itself (`header-float-markup`), and, on a page with
96
+ `data-gw-action="accent"`, accent button words below 4.5:1 (`action-contrast`).
50
97
 
51
98
  ## A full example
52
99
 
53
100
  ```css
54
- /* site.css, linked after website.css. The whole theme of a GemPages-style site. */
101
+ /* site.css, linked after website.css. The whole CSS of a GemPages-style brand. */
55
102
  :root {
56
- /* accent, light pages: #1e5eff on #ffffff is 5.1:1 */
57
- --gw-accent-light: #1e5eff;
58
- --gw-accent-strong-light: #1547d1;
59
- --gw-accent-soft-light: #eaf0ff;
103
+ /* light pages. #3c67ff fills the accent button: white words on it are 4.58:1 */
104
+ --gw-accent-light: #3c67ff;
105
+ /* EXAMPLE, not confirmed by the GemPages team: accent text needs a darker step, because #3c67ff is 4.13:1 on
106
+ --gw-bg-soft. #2f52d6 is 6.36:1 on white and 5.73:1 on --gw-bg-soft, and the button's hover. */
107
+ --gw-accent-strong-light: #2f52d6;
108
+ --gw-accent-soft-light: #eaefff;
60
109
  --gw-on-accent-light: #ffffff;
61
- /* accent, dark mode and night bands: #6b98ff on #0d0d12 is 7.0:1 */
62
- --gw-accent-dark: #6b98ff;
63
- --gw-accent-strong-dark: #a8c0ff;
110
+ /* dark mode and night bands: #7c9bff is 7.37:1 on #0d0d12, and #0d0d12 words on it are 7.37:1 */
111
+ --gw-accent-dark: #7c9bff;
112
+ --gw-accent-strong-dark: #a9beff;
64
113
  --gw-accent-soft-dark: #16213d;
65
114
  --gw-on-accent-dark: #0d0d12;
66
115
  /* wash: only if the brand has its own secondary hues; otherwise leave the kit's */
67
- --gw-wash-violet: #7b9cff;
68
116
  }
69
117
  ```
70
118
 
71
119
  ```html
120
+ <html lang="en" data-gw-action="accent" data-gw-radius="soft">
72
121
  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
73
122
  …
74
- <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>
123
+ <header class="gw-header gw-header--float" data-gw-header>
124
+ <div class="gw-wrap gw-header__bar">
125
+ <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>
75
126
  ```
76
127
 
77
128
  ## Check the contrast before you ship
@@ -87,10 +138,15 @@ So:
87
138
  3. `--gw-accent-dark` and `--gw-accent-strong-dark` on `#0d0d12` (dark page) and on `#0c0e14` (night band): at
88
139
  least 4.5:1.
89
140
  4. `--gw-accent-strong-dark` on `--gw-accent-soft-dark`: at least 4.5:1.
141
+ 5. **With `data-gw-action="accent"`**: `--gw-on-accent-light` on `--gw-accent-light` and on
142
+ `--gw-accent-strong-light`, and `--gw-on-accent-dark` on `--gw-accent-dark` and on `--gw-accent-strong-dark`: at
143
+ least 4.5:1. `check` computes these four and fails the site below it (write the colours as `#rrggbb`); the kit's
144
+ build does the same for its own colours.
90
145
 
91
146
  Any WCAG contrast checker works (paste the two hex values). A bright brand colour (yellow, light green, cyan) almost
92
147
  never passes on white: darken it for `-light` and keep the bright one for `-dark`. If you cannot get 4.5:1 without
93
148
  changing the brand colour beyond recognition, keep the kit's accent and use the brand colour only in the logo and
94
- illustrations.
149
+ illustrations, and keep the ink button.
95
150
 
96
- Then open the site in light and dark (the toggle in the header), at 320px and at 1366px, and look at a night band.
151
+ Then open the site in light and dark (the toggle in the header), at 320px and at 1366px, and look at a night band and
152
+ at the header while the page scrolls under it.