@danieldeusing/design 0.1.0 → 0.1.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
@@ -32,7 +32,7 @@ Link the built bundle from jsDelivr and you have the whole look. **Pin a release
32
32
  <meta name="viewport" content="width=device-width, initial-scale=1" />
33
33
  <meta name="theme-color" content="#f5efe2" />
34
34
 
35
- <!-- pre-paint: apply the saved theme before first paint to avoid a flash -->
35
+ <!-- pre-paint: apply the saved theme + resolution zoom before first paint -->
36
36
  <script>
37
37
  (() => {
38
38
  const bg = { warm: "#f5efe2", green: "#020604", mono: "#050505", paper: "#fafafa" };
@@ -41,11 +41,16 @@ Link the built bundle from jsDelivr and you have the whole look. **Pin a release
41
41
  document.documentElement.dataset.theme = t;
42
42
  document.querySelector('meta[name=theme-color]')?.setAttribute("content", bg[t]);
43
43
  })();
44
+ // resolution-independent zoom: scale the whole layout up past a 1920px reference
45
+ (() => {
46
+ const z = () => { document.documentElement.style.zoom = String(Math.max(1, innerWidth / 1920)); };
47
+ z(); addEventListener("resize", z);
48
+ })();
44
49
  </script>
45
50
 
46
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/danieldeusing/danieldeusing-design@v0.1.0/dist/danieldeusing-design.min.css" />
51
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/danieldeusing/danieldeusing-design@v0.1.2/dist/danieldeusing-design.min.css" />
47
52
  <!-- optional: the real JetBrains Mono webfont (otherwise falls back to Menlo) -->
48
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/danieldeusing/danieldeusing-design@v0.1.0/src/fonts.css" />
53
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/danieldeusing/danieldeusing-design@v0.1.2/src/fonts.css" />
49
54
  </head>
50
55
  <body>
51
56
  <p class="prompt">cat hello.txt</p>
@@ -54,7 +59,7 @@ Link the built bundle from jsDelivr and you have the whole look. **Pin a release
54
59
 
55
60
  <script type="module">
56
61
  import { initThemeSwitcher, initDropdowns, initTerminal } from
57
- "https://cdn.jsdelivr.net/gh/danieldeusing/danieldeusing-design@v0.1.0/runtime/index.js";
62
+ "https://cdn.jsdelivr.net/gh/danieldeusing/danieldeusing-design@v0.1.2/runtime/index.js";
58
63
  initThemeSwitcher();
59
64
  initDropdowns();
60
65
  initTerminal();
@@ -105,14 +110,17 @@ Four dependency-free ES modules, tree-shakeable from `@danieldeusing/design/runt
105
110
  | `setTheme(name)` / `initThemeSwitcher()` | Switch themes and wire `[data-theme-value]` buttons + `[data-theme-label]`. |
106
111
  | `initTerminal()` | The `$ command` typing animation. No-ops under reduced motion / `html.anim-off`. |
107
112
  | `initDropdowns()` | `<details class="dropdown">` behaviour: one-open, click-away, Escape. |
113
+ | `initAnimToggle()` | Wire `[data-anim-toggle]` buttons (`.anim-toggle`) to flip `html.anim-off` + persist it. |
108
114
  | `initResolutionZoom(1920)` | Scale the whole layout up on screens wider than the reference width. |
109
115
 
110
116
  ```js
111
- import { applyStoredTheme, initThemeSwitcher, initDropdowns, initTerminal } from "@danieldeusing/design/runtime";
117
+ import { applyStoredTheme, initThemeSwitcher, initDropdowns, initTerminal, initAnimToggle, initResolutionZoom } from "@danieldeusing/design/runtime";
112
118
  applyStoredTheme(); // ideally inline, pre-paint
119
+ initResolutionZoom(); // ideally inline, pre-paint (no flash)
113
120
  initThemeSwitcher();
114
121
  initDropdowns();
115
122
  initTerminal();
123
+ initAnimToggle();
116
124
  ```
117
125
 
118
126
  The runtime is **progressive enhancement**: with JS disabled, or `prefers-reduced-motion`, all
@@ -1,4 +1,4 @@
1
- /*! danieldeusing-design v0.1.0 | MIT | https://github.com/danieldeusing/danieldeusing-design */
1
+ /*! danieldeusing-design v0.1.2 | MIT | https://github.com/danieldeusing/danieldeusing-design */
2
2
  /* ===== reset.css ===== */
3
3
  /*
4
4
  * danieldeusing-design — minimal reset.
@@ -352,6 +352,27 @@ body::after {
352
352
  color: var(--primary);
353
353
  }
354
354
 
355
+ /* Animations on/off toggle — a chrome control (footer/status bar). Pairs with
356
+ runtime/anim.js and the html.anim-off kill-switch. The [data-anim-box] glyph
357
+ lights up in the primary colour while animations are enabled. */
358
+ .anim-toggle {
359
+ display: inline-flex;
360
+ align-items: center;
361
+ gap: 6px;
362
+ font: inherit;
363
+ color: var(--muted-foreground);
364
+ background: none;
365
+ border: 0;
366
+ cursor: pointer;
367
+ transition: color 0.15s ease;
368
+ }
369
+ .anim-toggle:hover {
370
+ color: var(--primary);
371
+ }
372
+ .anim-toggle[aria-pressed="true"] [data-anim-box] {
373
+ color: var(--primary);
374
+ }
375
+
355
376
  /* ASCII horizontal rule — a clipped, full-width row of box-drawing dashes that
356
377
  reads like a terminal separator. Use on an <hr> or <div>; the dashes are a
357
378
  decorative ::before clipped to the element width. */
@@ -506,10 +527,9 @@ html.anim-off *,
506
527
  html.anim-off *::before,
507
528
  html.anim-off *::after {
508
529
  animation: none !important;
509
- transition: none !important;
510
530
  }
511
531
  html.anim-off .cursor-block {
512
- opacity: 1;
532
+ display: none;
513
533
  }
514
534
 
515
535
  /* ─────────────────────────────────────────────────────────────────────────
@@ -541,6 +561,21 @@ html.anim-off .cursor-block {
541
561
  opacity: 0;
542
562
  }
543
563
  }
564
+ /* a revealed box cascades its direct children (rows/lines) in one by one —
565
+ applies to every card on every page */
566
+ html.term-anim [data-term] [data-term-out].term-show > * {
567
+ animation: ddd-term-output 0.25s steps(3, end) both;
568
+ }
569
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(2) { animation-delay: 0.09s; }
570
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(3) { animation-delay: 0.18s; }
571
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(4) { animation-delay: 0.27s; }
572
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(5) { animation-delay: 0.36s; }
573
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(6) { animation-delay: 0.45s; }
574
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(7) { animation-delay: 0.54s; }
575
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(8) { animation-delay: 0.63s; }
576
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(9) { animation-delay: 0.72s; }
577
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(10) { animation-delay: 0.81s; }
578
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(n + 11) { animation-delay: 0.9s; }
544
579
  /* caret at the typing edge (created by runtime/terminal.js) */
545
580
  .term-caret {
546
581
  display: inline-block;
@@ -1,2 +1,2 @@
1
- /*! danieldeusing-design v0.1.0 | MIT | https://github.com/danieldeusing/danieldeusing-design */
2
- *,*::before,*::after{box-sizing: border-box}*{margin: 0}html{-webkit-text-size-adjust: 100%;text-size-adjust: 100%;tab-size: 4}body{min-height: 100vh;min-height: 100dvh;line-height: 1.5}img,picture,video,canvas,svg{display: block;max-width: 100%}input,button,textarea,select{font: inherit;color: inherit}p,h1,h2,h3,h4,h5,h6{overflow-wrap: break-word}ul,ol{list-style: none;padding: 0}a{color: inherit;text-decoration: none}table{border-collapse: collapse}:root{--background: #f5efe2;--foreground: #43352a;--card: #efe7d4;--card-foreground: #43352a;--popover: #efe7d4;--popover-foreground: #43352a;--primary: #8a4516;--primary-foreground: #f8f3e8;--secondary: #ece3cf;--secondary-foreground: #43352a;--muted: #ece3cf;--muted-foreground: #71614e;--accent: #8a4516;--accent-foreground: #f8f3e8;--destructive: #a02c2c;--border: #d9cdb6;--input: #d9cdb6;--ring: #8a4516;--glow: rgba(160,82,45,0.1);--glow-soft: rgba(160,82,45,0.05);--scanline-opacity: 0.15;--radius: 0rem;--font-mono: "JetBrains Mono Variable","JetBrains Mono","Menlo",monospace}html[data-theme="green"]{--background: #020604;--foreground: #4fdd7d;--card: #04110a;--card-foreground: #4fdd7d;--popover: #04110a;--popover-foreground: #4fdd7d;--primary: #33ff66;--primary-foreground: #02160a;--secondary: #071509;--secondary-foreground: #4fdd7d;--muted: #071509;--muted-foreground: #3da45e;--accent: #33ff66;--accent-foreground: #02160a;--destructive: #ff5c5c;--border: #1c4a2c;--input: #1c4a2c;--ring: #33ff66;--glow: rgba(51,255,102,0.35);--glow-soft: rgba(51,255,102,0.12);--scanline-opacity: 0.5}html[data-theme="mono"]{--background: #050505;--foreground: #d4d4d4;--card: #0d0d0d;--card-foreground: #d4d4d4;--popover: #0d0d0d;--popover-foreground: #d4d4d4;--primary: #ffffff;--primary-foreground: #050505;--secondary: #111111;--secondary-foreground: #d4d4d4;--muted: #111111;--muted-foreground: #8a8a8a;--accent: #ffffff;--accent-foreground: #050505;--destructive: #ff5c5c;--border: #333333;--input: #333333;--ring: #ffffff;--glow: rgba(255,255,255,0.25);--glow-soft: rgba(255,255,255,0.08);--scanline-opacity: 0.45}html[data-theme="paper"]{--background: #fafafa;--foreground: #1f1f1f;--card: #f1f1f1;--card-foreground: #1f1f1f;--popover: #f1f1f1;--popover-foreground: #1f1f1f;--primary: #000000;--primary-foreground: #fafafa;--secondary: #efefef;--secondary-foreground: #1f1f1f;--muted: #efefef;--muted-foreground: #595959;--accent: #000000;--accent-foreground: #fafafa;--destructive: #b3261e;--border: #d4d4d4;--input: #d4d4d4;--ring: #000000;--glow: rgba(0,0,0,0.06);--glow-soft: rgba(0,0,0,0.04);--scanline-opacity: 0.12}html{scroll-behavior: smooth}*,*::before,*::after{border-color: var(--border)}:focus-visible{outline: 2px solid var(--ring);outline-offset: 2px}body{background: var(--background);color: var(--foreground);font-family: var(--font-mono);font-size: 0.75rem;line-height: 1.5;overflow-x: hidden;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale}button:not(:disabled),summary,[role="button"]{cursor: pointer}body::after{content: "";position: fixed;inset: 0;z-index: 40;pointer-events: none;background: repeating-linear-gradient( 0deg,rgba(0,0,0,0.25) 0px,rgba(0,0,0,0.25) 1px,transparent 1px,transparent 3px );opacity: var(--scanline-opacity)}::selection{background: var(--primary);color: var(--primary-foreground)}.glow{color: var(--primary);text-shadow: 0 0 8px var(--glow)}.glow-lg{color: var(--primary);text-shadow: 0 0 12px var(--glow)}.prompt{font-size: 0.75rem;font-weight: 700;color: var(--muted-foreground)}.prompt::before{content: "$ ";color: var(--primary)}.comment::before{content: "# ";color: var(--border)}.cursor-block{display: inline-block;width: 0.55em;height: 1em;margin-left: 8px;background: var(--primary);vertical-align: baseline;transform: translateY(0.12em);animation: ddd-blink 1.1s step-end infinite}@keyframes ddd-blink{50%{opacity: 0}}.btn-terminal{display: inline-block;background: var(--primary);color: var(--primary-foreground);font-weight: 700;padding: 12px 24px;box-shadow: 0 0 16px var(--glow);transition: box-shadow 0.15s ease,transform 0.15s ease}.btn-terminal::before{content: "> "}.btn-terminal:hover{box-shadow: 0 0 28px var(--glow);transform: translateY(-2px)}.link-quiet{color: var(--muted-foreground);text-decoration: underline;text-underline-offset: 4px;transition: color 0.15s ease}.link-quiet:hover{color: var(--primary)}.ascii-rule{margin: 1rem 0;border: 0;color: var(--border);line-height: 1;white-space: nowrap;overflow: hidden;user-select: none}.ascii-rule::before{content: "────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"}.card-terminal{background: var(--card);border: 1px solid var(--border);transition: border-color 0.15s ease,box-shadow 0.15s ease}.card-terminal:hover{border-color: var(--primary);box-shadow: 0 0 20px var(--glow-soft)}.dropdown{position: relative}.dropdown>summary{display: inline-flex;align-items: center;gap: 6px;cursor: pointer;user-select: none;list-style: none;color: var(--muted-foreground);transition: color 0.15s ease}.dropdown>summary::-webkit-details-marker{display: none}.dropdown>summary:hover,.dropdown[open]>summary{color: var(--primary)}.dropdown-panel{position: absolute;right: 0;bottom: calc(100% + 12px);z-index: 50;min-width: 128px;margin: 0;padding: 4px 0;list-style: none;background: var(--card);border: 1px solid var(--border);border-radius: 0;box-shadow: 0 0 20px var(--glow-soft)}.dropdown-panel--down{top: calc(100% + 8px);bottom: auto;left: 0;right: auto}.dropdown-item{display: flex;align-items: center;gap: 8px;width: 100%;padding: 5px 14px;text-align: left;font: inherit;line-height: 1.5;background: none;border: 0;color: var(--muted-foreground);cursor: pointer;white-space: nowrap;transition: color 0.15s ease}.dropdown-item:hover{color: var(--primary);background: var(--muted)}.dropdown-item[aria-current="true"],html:not([data-theme]) .dropdown-item[data-theme-value="warm"],html[data-theme="green"] .dropdown-item[data-theme-value="green"],html[data-theme="mono"] .dropdown-item[data-theme-value="mono"],html[data-theme="paper"] .dropdown-item[data-theme-value="paper"],html[data-theme="warm"] .dropdown-item[data-theme-value="warm"]{color: var(--primary);text-shadow: 0 0 8px var(--glow)}.eli5{margin: 1.25em 0;padding: 0.7em 1em;color: var(--foreground);background: color-mix(in srgb,var(--primary) 7%,var(--card));border: 1px solid var(--border);border-left: 3px solid var(--primary);border-radius: var(--radius);font-size: 0.92em;line-height: 1.6}.eli5::before{content: "ELI5";display: inline-flex;align-items: center;justify-content: center;margin-right: 0.6em;vertical-align: 0.08em;font-family: var(--font-mono);font-size: 0.72em;font-weight: 700;line-height: 1;letter-spacing: 0.04em;padding: 0.34em 0.6em;border-radius: 5px;background: #b42318;color: #fff}.eli5-term{font-weight: 700;color: var(--primary)}html.anim-off *,html.anim-off *::before,html.anim-off *::after{animation: none !important;transition: none !important}html.anim-off .cursor-block{opacity: 1}@media (prefers-reduced-motion: no-preference){html.term-anim [data-term] .prompt:not(.term-live),html.term-anim [data-term] .prompt:not(.term-live)::before{color: transparent}html.term-anim [data-term] [data-term-out]:not(.term-show){visibility: hidden}html.term-anim [data-term] [data-term-out].term-show{animation: ddd-term-output 0.2s steps(3,end) both}@keyframes ddd-term-output{from{opacity: 0}}.term-caret{display: inline-block;width: 0.55em;height: 1em;margin-left: 2px;background: var(--primary);vertical-align: baseline;transform: translateY(0.12em)}}
1
+ /*! danieldeusing-design v0.1.2 | MIT | https://github.com/danieldeusing/danieldeusing-design */
2
+ *,*::before,*::after{box-sizing: border-box}*{margin: 0}html{-webkit-text-size-adjust: 100%;text-size-adjust: 100%;tab-size: 4}body{min-height: 100vh;min-height: 100dvh;line-height: 1.5}img,picture,video,canvas,svg{display: block;max-width: 100%}input,button,textarea,select{font: inherit;color: inherit}p,h1,h2,h3,h4,h5,h6{overflow-wrap: break-word}ul,ol{list-style: none;padding: 0}a{color: inherit;text-decoration: none}table{border-collapse: collapse}:root{--background: #f5efe2;--foreground: #43352a;--card: #efe7d4;--card-foreground: #43352a;--popover: #efe7d4;--popover-foreground: #43352a;--primary: #8a4516;--primary-foreground: #f8f3e8;--secondary: #ece3cf;--secondary-foreground: #43352a;--muted: #ece3cf;--muted-foreground: #71614e;--accent: #8a4516;--accent-foreground: #f8f3e8;--destructive: #a02c2c;--border: #d9cdb6;--input: #d9cdb6;--ring: #8a4516;--glow: rgba(160,82,45,0.1);--glow-soft: rgba(160,82,45,0.05);--scanline-opacity: 0.15;--radius: 0rem;--font-mono: "JetBrains Mono Variable","JetBrains Mono","Menlo",monospace}html[data-theme="green"]{--background: #020604;--foreground: #4fdd7d;--card: #04110a;--card-foreground: #4fdd7d;--popover: #04110a;--popover-foreground: #4fdd7d;--primary: #33ff66;--primary-foreground: #02160a;--secondary: #071509;--secondary-foreground: #4fdd7d;--muted: #071509;--muted-foreground: #3da45e;--accent: #33ff66;--accent-foreground: #02160a;--destructive: #ff5c5c;--border: #1c4a2c;--input: #1c4a2c;--ring: #33ff66;--glow: rgba(51,255,102,0.35);--glow-soft: rgba(51,255,102,0.12);--scanline-opacity: 0.5}html[data-theme="mono"]{--background: #050505;--foreground: #d4d4d4;--card: #0d0d0d;--card-foreground: #d4d4d4;--popover: #0d0d0d;--popover-foreground: #d4d4d4;--primary: #ffffff;--primary-foreground: #050505;--secondary: #111111;--secondary-foreground: #d4d4d4;--muted: #111111;--muted-foreground: #8a8a8a;--accent: #ffffff;--accent-foreground: #050505;--destructive: #ff5c5c;--border: #333333;--input: #333333;--ring: #ffffff;--glow: rgba(255,255,255,0.25);--glow-soft: rgba(255,255,255,0.08);--scanline-opacity: 0.45}html[data-theme="paper"]{--background: #fafafa;--foreground: #1f1f1f;--card: #f1f1f1;--card-foreground: #1f1f1f;--popover: #f1f1f1;--popover-foreground: #1f1f1f;--primary: #000000;--primary-foreground: #fafafa;--secondary: #efefef;--secondary-foreground: #1f1f1f;--muted: #efefef;--muted-foreground: #595959;--accent: #000000;--accent-foreground: #fafafa;--destructive: #b3261e;--border: #d4d4d4;--input: #d4d4d4;--ring: #000000;--glow: rgba(0,0,0,0.06);--glow-soft: rgba(0,0,0,0.04);--scanline-opacity: 0.12}html{scroll-behavior: smooth}*,*::before,*::after{border-color: var(--border)}:focus-visible{outline: 2px solid var(--ring);outline-offset: 2px}body{background: var(--background);color: var(--foreground);font-family: var(--font-mono);font-size: 0.75rem;line-height: 1.5;overflow-x: hidden;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale}button:not(:disabled),summary,[role="button"]{cursor: pointer}body::after{content: "";position: fixed;inset: 0;z-index: 40;pointer-events: none;background: repeating-linear-gradient( 0deg,rgba(0,0,0,0.25) 0px,rgba(0,0,0,0.25) 1px,transparent 1px,transparent 3px );opacity: var(--scanline-opacity)}::selection{background: var(--primary);color: var(--primary-foreground)}.glow{color: var(--primary);text-shadow: 0 0 8px var(--glow)}.glow-lg{color: var(--primary);text-shadow: 0 0 12px var(--glow)}.prompt{font-size: 0.75rem;font-weight: 700;color: var(--muted-foreground)}.prompt::before{content: "$ ";color: var(--primary)}.comment::before{content: "# ";color: var(--border)}.cursor-block{display: inline-block;width: 0.55em;height: 1em;margin-left: 8px;background: var(--primary);vertical-align: baseline;transform: translateY(0.12em);animation: ddd-blink 1.1s step-end infinite}@keyframes ddd-blink{50%{opacity: 0}}.btn-terminal{display: inline-block;background: var(--primary);color: var(--primary-foreground);font-weight: 700;padding: 12px 24px;box-shadow: 0 0 16px var(--glow);transition: box-shadow 0.15s ease,transform 0.15s ease}.btn-terminal::before{content: "> "}.btn-terminal:hover{box-shadow: 0 0 28px var(--glow);transform: translateY(-2px)}.link-quiet{color: var(--muted-foreground);text-decoration: underline;text-underline-offset: 4px;transition: color 0.15s ease}.link-quiet:hover{color: var(--primary)}.anim-toggle{display: inline-flex;align-items: center;gap: 6px;font: inherit;color: var(--muted-foreground);background: none;border: 0;cursor: pointer;transition: color 0.15s ease}.anim-toggle:hover{color: var(--primary)}.anim-toggle[aria-pressed="true"] [data-anim-box]{color: var(--primary)}.ascii-rule{margin: 1rem 0;border: 0;color: var(--border);line-height: 1;white-space: nowrap;overflow: hidden;user-select: none}.ascii-rule::before{content: "────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"}.card-terminal{background: var(--card);border: 1px solid var(--border);transition: border-color 0.15s ease,box-shadow 0.15s ease}.card-terminal:hover{border-color: var(--primary);box-shadow: 0 0 20px var(--glow-soft)}.dropdown{position: relative}.dropdown>summary{display: inline-flex;align-items: center;gap: 6px;cursor: pointer;user-select: none;list-style: none;color: var(--muted-foreground);transition: color 0.15s ease}.dropdown>summary::-webkit-details-marker{display: none}.dropdown>summary:hover,.dropdown[open]>summary{color: var(--primary)}.dropdown-panel{position: absolute;right: 0;bottom: calc(100% + 12px);z-index: 50;min-width: 128px;margin: 0;padding: 4px 0;list-style: none;background: var(--card);border: 1px solid var(--border);border-radius: 0;box-shadow: 0 0 20px var(--glow-soft)}.dropdown-panel--down{top: calc(100% + 8px);bottom: auto;left: 0;right: auto}.dropdown-item{display: flex;align-items: center;gap: 8px;width: 100%;padding: 5px 14px;text-align: left;font: inherit;line-height: 1.5;background: none;border: 0;color: var(--muted-foreground);cursor: pointer;white-space: nowrap;transition: color 0.15s ease}.dropdown-item:hover{color: var(--primary);background: var(--muted)}.dropdown-item[aria-current="true"],html:not([data-theme]) .dropdown-item[data-theme-value="warm"],html[data-theme="green"] .dropdown-item[data-theme-value="green"],html[data-theme="mono"] .dropdown-item[data-theme-value="mono"],html[data-theme="paper"] .dropdown-item[data-theme-value="paper"],html[data-theme="warm"] .dropdown-item[data-theme-value="warm"]{color: var(--primary);text-shadow: 0 0 8px var(--glow)}.eli5{margin: 1.25em 0;padding: 0.7em 1em;color: var(--foreground);background: color-mix(in srgb,var(--primary) 7%,var(--card));border: 1px solid var(--border);border-left: 3px solid var(--primary);border-radius: var(--radius);font-size: 0.92em;line-height: 1.6}.eli5::before{content: "ELI5";display: inline-flex;align-items: center;justify-content: center;margin-right: 0.6em;vertical-align: 0.08em;font-family: var(--font-mono);font-size: 0.72em;font-weight: 700;line-height: 1;letter-spacing: 0.04em;padding: 0.34em 0.6em;border-radius: 5px;background: #b42318;color: #fff}.eli5-term{font-weight: 700;color: var(--primary)}html.anim-off *,html.anim-off *::before,html.anim-off *::after{animation: none !important}html.anim-off .cursor-block{display: none}@media (prefers-reduced-motion: no-preference){html.term-anim [data-term] .prompt:not(.term-live),html.term-anim [data-term] .prompt:not(.term-live)::before{color: transparent}html.term-anim [data-term] [data-term-out]:not(.term-show){visibility: hidden}html.term-anim [data-term] [data-term-out].term-show{animation: ddd-term-output 0.2s steps(3,end) both}@keyframes ddd-term-output{from{opacity: 0}}html.term-anim [data-term] [data-term-out].term-show>*{animation: ddd-term-output 0.25s steps(3,end) both}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(2){animation-delay: 0.09s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(3){animation-delay: 0.18s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(4){animation-delay: 0.27s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(5){animation-delay: 0.36s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(6){animation-delay: 0.45s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(7){animation-delay: 0.54s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(8){animation-delay: 0.63s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(9){animation-delay: 0.72s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(10){animation-delay: 0.81s}html.term-anim [data-term] [data-term-out].term-show>*:nth-child(n + 11){animation-delay: 0.9s}.term-caret{display: inline-block;width: 0.55em;height: 1em;margin-left: 2px;background: var(--primary);vertical-align: baseline;transform: translateY(0.12em)}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danieldeusing/design",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The danieldeusing terminal design system — framework-agnostic CSS tokens, components, and a vanilla-JS runtime for the CRT/JetBrains-Mono look used across danieldeusing.de, seedr, and briefs.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,50 @@
1
+ /*
2
+ * danieldeusing-design — animations on/off toggle.
3
+ *
4
+ * Wires up a footer/chrome control that turns every keyframe animation on or off,
5
+ * persisting the choice so it survives reloads. Pairs with the html.anim-off
6
+ * kill-switch in components.css and the pre-paint gate in terminal.js.
7
+ *
8
+ * Markup contract (style it with the .anim-toggle component class):
9
+ * <button type="button" class="anim-toggle" data-anim-toggle aria-pressed="true">
10
+ * <span data-anim-box aria-hidden="true">[x]</span>
11
+ * <span>anim</span>
12
+ * </button>
13
+ *
14
+ * The persisted key is "anim" ("on" | "off"); apply it pre-paint (inline, in
15
+ * <head>) the same way the theme is applied, so the choice never flashes.
16
+ */
17
+
18
+ export function initAnimToggle() {
19
+ const toggles = Array.from(document.querySelectorAll("[data-anim-toggle]"));
20
+ if (!toggles.length) return;
21
+
22
+ const sync = () => {
23
+ const on = !document.documentElement.classList.contains("anim-off");
24
+ for (const toggle of toggles) {
25
+ toggle.setAttribute("aria-pressed", String(on));
26
+ const box = toggle.querySelector("[data-anim-box]");
27
+ if (box) box.textContent = on ? "[x]" : "[ ]";
28
+ }
29
+ };
30
+ sync();
31
+
32
+ for (const toggle of toggles) {
33
+ toggle.addEventListener("click", () => {
34
+ const html = document.documentElement;
35
+ const turningOff = !html.classList.contains("anim-off");
36
+ if (turningOff) {
37
+ html.classList.add("anim-off");
38
+ html.classList.remove("term-anim"); // stop the terminal typing mid-run
39
+ } else {
40
+ html.classList.remove("anim-off");
41
+ }
42
+ try {
43
+ localStorage.setItem("anim", turningOff ? "off" : "on");
44
+ } catch {
45
+ /* private mode */
46
+ }
47
+ sync();
48
+ });
49
+ }
50
+ }
package/runtime/index.js CHANGED
@@ -13,4 +13,5 @@
13
13
  export * from "./theme.js";
14
14
  export * from "./zoom.js";
15
15
  export * from "./dropdown.js";
16
+ export * from "./anim.js";
16
17
  export * from "./terminal.js";
@@ -23,8 +23,24 @@
23
23
 
24
24
  export function initTerminal() {
25
25
  if (typeof window === "undefined") return;
26
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
27
- // honour the manual kill-switch (components.css html.anim-off)
26
+
27
+ // Animation policy: an explicit pick (localStorage "anim", e.g. from a footer
28
+ // toggle) wins over the OS setting. "off" — or no pick while the OS prefers
29
+ // reduced motion — disables every animation by marking html.anim-off
30
+ // (components.css then kills keyframes and hides the cursor). An explicit "on"
31
+ // animates even when the OS prefers reduced motion.
32
+ let animPref = null;
33
+ try {
34
+ animPref = localStorage.getItem("anim");
35
+ } catch {
36
+ /* localStorage can throw in private mode / sandboxed frames */
37
+ }
38
+ const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
39
+ if (animPref === "off" || (animPref === null && prefersReduced)) {
40
+ document.documentElement.classList.add("anim-off");
41
+ return;
42
+ }
43
+ // already gated off pre-paint (e.g. by an inline script) — respect it
28
44
  if (document.documentElement.classList.contains("anim-off")) return;
29
45
 
30
46
  document.documentElement.classList.add("term-anim");
@@ -75,7 +91,10 @@ export function initTerminal() {
75
91
  if (offscreen) prompt.classList.add("term-live");
76
92
  else await typePrompt(prompt);
77
93
  }
78
- await revealOutputs(section, offscreen);
94
+ // reveal the box and let its rows cascade in, but don't await it: the box
95
+ // animation runs in parallel so the next prompt keeps typing and nothing
96
+ // waits on a box finishing.
97
+ void revealOutputs(section, offscreen);
79
98
  };
80
99
 
81
100
  // sections play one after another, in the order they were triggered
package/runtime/theme.js CHANGED
@@ -102,6 +102,8 @@ export function initThemeSwitcher(options) {
102
102
  const theme = /** @type {HTMLElement} */ (button).dataset.themeValue;
103
103
  if (theme) setTheme(/** @type {Theme} */ (theme), options);
104
104
  syncLabel();
105
+ // close the enclosing dropdown after a pick (no-op if not inside one)
106
+ button.closest("details.dropdown")?.removeAttribute("open");
105
107
  });
106
108
  });
107
109
  }
@@ -86,6 +86,27 @@
86
86
  color: var(--primary);
87
87
  }
88
88
 
89
+ /* Animations on/off toggle — a chrome control (footer/status bar). Pairs with
90
+ runtime/anim.js and the html.anim-off kill-switch. The [data-anim-box] glyph
91
+ lights up in the primary colour while animations are enabled. */
92
+ .anim-toggle {
93
+ display: inline-flex;
94
+ align-items: center;
95
+ gap: 6px;
96
+ font: inherit;
97
+ color: var(--muted-foreground);
98
+ background: none;
99
+ border: 0;
100
+ cursor: pointer;
101
+ transition: color 0.15s ease;
102
+ }
103
+ .anim-toggle:hover {
104
+ color: var(--primary);
105
+ }
106
+ .anim-toggle[aria-pressed="true"] [data-anim-box] {
107
+ color: var(--primary);
108
+ }
109
+
89
110
  /* ASCII horizontal rule — a clipped, full-width row of box-drawing dashes that
90
111
  reads like a terminal separator. Use on an <hr> or <div>; the dashes are a
91
112
  decorative ::before clipped to the element width. */
@@ -240,10 +261,9 @@ html.anim-off *,
240
261
  html.anim-off *::before,
241
262
  html.anim-off *::after {
242
263
  animation: none !important;
243
- transition: none !important;
244
264
  }
245
265
  html.anim-off .cursor-block {
246
- opacity: 1;
266
+ display: none;
247
267
  }
248
268
 
249
269
  /* ─────────────────────────────────────────────────────────────────────────
@@ -275,6 +295,21 @@ html.anim-off .cursor-block {
275
295
  opacity: 0;
276
296
  }
277
297
  }
298
+ /* a revealed box cascades its direct children (rows/lines) in one by one —
299
+ applies to every card on every page */
300
+ html.term-anim [data-term] [data-term-out].term-show > * {
301
+ animation: ddd-term-output 0.25s steps(3, end) both;
302
+ }
303
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(2) { animation-delay: 0.09s; }
304
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(3) { animation-delay: 0.18s; }
305
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(4) { animation-delay: 0.27s; }
306
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(5) { animation-delay: 0.36s; }
307
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(6) { animation-delay: 0.45s; }
308
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(7) { animation-delay: 0.54s; }
309
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(8) { animation-delay: 0.63s; }
310
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(9) { animation-delay: 0.72s; }
311
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(10) { animation-delay: 0.81s; }
312
+ html.term-anim [data-term] [data-term-out].term-show > *:nth-child(n + 11) { animation-delay: 0.9s; }
278
313
  /* caret at the typing edge (created by runtime/terminal.js) */
279
314
  .term-caret {
280
315
  display: inline-block;