@davidsouther/jiffies 2026.4.1 → 2026.24.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.
Files changed (121) hide show
  1. package/README.md +0 -3
  2. package/package.json +11 -6
  3. package/src/404.html +1 -1
  4. package/src/components/accordion.ts +25 -0
  5. package/src/components/alert.ts +47 -0
  6. package/src/components/card.ts +54 -0
  7. package/src/components/children.ts +11 -0
  8. package/src/components/form.ts +25 -0
  9. package/src/components/index.ts +22 -0
  10. package/src/components/link.ts +22 -0
  11. package/src/components/modal.ts +15 -0
  12. package/src/components/nav.ts +42 -0
  13. package/src/components/property.ts +32 -0
  14. package/src/components/tabs.ts +82 -0
  15. package/src/components/virtual_scroll.ts +1 -1
  16. package/src/dom/README.md +7 -2
  17. package/src/dom/SKILL.md +201 -0
  18. package/src/dom/dom.ts +185 -41
  19. package/src/dom/fc.ts +3 -2
  20. package/src/dom/form/form.app.ts +35 -41
  21. package/src/dom/form/form.ts +79 -10
  22. package/src/dom/form/index.html +2 -2
  23. package/src/dom/hydrate.ts +206 -0
  24. package/src/dom/navigation/index.ts +349 -0
  25. package/src/dom/render.ts +41 -0
  26. package/src/dom/svg.ts +6 -2
  27. package/src/fs_node.ts +2 -2
  28. package/src/log.ts +154 -2
  29. package/src/server/http/response.ts +6 -3
  30. package/src/server/http/sitemap.ts +10 -34
  31. package/src/server/http/static.ts +0 -2
  32. package/src/server/live-reload.ts +208 -0
  33. package/src/server/main.ts +14 -7
  34. package/src/server/ws/frame.ts +36 -0
  35. package/src/server/ws/handshake.ts +42 -0
  36. package/src/server/ws/index.ts +100 -0
  37. package/src/ssg/bundle.ts +85 -0
  38. package/src/ssg/copy-public.ts +44 -0
  39. package/src/ssg/discover.ts +143 -0
  40. package/src/ssg/main.ts +168 -0
  41. package/src/ssg/rewrite.ts +18 -0
  42. package/src/ssg/ssg.ts +134 -0
  43. package/src/components/test.ts +0 -5
  44. package/src/components/virtual_scroll.test.ts +0 -30
  45. package/src/context.test.ts +0 -58
  46. package/src/context.ts +0 -67
  47. package/src/diff.test.ts +0 -48
  48. package/src/dom/fc.test.ts +0 -43
  49. package/src/dom/form/form.test.ts +0 -0
  50. package/src/dom/html.test.ts +0 -74
  51. package/src/dom/observable.test.ts +0 -43
  52. package/src/dom/test.ts +0 -11
  53. package/src/equal.test.ts +0 -23
  54. package/src/flags.test.ts +0 -43
  55. package/src/flags.ts +0 -53
  56. package/src/fs.test.ts +0 -106
  57. package/src/fs_win.test.ts +0 -11
  58. package/src/generator.test.ts +0 -27
  59. package/src/index.html +0 -82
  60. package/src/is_browser.js +0 -1
  61. package/src/lock.test.ts +0 -17
  62. package/src/observable/observable.test.ts +0 -73
  63. package/src/pico/_variables.scss +0 -66
  64. package/src/pico/components/_accordion.scss +0 -112
  65. package/src/pico/components/_button-group.scss +0 -51
  66. package/src/pico/components/_card.scss +0 -47
  67. package/src/pico/components/_dropdown.scss +0 -203
  68. package/src/pico/components/_modal.scss +0 -181
  69. package/src/pico/components/_nav.scss +0 -79
  70. package/src/pico/components/_progress.scss +0 -70
  71. package/src/pico/components/_property.scss +0 -34
  72. package/src/pico/content/_button.scss +0 -152
  73. package/src/pico/content/_code.scss +0 -63
  74. package/src/pico/content/_embedded.scss +0 -0
  75. package/src/pico/content/_form-alt.scss +0 -276
  76. package/src/pico/content/_form.scss +0 -259
  77. package/src/pico/content/_misc.scss +0 -0
  78. package/src/pico/content/_table.scss +0 -28
  79. package/src/pico/content/_toggle.scss +0 -132
  80. package/src/pico/content/_typography.scss +0 -232
  81. package/src/pico/layout/_container.scss +0 -40
  82. package/src/pico/layout/_document.scss +0 -0
  83. package/src/pico/layout/_flex.scss +0 -46
  84. package/src/pico/layout/_grid.scss +0 -24
  85. package/src/pico/layout/_scroller.scss +0 -16
  86. package/src/pico/layout/_section.scss +0 -8
  87. package/src/pico/layout/_sectioning.scss +0 -55
  88. package/src/pico/pico.scss +0 -60
  89. package/src/pico/reset/_accessibility.scss +0 -34
  90. package/src/pico/reset/_button.scss +0 -17
  91. package/src/pico/reset/_code.scss +0 -15
  92. package/src/pico/reset/_document.scss +0 -48
  93. package/src/pico/reset/_embedded.scss +0 -39
  94. package/src/pico/reset/_form.scss +0 -97
  95. package/src/pico/reset/_misc.scss +0 -23
  96. package/src/pico/reset/_nav.scss +0 -5
  97. package/src/pico/reset/_progress.scss +0 -4
  98. package/src/pico/reset/_table.scss +0 -8
  99. package/src/pico/reset/_typography.scss +0 -25
  100. package/src/pico/themes/default/_colors.scss +0 -65
  101. package/src/pico/themes/default/_dark.scss +0 -148
  102. package/src/pico/themes/default/_light.scss +0 -149
  103. package/src/pico/themes/default/_styles.scss +0 -272
  104. package/src/pico/themes/default.scss +0 -34
  105. package/src/pico/utilities/_accessibility.scss +0 -3
  106. package/src/pico/utilities/_loading.scss +0 -52
  107. package/src/pico/utilities/_reduce-motion.scss +0 -27
  108. package/src/pico/utilities/_tooltip.scss +0 -101
  109. package/src/result.test.ts +0 -101
  110. package/src/scope/describe.ts +0 -81
  111. package/src/scope/display/console.ts +0 -26
  112. package/src/scope/display/dom.ts +0 -36
  113. package/src/scope/display/junit.ts +0 -64
  114. package/src/scope/execute.ts +0 -110
  115. package/src/scope/expect.ts +0 -169
  116. package/src/scope/fix.ts +0 -30
  117. package/src/scope/index.ts +0 -11
  118. package/src/scope/scope.ts +0 -21
  119. package/src/scope/state.ts +0 -13
  120. package/src/test.mjs +0 -33
  121. package/src/test_all.ts +0 -35
package/src/index.html DELETED
@@ -1,82 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Jiffies Tests</title>
5
- <base href="/" />
6
- <link rel="stylesheet" href="/pico/pico.css?$enable-viewport=none" />
7
- </head>
8
- <body>
9
- <header></header>
10
-
11
- <main>
12
- <article id="test_output"></article>
13
- </main>
14
-
15
- <div style="display: none" id="status"></div>
16
- <div style="display: none" id="json"></div>
17
- <div style="display: none" id="xml"></div>
18
-
19
- <script type="module">
20
- await Promise.all([
21
- import("./test_all.js"),
22
- import("./dom/test.js"),
23
- import("./components/test.js"),
24
- ]);
25
-
26
- import { onConsole } from "./scope/display/console.ts";
27
- import { displayStatistics } from "./scope/display/dom.ts";
28
- import { asXML } from "./scope/display/junit.ts";
29
- import { execute } from "./scope/execute.ts";
30
-
31
- function write(id, value) {
32
- const start = `--- START_${id.toUpperCase()} ---`;
33
- const end = `--- END ---`;
34
- document.getElementById(
35
- id
36
- ).innerHTML = `\n${start}\n${value}\n${end}\n`;
37
- }
38
-
39
- (async () => {
40
- const results = await execute();
41
- displayStatistics(results, document.getElementById("test_output"));
42
- onConsole(results);
43
- write("status", results.failed);
44
- write("json", JSON.stringify(results));
45
- write("xml", asXML(results));
46
- })();
47
- </script>
48
- <script type="module">
49
- import { li, nav, strong, ul } from "./dom/html.ts";
50
- import { link } from "./dom/router/link.ts";
51
-
52
- const getUrls = async () => {
53
- const sitemap = await fetch("/sitemap.json");
54
- if (sitemap.ok) {
55
- return await sitemap.json();
56
- } else {
57
- return [
58
- "/index.html",
59
- "/apps/computron5k/index.html",
60
- "/apps/chess/index.html",
61
- ];
62
- }
63
- };
64
-
65
- const urls = (await getUrls()).map((url) => ({
66
- href: url.replace("index.html", ""),
67
- link:
68
- url.match(/\/(?<link>[^/]+)(\/index\.html)?$/)?.groups.link ??
69
- url.replace("index.html", ""),
70
- }));
71
-
72
- document
73
- .querySelector("body > header")
74
- .appendChild(
75
- nav(
76
- ul(li(strong("Jiffy Apps"))),
77
- ul(...urls.map((url) => li(link(url))))
78
- )
79
- );
80
- </script>
81
- </body>
82
- </html>
package/src/is_browser.js DELETED
@@ -1 +0,0 @@
1
- export const IsBrowser = typeof window !== "undefined";
package/src/lock.test.ts DELETED
@@ -1,17 +0,0 @@
1
- import { lock } from "./lock.ts";
2
- import { describe, expect, it } from "./scope/index.ts";
3
-
4
- describe("Lock", () => {
5
- it("prevents reentry", () => {
6
- let count = 0;
7
- const inc = lock(() => {
8
- if (count > 4) {
9
- return;
10
- }
11
- inc();
12
- count++;
13
- });
14
- inc();
15
- expect(count).toBe(1);
16
- });
17
- });
@@ -1,73 +0,0 @@
1
- import { describe, expect, it } from "../scope/index.ts";
2
- import { filter, map, Observable, Subject } from "./observable.ts";
3
-
4
- describe("Observables", () => {
5
- describe("basics", () => {
6
- it("can create and observe a scalar subject", async () => {
7
- const subject = new Subject<number>();
8
-
9
- let resolved = 42;
10
-
11
- subject.subscribe({
12
- next: (n) => {
13
- resolved = n;
14
- },
15
- });
16
-
17
- expect(resolved).toBe(42);
18
- await subject.next(64);
19
- expect(resolved).toBe(64);
20
- });
21
- });
22
-
23
- describe("creation", () => {
24
- it("builds an observable of items", async () => {
25
- const stream = Observable.of(2, 4, 8, 16);
26
- const values: number[] = [];
27
- await new Promise<void>((resolve) => {
28
- stream.subscribe({
29
- next: (n) => {
30
- values.unshift(n);
31
- },
32
- complete: () => {
33
- resolve();
34
- },
35
- });
36
- });
37
- expect(values).toEqual([16, 8, 4, 2]);
38
- });
39
- });
40
-
41
- describe("pipes", () => {
42
- it("runs a pipe", async () => {
43
- const subject = new Subject<number>();
44
-
45
- let resolved = 0;
46
- const inflate = (i: number) => (n: number) => n + i;
47
- const biggerThan = (i: number) => (n: number) => n > i;
48
- const assign = (n: number) => {
49
- resolved = n;
50
- };
51
-
52
- subject.pipe(map(inflate(22)), filter(biggerThan(70))).subscribe(assign);
53
-
54
- expect(resolved).toBe(0);
55
- await subject.next(42);
56
- expect(resolved).toBe(0);
57
- await subject.next(50);
58
- expect(resolved).toBe(72);
59
- });
60
- });
61
-
62
- describe("Subject", () => {
63
- it("is callable", async () => {
64
- const subject = new Subject<number>();
65
- let resolved = 0;
66
- subject.subscribe((n) => {
67
- resolved = n;
68
- });
69
- await subject.next(42);
70
- expect(resolved).toBe(42);
71
- });
72
- });
73
- });
@@ -1,66 +0,0 @@
1
- // Config
2
- // ––––––––––––––––––––
3
-
4
- // Enable <header>, <main>, <footer> inside <body> as a container
5
- $enable-semantic-container: true !default;
6
-
7
- // Enable .container and .container-fluid
8
- $enable-class-container: false !default;
9
-
10
- // Enable a centered viewport for <header>, <main>, <footer> inside <body>
11
- // Options are "all" to center all, "main" to only center main, and "none" to treat all as fluid.
12
- $enable-viewport: "none" !default;
13
-
14
- // Enable responsive spacings for <header>, <main>, <footer>, <section>, <article>
15
- // Fixed spacings if disabled
16
- $enable-responsive-spacings: true !default;
17
-
18
- // Enable responsive typography
19
- // Fixed root element size if disabled
20
- $enable-responsive-typography: true !default;
21
-
22
- // Enable .classes
23
- // .classless version if disabled
24
- $enable-classes: true !default;
25
-
26
- // Enable .grid class
27
- $enable-grid: true !default;
28
-
29
- // Enable transitions
30
- $enable-transitions: true !default;
31
-
32
- // Enable overriding with !important
33
- $enable-important: true !default;
34
-
35
- // Responsive
36
- // ––––––––––––––––––––
37
-
38
- // xs: Extra small (portrait phones)
39
- // sm: Small (landscape phones)
40
- // md: Medium (tablets)
41
- // lg: Large (desktops)
42
- // xl: Extra large (large desktops, TVs)
43
-
44
- // NOTE:
45
- // To provide an easy and fine styling on each breakpoint
46
- // we didn't use @each, @mixin or @include.
47
- // That means you need to edit each CSS selector file to add a breakpoint
48
-
49
- // Breakpoints
50
- // 'null' disable the breakpoint
51
- $breakpoints: (
52
- xs: 0,
53
- sm: 576px,
54
- md: 768px,
55
- lg: 992px,
56
- xl: 1200px,
57
- ) !default;
58
-
59
- // Viewports
60
- $viewports: (
61
- // 'null' disable the viewport on a breakpoint
62
- sm: 510px,
63
- md: 700px,
64
- lg: 920px,
65
- xl: 1130px
66
- ) !default;
@@ -1,112 +0,0 @@
1
- details {
2
- display: block;
3
- margin-bottom: var(--spacing);
4
- padding-bottom: var(--spacing);
5
- border-bottom: var(--border-width) solid var(--accordion-border-color);
6
-
7
- summary {
8
- line-height: 1rem;
9
- list-style-type: none;
10
- cursor: pointer;
11
-
12
- &:not([role]) {
13
- color: var(--accordion-close-summary-color);
14
- }
15
-
16
- @if $enable-transitions {
17
- transition: color var(--transition);
18
- }
19
-
20
- // Reset marker
21
- &::-webkit-details-marker {
22
- display: none;
23
- }
24
-
25
- &::marker {
26
- display: none;
27
- }
28
-
29
- &::-moz-list-bullet {
30
- list-style-type: none;
31
- }
32
-
33
- // Marker
34
- &::after {
35
- display: block;
36
- width: 1rem;
37
- height: 1rem;
38
- margin-inline-start: calc(var(--spacing, 1rem) * 0.5);
39
- float: right;
40
- transform: rotate(-90deg);
41
- background-image: var(--icon-chevron);
42
- background-position: right center;
43
- background-size: 1rem auto;
44
- background-repeat: no-repeat;
45
- content: "";
46
-
47
- @if $enable-transitions {
48
- transition: transform var(--transition);
49
- }
50
- }
51
-
52
- &:focus {
53
- outline: none;
54
-
55
- &:not([role="button"]) {
56
- color: var(--accordion-active-summary-color);
57
- }
58
- }
59
-
60
- // Type button
61
- &[role="button"] {
62
- width: 100%;
63
- text-align: left;
64
-
65
- // Marker
66
- &::after {
67
- height: calc(1rem * var(--line-height, 1.5));
68
- background-image: var(--icon-chevron-button);
69
- }
70
-
71
- @if $enable-classes {
72
- // .contrast
73
- &:not(.outline).contrast {
74
- // Marker
75
- &::after {
76
- background-image: var(--icon-chevron-button-inverse);
77
- }
78
- }
79
- }
80
- }
81
- }
82
-
83
- // Open
84
- &[open] {
85
- > summary {
86
- margin-bottom: calc(var(--spacing));
87
-
88
- &:not([role]) {
89
- &:not(:focus) {
90
- color: var(--accordion-open-summary-color);
91
- }
92
- }
93
-
94
- &::after {
95
- transform: rotate(0);
96
- }
97
- }
98
- }
99
- }
100
-
101
- [dir="rtl"] {
102
- details {
103
- summary {
104
- text-align: right;
105
-
106
- &::after {
107
- float: left;
108
- background-position: left center;
109
- }
110
- }
111
- }
112
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Input group and button group
3
- */
4
-
5
- .button-group,
6
- .input-group {
7
- // Stick buttons together
8
- display: flex;
9
- flex-direction: row;
10
-
11
- // Buttons have no margin-bottom set
12
- & > button,
13
- & > label,
14
- & > select,
15
- & > input:not([type="checkbox"]):not([type="radio"]) {
16
- margin-bottom: 0;
17
- }
18
-
19
- // No round corners in button groups
20
- & > button:not(:last-child),
21
- & > [role="button"]:not(:last-child),
22
- & > input:not(:last-child),
23
- & > select:not(:last-child) {
24
- border-top-right-radius: 0;
25
- border-bottom-right-radius: 0;
26
- /* border-right: 0;*/
27
- }
28
-
29
- & > [role="button"]:not(:first-child),
30
- & > input:not(:first-child),
31
- & > button:not(:first-child),
32
- & > select:not(:first-child) {
33
- border-top-left-radius: 0;
34
- border-bottom-left-radius: 0;
35
- border-left: 0;
36
- }
37
- }
38
-
39
- // Radio groups need a text label
40
- .input-group > [type="radio"] ~ label {
41
- margin-right: 0;
42
- }
43
-
44
- // The selected radio button will be outlined when outline class is choose and in reverse
45
- .input-group:not(.outline)
46
- > input[type="radio"]:not(:checked)
47
- + label[role="button"],
48
- .input-group.outline > input[type="radio"]:checked + label[role="button"] {
49
- --background-color: transparent;
50
- --color: var(--primary);
51
- }
@@ -1,47 +0,0 @@
1
- article {
2
- display: flex;
3
- flex-direction: column;
4
- margin: var(--block-spacing-vertical) 0;
5
- padding: var(--block-spacing-vertical) var(--block-spacing-horizontal);
6
- border-radius: var(--border-radius);
7
- background: var(--card-background-color);
8
- box-shadow: var(--card-box-shadow);
9
-
10
- > header,
11
- > footer {
12
- display: flex;
13
- flex-direction: row;
14
- justify-content: space-between;
15
- margin-right: calc(var(--block-spacing-horizontal) * -1);
16
- margin-left: calc(var(--block-spacing-horizontal) * -1);
17
- padding: calc(var(--block-spacing-vertical) * 0.66)
18
- var(--block-spacing-horizontal);
19
- background-color: var(--card-sectionning-background-color);
20
- }
21
-
22
- > header {
23
- margin-top: calc(var(--block-spacing-vertical) * -1);
24
- margin-bottom: var(--block-spacing-vertical);
25
- border-bottom: var(--border-width) solid var(--card-border-color);
26
-
27
- nav ul {
28
- &:first-of-type {
29
- margin: inherit;
30
- }
31
-
32
- li {
33
- padding: 0 calc(var(--nav-element-spacing-horizontal) / 2);
34
- }
35
- }
36
- }
37
-
38
- > main {
39
- flex: 1;
40
- }
41
-
42
- > footer {
43
- margin-top: var(--block-spacing-vertical);
44
- margin-bottom: calc(var(--block-spacing-vertical) * -1);
45
- border-top: var(--border-width) solid var(--card-border-color);
46
- }
47
- }
@@ -1,203 +0,0 @@
1
- details[role="list"],
2
- li[role="list"] {
3
- position: relative;
4
- }
5
-
6
- details[role="list"] summary + ul,
7
- li[role="list"] > ul {
8
- display: flex;
9
- z-index: 99;
10
- position: absolute;
11
- top: auto;
12
- right: 0;
13
- left: 0;
14
- flex-direction: column;
15
- margin: 0;
16
- padding: 0;
17
- border: var(--border-width) solid var(--dropdown-border-color);
18
- border-radius: var(--border-radius);
19
- border-top-right-radius: 0;
20
- border-top-left-radius: 0;
21
- background-color: var(--dropdown-background-color);
22
- box-shadow: var(--card-box-shadow);
23
- color: var(--dropdown-color);
24
- white-space: nowrap;
25
-
26
- li {
27
- width: 100%;
28
- margin-bottom: 0;
29
- padding: calc(var(--form-element-spacing-vertical) * 0.5)
30
- var(--form-element-spacing-horizontal);
31
- list-style: none;
32
-
33
- &:first-of-type {
34
- margin-top: calc(var(--form-element-spacing-vertical) * 0.5);
35
- }
36
-
37
- &:last-of-type {
38
- margin-bottom: calc(var(--form-element-spacing-vertical) * 0.5);
39
- }
40
-
41
- a {
42
- display: block;
43
- margin: calc(var(--form-element-spacing-vertical) * -0.5)
44
- calc(var(--form-element-spacing-horizontal) * -1);
45
- padding: calc(var(--form-element-spacing-vertical) * 0.5)
46
- var(--form-element-spacing-horizontal);
47
- overflow: hidden;
48
- color: var(--dropdown-color);
49
- text-decoration: none;
50
- text-overflow: ellipsis;
51
-
52
- &:hover {
53
- background-color: var(--dropdown-hover-background-color);
54
- }
55
- }
56
- }
57
- }
58
-
59
- // Marker
60
- details[role="list"] summary,
61
- li[role="list"] > a {
62
- &::after {
63
- display: block;
64
- width: 1rem;
65
- height: calc(1rem * var(--line-height, 1.5));
66
- margin-inline-start: 0.5rem;
67
- float: right;
68
- transform: rotate(0deg);
69
- background-position: right center;
70
- background-size: 1rem auto;
71
- background-repeat: no-repeat;
72
- content: "";
73
- }
74
- }
75
-
76
- // Global dropdown only
77
- details[role="list"] {
78
- padding: 0;
79
- border-bottom: none;
80
-
81
- // Style <summary> as <select>
82
- summary {
83
- margin-bottom: 0;
84
-
85
- &:not([role]) {
86
- height: calc(
87
- 1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 +
88
- var(--border-width) * 2
89
- );
90
- padding: var(--form-element-spacing-vertical)
91
- var(--form-element-spacing-horizontal);
92
- border: var(--border-width) solid var(--form-element-border-color);
93
- border-radius: var(--border-radius);
94
- background-color: var(--form-element-background-color);
95
- color: var(--form-element-placeholder-color);
96
- line-height: inherit;
97
- cursor: pointer;
98
-
99
- @if $enable-transitions {
100
- transition: background-color var(--transition),
101
- border-color var(--transition), color var(--transition),
102
- box-shadow var(--transition);
103
- }
104
-
105
- &:active,
106
- &:focus {
107
- border-color: var(--form-element-active-border-color);
108
- background-color: var(--form-element-active-background-color);
109
- }
110
-
111
- &:focus {
112
- box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color);
113
- }
114
- }
115
- }
116
-
117
- // Close for details[role="list"]
118
- &[open] summary {
119
- border-bottom-right-radius: 0;
120
- border-bottom-left-radius: 0;
121
-
122
- &::before {
123
- display: block;
124
- z-index: 1;
125
- position: fixed;
126
- top: 0;
127
- right: 0;
128
- bottom: 0;
129
- left: 0;
130
- background: none;
131
- content: "";
132
- cursor: default;
133
- }
134
- }
135
- }
136
-
137
- // All Dropdowns inside <nav>
138
- nav details[role="list"] summary,
139
- nav li[role="list"] a {
140
- display: flex;
141
- direction: ltr;
142
- }
143
-
144
- nav details[role="list"] summary + ul,
145
- nav li[role="list"] > ul {
146
- min-width: fit-content;
147
- border-radius: var(--border-radius);
148
-
149
- li a {
150
- border-radius: 0;
151
- }
152
- }
153
-
154
- // Dropdowns inside <nav> as nested <details>
155
- nav details[role="list"] {
156
- summary,
157
- summary:not([role]) {
158
- height: auto;
159
- padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);
160
- }
161
-
162
- &[open] summary {
163
- border-radius: var(--border-radius);
164
- }
165
-
166
- summary + ul {
167
- margin-top: var(--outline-width);
168
- margin-inline-start: 0;
169
- }
170
-
171
- summary[role="link"] {
172
- margin-bottom: calc(var(--nav-link-spacing-vertical) * -1);
173
- line-height: var(--line-height);
174
-
175
- + ul {
176
- margin-top: calc(var(--nav-link-spacing-vertical) + var(--outline-width));
177
- margin-inline-start: calc(var(--nav-link-spacing-horizontal) * -1);
178
- }
179
- }
180
- }
181
-
182
- // Dropdowns inside a <nav> without using <details>
183
- li[role="list"] {
184
- // Open on hover (for mobile)
185
- // or on active/focus (for keyboard navigation)
186
- &:hover > ul,
187
- a:active ~ ul,
188
- a:focus ~ ul {
189
- display: flex;
190
- }
191
-
192
- > ul {
193
- display: none;
194
- margin-top: calc(var(--nav-link-spacing-vertical) + var(--outline-width));
195
- margin-inline-start: calc(
196
- var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal)
197
- );
198
- }
199
-
200
- > a::after {
201
- background-image: var(--icon-chevron);
202
- }
203
- }