@conduction/docusaurus-preset 3.44.1 → 3.44.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/docusaurus-preset",
3
- "version": "3.44.1",
3
+ "version": "3.44.2",
4
4
  "scripts": {
5
5
  "prepack": "node scripts/prepack-bundle-css.js",
6
6
  "test": "node --test"
@@ -244,174 +244,190 @@ export default function GameModal({games: gamesProp, share: shareConfig, classNa
244
244
  <div className={styles.overlay} onClick={close} />
245
245
  <div className={styles.panel}>
246
246
  <button type="button" className={styles.close} onClick={close} aria-label={translate({id: 'preset.gameModal.close', message: 'Close', description: 'Accessible label for the close (×) button on the game-over modal'})}>×</button>
247
- <p className={styles.eyebrow}>{eyebrow}</p>
248
- <h2 className={styles.title} id="gm-title">{title}</h2>
249
- <p className={styles.subtitle}>{subtitle}</p>
247
+ {/* Three blocks, laid out in two columns from 880px up: the
248
+ headline top left, what you do with the run top right,
249
+ and the roster of sixteen games across the bottom in two
250
+ columns of eight. In one column the roster alone ran past
251
+ the bottom of the window, which put the total and the
252
+ share buttons out of reach on a laptop. The DOM order is
253
+ the reading order either way; only the grid moves the
254
+ roster above the share block on a wide screen. */}
255
+ <div className={styles.body}>
256
+ <div className={styles.intro}>
257
+ <p className={styles.eyebrow}>{eyebrow}</p>
258
+ <h2 className={styles.title} id="gm-title">{title}</h2>
259
+ <p className={styles.subtitle}>{subtitle}</p>
250
260
 
251
- {typeof event.score !== 'undefined' && (
252
- <span className={styles.scorePill}>{event.summary || translate({id: 'preset.gameModal.scorePill', message: 'score: {score}', description: 'Default score pill text. {score} is the numeric score.'}, {score: event.score})}</span>
253
- )}
261
+ {typeof event.score !== 'undefined' && (
262
+ <span className={styles.scorePill}>{event.summary || translate({id: 'preset.gameModal.scorePill', message: 'score: {score}', description: 'Default score pill text. {score} is the numeric score.'}, {score: event.score})}</span>
263
+ )}
254
264
 
255
- <div className={styles.progress}>
256
- <div className={styles.progressLabel}>
257
- <span>
258
- <Translate
259
- id="preset.gameModal.progress.found"
260
- description="Progress label below the game-over copy. {found} bolded count of games discovered; {total} is the total."
261
- values={{
262
- found: <strong>{foundCount}</strong>,
263
- total: total,
264
- }}>
265
- {'{found} / {total} mini-games found'}
266
- </Translate>
267
- </span>
268
- <span>{percent}%</span>
265
+ <div className={styles.progress}>
266
+ <div className={styles.progressLabel}>
267
+ <span>
268
+ <Translate
269
+ id="preset.gameModal.progress.found"
270
+ description="Progress label below the game-over copy. {found} bolded count of games discovered; {total} is the total."
271
+ values={{
272
+ found: <strong>{foundCount}</strong>,
273
+ total: total,
274
+ }}>
275
+ {'{found} / {total} mini-games found'}
276
+ </Translate>
277
+ </span>
278
+ <span>{percent}%</span>
279
+ </div>
280
+ <div className={styles.progressBar}>
281
+ <div className={styles.progressFill} style={{width: percent + '%'}} />
282
+ </div>
283
+ </div>
269
284
  </div>
270
- <div className={styles.progressBar}>
271
- <div className={styles.progressFill} style={{width: percent + '%'}} />
272
- </div>
273
- </div>
274
285
 
275
- <ul className={styles.grid}>
276
- {games.map((g) => {
277
- const best = bestFor(scores, g.id);
278
- const isFound = Boolean(scores.games[g.id] && scores.games[g.id].found);
279
- return (
280
- <li key={g.id} className={isFound ? styles.gridItemFound : styles.gridItem}>
281
- <span className={styles.gridHex} aria-hidden="true" />
282
- <span className={styles.gridLabel}>{g.label}</span>
283
- {best !== null && (
284
- <span className={styles.gridScore}>{formatScore(best, locale)}</span>
285
- )}
286
- </li>
287
- );
288
- })}
289
- </ul>
286
+ <div className={styles.roster}>
287
+ <ul className={styles.grid}>
288
+ {games.map((g) => {
289
+ const best = bestFor(scores, g.id);
290
+ const isFound = Boolean(scores.games[g.id] && scores.games[g.id].found);
291
+ return (
292
+ <li key={g.id} className={isFound ? styles.gridItemFound : styles.gridItem}>
293
+ <span className={styles.gridHex} aria-hidden="true" />
294
+ <span className={styles.gridLabel}>{g.label}</span>
295
+ {best !== null && (
296
+ <span className={styles.gridScore}>{formatScore(best, locale)}</span>
297
+ )}
298
+ </li>
299
+ );
300
+ })}
301
+ </ul>
302
+ </div>
290
303
 
291
- {grandTotal > 0 && (
292
- <p className={styles.total}>
293
- <Translate
294
- id="preset.gameModal.totalScore"
295
- description="Total score line under the games list. {score} is the sum of the player's best score in every game."
296
- values={{score: <strong>{formatScore(grandTotal, locale)}</strong>}}>
297
- {'Total score {score}'}
298
- </Translate>
299
- </p>
300
- )}
304
+ <div className={styles.aside}>
305
+ {grandTotal > 0 && (
306
+ <p className={styles.total}>
307
+ <Translate
308
+ id="preset.gameModal.totalScore"
309
+ description="Total score line under the games list. {score} is the sum of the player's best score in every game."
310
+ values={{score: <strong>{formatScore(grandTotal, locale)}</strong>}}>
311
+ {'Total score {score}'}
312
+ </Translate>
313
+ </p>
314
+ )}
301
315
 
302
- <p className={styles.cta}>
303
- {/* Two messages picked here rather than one ICU plural.
304
- Docusaurus's translate() only substitutes {placeholder};
305
- it does not expand plurals, so an ICU string renders to
306
- the reader verbatim, braces and all, in every locale. */}
307
- {foundCount < total
308
- ? (total - foundCount === 1
309
- ? translate({
310
- id: 'preset.gameModal.cta.remaining.one',
311
- message: 'One more game hidden somewhere. Keep clicking.',
312
- description: 'CTA on the game-over modal when exactly one mini-game is still hidden.',
313
- })
316
+ <p className={styles.cta}>
317
+ {/* Two messages picked here rather than one ICU plural.
318
+ Docusaurus's translate() only substitutes {placeholder};
319
+ it does not expand plurals, so an ICU string renders to
320
+ the reader verbatim, braces and all, in every locale. */}
321
+ {foundCount < total
322
+ ? (total - foundCount === 1
323
+ ? translate({
324
+ id: 'preset.gameModal.cta.remaining.one',
325
+ message: 'One more game hidden somewhere. Keep clicking.',
326
+ description: 'CTA on the game-over modal when exactly one mini-game is still hidden.',
327
+ })
328
+ : translate(
329
+ {
330
+ id: 'preset.gameModal.cta.remaining.other',
331
+ message: '{remaining} more games hidden somewhere. Keep clicking.',
332
+ description: 'CTA on the game-over modal when several mini-games are still hidden. {remaining} is how many.',
333
+ },
334
+ {remaining: total - foundCount},
335
+ ))
314
336
  : translate(
315
337
  {
316
- id: 'preset.gameModal.cta.remaining.other',
317
- message: '{remaining} more games hidden somewhere. Keep clicking.',
318
- description: 'CTA on the game-over modal when several mini-games are still hidden. {remaining} is how many.',
338
+ id: 'preset.gameModal.cta.allFound',
339
+ message: 'All {total} found. You read the kit.',
340
+ description: 'CTA on the game-over modal when every mini-game has been discovered. {total} is how many games there are.',
319
341
  },
320
- {remaining: total - foundCount},
321
- ))
322
- : translate(
323
- {
324
- id: 'preset.gameModal.cta.allFound',
325
- message: 'All {total} found. You read the kit.',
326
- description: 'CTA on the game-over modal when every mini-game has been discovered. {total} is how many games there are.',
327
- },
328
- {total},
329
- )}
330
- </p>
331
-
332
- {/* Posting a score is the whole competition: there is no
333
- leaderboard to submit to, so the share block is where a run
334
- turns into something other people can see. */}
335
- <div className={styles.share}>
336
- <p className={styles.shareHead}>
337
- <Translate id="preset.gameModal.share.head" description="Heading above the share buttons on the game-over modal">
338
- Post your score
339
- </Translate>
340
- </p>
341
- <p className={styles.shareHint}>
342
- <Translate id="preset.gameModal.share.hint" description="Line under the share heading telling the player to attach a screenshot of the modal">
343
- Add a screenshot of this card, so people can see the run behind the number.
344
- </Translate>
345
- </p>
342
+ {total},
343
+ )}
344
+ </p>
346
345
 
347
- <div className={styles.shareButtons}>
348
- <button
349
- type="button"
350
- className={styles.shareBtn}
351
- onClick={() => (instance ? shareOnMastodon(instance) : setAskInstance(true))}>
352
- <Translate id="preset.gameModal.share.mastodon" description="Share-on-Mastodon button label">Mastodon</Translate>
353
- </button>
354
- <button type="button" className={styles.shareBtn} onClick={shareOnLinkedIn}>
355
- <Translate id="preset.gameModal.share.linkedin" description="Share-on-LinkedIn button label">LinkedIn</Translate>
356
- </button>
357
- <button type="button" className={styles.shareBtn} onClick={copyShareText}>
358
- <Translate id="preset.gameModal.share.copy" description="Copy-the-post-text button label">Copy the post</Translate>
359
- </button>
360
- <span className={styles.shareStatus} role="status" aria-live="polite">
361
- {copied && (
362
- <Translate id="preset.gameModal.share.copied" description="Confirmation shown after the post text is copied to the clipboard">
363
- Copied. Paste it with your screenshot.
346
+ {/* Posting a score is the whole competition: there is no
347
+ leaderboard to submit to, so the share block is where a run
348
+ turns into something other people can see. */}
349
+ <div className={styles.share}>
350
+ <p className={styles.shareHead}>
351
+ <Translate id="preset.gameModal.share.head" description="Heading above the share buttons on the game-over modal">
352
+ Post your score
364
353
  </Translate>
365
- )}
366
- </span>
367
- </div>
368
-
369
- {askInstance && (
370
- /* Mastodon has no central share endpoint, so the post can
371
- only be opened on the player's own instance. Asked once,
372
- then remembered. */
373
- <form
374
- className={styles.instanceRow}
375
- onSubmit={(e) => { e.preventDefault(); shareOnMastodon(e.target.elements.instance.value); }}>
376
- <label className={styles.instanceLabel} htmlFor="gm-instance">
377
- <Translate id="preset.gameModal.share.instanceLabel" description="Label for the input asking which Mastodon instance the player is on">
378
- Your Mastodon instance
354
+ </p>
355
+ <p className={styles.shareHint}>
356
+ <Translate id="preset.gameModal.share.hint" description="Line under the share heading telling the player to attach a screenshot of the modal">
357
+ Add a screenshot of this card, so people can see the run behind the number.
379
358
  </Translate>
380
- </label>
381
- <input
382
- id="gm-instance"
383
- name="instance"
384
- className={styles.instanceInput}
385
- defaultValue={instance}
386
- placeholder="mastodon.nl"
387
- autoComplete="off"
388
- />
389
- <button type="submit" className={styles.shareBtn}>
390
- <Translate id="preset.gameModal.share.instanceGo" description="Submit button next to the Mastodon instance input">Open</Translate>
391
- </button>
392
- </form>
393
- )}
359
+ </p>
394
360
 
395
- {shareConfig && pickLocale(shareConfig.prize) && (
396
- /* The prize sentence stays text and only the rules link is
397
- a link: a whole underlined paragraph reads as one long
398
- link and hides where it goes. */
399
- <p className={styles.prize}>
400
- {pickLocale(shareConfig.prize)}
401
- {shareConfig.prizeHref && (
402
- <>
403
- {' '}
404
- <a href={shareConfig.prizeHref}>
405
- {pickLocale(shareConfig.prizeLinkLabel) || translate({
406
- id: 'preset.gameModal.share.rules',
407
- message: 'Read the rules',
408
- description: 'Link to the giveaway rules, shown after the prize line in the share block',
409
- })}
410
- </a>
411
- </>
361
+ <div className={styles.shareButtons}>
362
+ <button
363
+ type="button"
364
+ className={styles.shareBtn}
365
+ onClick={() => (instance ? shareOnMastodon(instance) : setAskInstance(true))}>
366
+ <Translate id="preset.gameModal.share.mastodon" description="Share-on-Mastodon button label">Mastodon</Translate>
367
+ </button>
368
+ <button type="button" className={styles.shareBtn} onClick={shareOnLinkedIn}>
369
+ <Translate id="preset.gameModal.share.linkedin" description="Share-on-LinkedIn button label">LinkedIn</Translate>
370
+ </button>
371
+ <button type="button" className={styles.shareBtn} onClick={copyShareText}>
372
+ <Translate id="preset.gameModal.share.copy" description="Copy-the-post-text button label">Copy the post</Translate>
373
+ </button>
374
+ <span className={styles.shareStatus} role="status" aria-live="polite">
375
+ {copied && (
376
+ <Translate id="preset.gameModal.share.copied" description="Confirmation shown after the post text is copied to the clipboard">
377
+ Copied. Paste it with your screenshot.
378
+ </Translate>
379
+ )}
380
+ </span>
381
+ </div>
382
+
383
+ {askInstance && (
384
+ /* Mastodon has no central share endpoint, so the post can
385
+ only be opened on the player's own instance. Asked once,
386
+ then remembered. */
387
+ <form
388
+ className={styles.instanceRow}
389
+ onSubmit={(e) => { e.preventDefault(); shareOnMastodon(e.target.elements.instance.value); }}>
390
+ <label className={styles.instanceLabel} htmlFor="gm-instance">
391
+ <Translate id="preset.gameModal.share.instanceLabel" description="Label for the input asking which Mastodon instance the player is on">
392
+ Your Mastodon instance
393
+ </Translate>
394
+ </label>
395
+ <input
396
+ id="gm-instance"
397
+ name="instance"
398
+ className={styles.instanceInput}
399
+ defaultValue={instance}
400
+ placeholder="mastodon.nl"
401
+ autoComplete="off"
402
+ />
403
+ <button type="submit" className={styles.shareBtn}>
404
+ <Translate id="preset.gameModal.share.instanceGo" description="Submit button next to the Mastodon instance input">Open</Translate>
405
+ </button>
406
+ </form>
412
407
  )}
413
- </p>
414
- )}
408
+
409
+ {shareConfig && pickLocale(shareConfig.prize) && (
410
+ /* The prize sentence stays text and only the rules link is
411
+ a link: a whole underlined paragraph reads as one long
412
+ link and hides where it goes. */
413
+ <p className={styles.prize}>
414
+ {pickLocale(shareConfig.prize)}
415
+ {shareConfig.prizeHref && (
416
+ <>
417
+ {' '}
418
+ <a href={shareConfig.prizeHref}>
419
+ {pickLocale(shareConfig.prizeLinkLabel) || translate({
420
+ id: 'preset.gameModal.share.rules',
421
+ message: 'Read the rules',
422
+ description: 'Link to the giveaway rules, shown after the prize line in the share block',
423
+ })}
424
+ </a>
425
+ </>
426
+ )}
427
+ </p>
428
+ )}
429
+ </div>
430
+ </div>
415
431
  </div>
416
432
 
417
433
  <div className={styles.actions}>
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * <GameModal /> styles. Mirrors preview/components/game-modal.css
3
3
  * for the panel + overlay; cobalt-on-white per huisstijl.
4
+ *
5
+ * From 880px the card is a wide two-column grid: the headline and how
6
+ * far you are top left, the total and the share block top right, and
7
+ * the roster of sixteen games across the bottom in two columns of
8
+ * eight. In a single
9
+ * narrow column the roster alone ran past the bottom of the window,
10
+ * which put the total and the share buttons out of reach on a laptop.
11
+ * Below 880px it stacks in DOM order, because two columns of 190px are
12
+ * worse than a long card you can scroll.
4
13
  */
5
14
 
6
15
  .modal {
@@ -30,6 +39,39 @@
30
39
  width: calc(100% - 48px);
31
40
  max-width: 520px;
32
41
  box-shadow: var(--shadow-3, 0 30px 80px -20px rgba(10, 23, 47, 0.4));
42
+ /* A short window is still a window. Scrolling the card is worse than
43
+ fitting and far better than a Play again button nobody can reach. */
44
+ max-height: calc(100vh - 48px);
45
+ overflow-y: auto;
46
+ }
47
+
48
+ .body {
49
+ display: grid;
50
+ gap: 0;
51
+ }
52
+
53
+ @media (min-width: 880px) {
54
+ .panel { max-width: 880px; }
55
+ .body {
56
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
57
+ grid-template-areas:
58
+ 'intro aside'
59
+ 'roster roster';
60
+ column-gap: 40px;
61
+ }
62
+ .intro { grid-area: intro; }
63
+ .aside { grid-area: aside; }
64
+ .roster { grid-area: roster; }
65
+
66
+ /* Eight and eight rather than sixteen. Multi-column fills downwards,
67
+ so the list still reads in roster order. */
68
+ .grid {
69
+ display: block;
70
+ columns: 2;
71
+ column-gap: 32px;
72
+ }
73
+ .gridItem,
74
+ .gridItemFound { break-inside: avoid; }
33
75
  }
34
76
 
35
77
  .close {
@@ -86,12 +128,15 @@
86
128
  letter-spacing: 0.04em;
87
129
  padding: 6px 12px;
88
130
  border-radius: var(--radius-pill);
89
- margin-bottom: 24px;
131
+ margin-bottom: 8px;
90
132
  }
91
133
 
92
134
  .progress {
93
135
  margin: 24px 0 16px;
94
136
  }
137
+ /* Last thing in the headline column, directly above the roster it
138
+ counts. Nothing follows it there, so it needs no tail margin. */
139
+ .intro .progress { margin-bottom: 0; }
95
140
  .progressLabel {
96
141
  display: flex;
97
142
  justify-content: space-between;
@@ -122,14 +167,15 @@
122
167
  margin: 0 0 20px;
123
168
  display: flex;
124
169
  flex-direction: column;
125
- gap: 6px;
170
+ gap: 4px;
126
171
  }
127
172
  .gridItem,
128
173
  .gridItemFound {
129
174
  display: flex;
130
175
  align-items: center;
131
176
  gap: 10px;
132
- font-size: 14px;
177
+ font-size: 13px;
178
+ line-height: 1.5;
133
179
  color: var(--c-cobalt-400);
134
180
  }
135
181
  .gridItemFound { color: var(--c-cobalt-900); font-weight: 500; }
@@ -152,7 +198,7 @@
152
198
  }
153
199
 
154
200
  .total {
155
- margin: 12px 0 0;
201
+ margin: 0;
156
202
  font-size: 15px;
157
203
  color: var(--c-cobalt-700);
158
204
  }
@@ -248,7 +294,7 @@
248
294
  .cta {
249
295
  font-size: 13px;
250
296
  color: var(--c-cobalt-400);
251
- margin: 16px 0 24px;
297
+ margin: 10px 0 0;
252
298
  font-style: italic;
253
299
  }
254
300
 
@@ -256,6 +302,7 @@
256
302
  display: flex;
257
303
  gap: 8px;
258
304
  justify-content: flex-end;
305
+ margin-top: 20px;
259
306
  }
260
307
 
261
308
  .btnSecondary {