its-swiss 0.3.0 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +265 -1
- data/README.md +197 -44
- data/app/assets/stylesheets/its-swiss.css +4 -3
- data/app/assets/stylesheets/its_swiss/components.css +164 -52
- data/app/assets/stylesheets/its_swiss/faces.css +204 -0
- data/app/assets/stylesheets/its_swiss/grid.css +12 -4
- data/app/assets/stylesheets/its_swiss/reset.css +6 -1
- data/app/assets/stylesheets/its_swiss/specimen.css +12 -15
- data/app/assets/stylesheets/its_swiss/tokens.css +62 -28
- data/app/assets/stylesheets/its_swiss/type.css +166 -76
- data/app/helpers/its_swiss/application_helper.rb +62 -2
- data/app/views/its_swiss/specimen/_figure.html.erb +28 -0
- data/app/views/its_swiss/specimen/_footer.html.erb +4 -2
- data/app/views/its_swiss/specimen/_form.html.erb +13 -0
- data/app/views/its_swiss/specimen/_grid.html.erb +16 -4
- data/app/views/its_swiss/specimen/_pairs.html.erb +13 -1
- data/app/views/its_swiss/specimen/_take.html.erb +1 -0
- data/app/views/its_swiss/specimen/_type.html.erb +10 -8
- data/app/views/its_swiss/specimen/_values.html.erb +2 -2
- data/app/views/layouts/its_swiss/shell.html.erb +1 -1
- data/lib/generators/its_swiss/install/templates/theme.css +20 -3
- data/lib/its_swiss/version.rb +1 -1
- data/lib/its_swiss.rb +20 -2
- metadata +4 -2
|
@@ -17,12 +17,21 @@
|
|
|
17
17
|
underneath. The mark is the application's to fill; the library only knows
|
|
18
18
|
that it does not break and that the nav sits beside it. */
|
|
19
19
|
|
|
20
|
+
/* Aligned on the under edge rather than on a baseline. Every child of this
|
|
21
|
+
row is a block of one line, and a block's under edge is the baseline of
|
|
22
|
+
its last line — so aligning the edges aligns the baselines, and does it
|
|
23
|
+
by construction rather than by asking the browser where the baseline is.
|
|
24
|
+
That question has more than one answer: the mark is a block and the nav
|
|
25
|
+
is a flex container, browsers synthesize a flex container's baseline
|
|
26
|
+
differently from a block's, and one that put the two a few pixels apart
|
|
27
|
+
grew this row past its three lines and carried the error down the rest
|
|
28
|
+
of the page. Nothing here needs the answer. */
|
|
20
29
|
.masthead {
|
|
21
30
|
display: flex;
|
|
22
31
|
flex-wrap: wrap;
|
|
23
|
-
align-items:
|
|
24
|
-
gap: var(--
|
|
25
|
-
padding-block: var(--
|
|
32
|
+
align-items: end;
|
|
33
|
+
gap: var(--line) var(--space-4);
|
|
34
|
+
padding-block: var(--line) calc(var(--line) - var(--rule-hair));
|
|
26
35
|
border-bottom: var(--rule-hair) solid var(--rule);
|
|
27
36
|
}
|
|
28
37
|
|
|
@@ -45,8 +54,8 @@
|
|
|
45
54
|
.nav {
|
|
46
55
|
display: flex;
|
|
47
56
|
flex-wrap: wrap;
|
|
48
|
-
align-items:
|
|
49
|
-
gap: var(--space-3);
|
|
57
|
+
align-items: end;
|
|
58
|
+
gap: var(--line) var(--space-3);
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
.nav a { text-decoration: none; }
|
|
@@ -64,14 +73,20 @@
|
|
|
64
73
|
/* --- Footer -------------------------------------------------------------
|
|
65
74
|
A hairline, the micro register, and whatever the application has to say
|
|
66
75
|
after the page. It carries no links of its own: what belongs down there
|
|
67
|
-
is not something a library can know.
|
|
76
|
+
is not something a library can know.
|
|
77
|
+
|
|
78
|
+
A container, not a text block: what it holds goes in a paragraph, which
|
|
79
|
+
the shell writes when it is handed plain text. Trimmed text has to be in
|
|
80
|
+
an element that is trimmed, and a footer that held text directly would
|
|
81
|
+
be in step and off the baseline in a browser that ignores the faces. */
|
|
68
82
|
|
|
69
83
|
.footer {
|
|
70
|
-
margin-top: var(--
|
|
71
|
-
padding-block: calc(var(--
|
|
84
|
+
margin-top: var(--line-4);
|
|
85
|
+
padding-block: calc(var(--line) - var(--rule-hair)) var(--line);
|
|
72
86
|
border-top: var(--rule-hair) solid var(--rule);
|
|
87
|
+
font-family: var(--face-200), var(--font-family);
|
|
73
88
|
font-size: var(--size-1);
|
|
74
|
-
line-height: var(--
|
|
89
|
+
line-height: var(--line);
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
/* --- Rules --------------------------------------------------------------
|
|
@@ -87,14 +102,56 @@
|
|
|
87
102
|
the page. Both weights fit the same box, which is why the heavy one only
|
|
88
103
|
changes the drawing. */
|
|
89
104
|
hr {
|
|
90
|
-
height: var(--
|
|
105
|
+
height: var(--line);
|
|
91
106
|
border: 0;
|
|
92
107
|
border-top: var(--rule-hair) solid var(--rule);
|
|
93
|
-
margin-block: var(--
|
|
108
|
+
margin-block: var(--line) 0;
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
hr.rule--heavy { border-top-width: var(--rule-heavy); border-color: var(--ink); }
|
|
97
112
|
|
|
113
|
+
/* --- Pictures -----------------------------------------------------------
|
|
114
|
+
The hard case, and the one place the convention nearly does not transfer.
|
|
115
|
+
In print a picture is cropped to a field, and a field is a whole number
|
|
116
|
+
of lines — Müller-Brockmann sized photographs exactly that way. On screen
|
|
117
|
+
the width is fluid, so the height is a fraction of a moving number and
|
|
118
|
+
lands wherever it lands, and the column below it is off the grid for the
|
|
119
|
+
rest of the page.
|
|
120
|
+
|
|
121
|
+
round() closes that: the box is the picture's natural height taken up to
|
|
122
|
+
the next whole line, recomputed as the container resizes. The picture is
|
|
123
|
+
then fitted inside it — contain by default, because a library must not
|
|
124
|
+
crop an image it did not choose, and the leftover is at most a line of
|
|
125
|
+
space. .figure--cover crops instead, which is the Müller-Brockmann move
|
|
126
|
+
and yours to make where you own the picture.
|
|
127
|
+
|
|
128
|
+
--ratio is the application's to declare. An intrinsic ratio cannot be
|
|
129
|
+
read in CSS, and guessing one is how a picture ends up letterboxed
|
|
130
|
+
against nothing.
|
|
131
|
+
|
|
132
|
+
No @supports: where round() is unknown the declaration drops and the
|
|
133
|
+
picture takes its natural height, which is what it would have done. */
|
|
134
|
+
.figure {
|
|
135
|
+
container-type: inline-size;
|
|
136
|
+
margin-block: var(--line);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.figure > img,
|
|
140
|
+
.figure > svg,
|
|
141
|
+
.figure > video {
|
|
142
|
+
inline-size: 100%;
|
|
143
|
+
block-size: round(up, calc(100cqw / var(--ratio, 1.5)), var(--line));
|
|
144
|
+
object-fit: contain;
|
|
145
|
+
/* The space the rounding adds falls under the picture, where the grid
|
|
146
|
+
wants it, rather than being split above and below it. */
|
|
147
|
+
object-position: left top;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.figure--cover > img,
|
|
151
|
+
.figure--cover > video { object-fit: cover; }
|
|
152
|
+
|
|
153
|
+
.figure figcaption { margin-block-start: var(--line); }
|
|
154
|
+
|
|
98
155
|
/* --- Table --------------------------------------------------------------
|
|
99
156
|
A grid of values, ruled horizontally and not vertically: the columns are
|
|
100
157
|
already held apart by the type, and a vertical rule between them draws a
|
|
@@ -107,15 +164,24 @@
|
|
|
107
164
|
|
|
108
165
|
.table caption {
|
|
109
166
|
color: var(--ink-quiet);
|
|
110
|
-
margin-bottom: var(--
|
|
167
|
+
margin-bottom: var(--line);
|
|
111
168
|
}
|
|
112
169
|
|
|
170
|
+
/* A cell is the shape a control is: a line the type is set on, and a line
|
|
171
|
+
the rule closes, with the rule's width taken out of the second. Nothing
|
|
172
|
+
above the type but the cap correction, which is nothing where the faces
|
|
173
|
+
work: the type's baseline is the under edge of its line and the cell's
|
|
174
|
+
over edge is on the grid, and anything else put between the two moves
|
|
175
|
+
the baseline off it. Aligned to the top for the same reason:
|
|
176
|
+
every cell in a row starts where the row does, so every cell's first
|
|
177
|
+
baseline is one line down, and a row is as tall as its tallest cell,
|
|
178
|
+
which is whole lines. */
|
|
113
179
|
.table th,
|
|
114
180
|
.table td {
|
|
115
|
-
padding-block:
|
|
181
|
+
padding-block: var(--cap-correction) calc(var(--line) - var(--rule-hair));
|
|
116
182
|
padding-inline-end: var(--space-3);
|
|
117
183
|
border-bottom: var(--rule-hair) solid var(--rule);
|
|
118
|
-
vertical-align:
|
|
184
|
+
vertical-align: top;
|
|
119
185
|
}
|
|
120
186
|
|
|
121
187
|
.table th {
|
|
@@ -145,7 +211,7 @@
|
|
|
145
211
|
display: grid;
|
|
146
212
|
grid-template-columns: max-content 1fr;
|
|
147
213
|
column-gap: var(--space-3);
|
|
148
|
-
row-gap:
|
|
214
|
+
row-gap: 0;
|
|
149
215
|
}
|
|
150
216
|
|
|
151
217
|
.pairs dt { font-weight: 700; }
|
|
@@ -157,7 +223,7 @@
|
|
|
157
223
|
row-gap: 0;
|
|
158
224
|
}
|
|
159
225
|
|
|
160
|
-
.pairs--stacked dt { margin-top: var(--
|
|
226
|
+
.pairs--stacked dt { margin-top: var(--line); }
|
|
161
227
|
|
|
162
228
|
.pairs--stacked dt:first-child { margin-top: 0; }
|
|
163
229
|
|
|
@@ -171,7 +237,7 @@
|
|
|
171
237
|
display: flex;
|
|
172
238
|
flex-direction: column;
|
|
173
239
|
align-items: flex-start;
|
|
174
|
-
gap: var(--
|
|
240
|
+
gap: var(--line);
|
|
175
241
|
max-width: var(--measure);
|
|
176
242
|
}
|
|
177
243
|
|
|
@@ -179,20 +245,24 @@
|
|
|
179
245
|
flex-direction: row;
|
|
180
246
|
flex-wrap: wrap;
|
|
181
247
|
align-items: flex-end;
|
|
182
|
-
gap: var(--space-2);
|
|
248
|
+
gap: var(--line) var(--space-2);
|
|
183
249
|
}
|
|
184
250
|
|
|
185
251
|
.field {
|
|
186
252
|
display: flex;
|
|
187
253
|
flex-direction: column;
|
|
188
|
-
gap:
|
|
254
|
+
gap: 0;
|
|
189
255
|
width: 100%;
|
|
190
256
|
}
|
|
191
257
|
|
|
258
|
+
/* On the over edge. The label is a line and the control is two, and both
|
|
259
|
+
put their baseline one line below their own top — so starting them on
|
|
260
|
+
the same line puts the label's baseline on the control's, without the
|
|
261
|
+
browser being asked where either baseline is. */
|
|
192
262
|
.field--inline {
|
|
193
263
|
flex-direction: row;
|
|
194
|
-
align-items:
|
|
195
|
-
gap: var(--space-2);
|
|
264
|
+
align-items: flex-start;
|
|
265
|
+
gap: var(--line) var(--space-2);
|
|
196
266
|
}
|
|
197
267
|
|
|
198
268
|
.field input,
|
|
@@ -206,25 +276,37 @@
|
|
|
206
276
|
font: inherit;
|
|
207
277
|
/* No padding above it: the rule belongs under the text, not a line's
|
|
208
278
|
height below it, or the label reads as belonging to nothing. */
|
|
209
|
-
|
|
279
|
+
block-size: var(--line-2);
|
|
280
|
+
/* The rule sits a whole line under the text rather than a few pixels
|
|
281
|
+
under it. A control is two lines of box: one the text is set on, one
|
|
282
|
+
the rule closes. */
|
|
283
|
+
padding-block: 0 calc(var(--line) - var(--rule-hair));
|
|
210
284
|
}
|
|
211
285
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
.field select { height: var(--space-4); }
|
|
217
|
-
|
|
218
|
-
.field textarea { resize: vertical; }
|
|
286
|
+
.field textarea {
|
|
287
|
+
block-size: var(--line-4);
|
|
288
|
+
resize: vertical;
|
|
289
|
+
}
|
|
219
290
|
|
|
220
|
-
/* A checkbox or a radio is the one control whose label sits beside it.
|
|
291
|
+
/* A checkbox or a radio is the one control whose label sits beside it. On
|
|
292
|
+
the under edge: the label's under edge is its baseline, and the box the
|
|
293
|
+
browser draws then stands on that baseline the way a glyph does, at
|
|
294
|
+
whatever size the browser drew it. */
|
|
221
295
|
.choice {
|
|
222
296
|
display: inline-flex;
|
|
223
|
-
align-items:
|
|
224
|
-
gap: var(--space-1);
|
|
297
|
+
align-items: flex-end;
|
|
298
|
+
gap: var(--line) var(--space-1);
|
|
225
299
|
}
|
|
226
300
|
|
|
227
|
-
|
|
301
|
+
/* The browser draws this one at a size of its own, so it takes none of the
|
|
302
|
+
box a text control is given — including the two lines of height, which
|
|
303
|
+
it would otherwise wear as a 48px checkbox. */
|
|
304
|
+
.choice input {
|
|
305
|
+
inline-size: auto;
|
|
306
|
+
block-size: auto;
|
|
307
|
+
border: revert;
|
|
308
|
+
padding-block: 0;
|
|
309
|
+
}
|
|
228
310
|
|
|
229
311
|
/* Refused. The accent is the whole signal here and it is a rule, not a
|
|
230
312
|
fill: the field is still the field. */
|
|
@@ -234,8 +316,9 @@
|
|
|
234
316
|
|
|
235
317
|
.field__error {
|
|
236
318
|
color: var(--accent);
|
|
319
|
+
font-family: var(--face-200), var(--font-family);
|
|
237
320
|
font-size: var(--size-1);
|
|
238
|
-
line-height: var(--
|
|
321
|
+
line-height: var(--line);
|
|
239
322
|
}
|
|
240
323
|
|
|
241
324
|
/* --- Buttons ------------------------------------------------------------
|
|
@@ -244,37 +327,61 @@
|
|
|
244
327
|
|
|
245
328
|
Every one of them is the same box, so a row of them sits on one line and
|
|
246
329
|
the only difference between them is what fills it. No radius, no shadow,
|
|
247
|
-
and the keyline is the hairline drawn everywhere else.
|
|
330
|
+
and the keyline is the hairline drawn everywhere else.
|
|
331
|
+
|
|
332
|
+
A button is a box, not a line of type: two lines tall, on the grid, with
|
|
333
|
+
its label centred in it. The label's baseline is the one baseline in the
|
|
334
|
+
library that is not on a line, and on purpose — a button is read as a
|
|
335
|
+
shape, and a label set on the second line of a two-line box reads as a
|
|
336
|
+
field with a rule under it, which is the one thing a button must not be
|
|
337
|
+
mistaken for.
|
|
338
|
+
|
|
339
|
+
Centred by cap: a face puts the label's baseline on the under edge of
|
|
340
|
+
its line, so the line is let down by half the cap height and the cap
|
|
341
|
+
sits across the middle. Half a cap on a 64th of a pixel, because the
|
|
342
|
+
browser lays out in 64ths and snaps each padding on its own, and two
|
|
343
|
+
halves of a fraction each snapped down are a box a 64th short; rounded
|
|
344
|
+
up to the unit first, the two paddings sum to the line exactly. Where
|
|
345
|
+
the faces are not honoured the font's own metrics put the cap near the
|
|
346
|
+
middle of its line already, and the paddings are simply equal. The
|
|
347
|
+
keyline is drawn inside the box rather than laid out around it, so the
|
|
348
|
+
arithmetic is the label's line and two paddings, and nothing else. */
|
|
248
349
|
|
|
249
350
|
.button {
|
|
351
|
+
--keyline: var(--rule);
|
|
352
|
+
--lift: round(up, 1cap / 2, 0.015625px);
|
|
250
353
|
display: inline-block;
|
|
251
|
-
border:
|
|
354
|
+
border: 0;
|
|
355
|
+
box-shadow: inset 0 0 0 var(--rule-hair) var(--keyline);
|
|
252
356
|
background: transparent;
|
|
253
357
|
color: var(--ink);
|
|
254
|
-
|
|
255
|
-
padding-block: calc(var(--
|
|
358
|
+
min-block-size: var(--line-2);
|
|
359
|
+
padding-block: var(--lift) calc(var(--line) - var(--lift));
|
|
256
360
|
padding-inline: var(--space-2);
|
|
257
361
|
cursor: pointer;
|
|
258
362
|
text-decoration: none;
|
|
259
363
|
}
|
|
260
364
|
|
|
261
|
-
.
|
|
365
|
+
html.no-metric-overrides .button,
|
|
366
|
+
html.no-metric-overrides .skip-link { padding-block: var(--half-line); }
|
|
367
|
+
|
|
368
|
+
.button:hover { --keyline: var(--ink); }
|
|
262
369
|
|
|
263
370
|
/* A solid block, the way a Swiss poster sets a call to action. Ink rather
|
|
264
371
|
than the accent: the primary action is the one you were going to take,
|
|
265
372
|
and the accent is for the one you have to be told about. */
|
|
266
373
|
.button--primary {
|
|
267
|
-
|
|
374
|
+
--keyline: var(--ink);
|
|
268
375
|
background: var(--ink);
|
|
269
376
|
color: var(--paper);
|
|
270
377
|
}
|
|
271
378
|
|
|
272
|
-
.button--primary:hover {
|
|
379
|
+
.button--primary:hover { --keyline: var(--ink-quiet); background: var(--ink-quiet); }
|
|
273
380
|
|
|
274
381
|
/* The one filled area the accent gets, for the action a page exists to
|
|
275
382
|
offer. Used once per page or not at all. */
|
|
276
383
|
.button--accent {
|
|
277
|
-
|
|
384
|
+
--keyline: var(--accent);
|
|
278
385
|
background: var(--accent);
|
|
279
386
|
color: var(--accent-ink);
|
|
280
387
|
}
|
|
@@ -287,7 +394,7 @@
|
|
|
287
394
|
because a keyboard reaches this button and touch never hovers at all. */
|
|
288
395
|
.button--danger:hover,
|
|
289
396
|
.button--danger:focus-visible {
|
|
290
|
-
|
|
397
|
+
--keyline: var(--accent);
|
|
291
398
|
color: var(--accent);
|
|
292
399
|
}
|
|
293
400
|
|
|
@@ -318,7 +425,7 @@
|
|
|
318
425
|
|
|
319
426
|
.copy {
|
|
320
427
|
border: 0;
|
|
321
|
-
padding: 0;
|
|
428
|
+
padding: var(--cap-correction) 0 0;
|
|
322
429
|
background: none;
|
|
323
430
|
color: inherit;
|
|
324
431
|
cursor: pointer;
|
|
@@ -343,13 +450,16 @@
|
|
|
343
450
|
A run of numbers under a hairline. The current page carries weight as
|
|
344
451
|
well as the accent, for the same reason the current nav item does. */
|
|
345
452
|
|
|
453
|
+
/* On the under edge, for the reason the masthead is: a run of one-line
|
|
454
|
+
blocks has a shared baseline already, and asking for one is asking a
|
|
455
|
+
question two browsers answer differently. */
|
|
346
456
|
.pagination {
|
|
347
457
|
display: flex;
|
|
348
458
|
flex-wrap: wrap;
|
|
349
|
-
align-items:
|
|
350
|
-
gap:
|
|
351
|
-
margin-top: var(--
|
|
352
|
-
padding-top: calc(var(--
|
|
459
|
+
align-items: end;
|
|
460
|
+
gap: 0 var(--space-2);
|
|
461
|
+
margin-top: var(--line-2);
|
|
462
|
+
padding-top: calc(var(--line) - var(--rule-hair));
|
|
353
463
|
border-top: var(--rule-hair) solid var(--rule);
|
|
354
464
|
}
|
|
355
465
|
|
|
@@ -376,7 +486,7 @@
|
|
|
376
486
|
|
|
377
487
|
.errors {
|
|
378
488
|
width: 100%;
|
|
379
|
-
padding-top: calc(var(--
|
|
489
|
+
padding-top: calc(var(--line) - var(--rule-heavy));
|
|
380
490
|
border-top: var(--rule-heavy) solid var(--accent);
|
|
381
491
|
}
|
|
382
492
|
|
|
@@ -384,7 +494,7 @@
|
|
|
384
494
|
|
|
385
495
|
.errors ul {
|
|
386
496
|
color: var(--ink-quiet);
|
|
387
|
-
margin-top:
|
|
497
|
+
margin-top: 0;
|
|
388
498
|
}
|
|
389
499
|
|
|
390
500
|
.empty { color: var(--ink-quiet); }
|
|
@@ -413,10 +523,12 @@
|
|
|
413
523
|
left: var(--space-2);
|
|
414
524
|
top: var(--space-2);
|
|
415
525
|
z-index: 1;
|
|
416
|
-
|
|
526
|
+
--lift: round(up, 1cap / 2, 0.015625px);
|
|
527
|
+
min-block-size: var(--line-2);
|
|
528
|
+
padding-block: var(--lift) calc(var(--line) - var(--lift));
|
|
417
529
|
padding-inline: var(--space-2);
|
|
418
530
|
background: var(--paper);
|
|
419
|
-
|
|
531
|
+
box-shadow: inset 0 0 0 var(--rule-hair) var(--ink);
|
|
420
532
|
transform: translateY(calc(-100% - var(--space-4)));
|
|
421
533
|
}
|
|
422
534
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/* Faces.
|
|
2
|
+
*
|
|
3
|
+
* Where the baseline actually comes from. A line box puts its baseline
|
|
4
|
+
* wherever the font's ascent and descent leave it: half the leading either
|
|
5
|
+
* side of the ascent-plus-descent, and then the ascent down. Those two
|
|
6
|
+
* numbers are the font file's, so every line of type on the page sat on a
|
|
7
|
+
* baseline the library had never been told and could not move, and a page
|
|
8
|
+
* that was on the grid in one font was off it in the next.
|
|
9
|
+
*
|
|
10
|
+
* These faces replace the numbers. Each one is the application's typeface —
|
|
11
|
+
* or, until the application names one, the grotesque that is on the machine
|
|
12
|
+
* — declared again with its ascent set to the leading it will be set on and
|
|
13
|
+
* its descent set to nothing. A line box is then exactly the ascent tall, the
|
|
14
|
+
* half-leading either side of it is zero, and the baseline is the under edge
|
|
15
|
+
* of the line box: a block of type ends on its last baseline, every earlier
|
|
16
|
+
* baseline is a whole number of lines above it, and none of it depends on a
|
|
17
|
+
* metric the library cannot see.
|
|
18
|
+
*
|
|
19
|
+
* One face per ratio of leading to size, because that ratio is the ascent.
|
|
20
|
+
* The ladder in tokens.css produces three: body, subhead and title are set
|
|
21
|
+
* on one and a half times their size, the small register and the section on
|
|
22
|
+
* twice it, and the subgrid puts the small register on its own size. The
|
|
23
|
+
* name is the ratio, and the number in it is the ascent-override — so an
|
|
24
|
+
* application declaring its own font under the same names (the helper
|
|
25
|
+
* its_swiss_typeface writes them) has nothing to work out.
|
|
26
|
+
*
|
|
27
|
+
* Nothing here is downloaded. src: local() is the font already on the
|
|
28
|
+
* machine, named by the names the machine knows it by, and the chain runs
|
|
29
|
+
* from the Helveticas through the system grotesques to the ones a Linux
|
|
30
|
+
* ships with. A machine with none of them falls through to --font-family,
|
|
31
|
+
* with the font's own metrics and whatever baseline they produce — the page
|
|
32
|
+
* is still readable, and still in step, and no longer registered.
|
|
33
|
+
*
|
|
34
|
+
* Each bold face names the regular ones after its own. A face whose every
|
|
35
|
+
* local() fails is a face that does not load, and the browser goes on to
|
|
36
|
+
* the next family in the list — the grotesque stack, with its own metrics —
|
|
37
|
+
* for every heading on the page, while the body stays on the grid. Falling
|
|
38
|
+
* through to the regular file instead keeps the metrics and loses the
|
|
39
|
+
* weight, which with synthesis off is a heading set regular: visible, and
|
|
40
|
+
* still registered.
|
|
41
|
+
*
|
|
42
|
+
* The application's own @font-face under these names wins: they are
|
|
43
|
+
* unlayered, and a name defined outside a layer beats one defined inside
|
|
44
|
+
* it, just as its rules do.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
@layer its-swiss.faces {
|
|
48
|
+
/* Body, subhead, page title: the size and a half of it again. */
|
|
49
|
+
@font-face {
|
|
50
|
+
font-family: "its-swiss-150";
|
|
51
|
+
font-weight: 400;
|
|
52
|
+
src: local("Helvetica Neue"), local("HelveticaNeue"),
|
|
53
|
+
local("Helvetica"),
|
|
54
|
+
local("Segoe UI"), local("SegoeUI"),
|
|
55
|
+
local("Arial"), local("ArialMT"),
|
|
56
|
+
local("Liberation Sans"), local("LiberationSans"),
|
|
57
|
+
local("Roboto"), local("Roboto-Regular"),
|
|
58
|
+
local("Noto Sans"), local("NotoSans-Regular"),
|
|
59
|
+
local("DejaVu Sans"), local("DejaVuSans");
|
|
60
|
+
ascent-override: 150%;
|
|
61
|
+
descent-override: 0%;
|
|
62
|
+
line-gap-override: 0%;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@font-face {
|
|
66
|
+
font-family: "its-swiss-150";
|
|
67
|
+
font-weight: 700;
|
|
68
|
+
src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"),
|
|
69
|
+
local("Helvetica Bold"), local("Helvetica-Bold"),
|
|
70
|
+
local("Segoe UI Bold"), local("SegoeUI-Bold"),
|
|
71
|
+
local("Arial Bold"), local("Arial-BoldMT"),
|
|
72
|
+
local("Liberation Sans Bold"), local("LiberationSans-Bold"),
|
|
73
|
+
local("Roboto Bold"), local("Roboto-Bold"),
|
|
74
|
+
local("Noto Sans Bold"), local("NotoSans-Bold"),
|
|
75
|
+
local("DejaVu Sans Bold"), local("DejaVuSans-Bold"),
|
|
76
|
+
/* And the regular faces after them: a machine with no bold the
|
|
77
|
+
browser can name is better served by the same face at one weight,
|
|
78
|
+
with the metrics that put it on the grid, than by another family
|
|
79
|
+
with its own. Synthesis is off, so it renders regular. */
|
|
80
|
+
local("Helvetica Neue"), local("HelveticaNeue"),
|
|
81
|
+
local("Helvetica"),
|
|
82
|
+
local("Segoe UI"), local("SegoeUI"),
|
|
83
|
+
local("Arial"), local("ArialMT"),
|
|
84
|
+
local("Liberation Sans"), local("LiberationSans"),
|
|
85
|
+
local("Roboto"), local("Roboto-Regular"),
|
|
86
|
+
local("Noto Sans"), local("NotoSans-Regular"),
|
|
87
|
+
local("DejaVu Sans"), local("DejaVuSans");
|
|
88
|
+
ascent-override: 150%;
|
|
89
|
+
descent-override: 0%;
|
|
90
|
+
line-gap-override: 0%;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* The small register and the section: twice the size. */
|
|
94
|
+
@font-face {
|
|
95
|
+
font-family: "its-swiss-200";
|
|
96
|
+
font-weight: 400;
|
|
97
|
+
src: local("Helvetica Neue"), local("HelveticaNeue"),
|
|
98
|
+
local("Helvetica"),
|
|
99
|
+
local("Segoe UI"), local("SegoeUI"),
|
|
100
|
+
local("Arial"), local("ArialMT"),
|
|
101
|
+
local("Liberation Sans"), local("LiberationSans"),
|
|
102
|
+
local("Roboto"), local("Roboto-Regular"),
|
|
103
|
+
local("Noto Sans"), local("NotoSans-Regular"),
|
|
104
|
+
local("DejaVu Sans"), local("DejaVuSans");
|
|
105
|
+
ascent-override: 200%;
|
|
106
|
+
descent-override: 0%;
|
|
107
|
+
line-gap-override: 0%;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@font-face {
|
|
111
|
+
font-family: "its-swiss-200";
|
|
112
|
+
font-weight: 700;
|
|
113
|
+
src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"),
|
|
114
|
+
local("Helvetica Bold"), local("Helvetica-Bold"),
|
|
115
|
+
local("Segoe UI Bold"), local("SegoeUI-Bold"),
|
|
116
|
+
local("Arial Bold"), local("Arial-BoldMT"),
|
|
117
|
+
local("Liberation Sans Bold"), local("LiberationSans-Bold"),
|
|
118
|
+
local("Roboto Bold"), local("Roboto-Bold"),
|
|
119
|
+
local("Noto Sans Bold"), local("NotoSans-Bold"),
|
|
120
|
+
local("DejaVu Sans Bold"), local("DejaVuSans-Bold"),
|
|
121
|
+
/* And the regular faces after them: a machine with no bold the
|
|
122
|
+
browser can name is better served by the same face at one weight,
|
|
123
|
+
with the metrics that put it on the grid, than by another family
|
|
124
|
+
with its own. Synthesis is off, so it renders regular. */
|
|
125
|
+
local("Helvetica Neue"), local("HelveticaNeue"),
|
|
126
|
+
local("Helvetica"),
|
|
127
|
+
local("Segoe UI"), local("SegoeUI"),
|
|
128
|
+
local("Arial"), local("ArialMT"),
|
|
129
|
+
local("Liberation Sans"), local("LiberationSans"),
|
|
130
|
+
local("Roboto"), local("Roboto-Regular"),
|
|
131
|
+
local("Noto Sans"), local("NotoSans-Regular"),
|
|
132
|
+
local("DejaVu Sans"), local("DejaVuSans");
|
|
133
|
+
ascent-override: 200%;
|
|
134
|
+
descent-override: 0%;
|
|
135
|
+
line-gap-override: 0%;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* The subgrid: the small register on a half-line, which is its own size. */
|
|
139
|
+
@font-face {
|
|
140
|
+
font-family: "its-swiss-100";
|
|
141
|
+
font-weight: 400;
|
|
142
|
+
src: local("Helvetica Neue"), local("HelveticaNeue"),
|
|
143
|
+
local("Helvetica"),
|
|
144
|
+
local("Segoe UI"), local("SegoeUI"),
|
|
145
|
+
local("Arial"), local("ArialMT"),
|
|
146
|
+
local("Liberation Sans"), local("LiberationSans"),
|
|
147
|
+
local("Roboto"), local("Roboto-Regular"),
|
|
148
|
+
local("Noto Sans"), local("NotoSans-Regular"),
|
|
149
|
+
local("DejaVu Sans"), local("DejaVuSans");
|
|
150
|
+
ascent-override: 100%;
|
|
151
|
+
descent-override: 0%;
|
|
152
|
+
line-gap-override: 0%;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@font-face {
|
|
156
|
+
font-family: "its-swiss-100";
|
|
157
|
+
font-weight: 700;
|
|
158
|
+
src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"),
|
|
159
|
+
local("Helvetica Bold"), local("Helvetica-Bold"),
|
|
160
|
+
local("Segoe UI Bold"), local("SegoeUI-Bold"),
|
|
161
|
+
local("Arial Bold"), local("Arial-BoldMT"),
|
|
162
|
+
local("Liberation Sans Bold"), local("LiberationSans-Bold"),
|
|
163
|
+
local("Roboto Bold"), local("Roboto-Bold"),
|
|
164
|
+
local("Noto Sans Bold"), local("NotoSans-Bold"),
|
|
165
|
+
local("DejaVu Sans Bold"), local("DejaVuSans-Bold"),
|
|
166
|
+
/* And the regular faces after them: a machine with no bold the
|
|
167
|
+
browser can name is better served by the same face at one weight,
|
|
168
|
+
with the metrics that put it on the grid, than by another family
|
|
169
|
+
with its own. Synthesis is off, so it renders regular. */
|
|
170
|
+
local("Helvetica Neue"), local("HelveticaNeue"),
|
|
171
|
+
local("Helvetica"),
|
|
172
|
+
local("Segoe UI"), local("SegoeUI"),
|
|
173
|
+
local("Arial"), local("ArialMT"),
|
|
174
|
+
local("Liberation Sans"), local("LiberationSans"),
|
|
175
|
+
local("Roboto"), local("Roboto-Regular"),
|
|
176
|
+
local("Noto Sans"), local("NotoSans-Regular"),
|
|
177
|
+
local("DejaVu Sans"), local("DejaVuSans");
|
|
178
|
+
ascent-override: 100%;
|
|
179
|
+
descent-override: 0%;
|
|
180
|
+
line-gap-override: 0%;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Code. Inline, it is given no leading at all and so never asks the line
|
|
184
|
+
for room, and the ascent is beside the point; the descent still has to
|
|
185
|
+
be nothing, so that what the browser reports as the under edge of a run
|
|
186
|
+
of it is its baseline. As a block — a <pre> — it is set at nine tenths
|
|
187
|
+
of the body on the body's line, and five thirds of nine tenths is the
|
|
188
|
+
one and a half the body is set on. */
|
|
189
|
+
@font-face {
|
|
190
|
+
font-family: "its-swiss-mono";
|
|
191
|
+
src: local("SF Mono"), local("SFMono-Regular"),
|
|
192
|
+
local("Menlo"), local("Menlo Regular"), local("Menlo-Regular"),
|
|
193
|
+
local("Consolas"),
|
|
194
|
+
local("Cascadia Mono"), local("CascadiaMono"),
|
|
195
|
+
local("Liberation Mono"), local("LiberationMono"),
|
|
196
|
+
local("Roboto Mono"), local("RobotoMono-Regular"),
|
|
197
|
+
local("Noto Sans Mono"), local("NotoSansMono-Regular"),
|
|
198
|
+
local("DejaVu Sans Mono"), local("DejaVuSansMono"),
|
|
199
|
+
local("Courier New"), local("CourierNewPSMT");
|
|
200
|
+
ascent-override: 166.6667%;
|
|
201
|
+
descent-override: 0%;
|
|
202
|
+
line-gap-override: 0%;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -50,18 +50,26 @@
|
|
|
50
50
|
Almost every horizontal arrangement in an interface is one thing: items
|
|
51
51
|
on a shared baseline, a space apart, wrapping when they must. Said once
|
|
52
52
|
here the way the field's tracks are, so a component below says only how
|
|
53
|
-
it differs.
|
|
53
|
+
it differs.
|
|
54
|
+
|
|
55
|
+
Aligned on the over edge, and the baselines follow. Every item starts on
|
|
56
|
+
the line the row does, every item's baselines are whole lines below its
|
|
57
|
+
own top, so every item's first baseline is the same line — a word beside
|
|
58
|
+
a button beside a field — and the row is as tall as its tallest item,
|
|
59
|
+
which is whole lines. Asking for `baseline` instead asks the browser to
|
|
60
|
+
find one in each item, and a button, a form and a block of text are
|
|
61
|
+
three answers. */
|
|
54
62
|
.run {
|
|
55
63
|
display: flex;
|
|
56
64
|
flex-wrap: wrap;
|
|
57
|
-
align-items:
|
|
58
|
-
gap: var(--space-2);
|
|
65
|
+
align-items: flex-start;
|
|
66
|
+
gap: var(--line) var(--space-2);
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
/* The same idea turned ninety degrees. */
|
|
62
70
|
.stack {
|
|
63
71
|
display: flex;
|
|
64
72
|
flex-direction: column;
|
|
65
|
-
gap: var(--
|
|
73
|
+
gap: var(--line);
|
|
66
74
|
}
|
|
67
75
|
}
|
|
@@ -16,11 +16,16 @@
|
|
|
16
16
|
color: var(--ink);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
h1, h2, h3, h4, p, ul, ol, dl, dd, figure, fieldset, legend, blockquote {
|
|
19
|
+
h1, h2, h3, h4, p, ul, ol, dl, dd, figure, fieldset, legend, blockquote, pre {
|
|
20
20
|
margin: 0;
|
|
21
21
|
padding: 0;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/* A control brings a margin of its own in one browser and not another —
|
|
25
|
+
Firefox gives a textarea a pixel above and below — and a pixel above a
|
|
26
|
+
control is a column a pixel off the grid from that control down. */
|
|
27
|
+
button, input, select, textarea { margin: 0; }
|
|
28
|
+
|
|
24
29
|
ul, ol { list-style: none; }
|
|
25
30
|
|
|
26
31
|
fieldset { border: 0; }
|