@abreen/tada 1.18.0 → 1.18.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/README.md CHANGED
@@ -383,7 +383,7 @@ list of variables parsed using the [`front-matter`][front-matter] library).
383
383
  | `description` | Meta description for the page |
384
384
  | `toc` | Set to `true` to show a table of contents |
385
385
  | `slides` | Set to `true` on Markdown pages to treat top-level `---` as slide separators and add presentation controls |
386
- | `parent` & `parentLabel` | URL and label for a breadcrumb link displayed above the title |
386
+ | `breadcrumbs` | Ordered ancestor links with `label` and `url`; the page title completes the trail |
387
387
  | `published` | Year, month, and day of publishing (e.g, `2025-09-09`) |
388
388
 
389
389
  You may also add arbitrary fields in a page's front matter, and access them
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M504-480 320-664l56-56 240 240-240 240-56-56 184-184Z"/></svg>
@@ -48,10 +48,13 @@ export const MATERIAL_SYMBOLS = Object.freeze({
48
48
  symbol: 'warning',
49
49
  opticalSize: 20,
50
50
  }),
51
- parent: Object.freeze({
52
- cssVariable: '--icon-parent',
53
- symbol: 'south_east',
54
- opticalSize: 20,
51
+ breadcrumbSeparator: Object.freeze({
52
+ cssVariable: '--icon-breadcrumb-separator',
53
+ symbol: 'chevron_right',
54
+ opticalSize: 24,
55
+ // The authored selection differs from the shared weight/grade defaults.
56
+ weight: 400,
57
+ grade: 0,
55
58
  }),
56
59
  search: Object.freeze({
57
60
  cssVariable: '--icon-search',
package/build/types.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import type { BundledLanguage } from 'shiki';
2
2
  import type { TraceCache } from './build-types';
3
3
 
4
+ /** An explicitly authored ancestor in a page's breadcrumb trail. */
5
+ export interface BreadcrumbEntry {
6
+ label: string;
7
+ url: string;
8
+ }
9
+
4
10
  export type PlainTextLanguage = 'text' | 'txt' | 'plain';
5
11
 
6
12
  export interface SerifFontTuning {
@@ -15,8 +15,8 @@ import {
15
15
  getSupportedConfigFileNamesText,
16
16
  } from '../config-files';
17
17
  import {
18
- resolveParentLinkTarget,
19
- validateParentLink,
18
+ resolveBreadcrumbLinkTarget,
19
+ validateBreadcrumbs,
20
20
  } from '../validate-config-links';
21
21
  import {
22
22
  extractJavaMethodToc,
@@ -66,8 +66,8 @@ export { stripHtmlComments } from './html-comments';
66
66
 
67
67
  const REQUIRED_FRONT_MATTER_FIELDS = ['title'];
68
68
 
69
- // Page variables that renderers assign themselves. Front matter that sets one
70
- // would be overwritten or, for `template`, select a nonexistent template.
69
+ // Internal page variables and retired front matter keys. Internal values would
70
+ // be overwritten or, for `template`, select a nonexistent template.
71
71
  const RESERVED_FRONT_MATTER_KEYS: readonly string[] = [
72
72
  'template',
73
73
  'titleHtml',
@@ -77,6 +77,8 @@ const RESERVED_FRONT_MATTER_KEYS: readonly string[] = [
77
77
  'codeFilePath',
78
78
  'downloadName',
79
79
  'filePath',
80
+ 'parent',
81
+ 'parentLabel',
80
82
  ];
81
83
 
82
84
  function renderInlineField(
@@ -456,7 +458,7 @@ interface PreparePageVariablesInput {
456
458
  * Turns parsed front matter into page variables. Shared by every renderer
457
459
  * whose source has front matter, so each one applies the same rules:
458
460
  * Lodash processing of string values, inline Markdown for the title and
459
- * description, required fields, author lookup, and parent link validation.
461
+ * description, required fields, author lookup, and breadcrumb validation.
460
462
  */
461
463
  function preparePageVariables({
462
464
  rawPageVariables,
@@ -484,12 +486,29 @@ function preparePageVariables({
484
486
  subPath,
485
487
  isWatchMode,
486
488
  });
489
+ const templateString = (value: unknown): unknown =>
490
+ typeof value === 'string' ? compileTemplate(value)(siteOnlyParams) : value;
487
491
  const pageVariables: Record<string, unknown> = Object.fromEntries(
488
- Object.entries(rawPageVariables).map(([k, v]) => [
489
- k,
490
- typeof v === 'string' ? compileTemplate(v)(siteOnlyParams) : v,
492
+ Object.entries(rawPageVariables).map(([key, value]) => [
493
+ key,
494
+ templateString(value),
491
495
  ]),
492
496
  );
497
+ if (Array.isArray(pageVariables.breadcrumbs)) {
498
+ pageVariables.breadcrumbs = pageVariables.breadcrumbs.map(
499
+ (entry: unknown) => {
500
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
501
+ return entry;
502
+ }
503
+ const breadcrumb = entry as Record<string, unknown>;
504
+ return {
505
+ ...breadcrumb,
506
+ label: templateString(breadcrumb.label),
507
+ url: templateString(breadcrumb.url),
508
+ };
509
+ },
510
+ );
511
+ }
493
512
 
494
513
  // Render title and description as inline Markdown
495
514
  const frontMatterMd = createMarkdown(siteVariables, {
@@ -502,21 +521,21 @@ function preparePageVariables({
502
521
  validateFrontMatter(pageVariables, filePath);
503
522
  resolveAuthor(pageVariables, filePath, dependencyCollector);
504
523
 
505
- const parentError = validateParentLink(
506
- pageVariables.parent,
524
+ const breadcrumbs = pageVariables.breadcrumbs;
525
+ validateBreadcrumbs(
526
+ breadcrumbs,
507
527
  filePath,
508
528
  validInternalTargets,
509
529
  sourceUrlPath,
510
530
  );
511
- if (parentError) {
512
- throw new Error(parentError);
513
- }
514
- const resolvedParentTarget = resolveParentLinkTarget(
515
- pageVariables.parent,
516
- sourceUrlPath,
517
- );
518
- if (resolvedParentTarget) {
519
- dependencyCollector?.internalTargets?.add(resolvedParentTarget);
531
+ for (const entry of breadcrumbs ?? []) {
532
+ const resolvedTarget = resolveBreadcrumbLinkTarget(
533
+ entry.url,
534
+ sourceUrlPath,
535
+ );
536
+ if (resolvedTarget) {
537
+ dependencyCollector?.internalTargets?.add(resolvedTarget);
538
+ }
520
539
  }
521
540
 
522
541
  return pageVariables;
@@ -1,4 +1,5 @@
1
1
  import path from 'path';
2
+ import type { BreadcrumbEntry } from './types';
2
3
  import { encodeAuthoredUrl } from './template-globals';
3
4
  import { normalizeOutputPath } from './utils/paths';
4
5
 
@@ -208,47 +209,86 @@ function splitHref(href: string): { pathname: string; suffix: string } {
208
209
  return { pathname: match ? match[1] : href, suffix: match ? match[2] : '' };
209
210
  }
210
211
 
211
- export function validateParentLink(
212
- parent: unknown,
212
+ export function validateBreadcrumbs(
213
+ breadcrumbs: unknown,
213
214
  filePath: string,
214
215
  validTargets: ReadonlySet<string>,
215
216
  sourceUrlPath: string,
216
- ): string | null {
217
- if (typeof parent === 'string' && splitHref(parent).pathname === '') {
218
- return `${filePath}: broken parent link: "${parent}"`;
217
+ ): asserts breadcrumbs is BreadcrumbEntry[] | undefined {
218
+ if (breadcrumbs === undefined) {
219
+ return;
220
+ }
221
+ if (!Array.isArray(breadcrumbs)) {
222
+ throw new Error(`${filePath}: breadcrumbs must be a list`);
219
223
  }
224
+ breadcrumbs.forEach((entry: unknown, index) => {
225
+ const context = `${filePath}: breadcrumb entry ${index + 1}`;
226
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
227
+ throw new Error(`${context} must be an object with label and url fields`);
228
+ }
229
+ const { label, url } = entry as Record<string, unknown>;
230
+ for (const [field, value] of Object.entries({ label, url })) {
231
+ if (typeof value !== 'string' || !value.trim()) {
232
+ throw new Error(`${context}: ${field} must be a nonempty string`);
233
+ }
234
+ }
235
+ const error = validateBreadcrumbLink(
236
+ url as string,
237
+ filePath,
238
+ validTargets,
239
+ sourceUrlPath,
240
+ index + 1,
241
+ );
242
+ if (error) {
243
+ throw new Error(error);
244
+ }
245
+ });
246
+ }
220
247
 
221
- const resolvedTarget = resolveParentLinkTarget(parent, sourceUrlPath);
248
+ export function validateBreadcrumbLink(
249
+ url: string,
250
+ filePath: string,
251
+ validTargets: ReadonlySet<string>,
252
+ sourceUrlPath: string,
253
+ entryNumber = 1,
254
+ ): string | null {
255
+ const context = `${filePath}: breadcrumb entry ${entryNumber}`;
256
+ const resolvedTarget = resolveBreadcrumbLinkTarget(url, sourceUrlPath);
222
257
  if (!resolvedTarget) {
223
- return null;
258
+ return `${context}: broken breadcrumb link: "${url}" (must be an internal site link with a pathname)`;
224
259
  }
225
260
 
226
261
  const directoryError = validateDirectoryLink(
227
- parent as string,
262
+ url,
228
263
  resolvedTarget,
229
264
  validTargets,
230
- `${filePath}: parent link`,
265
+ context,
231
266
  );
232
267
  if (directoryError) {
233
268
  return directoryError;
234
269
  }
235
270
 
236
271
  if (!validTargets.has(resolvedTarget)) {
237
- return `${filePath}: broken parent link: "${parent}"`;
272
+ return `${context}: broken breadcrumb link: "${url}"`;
238
273
  }
239
274
 
240
275
  return null;
241
276
  }
242
277
 
243
- export function resolveParentLinkTarget(
244
- parent: unknown,
278
+ export function resolveBreadcrumbLinkTarget(
279
+ url: string,
245
280
  sourceUrlPath: string,
246
281
  ): string | null {
247
- if (!parent || typeof parent !== 'string') {
282
+ // Breadcrumbs always point into this site. Backslashes also have special
283
+ // URL semantics in browsers, so they cannot be used as path separators.
284
+ if (
285
+ /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url) ||
286
+ url.startsWith('//') ||
287
+ url.includes('\\')
288
+ ) {
248
289
  return null;
249
290
  }
250
-
251
- const { pathname } = splitHref(parent);
291
+ const { pathname } = splitHref(url);
252
292
  if (!pathname) {
253
293
  return null;
254
294
  }
@@ -1,6 +1,9 @@
1
1
  ---
2
- parent: /labs/00/index.html
3
- parentLabel: Lab 0
2
+ breadcrumbs:
3
+ - label: Labs
4
+ url: /labs/index.html
5
+ - label: Lab 0
6
+ url: /labs/00/index.html
4
7
  title: Counting vowels
5
8
  author: alex
6
9
  description: An example of a literate Java program that takes predefined input.
@@ -15,7 +18,7 @@ The <dfn>standard in</dfn> (or `stdin`) is one of three streams available to a
15
18
  running program. The other two streams are the standard out and standard error.
16
19
 
17
20
  <figure>
18
- <svg viewBox="0 50 410 90" xmlns="http://www.w3.org/2000/svg" font-size="14" fill="none" style="margin:1rem auto;display:block;max-width:410px">
21
+ <svg viewBox="15 50 380 90" xmlns="http://www.w3.org/2000/svg" font-size="14" fill="none" style="margin:1rem auto;display:block;max-width:380px">
19
22
  <defs>
20
23
  <marker id="arrow" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
21
24
  <path d="M0,0 L8,4 L0,8 Z" stroke="currentColor" fill="currentColor"></path>
@@ -1,6 +1,7 @@
1
1
  ---
2
- parent: /labs/index.html
3
- parentLabel: Labs
2
+ breadcrumbs:
3
+ - label: Labs
4
+ url: /labs/index.html
4
5
  title: Lab 0
5
6
  author: alex
6
7
  description: An example lab page for <%= site.title %>.
@@ -1,6 +1,7 @@
1
1
  ---
2
- parent: /labs/index.html
3
- parentLabel: Labs
2
+ breadcrumbs:
3
+ - label: Labs
4
+ url: /labs/index.html
4
5
  title: Lab 1
5
6
  author: alex
6
7
  description: A lab page for <%= site.title %> demonstrating the trace feature.
@@ -1,6 +1,7 @@
1
1
  ---
2
- parent: ../index.html
3
- parentLabel: Labs
2
+ breadcrumbs:
3
+ - label: Labs
4
+ url: ../index.html
4
5
  title: Lab 2
5
6
  author: alex
6
7
  description: A page demonstrating slides mode.
@@ -1,6 +1,7 @@
1
1
  ---
2
- parentLabel: Lectures
3
- parent: /lectures/index.html
2
+ breadcrumbs:
3
+ - label: Lectures
4
+ url: /lectures/index.html
4
5
  title: Lecture 1
5
6
  description: An example lecture page.
6
7
  author: alex
@@ -1,6 +1,7 @@
1
1
  ---
2
- parentLabel: Lectures
3
- parent: /lectures/index.html
2
+ breadcrumbs:
3
+ - label: Lectures
4
+ url: /lectures/index.html
4
5
  title: Lecture 2
5
6
  description: An example lecture page demonstrating partials.
6
7
  author: alex
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.18.0",
3
+ "version": "1.18.2",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
package/src/_content.scss CHANGED
@@ -364,18 +364,34 @@ dfn {
364
364
  color: var(--theme-color-text);
365
365
  }
366
366
 
367
- a.breadcrumb {
368
- @include user-select(none);
369
- @include subdued-link;
367
+ .breadcrumbs {
368
+ ol {
369
+ display: flex;
370
+ flex-wrap: wrap;
371
+ gap: 0.25em;
372
+ padding: 0;
373
+ margin: 0;
374
+ list-style: none;
375
+ }
376
+
377
+ li {
378
+ display: flex;
379
+ gap: 0.25em;
380
+ align-items: center;
381
+ min-width: 0;
382
+ overflow-wrap: anywhere;
383
+ }
370
384
 
371
- &::after {
372
- display: inline-block;
373
- width: var(--icon-parent-size);
374
- height: var(--icon-parent-size);
375
- vertical-align: middle;
376
- content: '';
385
+ a {
386
+ @include subdued-link;
387
+ }
388
+
389
+ [aria-current='page'] {
390
+ font-style: italic;
391
+ }
377
392
 
378
- @include contained-mask(var(--icon-parent));
393
+ .material-symbol-icon {
394
+ flex-shrink: 0;
379
395
  }
380
396
  }
381
397
 
package/src/_layout.scss CHANGED
@@ -152,14 +152,16 @@ body.literate main table {
152
152
  .info {
153
153
  @include user-select(none);
154
154
 
155
- display: grid;
156
- grid-template-columns: 1fr auto;
155
+ // Flex instead of a `1fr auto` grid: WebKit keeps the auto column's width
156
+ // after the font changes, which wraps the author avatar onto its own line.
157
+ display: flex;
157
158
  align-items: baseline;
159
+ justify-content: space-between;
158
160
  font-size: var(--font-size-smaller);
159
161
  color: var(--fg2-color);
160
162
 
161
163
  @media (width < 450px) {
162
- grid-template-columns: 1fr;
164
+ flex-direction: column;
163
165
  }
164
166
 
165
167
  aside.author {
@@ -22,3 +22,10 @@
22
22
  --material-symbol-icon: var(--icon-contrast-high);
23
23
  --material-symbol-icon-size: var(--icon-contrast-high-size);
24
24
  }
25
+
26
+ .material-symbol-icon-breadcrumb-separator {
27
+ --material-symbol-icon: var(--icon-breadcrumb-separator);
28
+ --material-symbol-icon-size: var(--icon-breadcrumb-separator-size);
29
+
30
+ color: var(--fg2-color);
31
+ }
@@ -2,6 +2,11 @@ $shift: 8px;
2
2
  $duration: 150ms;
3
3
  $movement-easing: cubic-bezier(0.2, 0, 0, 1);
4
4
 
5
+ :root {
6
+ --page-transition-duration: #{$duration};
7
+ --page-transition-movement-easing: #{$movement-easing};
8
+ }
9
+
5
10
  @keyframes page-fade-out {
6
11
  to {
7
12
  opacity: 0;
@@ -38,19 +43,38 @@ $movement-easing: cubic-bezier(0.2, 0, 0, 1);
38
43
  }
39
44
  }
40
45
 
41
- // Page heading and info: crossfade in place instead of sliding with the page
46
+ // Page heading parts: crossfade in place instead of sliding with the page
42
47
  // view-transition-name is set via inline style by the navigator
43
- // only when .title-and-info is visible in the viewport
44
-
48
+ // only when .title-and-info is visible in the viewport. All parts share one
49
+ // duration and easing so they move in step when the layout changes; the
50
+ // crossfades inherit the group timing.
51
+ ::view-transition-group(page-breadcrumbs),
52
+ ::view-transition-old(page-breadcrumbs),
53
+ ::view-transition-new(page-breadcrumbs),
54
+ ::view-transition-group(page-title),
45
55
  ::view-transition-old(page-title),
46
56
  ::view-transition-new(page-title),
57
+ ::view-transition-group(page-info),
47
58
  ::view-transition-old(page-info),
48
- ::view-transition-new(page-info),
49
- ::view-transition-old(page-breadcrumb),
50
- ::view-transition-new(page-breadcrumb) {
59
+ ::view-transition-new(page-info) {
51
60
  animation-duration: $duration;
52
61
  }
53
62
 
63
+ ::view-transition-group(page-breadcrumbs),
64
+ ::view-transition-group(page-title),
65
+ ::view-transition-group(page-info) {
66
+ animation-timing-function: $movement-easing;
67
+ }
68
+
69
+ // Breadcrumbs: the whole trail is one group, so every item moves together when
70
+ // the layout changes. The default plus-lighter crossfade keeps unchanged items
71
+ // opaque while added or removed items fade. Snapshots keep their natural size
72
+ // so a change in trail width does not stretch the text.
73
+ ::view-transition-old(page-breadcrumbs),
74
+ ::view-transition-new(page-breadcrumbs) {
75
+ inline-size: auto;
76
+ }
77
+
54
78
  // The site-wide banner does not change between pages. Keep it out of the root
55
79
  // slide and pin its named transition layer in place without a crossfade.
56
80
  .site-banner {
@@ -117,25 +117,28 @@ function dispatchNavigationEvent(
117
117
  );
118
118
  }
119
119
 
120
- function cleanupViewTransitionNames(document: Document): void {
121
- const newH1 = document.querySelector(
122
- '.title-and-info h1',
123
- ) as HTMLElement | null;
124
- const newInfo = document.querySelector(
125
- '.title-and-info .info',
126
- ) as HTMLElement | null;
127
- const newBreadcrumb = document.querySelector(
128
- '.title-and-info a.breadcrumb',
129
- ) as HTMLElement | null;
130
-
131
- if (newH1) {
132
- newH1.style.viewTransitionName = '';
133
- }
134
- if (newInfo) {
135
- newInfo.style.viewTransitionName = '';
120
+ // Heading parts that get their own layer instead of sliding with the page. The
121
+ // breadcrumb trail is one group so every item, including the current one,
122
+ // moves together when the page layout changes.
123
+ const TITLE_TRANSITION_NAMES = [
124
+ ['nav.breadcrumbs', 'page-breadcrumbs'],
125
+ ['h1', 'page-title'],
126
+ ['.info', 'page-info'],
127
+ ] as const;
128
+
129
+ function setTitleTransitionNames(titleEl: Element, enabled: boolean): void {
130
+ for (const [selector, name] of TITLE_TRANSITION_NAMES) {
131
+ const element = titleEl.querySelector<HTMLElement>(selector);
132
+ if (element) {
133
+ element.style.viewTransitionName = enabled ? name : '';
134
+ }
136
135
  }
137
- if (newBreadcrumb) {
138
- newBreadcrumb.style.viewTransitionName = '';
136
+ }
137
+
138
+ function cleanupViewTransitionNames(document: Document): void {
139
+ const titleEl = document.querySelector('.title-and-info');
140
+ if (titleEl) {
141
+ setTitleTransitionNames(titleEl, false);
139
142
  }
140
143
  }
141
144
 
@@ -343,20 +346,7 @@ export async function navigateToUrl(
343
346
  titleEl != null && titleEl.getBoundingClientRect().top >= headerHeight;
344
347
 
345
348
  if (titleVisible) {
346
- const h1 = titleEl.querySelector('h1') as HTMLElement | null;
347
- const info = titleEl.querySelector('.info') as HTMLElement | null;
348
- const breadcrumb = titleEl.querySelector(
349
- 'a.breadcrumb',
350
- ) as HTMLElement | null;
351
- if (h1) {
352
- h1.style.viewTransitionName = 'page-title';
353
- }
354
- if (info) {
355
- info.style.viewTransitionName = 'page-info';
356
- }
357
- if (breadcrumb) {
358
- breadcrumb.style.viewTransitionName = 'page-breadcrumb';
359
- }
349
+ setTitleTransitionNames(titleEl, true);
360
350
  }
361
351
 
362
352
  document.documentElement.classList.add(
@@ -376,20 +366,7 @@ export async function navigateToUrl(
376
366
  newTitleEl.getBoundingClientRect().top >= newHeaderHeight;
377
367
 
378
368
  if (newTitleVisible && newTitleEl) {
379
- const h1 = newTitleEl.querySelector('h1') as HTMLElement | null;
380
- const info = newTitleEl.querySelector('.info') as HTMLElement | null;
381
- const breadcrumb = newTitleEl.querySelector(
382
- 'a.breadcrumb',
383
- ) as HTMLElement | null;
384
- if (h1) {
385
- h1.style.viewTransitionName = 'page-title';
386
- }
387
- if (info) {
388
- info.style.viewTransitionName = 'page-info';
389
- }
390
- if (breadcrumb) {
391
- breadcrumb.style.viewTransitionName = 'page-breadcrumb';
392
- }
369
+ setTitleTransitionNames(newTitleEl, true);
393
370
  }
394
371
  });
395
372
 
@@ -1,6 +1,19 @@
1
1
  <div class="title-and-info" data-pagefind-body>
2
- <% if (page.parent) { %>
3
- <a class="breadcrumb" href="<%- encodeAuthoredUrl(page.parent) %>" data-pagefind-ignore><%- page.parentLabel %></a>
2
+ <% if (page.breadcrumbs && page.breadcrumbs.length) { %>
3
+ <nav class="breadcrumbs" aria-label="Breadcrumb" data-pagefind-ignore>
4
+ <ol>
5
+ <% page.breadcrumbs.forEach((entry, index) => { %>
6
+ <li>
7
+ <% if (index > 0) { %><span class="material-symbol-icon material-symbol-icon-breadcrumb-separator" aria-hidden="true"></span><% } %>
8
+ <a href="<%- encodeAuthoredUrl(entry.url) %>"><%- entry.label %></a>
9
+ </li>
10
+ <% }); %>
11
+ <li>
12
+ <span class="material-symbol-icon material-symbol-icon-breadcrumb-separator" aria-hidden="true"></span>
13
+ <span aria-current="page"><%- page.title %></span>
14
+ </li>
15
+ </ol>
16
+ </nav>
4
17
  <% } %>
5
18
  <h1 data-pagefind-weight="10" data-pagefind-meta="title_html[data-title-html]" data-title-html="<%- page.titleHtml %>"><%= page.titleHtml %></h1>
6
19
 
@@ -33,8 +33,10 @@
33
33
 
34
34
  // Main background color (the color of the page background)
35
35
  --bg-color: hsl(var(--tint-hue) calc(5% * var(--tint-amount)) 97%);
36
+
37
+ // Same saturation as --bg-color, so frosting doesn't add color to the page
36
38
  --bg-color-translucent: hsl(
37
- var(--tint-hue) calc(30% * var(--tint-amount)) 95% / 66%
39
+ var(--tint-hue) calc(5% * var(--tint-amount)) 95% / 66%
38
40
  );
39
41
 
40
42
  // Secondary background color (lighter)
@@ -117,7 +119,7 @@
117
119
  --link-color-hover: <%= linkColorHoverDark %>;
118
120
  --bg-color: hsl(var(--tint-hue) calc(3% * var(--tint-amount)) 5%);
119
121
  --bg-color-translucent: hsl(
120
- var(--tint-hue) calc(15% * var(--tint-amount)) 10% / 66%
122
+ var(--tint-hue) calc(3% * var(--tint-amount)) 10% / 66%
121
123
  );
122
124
  --bg2-color: hsl(var(--tint-hue) calc(12% * var(--tint-amount)) 12%);
123
125
  --bg2-color-translucent: hsl(
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 -960 960 960" width="20"><path d="M386-224v-58h250.69L200-718.69 241.31-760 678-323.31V-574h58v350H386Z"/></svg>