@plus234feed/ds 0.3.0 → 0.5.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/README.md +69 -138
- package/package.json +3 -2
- package/src/components.css +258 -45
package/README.md
CHANGED
|
@@ -1,90 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @plus234feed/ds
|
|
2
2
|
|
|
3
|
-
Editorial tokens, a named type scale and the
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The Plus234Feed design system. Editorial tokens, a named type scale and the
|
|
4
|
+
components a news site needs: newspaper hierarchy, accessible colour in both
|
|
5
|
+
themes, plain CSS.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
No framework, no JavaScript, no build step for consumers. Four stylesheets and
|
|
8
|
+
four font files.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
rules. Each component page says what it is, when to use it, when not to, live
|
|
12
|
-
examples with copyable HTML, and the tokens it reads.
|
|
13
|
-
|
|
14
|
-
## Why this exists
|
|
15
|
-
|
|
16
|
-
The frontend had 252 component files and 40,638 lines of TSX, but only 25
|
|
17
|
-
shared components. Measured on 2026-11-04:
|
|
18
|
-
|
|
19
|
-
| Symptom | Count |
|
|
20
|
-
| --- | --- |
|
|
21
|
-
| `page.tsx` files not using `PageLayout` | 92 (16 do) |
|
|
22
|
-
| Files re-implementing an article card | 47 |
|
|
23
|
-
| `rounded-*` variants in use | 11 |
|
|
24
|
-
| Ad-hoc text sizes in use | 9, dominated by `text-sm` (1,019) and `text-xs` (720) |
|
|
25
|
-
| Hardcoded uses of a single green | 27 |
|
|
26
|
-
| Variables used but defined nowhere | 5 |
|
|
27
|
-
|
|
28
|
-
That last row was a live bug, not a matter of taste. `--text-muted` (42 uses),
|
|
29
|
-
`--border` (36), `--bg-elevated` (13), `--bg-secondary` and `--bg-hover` were
|
|
30
|
-
referenced with no fallback and declared in no stylesheet, concentrated in
|
|
31
|
-
`components/prices/` and the cost of living pages.
|
|
32
|
-
|
|
33
|
-
Reader behaviour pointed the same way. Almost every visitor is new, and
|
|
34
|
-
almost nobody opens a second page. Readers arriving from search spend real
|
|
35
|
-
time on the article, finish it, and leave: the problem is not that they are
|
|
36
|
-
bored, it is that the page offers them nothing to do next.
|
|
10
|
+
Documentation: [ds.plus234feed.com](https://ds.plus234feed.com)
|
|
37
11
|
|
|
38
12
|
## Install
|
|
39
13
|
|
|
40
14
|
```bash
|
|
41
|
-
npm install
|
|
42
|
-
npm run build # tokens + gallery
|
|
43
|
-
npm test # 13 checks
|
|
15
|
+
npm install @plus234feed/ds
|
|
44
16
|
```
|
|
45
17
|
|
|
18
|
+
```css
|
|
19
|
+
@import "@plus234feed/ds/tokens.css"; /* custom properties, both themes */
|
|
20
|
+
@import "@plus234feed/ds/type.css"; /* .p234-headline and friends */
|
|
21
|
+
@import "@plus234feed/ds/components.css"; /* .p234-story and friends */
|
|
22
|
+
@import "@plus234feed/ds/fonts.css"; /* Charter, 64KB, four faces */
|
|
46
23
|
```
|
|
47
|
-
npm run build:tokens tokens/*.json -> build/*.css, build/tokens.json
|
|
48
|
-
npm run build:docs site/pages/** -> docs/**
|
|
49
|
-
npm run check build + test, what CI runs
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Authoring a docs page
|
|
53
24
|
|
|
54
|
-
|
|
55
|
-
|
|
25
|
+
`fonts.css` resolves `./fonts/*.woff2` relative to itself. If your bundler does
|
|
26
|
+
not follow that, copy the faces to a public path and rewrite the four `url()`
|
|
27
|
+
values:
|
|
56
28
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<docs-tokens names="ink,ink-muted"> generated swatches, both themes
|
|
62
|
-
<docs-type roles="display-1,headline"> generated specimens
|
|
63
|
-
<docs-scale group="space"> generated scale table
|
|
64
|
-
<docs-uses>--p234-color-ink, ...</docs-uses> the tokens a component reads
|
|
29
|
+
```bash
|
|
30
|
+
cp node_modules/@plus234feed/ds/src/fonts/*.woff2 public/fonts/
|
|
31
|
+
sed 's|url("./fonts/|url("/fonts/|g' \
|
|
32
|
+
node_modules/@plus234feed/ds/src/fonts.css > app/fonts.css
|
|
65
33
|
```
|
|
66
34
|
|
|
67
|
-
Navigation and the section sidebar are derived from the front matter, so a new
|
|
68
|
-
page appears in both by existing.
|
|
69
|
-
|
|
70
35
|
## Use it
|
|
71
36
|
|
|
72
|
-
```css
|
|
73
|
-
@import "@plus234feed/ds/fonts.css"; /* Charter, self-hosted, 64KB */
|
|
74
|
-
@import "@plus234feed/ds/tokens.css"; /* theme driven by a .dark class */
|
|
75
|
-
@import "@plus234feed/ds/type.css"; /* .p234-headline and friends */
|
|
76
|
-
@import "@plus234feed/ds/components.css";
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Use `tokens-auto.css` instead of `tokens.css` only on standalone pages that
|
|
80
|
-
have no theme toggle. It adds an OS-preference block. The app must not use it:
|
|
81
|
-
a reader who picked light has to get light on a dark laptop.
|
|
82
|
-
|
|
83
37
|
```html
|
|
84
|
-
<a class="p234-story p234-story--lead" href="
|
|
38
|
+
<a class="p234-story p234-story--lead" href="/article/...">
|
|
85
39
|
<span class="p234-kicker" data-section="markets">Market intelligence</span>
|
|
86
|
-
<h3 class="p234-story__headline">Pump prices are running
|
|
87
|
-
<p class="p234-story__standfirst">
|
|
40
|
+
<h3 class="p234-story__headline">Pump prices are running above the last official figure</h3>
|
|
41
|
+
<p class="p234-story__standfirst">The gap between the two is the story.</p>
|
|
88
42
|
<div class="p234-meta">
|
|
89
43
|
<span class="p234-meta__source">Plus234Feed</span>
|
|
90
44
|
<span class="p234-meta__dot">·</span>
|
|
@@ -94,76 +48,53 @@ a reader who picked light has to get light on a dark laptop.
|
|
|
94
48
|
</a>
|
|
95
49
|
```
|
|
96
50
|
|
|
97
|
-
##
|
|
51
|
+
## Themes
|
|
98
52
|
|
|
53
|
+
`tokens.css` is class-driven: add `dark` or `data-theme="dark"` to the root
|
|
54
|
+
element. It never reads the operating system preference, because a reader who
|
|
55
|
+
chose light should get light on a dark laptop.
|
|
56
|
+
|
|
57
|
+
Use `tokens-auto.css` instead only on a standalone page with no theme control.
|
|
58
|
+
|
|
59
|
+
## What is in it
|
|
60
|
+
|
|
61
|
+
| | |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| Tokens | colour, type, space, shape, charts, forms, layering, motion |
|
|
64
|
+
| Type | 17 named roles, serif for reading, tabular sans for figures |
|
|
65
|
+
| Components | story, masthead, footer, prose, chart, map, table, field, and more |
|
|
66
|
+
|
|
67
|
+
Colour is verified rather than eyeballed. Every pair that has to be readable is
|
|
68
|
+
checked in both themes at build time and the build fails on any that is not.
|
|
69
|
+
The chart palette is checked for colour-blind separation using OKLab and the
|
|
70
|
+
Machado simulation, which is also why it stops at the number of series it can
|
|
71
|
+
actually keep distinct instead of inventing more.
|
|
72
|
+
|
|
73
|
+
## Typeface
|
|
74
|
+
|
|
75
|
+
Charter, self-hosted. Bitstream contributed it to the X Consortium in 1992 with
|
|
76
|
+
permission to use, copy, modify, sublicense, sell and redistribute, provided
|
|
77
|
+
the notice stays intact. That notice ships at `src/fonts/LICENSE.charter.txt`.
|
|
78
|
+
BITSTREAM CHARTER is a registered trademark of Bitstream Inc.
|
|
79
|
+
|
|
80
|
+
## Developing
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm install
|
|
84
|
+
npm run build # tokens and documentation
|
|
85
|
+
npm test # 33 checks
|
|
86
|
+
npm run check # both, which is what CI runs
|
|
99
87
|
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
88
|
+
|
|
89
|
+
Releases publish from CI through an npm trusted publisher, so there is no token
|
|
90
|
+
in the repository:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm version patch # rebuilds the docs, commits, tags
|
|
94
|
+
git push --follow-tags
|
|
107
95
|
```
|
|
108
96
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
44 colour pairs are checked in every theme, 88 checks in all, and the build
|
|
115
|
-
exits non-zero on any failure. This is the point of the pipeline: a combination
|
|
116
|
-
nobody can read cannot reach the site. The first run caught three real
|
|
117
|
-
problems, two of which were dark-theme only.
|
|
118
|
-
|
|
119
|
-
The focus ring is checked differently. It is two colours, `focus` with
|
|
120
|
-
`focus-outer` drawn around it, and neither half clears 3:1 alone. The amber is
|
|
121
|
-
1.64:1 on white and relies on the dark outer ring there; on a dark page the
|
|
122
|
-
outer ring recedes and the amber carries it. So the rule is that at least one
|
|
123
|
-
half must clear 3:1 on every surface. See `checkFocus`.
|
|
124
|
-
|
|
125
|
-
## Rules the tokens cannot enforce
|
|
126
|
-
|
|
127
|
-
1. **Two ad slots on an article, maximum, and no mobile sticky.** The current
|
|
128
|
-
template carries six plus a sticky around a 147-word summary. Mobile is 64
|
|
129
|
-
per cent of search traffic.
|
|
130
|
-
2. **Every article ends with a next step that matches why the reader came.** A
|
|
131
|
-
visa reader gets visa stories, not the general feed. This is the module the
|
|
132
|
-
near-total absence of second pages is asking for.
|
|
133
|
-
3. **No raw hex.** If a colour is missing, add a token.
|
|
134
|
-
4. **No undefined tokens.** A prebuild check fails on any `--p234-` variable
|
|
135
|
-
this package does not declare.
|
|
136
|
-
5. **Pick a type role, never a font size.** Seventeen roles cover the site.
|
|
137
|
-
Reaching past them means a role is missing, which is a conversation.
|
|
138
|
-
6. **Direction is never colour alone.** Gainers and decliners carry an arrow.
|
|
139
|
-
7. **One lead per page.** If two stories both look like the lead, neither is.
|
|
140
|
-
|
|
141
|
-
## Type
|
|
142
|
-
|
|
143
|
-
Roles, not t-shirt sizes. Serif for what a reader reads, sans for what a reader
|
|
144
|
-
operates, tabular sans for every figure.
|
|
145
|
-
|
|
146
|
-
The serif is **Charter**, self-hosted from `src/fonts/`, 64KB for four faces,
|
|
147
|
-
`font-display: swap`. Charter is the face macOS and iOS already carry, which is
|
|
148
|
-
why the gallery looked right before a single font was loaded. Shipping it is
|
|
149
|
-
what gives Android readers, who are most of the audience, the same page
|
|
150
|
-
instead of a Noto Serif fallback.
|
|
151
|
-
|
|
152
|
-
Bitstream contributed Charter to the X Consortium in 1992 with permission to
|
|
153
|
-
use, copy, modify, sublicense, sell and redistribute, provided the notice stays
|
|
154
|
-
intact. That notice is `src/fonts/LICENSE.charter.txt` and ships with the
|
|
155
|
-
files. BITSTREAM CHARTER is a registered trademark of Bitstream Inc.
|
|
156
|
-
|
|
157
|
-
The sans is still the system stack headed by Inter, which the Next app already
|
|
158
|
-
loads. On a machine without Inter it resolves to San Francisco or Roboto. If
|
|
159
|
-
that drift matters it should be self-hosted the same way.
|
|
160
|
-
|
|
161
|
-
Running text never drops below 16px. `caption` and `kicker` are the only roles
|
|
162
|
-
allowed at 12px, and the tests enforce both floors.
|
|
163
|
-
|
|
164
|
-
## Scope
|
|
165
|
-
|
|
166
|
-
Phase 1 is this package plus the site chrome and the article template:
|
|
167
|
-
almost every session starts on an article, and the masthead and footer appear
|
|
168
|
-
on every page, so those are what make the site stop looking like two sites.
|
|
169
|
-
Fronts, data pages, account and forms follow.
|
|
97
|
+
## Licence
|
|
98
|
+
|
|
99
|
+
UNLICENSED. Published so Plus234Feed applications can install it; not offered
|
|
100
|
+
for reuse.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plus234feed/ds",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Plus234Feed design system. Editorial tokens, type scale and components for a news intelligence platform: newspaper hierarchy, accessible colour in both themes, one component per job.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"build:docs": "node scripts/build-docs.mjs",
|
|
45
45
|
"build": "npm run build:tokens && npm run build:docs",
|
|
46
46
|
"test": "node --test test/*.test.js",
|
|
47
|
-
"check": "npm run build && npm test"
|
|
47
|
+
"check": "npm run build && npm test",
|
|
48
|
+
"version": "npm run build && git add build docs"
|
|
48
49
|
},
|
|
49
50
|
"publishConfig": {
|
|
50
51
|
"access": "public"
|
package/src/components.css
CHANGED
|
@@ -328,6 +328,91 @@
|
|
|
328
328
|
color: var(--p234-color-ink);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
/* Headings, lists and small print, styled on the element itself.
|
|
332
|
+
*
|
|
333
|
+
* An article body arrives as markup from the pipeline and a policy page is
|
|
334
|
+
* written by hand, but both are the same thing: a document. Styling the
|
|
335
|
+
* element rather than a class is what lets a page body be plain semantic
|
|
336
|
+
* HTML, which is also the only version of these pages anyone will maintain.
|
|
337
|
+
*
|
|
338
|
+
* Section headings carry a hairline above instead of sitting in a bordered
|
|
339
|
+
* box. A page of eight boxes reads as eight unrelated panels; a page of eight
|
|
340
|
+
* ruled sections reads as one document, which is what it is. */
|
|
341
|
+
.p234-prose h2 {
|
|
342
|
+
font-family: var(--p234-type-headline-sm-family);
|
|
343
|
+
font-size: var(--p234-type-headline-sm-size);
|
|
344
|
+
line-height: var(--p234-type-headline-sm-leading);
|
|
345
|
+
letter-spacing: var(--p234-type-headline-sm-tracking);
|
|
346
|
+
font-weight: var(--p234-type-headline-sm-weight);
|
|
347
|
+
color: var(--p234-color-ink);
|
|
348
|
+
border-top: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
349
|
+
padding-top: var(--p234-space-4);
|
|
350
|
+
}
|
|
351
|
+
.p234-prose > * + h2 { margin-top: var(--p234-space-7); }
|
|
352
|
+
|
|
353
|
+
.p234-prose h3 {
|
|
354
|
+
font-family: var(--p234-type-headline-xs-family);
|
|
355
|
+
font-size: var(--p234-type-headline-xs-size);
|
|
356
|
+
line-height: var(--p234-type-headline-xs-leading);
|
|
357
|
+
letter-spacing: var(--p234-type-headline-xs-tracking);
|
|
358
|
+
font-weight: var(--p234-type-headline-xs-weight);
|
|
359
|
+
color: var(--p234-color-ink);
|
|
360
|
+
}
|
|
361
|
+
.p234-prose > * + h3 { margin-top: var(--p234-space-6); }
|
|
362
|
+
|
|
363
|
+
/* A heading followed straight by its own text should not carry a full gap:
|
|
364
|
+
the two belong together. */
|
|
365
|
+
.p234-prose h2 + *,
|
|
366
|
+
.p234-prose h3 + * { margin-top: var(--p234-space-3); }
|
|
367
|
+
|
|
368
|
+
.p234-prose ul,
|
|
369
|
+
.p234-prose ol { padding-left: var(--p234-space-6); }
|
|
370
|
+
.p234-prose ul { list-style: disc; }
|
|
371
|
+
.p234-prose ol { list-style: decimal; }
|
|
372
|
+
.p234-prose li + li { margin-top: var(--p234-space-3); }
|
|
373
|
+
.p234-prose li::marker { color: var(--p234-color-ink-subtle); }
|
|
374
|
+
|
|
375
|
+
.p234-prose strong { font-weight: 700; }
|
|
376
|
+
.p234-prose em { font-style: italic; }
|
|
377
|
+
.p234-prose hr {
|
|
378
|
+
border: 0;
|
|
379
|
+
border-top: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
380
|
+
}
|
|
381
|
+
.p234-prose code {
|
|
382
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
383
|
+
font-size: 0.9em;
|
|
384
|
+
background: var(--p234-color-paper-sunk);
|
|
385
|
+
padding: 0.1em 0.3em;
|
|
386
|
+
border-radius: var(--p234-radius-sm);
|
|
387
|
+
}
|
|
388
|
+
.p234-prose pre {
|
|
389
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
390
|
+
font-size: var(--p234-type-ui-sm-size);
|
|
391
|
+
line-height: 1.55;
|
|
392
|
+
background: var(--p234-color-paper-sunk);
|
|
393
|
+
border: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
394
|
+
border-radius: var(--p234-radius-sm);
|
|
395
|
+
padding: var(--p234-space-4);
|
|
396
|
+
overflow-x: auto;
|
|
397
|
+
}
|
|
398
|
+
.p234-prose pre code { background: none; padding: 0; }
|
|
399
|
+
|
|
400
|
+
/* The line under a document: last updated, a version, a notice. */
|
|
401
|
+
.p234-prose small {
|
|
402
|
+
display: block;
|
|
403
|
+
font-family: var(--p234-type-meta-family);
|
|
404
|
+
font-size: var(--p234-type-meta-size);
|
|
405
|
+
line-height: var(--p234-type-meta-leading);
|
|
406
|
+
color: var(--p234-color-ink-muted);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* A document page is a column of sections, not a reading column of 66
|
|
410
|
+
characters: its lists and definition rows want the wider measure. */
|
|
411
|
+
.p234-prose--document { max-width: var(--p234-measure-wide); }
|
|
412
|
+
.p234-prose--document > p,
|
|
413
|
+
.p234-prose--document > ul,
|
|
414
|
+
.p234-prose--document > ol { max-width: var(--p234-measure-text); }
|
|
415
|
+
|
|
331
416
|
/* --- ad slot -----------------------------------------------------------
|
|
332
417
|
Declared, labelled and bounded. The article template allows two of these,
|
|
333
418
|
total. The count is a design decision, not an inventory decision: at six
|
|
@@ -590,76 +675,88 @@
|
|
|
590
675
|
}
|
|
591
676
|
|
|
592
677
|
/* --- masthead ----------------------------------------------------------
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
678
|
+
A newspaper masthead, not an app bar. Three rows:
|
|
679
|
+
|
|
680
|
+
utility search, edition links, account
|
|
681
|
+
brand the wordmark, centred, with date and figures flanking it
|
|
682
|
+
sections the section nav, with a full width panel beneath it
|
|
683
|
+
|
|
684
|
+
The centred wordmark is the whole point: it is what makes the top of the
|
|
685
|
+
page read as a paper rather than a product. */
|
|
596
686
|
.p234-masthead {
|
|
597
687
|
position: sticky;
|
|
598
688
|
top: 0;
|
|
599
689
|
z-index: var(--p234-z-sticky);
|
|
600
690
|
background: var(--p234-color-paper);
|
|
601
|
-
border-bottom: var(--p234-border-
|
|
691
|
+
border-bottom: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
602
692
|
}
|
|
603
|
-
|
|
693
|
+
|
|
694
|
+
/* Row 1: utility. Hidden on phones, where the bar collapses to one row. */
|
|
695
|
+
.p234-masthead__utility {
|
|
696
|
+
display: none;
|
|
697
|
+
border-bottom: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
698
|
+
}
|
|
699
|
+
@media (min-width: 62rem) { .p234-masthead__utility { display: block; } }
|
|
700
|
+
.p234-masthead__utility-in {
|
|
604
701
|
max-width: var(--p234-layout-container);
|
|
605
702
|
margin: 0 auto;
|
|
606
|
-
padding: var(--p234-space-
|
|
607
|
-
display:
|
|
703
|
+
padding: var(--p234-space-3) var(--p234-layout-gutter-lg);
|
|
704
|
+
display: grid;
|
|
705
|
+
grid-template-columns: 1fr auto 1fr;
|
|
608
706
|
align-items: center;
|
|
609
707
|
gap: var(--p234-space-5);
|
|
610
708
|
}
|
|
611
|
-
|
|
612
|
-
.p234-masthead__bar { padding-inline: var(--p234-layout-gutter-lg); }
|
|
613
|
-
}
|
|
614
|
-
.p234-masthead__brand {
|
|
709
|
+
.p234-masthead__editions {
|
|
615
710
|
display: flex;
|
|
616
|
-
|
|
617
|
-
gap: var(--p234-space-
|
|
711
|
+
justify-content: center;
|
|
712
|
+
gap: var(--p234-space-5);
|
|
713
|
+
}
|
|
714
|
+
.p234-masthead__editions a {
|
|
715
|
+
font-family: var(--p234-type-ui-sm-family);
|
|
716
|
+
font-size: var(--p234-type-ui-sm-size);
|
|
717
|
+
font-weight: 600;
|
|
718
|
+
letter-spacing: 0.04em;
|
|
719
|
+
text-transform: uppercase;
|
|
720
|
+
color: var(--p234-color-ink-muted);
|
|
618
721
|
text-decoration: none;
|
|
619
|
-
|
|
620
|
-
|
|
722
|
+
white-space: nowrap;
|
|
723
|
+
}
|
|
724
|
+
.p234-masthead__editions a:hover,
|
|
725
|
+
.p234-masthead__editions a[aria-current] { color: var(--p234-color-ink); }
|
|
726
|
+
|
|
727
|
+
/* Row 2: the wordmark, centred. */
|
|
728
|
+
.p234-masthead__bar {
|
|
729
|
+
max-width: var(--p234-layout-container);
|
|
730
|
+
margin: 0 auto;
|
|
731
|
+
padding: var(--p234-space-4) var(--p234-layout-gutter);
|
|
732
|
+
display: grid;
|
|
733
|
+
grid-template-columns: 1fr auto 1fr;
|
|
734
|
+
align-items: center;
|
|
735
|
+
gap: var(--p234-space-4);
|
|
736
|
+
}
|
|
737
|
+
@media (min-width: 62rem) {
|
|
738
|
+
.p234-masthead__bar { padding: var(--p234-space-5) var(--p234-layout-gutter-lg); }
|
|
621
739
|
}
|
|
622
|
-
|
|
623
|
-
pages and white on dark, so neither a filter nor an opacity trick gets it
|
|
624
|
-
right. The pair swaps with the theme. */
|
|
740
|
+
.p234-masthead__brand { justify-self: center; display: block; text-decoration: none; }
|
|
625
741
|
.p234-masthead__logo { display: block; height: 1.75rem; width: auto; }
|
|
742
|
+
@media (min-width: 62rem) { .p234-masthead__logo { height: 2.5rem; } }
|
|
626
743
|
.p234-masthead__logo--dark { display: none; }
|
|
627
744
|
.dark .p234-masthead__logo--light,
|
|
628
745
|
[data-theme="dark"] .p234-masthead__logo--light { display: none; }
|
|
629
746
|
.dark .p234-masthead__logo--dark,
|
|
630
747
|
[data-theme="dark"] .p234-masthead__logo--dark { display: block; }
|
|
631
748
|
|
|
632
|
-
|
|
633
|
-
.p234-
|
|
634
|
-
|
|
635
|
-
font-weight: 900;
|
|
636
|
-
font-size: 1.25rem;
|
|
637
|
-
letter-spacing: -0.03em;
|
|
638
|
-
color: var(--p234-color-brand);
|
|
639
|
-
}
|
|
640
|
-
.p234-masthead__tagline {
|
|
641
|
-
font-family: var(--p234-type-caption-family);
|
|
642
|
-
font-size: var(--p234-type-caption-size);
|
|
643
|
-
color: var(--p234-color-ink-muted);
|
|
749
|
+
.p234-masthead__left { display: flex; align-items: center; gap: var(--p234-space-3); }
|
|
750
|
+
.p234-masthead__right { display: flex; align-items: center; justify-content: flex-end; gap: var(--p234-space-3); }
|
|
751
|
+
.p234-masthead__dateline {
|
|
644
752
|
display: none;
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
.p234-masthead__nav { display: none; gap: var(--p234-space-5); margin-left: auto; }
|
|
649
|
-
@media (min-width: 62rem) { .p234-masthead__nav { display: flex; } }
|
|
650
|
-
.p234-masthead__nav a {
|
|
651
|
-
font-family: var(--p234-type-ui-family);
|
|
652
|
-
font-size: var(--p234-type-ui-size);
|
|
653
|
-
font-weight: 600;
|
|
753
|
+
font-family: var(--p234-type-meta-family);
|
|
754
|
+
font-size: var(--p234-type-meta-size);
|
|
654
755
|
color: var(--p234-color-ink-muted);
|
|
655
|
-
|
|
656
|
-
white-space: nowrap;
|
|
756
|
+
line-height: 1.3;
|
|
657
757
|
}
|
|
658
|
-
.p234-
|
|
659
|
-
.p234-masthead__nav a[aria-current] { color: var(--p234-color-ink); }
|
|
758
|
+
@media (min-width: 62rem) { .p234-masthead__dateline { display: block; } }
|
|
660
759
|
|
|
661
|
-
.p234-masthead__actions { display: flex; align-items: center; gap: var(--p234-space-3); margin-left: auto; }
|
|
662
|
-
@media (min-width: 62rem) { .p234-masthead__actions { margin-left: 0; } }
|
|
663
760
|
.p234-masthead__icon {
|
|
664
761
|
display: inline-flex;
|
|
665
762
|
align-items: center;
|
|
@@ -674,6 +771,122 @@
|
|
|
674
771
|
}
|
|
675
772
|
.p234-masthead__icon:hover { background: var(--p234-color-paper-sunk); }
|
|
676
773
|
|
|
774
|
+
/* Phone only: the hamburger and the compact sign in. On desktop the utility
|
|
775
|
+
row carries these, so showing them twice is just clutter. */
|
|
776
|
+
@media (min-width: 62rem) {
|
|
777
|
+
.p234-masthead__menubtn,
|
|
778
|
+
.p234-masthead__compact { display: none !important; }
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/* Row 3: the sections. Centred on desktop, scrolls sideways on a phone. */
|
|
782
|
+
.p234-masthead__sections {
|
|
783
|
+
border-top: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
784
|
+
overflow-x: auto;
|
|
785
|
+
scrollbar-width: none;
|
|
786
|
+
}
|
|
787
|
+
.p234-masthead__sections::-webkit-scrollbar { display: none; }
|
|
788
|
+
.p234-masthead__sections-in {
|
|
789
|
+
max-width: var(--p234-layout-container);
|
|
790
|
+
margin: 0 auto;
|
|
791
|
+
padding: 0 var(--p234-layout-gutter);
|
|
792
|
+
display: flex;
|
|
793
|
+
gap: var(--p234-space-6);
|
|
794
|
+
}
|
|
795
|
+
@media (min-width: 62rem) {
|
|
796
|
+
.p234-masthead__sections-in { justify-content: center; padding-inline: var(--p234-layout-gutter-lg); }
|
|
797
|
+
}
|
|
798
|
+
.p234-masthead__section {
|
|
799
|
+
display: inline-flex;
|
|
800
|
+
align-items: center;
|
|
801
|
+
gap: var(--p234-space-2);
|
|
802
|
+
flex-shrink: 0;
|
|
803
|
+
background: none;
|
|
804
|
+
border: 0;
|
|
805
|
+
cursor: pointer;
|
|
806
|
+
padding: var(--p234-space-4) 0;
|
|
807
|
+
font-family: var(--p234-type-ui-family);
|
|
808
|
+
font-size: var(--p234-type-ui-size);
|
|
809
|
+
font-weight: 500;
|
|
810
|
+
color: var(--p234-color-ink);
|
|
811
|
+
text-decoration: none;
|
|
812
|
+
white-space: nowrap;
|
|
813
|
+
border-bottom: var(--p234-border-thick) solid transparent;
|
|
814
|
+
margin-bottom: -1px;
|
|
815
|
+
}
|
|
816
|
+
.p234-masthead__section:hover { border-bottom-color: var(--p234-color-rule-strong); }
|
|
817
|
+
.p234-masthead__section[aria-expanded="true"],
|
|
818
|
+
.p234-masthead__section[aria-current] { border-bottom-color: var(--p234-color-ink); }
|
|
819
|
+
|
|
820
|
+
/* The panel. Full width of the viewport, not a box anchored to a button:
|
|
821
|
+
a dropdown pinned under the last nav item runs off the right edge. */
|
|
822
|
+
.p234-megamenu {
|
|
823
|
+
position: absolute;
|
|
824
|
+
left: 0;
|
|
825
|
+
right: 0;
|
|
826
|
+
top: 100%;
|
|
827
|
+
z-index: var(--p234-z-dropdown);
|
|
828
|
+
background: var(--p234-color-paper);
|
|
829
|
+
border-top: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
830
|
+
border-bottom: var(--p234-border-hairline) solid var(--p234-color-rule);
|
|
831
|
+
box-shadow: var(--p234-shadow-overlay);
|
|
832
|
+
}
|
|
833
|
+
.p234-megamenu__in {
|
|
834
|
+
max-width: var(--p234-layout-container);
|
|
835
|
+
margin: 0 auto;
|
|
836
|
+
padding: var(--p234-space-7) var(--p234-layout-gutter-lg);
|
|
837
|
+
display: grid;
|
|
838
|
+
gap: var(--p234-space-7);
|
|
839
|
+
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
|
|
840
|
+
}
|
|
841
|
+
.p234-megamenu ul { list-style: none; margin: 0; padding: 0; }
|
|
842
|
+
.p234-megamenu li { margin-bottom: var(--p234-space-4); }
|
|
843
|
+
.p234-megamenu a {
|
|
844
|
+
font-family: var(--p234-type-ui-family);
|
|
845
|
+
font-size: var(--p234-type-ui-size);
|
|
846
|
+
color: var(--p234-color-ink);
|
|
847
|
+
text-decoration: none;
|
|
848
|
+
}
|
|
849
|
+
.p234-megamenu a:hover { text-decoration: underline; text-underline-offset: 2px; }
|
|
850
|
+
|
|
851
|
+
/* The phone drawer: a full screen panel, search first, then the sections as
|
|
852
|
+
accordions. <details> so it works with no JavaScript. */
|
|
853
|
+
.p234-drawer {
|
|
854
|
+
position: fixed;
|
|
855
|
+
inset: 0;
|
|
856
|
+
z-index: var(--p234-z-overlay);
|
|
857
|
+
background: var(--p234-color-paper);
|
|
858
|
+
overflow-y: auto;
|
|
859
|
+
padding: var(--p234-space-5) var(--p234-layout-gutter) var(--p234-space-9);
|
|
860
|
+
}
|
|
861
|
+
.p234-drawer__head { display: flex; justify-content: flex-start; margin-bottom: var(--p234-space-6); }
|
|
862
|
+
.p234-drawer__search { display: flex; gap: var(--p234-space-3); margin-bottom: var(--p234-space-6); }
|
|
863
|
+
.p234-drawer__group { border-top: var(--p234-border-hairline) solid var(--p234-color-rule); }
|
|
864
|
+
.p234-drawer__group > summary {
|
|
865
|
+
list-style: none;
|
|
866
|
+
display: flex;
|
|
867
|
+
align-items: center;
|
|
868
|
+
justify-content: space-between;
|
|
869
|
+
padding: var(--p234-space-5) 0;
|
|
870
|
+
cursor: pointer;
|
|
871
|
+
font-family: var(--p234-type-headline-xs-family);
|
|
872
|
+
font-size: var(--p234-type-headline-xs-size);
|
|
873
|
+
font-weight: var(--p234-type-headline-xs-weight);
|
|
874
|
+
color: var(--p234-color-ink);
|
|
875
|
+
}
|
|
876
|
+
.p234-drawer__group > summary::-webkit-details-marker { display: none; }
|
|
877
|
+
.p234-drawer__links {
|
|
878
|
+
display: grid;
|
|
879
|
+
grid-template-columns: 1fr 1fr;
|
|
880
|
+
gap: var(--p234-space-4);
|
|
881
|
+
padding-bottom: var(--p234-space-5);
|
|
882
|
+
}
|
|
883
|
+
.p234-drawer__links a {
|
|
884
|
+
font-family: var(--p234-type-ui-family);
|
|
885
|
+
font-size: var(--p234-type-ui-size);
|
|
886
|
+
color: var(--p234-color-ink);
|
|
887
|
+
text-decoration: none;
|
|
888
|
+
}
|
|
889
|
+
|
|
677
890
|
/* --- subnav ------------------------------------------------------------
|
|
678
891
|
Section level navigation: the price pages, the account panes. Scrolls
|
|
679
892
|
sideways on a phone rather than wrapping into two rows. */
|