@danieldeusing/design 0.5.0 → 0.6.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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! danieldeusing-design v0.
|
|
1
|
+
/*! danieldeusing-design v0.6.0 | MIT | https://github.com/danieldeusing/danieldeusing-design */
|
|
2
2
|
/* ===== reset.css ===== */
|
|
3
3
|
/*
|
|
4
4
|
* danieldeusing-design — minimal reset.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! danieldeusing-design v0.
|
|
1
|
+
/*! danieldeusing-design v0.6.0 | MIT | https://github.com/danieldeusing/danieldeusing-design */
|
|
2
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;--success: #1a6b2e;--warning: #845600;--info: #1c5f9e;--pending: #5f3dc4;--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;--success: #4ade80;--warning: #f5b32b;--info: #4dabf7;--pending: #b197fc;--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;--success: #4ade80;--warning: #f5b32b;--info: #4dabf7;--pending: #b197fc;--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;--success: #1a7031;--warning: #8f5d00;--info: #1c5f9e;--pending: #5f3dc4;--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}:root{--content-w: 78rem;--content-pad: 1.5rem;--fs-xs: 0.625rem;--fs-sm: 0.6875rem;--fs-base: 0.75rem;--fs-md: 0.8125rem;--fs-lg: 0.9375rem;--fs-xl: 1.125rem;--fs-2xl: 1.5rem;--lh-tight: 1.3;--lh-base: 1.5}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: var(--fs-base);line-height: var(--lh-base);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)}.dd-dot{display: inline-block;width: 11px;height: 11px;flex: none;border-radius: 50%;background: currentColor}.dd-flag{display: inline-block;width: 15px;height: 10px;flex: none;border: 1px solid var(--border);background-size: cover;background-repeat: no-repeat}.dd-flag-de{background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 12'><rect width='18' height='4' fill='%23262626'/><rect y='4' width='18' height='4' fill='%23c83232'/><rect y='8' width='18' height='4' fill='%23e8be32'/></svg>")}.dd-flag-en{background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 12'><rect width='18' height='12' fill='%23b22234'/><rect y='4' width='18' height='4' fill='%23ececec'/><rect width='8' height='6' fill='%233c3b6e'/></svg>")}.dd-flag-pt{background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 12'><rect width='18' height='12' fill='%231c9148'/><polygon points='9,1.5 15.5,6 9,10.5 2.5,6' fill='%23e8c832'/><rect x='8' y='5' width='2' height='2' fill='%232c3f87'/></svg>")}.dd-flag-es{background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 12'><rect width='6' height='12' fill='%2315734c'/><rect x='6' width='6' height='12' fill='%23ececec'/><rect x='12' width='6' height='12' fill='%23bf2233'/><rect x='8' y='5' width='2' height='2' fill='%237a5c3a'/></svg>")}.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)}}.tabs{display: flex;flex-wrap: wrap;gap: 0.15rem;margin: 1.35rem 0 0;border-bottom: 2px solid color-mix(in srgb,var(--border) 80%,transparent)}.tab{background: transparent;border: 0;margin-bottom: -2px;font: inherit;font-size: 0.86em;cursor: pointer;padding: 0.5rem 0.95rem;color: var(--muted-foreground);transition: background 0.12s ease,color 0.12s ease}.tab:hover{color: var(--primary);background: color-mix(in srgb,var(--primary) 10%,transparent)}.tab[aria-selected="true"]{background: var(--primary);color: var(--background);font-weight: 700}.tab:focus-visible{outline: 2px solid color-mix(in srgb,var(--primary) 55%,transparent);outline-offset: -2px}@media (pointer: coarse),(max-width: 40rem){.tab{padding: 0.7rem 0.8rem;min-height: 44px}}section.doc.tab-panel{margin-top: 1.35rem}details.fold{border-top: 1px solid color-mix(in srgb,currentColor 15%,transparent)}details.fold>summary{display: flex;align-items: center;gap: 0.5rem;cursor: pointer;list-style: none;padding: 0.55rem 0;font-size: 0.85rem;color: var(--muted-foreground)}details.fold>summary::-webkit-details-marker{display: none}details.fold>summary::before{content: "\25B8";flex: none;opacity: 0.6}details.fold[open]>summary::before{content: "\25BE"}details.fold>summary:hover{color: var(--primary)}details.fold>summary:focus-visible{outline: 2px solid color-mix(in srgb,currentColor 45%,transparent);outline-offset: 2px}.fold-body{font-size: 0.9rem;padding: 0 0 0.7rem 1.1rem}.fold-body>* + *{margin-top: 0.6rem}.legend{list-style: none;padding: 0;margin: 0.7rem 0 0;display: grid;gap: 0.4rem 1.4rem;font-size: 0.86rem}@media (min-width: 52rem){.legend{grid-template-columns: repeat(2,minmax(0,1fr))}}.legend>li{line-height: 1.6}.legend>li>:first-child{margin-right: 0.45rem}.tickstrip:not(:empty){border: 1px solid var(--border);background: var(--card);margin: 1.6rem 0 1.5rem}.ticktable{width: 100%;border-collapse: collapse;font-size: 0.82rem}.ticktable td{padding: 0.5rem 0.75rem;vertical-align: baseline;border: 0;white-space: nowrap}.ticktable td.tick-stats{width: 100%;white-space: normal}.tick{border-bottom: 1px solid color-mix(in srgb,var(--border) 55%,transparent)}.tick:last-child{border-bottom: 0}.tick-dot{font-size: 0.7em;width: 1.5rem;padding-right: 0 !important;border-left: 3px solid transparent;padding-left: 0.6rem !important}.tick--ok .tick-dot{color: var(--success);border-left-color: color-mix(in srgb,var(--success) 45%,transparent)}.tick--stale .tick-dot{color: var(--destructive);border-left-color: var(--destructive)}.tick--never .tick-dot{color: var(--muted-foreground);border-left-color: color-mix(in srgb,var(--muted-foreground) 35%,transparent)}.tick-name{font-weight: 700;color: var(--foreground);letter-spacing: 0.01em}.tick-last,.tick-next{font-variant-numeric: tabular-nums;color: var(--muted-foreground)}.tick-next{opacity: 0.72}.tick--stale .tick-last{color: var(--destructive);font-weight: 700}.tick--never .tick-last{font-style: italic}.tick-stats{color: var(--muted-foreground)}.tick-stats b{font-weight: 700;color: var(--primary);font-variant-numeric: tabular-nums}.tick-sep{font-style: normal;opacity: 0.35;margin: 0 0.5rem}.tick--stale{background: color-mix(in srgb,var(--destructive) 8%,transparent)}@media (max-width: 40rem){.ticktable,.ticktable tbody,.tick,.ticktable td{display: block}.tick{padding: 0.4rem 0.5rem}.ticktable td{display: inline-block;padding: 0.1rem 0.5rem 0.1rem 0}.ticktable td.tick-stats{display: block;width: auto}}.dgm-zoomable{position: relative;cursor: zoom-in}.dgm-zoomable::after{content: "⤢";position: absolute;top: 0.4rem;right: 0.5rem;font-size: 0.85rem;line-height: 1;opacity: 0.45;pointer-events: none;color: var(--muted-foreground)}.dgm-zoomable:hover::after,.dgm-zoomable:focus-visible::after{opacity: 1;color: var(--primary)}.dgm-zoomable:focus-visible{outline: 2px solid var(--primary);outline-offset: 3px}html.dgm-locked,html.dgm-locked body{overflow: hidden}.dgm-overlay{display: none;position: fixed;inset: 0;z-index: 90;background: color-mix(in srgb,var(--background) 96%,transparent);flex-direction: column}.dgm-overlay.open{display: flex}.dgm-bar{flex: none;display: flex;align-items: center;justify-content: flex-end;gap: 0.4rem;padding: 0.5rem 0.75rem;border-bottom: 1px solid var(--border);background: var(--card)}.dgm-btn{font: inherit;font-size: 0.82rem;cursor: pointer;padding: 0.3rem 0.7rem;min-height: 32px;background: transparent;border: 1px solid var(--border);color: var(--foreground)}.dgm-btn:hover{border-color: var(--primary);color: var(--primary)}.dgm-btn:focus-visible{outline: 2px solid var(--primary);outline-offset: 1px}.dgm-close{border-color: color-mix(in srgb,var(--destructive) 55%,transparent);color: var(--destructive)}.dgm-close:hover{border-color: var(--destructive);color: var(--destructive)}.dgm-stage{flex: 1;overflow: hidden;display: grid;place-items: center;cursor: grab;touch-action: none}.dgm-stage.is-grabbing{cursor: grabbing}.dgm-art{transform-origin: center center;will-change: transform}.dgm-art>svg,.dgm-art>img{display: block;max-width: none;max-height: none}@media print{.dgm-overlay,.dgm-zoomable::after{display: none !important}}.wrap{max-width: var(--content-w);margin-inline: auto;padding-inline: var(--content-pad)}.skip-link{position: absolute;left: -999px;top: 0;z-index: 100;background: var(--card);border: 1px solid var(--primary);padding: 0.5rem 1rem;color: var(--primary)}.skip-link:focus{left: 0.5rem;top: 0.5rem}.visually-hidden{position: absolute;width: 1px;height: 1px;overflow: hidden;clip: rect(0 0 0 0);white-space: nowrap}header.bar{position: sticky;top: 0;z-index: 30;display: flex;align-items: center;justify-content: space-between;gap: 1rem;padding: 0.6rem 1.25rem;border-bottom: 1px solid var(--border);background: var(--card)}header.bar .brand{display: inline-flex;align-items: center;font-weight: 700;font-size: 1rem}header.bar .brand,header.bar .brand a{color: inherit;text-decoration: none}header.bar .dropdown summary{padding: 0.6rem 0.4rem}.bar-right{display: flex;align-items: center;gap: 1rem;flex-shrink: 0}footer.status{position: fixed;inset-inline: 0;bottom: 0;z-index: 50;min-height: 2rem;display: flex;align-items: center;justify-content: space-between;gap: 1rem;padding: 0.35rem 1.25rem;border-top: 1px solid var(--border);background: var(--card);font-size: 0.7rem}footer.status .dropdown summary,footer.status .anim-toggle{padding: 0.55rem 0.4rem}.status-left{color: var(--muted-foreground);min-width: 0;overflow: hidden;text-overflow: ellipsis;white-space: nowrap}.status-right{display: flex;align-items: center;gap: 1rem;flex-shrink: 0}.sep{width: 1px;height: 0.9rem;background: var(--border)}.doc-link{color: var(--muted-foreground);text-decoration: none;transition: color 0.15s ease}.doc-link:hover{color: var(--primary)}.ls-row{display: flex;align-items: baseline;gap: 0.9rem}.ls-perm{color: var(--muted-foreground);font-size: 0.72rem;letter-spacing: 0}.ls-row--sub .ls-name{margin-left: 0.55rem;padding-left: 0.75rem;border-left: 1px solid color-mix(in srgb,var(--muted-foreground) 40%,transparent)}.ls-row--sub2 .ls-name{margin-left: 1.7rem}.ls-panel .ls-name{color: var(--muted-foreground)}.ls-panel .ls-row--dir .ls-name{color: var(--primary);font-weight: 700}.dropdown-panel.ls-panel{min-width: 17rem;left: auto !important;right: 0}:root{--ls-nav-w: 17rem;--ls-nav-top: 3rem;--ls-nav-bottom: 2.2rem}@media (min-width: 48.0625rem){html{--ls-nav-inset: 0rem}html:has(.ls-nav):not([data-ls-nav="off"]){--ls-nav-inset: var(--ls-nav-w)}body{padding-right: var(--ls-nav-inset)}header.bar{margin-right: calc(-1 * var(--ls-nav-inset))}.ls-nav{position: fixed;top: var(--ls-nav-top);right: 0;bottom: var(--ls-nav-bottom);width: var(--ls-nav-w);z-index: 25;display: flex;flex-direction: column;overflow: hidden;background: var(--card);border-left: 1px solid var(--border)}html:not(.anim-off) .ls-nav{transition: transform 0.18s ease}html[data-ls-nav="off"] .ls-nav{transform: translateX(100%)}.ls-nav-head{display: inline-flex;align-items: center;gap: 0.5rem;margin-left: 0.75rem}.ls-nav-title{font-weight: 700;font-size: 0.9rem}.ls-nav .ls-panel{flex: 1;min-height: 0;overflow-y: auto;list-style: none;margin: 0;padding: 0.5rem 0 1rem}.ls-nav-toggle{background: none;border: 0;font: inherit;cursor: pointer;line-height: 1;color: var(--muted-foreground);padding: 0.4rem 0.55rem}.ls-nav-toggle::after{content: "\00BB"}html[data-ls-nav="off"] .ls-nav-toggle::after{content: "\00AB"}.ls-nav-toggle:hover{color: var(--primary)}.ls-nav-toggle:focus-visible{outline: 2px solid color-mix(in srgb,var(--primary) 55%,transparent);outline-offset: -2px}}.nav-burger{display: none;background: none;border: 0;color: var(--muted-foreground);cursor: pointer;padding: 0.55rem;margin: -0.3rem -0.3rem -0.3rem 0}.nav-burger:hover{color: var(--primary)}.mobile-nav{display: none;list-style: none;margin: 0;padding: 0}.mobile-footer{display: none}@media (max-width: 48rem){.nav-burger{display: inline-flex;align-items: center}.site-nav{display: none;position: absolute;top: 100%;left: 0;right: 0;z-index: 40;background: var(--card);border-bottom: 1px solid var(--border);padding: 0.4rem 1.25rem 0.9rem;max-height: calc(100vh - 6rem);overflow-y: auto}.site-nav.open{display: block}.site-nav>.ls-nav,.mobile-nav{display: flex;flex-direction: column}.mobile-nav .mobile-item{display: flex;align-items: baseline;gap: 0.9rem;padding: 0.65rem 0;text-decoration: none;color: var(--foreground);font-size: 0.9rem;border-bottom: 1px solid var(--border)}.mobile-nav li:last-child .mobile-item{border-bottom: 0}.mobile-nav .mobile-item:active .ls-name,.mobile-nav .mobile-item:hover .ls-name{color: var(--primary)}footer.status{display: none}.mobile-footer{display: flex;flex-direction: column;gap: 0.55rem;margin-top: 0.8rem;border-top: 1px solid var(--border);padding-top: 0.8rem;font-size: 0.85rem}.mobile-footer .doc-link{padding: 0.2rem 0}.mobile-footer .mobile-theme summary{list-style: none;display: flex;align-items: center;gap: 0.5rem;color: var(--muted-foreground);cursor: pointer;padding: 0.2rem 0}.mobile-footer .mobile-theme summary::-webkit-details-marker{display: none}.mobile-footer .mf-chev{margin-left: auto;opacity: 0.7}.mobile-footer .mf-panel{display: flex;flex-direction: column;margin-top: 0.3rem}.mobile-footer .mf-panel .dropdown-item{text-align: left;padding: 0.4rem 0.6rem}.mobile-footer .anim-toggle{display: inline-flex;gap: 0.5rem;align-items: center;background: none;border: 0;padding: 0.2rem 0;color: var(--muted-foreground);font: inherit;cursor: pointer}}[data-tip]{cursor: help}span[data-tip],th[data-tip]{border-bottom: 1px dotted var(--muted-foreground)}#ddtip{position: fixed;z-index: 9999;display: none;max-width: 340px;background: var(--background);color: var(--foreground);border: 1px solid var(--muted-foreground);border-radius: 4px;padding: 7px 10px;font-size: 0.72rem;line-height: 1.45;font-weight: 400;text-align: left;white-space: normal;pointer-events: none;box-shadow: 0 4px 14px rgba(0,0,0,0.25)}@media print{@page{margin: 14mm 12mm}:root,html,html[data-theme]{--background: #ffffff;--foreground: #111111;--card: #ffffff;--card-foreground: #111111;--popover: #ffffff;--popover-foreground: #111111;--primary: #000000;--primary-foreground: #ffffff;--secondary: #ffffff;--secondary-foreground: #111111;--muted: #ffffff;--muted-foreground: #444444;--accent: #000000;--accent-foreground: #ffffff;--destructive: #7a1c1c;--border: #9a9a9a;--input: #9a9a9a;--ring: #000000;--glow: transparent;--glow-soft: transparent;--scanline-opacity: 0}html{zoom: 1 !important;font-size: 16px;scroll-behavior: auto}body{background: #ffffff;color: var(--foreground);font-size: 12px;line-height: 1.45;overflow: visible}body::after{display: none !important}*,*::before,*::after{animation: none !important;transition: none !important;text-shadow: none !important;box-shadow: none !important}html.term-anim [data-term] [data-term-out],html.term-anim [data-term] [data-term-out]:not(.term-show),html.term-anim [data-term] [data-term-out]>*{visibility: visible !important;opacity: 1 !important}html.term-anim [data-term] .prompt:not(.term-live){color: var(--muted-foreground) !important}html.term-anim [data-term] .prompt:not(.term-live)::before{color: var(--primary) !important}header.bar,footer.status,.site-nav,.nav-burger,.mobile-nav,.mobile-footer,.skip-link,.dropdown,.dropdown-panel,.anim-toggle,.cursor-block,.term-caret,.ascii-rule,.toc,#ddtip{display: none !important}body{padding-right: 0 !important}[data-tip]{cursor: auto}span[data-tip],th[data-tip]{border-bottom: 0}.wrap{max-width: none !important;margin: 0 !important;padding: 0 !important}.layout{display: block !important}.content{min-width: 0}h1,.title{font-size: 1.5rem !important}h2{font-size: 1.15rem !important}h3,.sub{font-size: 1rem !important}h4{font-size: 0.9rem !important}.prompt{font-size: 0.7rem}h1,h2,h3,h4,.prompt,.toc-label{break-after: avoid-page;break-inside: avoid}p,li,blockquote{orphans: 3;widows: 3}pre,figure,blockquote,tr,img,.eli5,.card-terminal{break-inside: avoid}svg{break-inside: auto}thead{display: table-header-group}.table-scroll,pre,.diagram{overflow: visible !important;max-width: 100% !important}pre{white-space: pre-wrap !important;overflow-wrap: anywhere;font-size: 0.75rem !important;background: transparent !important}pre.mermaid[data-processed]{white-space: normal !important}code{background: transparent !important;overflow-wrap: anywhere}table{width: 100% !important;table-layout: auto;border-collapse: collapse;font-size: 0.75rem !important}th,td{overflow-wrap: break-word;white-space: normal !important}td code,th code,td a,th a{overflow-wrap: anywhere}a{color: inherit;text-decoration: underline;text-underline-offset: 2px}a.card-terminal,a[class*="card"]{text-decoration: none}.glow,.glow-lg{color: inherit}.btn-terminal{border: 1px solid var(--border);background: transparent;color: var(--foreground)}.mermaid svg{display: block;margin-inline: auto;width: auto !important;height: auto !important;max-width: 100% !important;max-height: 235mm !important}.mermaid svg .node rect,.mermaid svg .node polygon,.mermaid svg .node circle,.mermaid svg .node path,.mermaid svg .label-container,.mermaid svg .cluster rect,.mermaid svg .actor,.mermaid svg .note{fill: #ffffff !important;stroke: #333333 !important}.mermaid svg .edgePath path,.mermaid svg .flowchart-link,.mermaid svg line,.mermaid svg .messageLine0,.mermaid svg .messageLine1{stroke: #333333 !important}.mermaid svg .arrowheadPath,.mermaid svg marker path,.mermaid svg marker polygon{fill: #333333 !important;stroke: #333333 !important}.mermaid svg text,.mermaid svg .nodeLabel,.mermaid svg .edgeLabel,.mermaid svg .cluster-label,.mermaid svg .messageText,.mermaid svg foreignObject div,.mermaid svg foreignObject span,.mermaid svg p{fill: #111111 !important;color: #111111 !important;background: transparent !important}.mermaid svg .edgeLabel rect,.mermaid svg .edgeLabel foreignObject>div{fill: #ffffff !important;background: #ffffff !important}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danieldeusing/design",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
"src",
|
|
46
|
+
"templates",
|
|
46
47
|
"runtime",
|
|
47
48
|
"dist",
|
|
48
49
|
"tokens",
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--
|
|
3
|
+
danieldeusing-design — one-page documentation template ("danieldeusing-docs").
|
|
4
|
+
|
|
5
|
+
A single self-contained HTML file: the design system loads build-free from the
|
|
6
|
+
jsDelivr CDN, so this opens directly in a browser with no build step.
|
|
7
|
+
|
|
8
|
+
Fill the {{PLACEHOLDERS}}. The CDN URLs are deliberately UNPINNED — one design system, every
|
|
9
|
+
surface on the current version (Daniel, 2026-08-05). A surface that ships the system's own
|
|
10
|
+
MARKUP against a specific release pins instead (cockpit does); see the danieldeusing-design
|
|
11
|
+
skill. Unpinned costs this page nothing visible ONLY because it is defended twice: the
|
|
12
|
+
`onerror` snapshot below covers a CDN that cannot be reached, and every token in the <style>
|
|
13
|
+
block carries a literal fallback for the window where a browser still holds an older build
|
|
14
|
+
(jsDelivr caches an unpinned url for SEVEN DAYS). Keep both when editing.
|
|
15
|
+
|
|
16
|
+
Chrome (fixed by the template, do not restyle):
|
|
17
|
+
- top-left : the "danieldeusing-docs" wordmark (always).
|
|
18
|
+
- top-right : a navigation dropdown — INCLUDE ONLY when the doc set has more
|
|
19
|
+
than one page; for a single-page doc, delete the <details id="nav">.
|
|
20
|
+
- bottom-left : [{{FOLDER}}] {{DOC_TITLE}} e.g. "[pagr] Source Code documentation".
|
|
21
|
+
- bottom-right : the link to danieldeusing.de + the theme (page settings) dropdown.
|
|
22
|
+
Content (you fill): the title, the full-width lede, and the <section> blocks. Every
|
|
23
|
+
section has an id and a matching entry in the "On this page" table of contents.
|
|
24
|
+
-->
|
|
25
|
+
<html lang="en">
|
|
26
|
+
<head>
|
|
27
|
+
<meta charset="utf-8" />
|
|
28
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
29
|
+
<title>{{DOC_TITLE}}</title>
|
|
30
|
+
<meta name="description" content="{{DOC_DESCRIPTION}}" />
|
|
31
|
+
<meta name="theme-color" content="#f5efe2" />
|
|
32
|
+
|
|
33
|
+
<!-- pre-paint: apply the saved theme + animation gate before first paint -->
|
|
34
|
+
<script>
|
|
35
|
+
(() => {
|
|
36
|
+
const bg = { warm: "#f5efe2", green: "#020604", mono: "#050505", paper: "#fafafa" };
|
|
37
|
+
let theme = "warm";
|
|
38
|
+
try {
|
|
39
|
+
const stored = localStorage.getItem("theme");
|
|
40
|
+
if (stored && stored in bg) theme = stored;
|
|
41
|
+
} catch {}
|
|
42
|
+
document.documentElement.dataset.theme = theme;
|
|
43
|
+
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", bg[theme]);
|
|
44
|
+
|
|
45
|
+
let animOff = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
46
|
+
try {
|
|
47
|
+
if (localStorage.getItem("anim") === "off") animOff = true;
|
|
48
|
+
} catch {}
|
|
49
|
+
if (animOff) document.documentElement.classList.add("anim-off");
|
|
50
|
+
})();
|
|
51
|
+
|
|
52
|
+
// Resolution-independent zoom: lay out at a fixed 1920px reference, then scale the
|
|
53
|
+
// whole view up on wider screens so the empty side-margins never exceed their
|
|
54
|
+
// 1920-reference proportion (a true zoom of everything, never below 1×). Pre-paint
|
|
55
|
+
// to avoid a flash.
|
|
56
|
+
(() => {
|
|
57
|
+
const REFERENCE_WIDTH = 1920;
|
|
58
|
+
const applyZoom = () => {
|
|
59
|
+
document.documentElement.style.zoom = String(Math.max(1, window.innerWidth / REFERENCE_WIDTH));
|
|
60
|
+
};
|
|
61
|
+
applyZoom();
|
|
62
|
+
window.addEventListener("resize", applyZoom);
|
|
63
|
+
})();
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<!-- The design system, tracked LIVE (unpinned): a doc always wears the current look.
|
|
67
|
+
`onerror` swaps in a byte-for-byte snapshot of the release this doc was written
|
|
68
|
+
against, served from this site — so a reader with no route to the CDN still gets
|
|
69
|
+
the page as it stood that day. Snapshots are stored ONCE per version under
|
|
70
|
+
/_design/, not once per doc, and /_design/ is served without a login (a published
|
|
71
|
+
stylesheet is not a secret, and a client scope must be able to read it too).
|
|
72
|
+
The version below is the snapshot this doc was written against — bump it (and add
|
|
73
|
+
the snapshot to danieldeusing-docs) when the design system moves; see the html-doc
|
|
74
|
+
skill, step 3. NOTE the path is site-absolute: it resolves on
|
|
75
|
+
docs.danieldeusing.de, NOT for a doc opened from disk over file://. -->
|
|
76
|
+
<link
|
|
77
|
+
rel="stylesheet"
|
|
78
|
+
href="https://cdn.jsdelivr.net/npm/@danieldeusing/design/dist/danieldeusing-design.min.css"
|
|
79
|
+
onerror="this.onerror=null;this.href='/_design/danieldeusing-design-0.4.0.min.css'"
|
|
80
|
+
/>
|
|
81
|
+
<link
|
|
82
|
+
rel="stylesheet"
|
|
83
|
+
href="https://cdn.jsdelivr.net/npm/@danieldeusing/design/src/fonts.css"
|
|
84
|
+
onerror="this.onerror=null;this.href='/_design/danieldeusing-design-0.4.0.fonts.css'"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<style>
|
|
88
|
+
/* Page-only layout. The design system ships the visual identity AND the measurements
|
|
89
|
+
(--content-w / --content-pad / --fs-*), so every value below is a TOKEN.
|
|
90
|
+
|
|
91
|
+
Each one carries a literal fallback on purpose. This page loads the design system
|
|
92
|
+
UNPINNED, and jsDelivr serves that url `max-age=604800` — seven days in the browser,
|
|
93
|
+
12h at the edge — so a reader can be holding a build that predates these tokens
|
|
94
|
+
(0.2.0 has neither `.wrap` nor `--fs-*`). A bare `var(--content-w)` would resolve to
|
|
95
|
+
nothing there and the page would go full-bleed with collapsed type. `var(--token,
|
|
96
|
+
literal)` takes the published value whenever it arrived and the old one when it did
|
|
97
|
+
not — the same contract as the offline <link> fallback above.
|
|
98
|
+
|
|
99
|
+
Note `padding-block` + `padding-inline`, never the `padding` shorthand: the
|
|
100
|
+
shorthand resets padding-inline and silently drops the left/right margins. */
|
|
101
|
+
.wrap { max-width: var(--content-w, 78rem); margin-inline: auto;
|
|
102
|
+
padding-block: 2.5rem 5rem; padding-inline: var(--content-pad, 1.5rem); }
|
|
103
|
+
|
|
104
|
+
/* header/footer chrome + nav now ship with the design system (src/chrome.css) */
|
|
105
|
+
|
|
106
|
+
/* content + table-of-contents layout (mirrors danieldeusing.de articles) */
|
|
107
|
+
.layout { display: grid; gap: 2.5rem; }
|
|
108
|
+
@media (min-width: 64rem) { .layout { grid-template-columns: minmax(0, 1fr) 13rem; } }
|
|
109
|
+
.content { min-width: 0; }
|
|
110
|
+
h1.title { font-size: var(--fs-2xl, 1.7rem); font-weight: 700; margin: 0.4rem 0 0.5rem; }
|
|
111
|
+
.lede { color: var(--muted-foreground); } /* full width — no max-width cap */
|
|
112
|
+
|
|
113
|
+
.toc { display: none; }
|
|
114
|
+
@media (min-width: 64rem) { .toc { display: block; } }
|
|
115
|
+
.toc-inner { position: sticky; top: 4.5rem; border-top: 1px solid var(--border); padding-top: 0.9rem; }
|
|
116
|
+
.toc-label { font-size: var(--fs-sm, 0.66rem); text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted-foreground); }
|
|
117
|
+
.toc nav { margin-top: 0.6rem; display: flex; flex-direction: column; }
|
|
118
|
+
.toc a { padding: 0.22rem 0; font-size: var(--fs-base, 0.76rem); color: var(--muted-foreground); text-decoration: none; transition: color 0.15s ease; }
|
|
119
|
+
.toc a:hover, .toc a.active { color: var(--primary); }
|
|
120
|
+
|
|
121
|
+
section.doc { margin-top: 2.5rem; scroll-margin-top: 4rem; }
|
|
122
|
+
.body { margin-top: 0.85rem; }
|
|
123
|
+
.body > * + * { margin-top: 0.75rem; }
|
|
124
|
+
|
|
125
|
+
/* content components the generator uses */
|
|
126
|
+
ol.steps { counter-reset: step; padding: 0; }
|
|
127
|
+
ol.steps > li { position: relative; padding-left: 2.25rem; margin-top: 0.6rem; counter-increment: step; }
|
|
128
|
+
ol.steps > li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 1.5rem; text-align: right; color: var(--primary); font-weight: 700; }
|
|
129
|
+
ul.plain { padding-left: 1.1rem; }
|
|
130
|
+
ul.plain > li { margin-top: 0.3rem; }
|
|
131
|
+
.grid { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
|
|
132
|
+
.pad { padding: 1rem; }
|
|
133
|
+
.muted { color: var(--muted-foreground); }
|
|
134
|
+
code.inline { font-family: inherit; background: var(--muted); border: 1px solid var(--border); border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.92em; }
|
|
135
|
+
pre.block { margin: 0; padding: 0.9rem 1rem; overflow-x: auto; font-size: var(--fs-md, 0.86rem); line-height: var(--lh-base, 1.5); background: var(--muted); border: 1px solid var(--border); border-radius: 6px; white-space: pre; }
|
|
136
|
+
/* Wide tables must scroll inside their own container, never the page.
|
|
137
|
+
Always wrap table.kv (and any custom table variant) like this:
|
|
138
|
+
<div class="table-scroll"><table class="kv">...</table></div>
|
|
139
|
+
Without the wrapper, a long code snippet or a multi-item cell forces
|
|
140
|
+
the table wider than its column and the WHOLE PAGE gains a horizontal
|
|
141
|
+
scrollbar instead of just the table — easy to miss until content is
|
|
142
|
+
wide enough to trigger it (many-column tables, file-path lists, long
|
|
143
|
+
inline code). Never set `white-space: nowrap` on a table cell that can
|
|
144
|
+
hold more than one short token (e.g. a list of file names) — nowrap on
|
|
145
|
+
a multi-item cell is what forces the runaway width in the first place. */
|
|
146
|
+
.table-scroll { overflow-x: auto; max-width: 100%; }
|
|
147
|
+
table.kv { width: 100%; border-collapse: collapse; font-size: var(--fs-lg, 0.92rem); }
|
|
148
|
+
table.kv th, table.kv td { text-align: left; vertical-align: top; padding: 0.4rem 0.75rem 0.4rem 0; border-bottom: 1px solid var(--border); }
|
|
149
|
+
table.kv th { color: var(--primary); font-weight: 700; white-space: nowrap; }
|
|
150
|
+
|
|
151
|
+
/* Mermaid diagrams. Before rendering, the <pre> shows the raw diagram source —
|
|
152
|
+
so with JS disabled (or if the CDN is unreachable) the page degrades to a
|
|
153
|
+
readable text diagram instead of an empty box. */
|
|
154
|
+
pre.mermaid { margin: 0; padding: 0.9rem 1rem; overflow-x: auto; font-size: var(--fs-md, 0.86rem); line-height: var(--lh-base, 1.5); background: var(--muted); border: 1px solid var(--border); border-radius: 6px; white-space: pre; }
|
|
155
|
+
pre.mermaid[data-processed] { white-space: normal; text-align: center; }
|
|
156
|
+
pre.mermaid svg { max-width: 100%; height: auto; }
|
|
157
|
+
</style>
|
|
158
|
+
</head>
|
|
159
|
+
|
|
160
|
+
<body>
|
|
161
|
+
<a href="#top" class="skip-link">skip to content</a>
|
|
162
|
+
<header class="bar">
|
|
163
|
+
<!-- top-left: ALWAYS danieldeusing-docs -->
|
|
164
|
+
<a href="#top" class="brand">
|
|
165
|
+
<span class="glow">danieldeusing-docs</span>
|
|
166
|
+
<span class="cursor-block" aria-hidden="true"></span>
|
|
167
|
+
</a>
|
|
168
|
+
<!-- top-right: navigation. Desktop = the `ls -l` dropdown; mobile = the
|
|
169
|
+
burger button toggling a full-width menu with the footer folded in
|
|
170
|
+
(chrome kit: src/chrome.css + runtime initBurgerNav). For a
|
|
171
|
+
single-page doc, delete the whole <nav> + burger. -->
|
|
172
|
+
<button type="button" class="nav-burger" data-nav-toggle aria-label="menu" aria-controls="site-nav" aria-expanded="false">
|
|
173
|
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M3 12h18"/><path d="M3 18h18"/></svg>
|
|
174
|
+
</button>
|
|
175
|
+
<nav aria-label="site" id="site-nav" class="site-nav"><details class="dropdown" id="nav">
|
|
176
|
+
<summary class="prompt" style="font-weight: 700" aria-label="site navigation">ls -l <span aria-hidden="true">▾</span></summary>
|
|
177
|
+
<ul class="dropdown-panel dropdown-panel--down ls-panel" role="list">
|
|
178
|
+
<li><a class="dropdown-item ls-row" href="{{NAV_1_HREF}}"><span class="ls-perm" aria-hidden="true">drwxr-xr-x</span><span class="ls-name">{{NAV_1_LABEL}}</span></a></li>
|
|
179
|
+
<li><a class="dropdown-item ls-row" href="{{NAV_2_HREF}}"><span class="ls-perm" aria-hidden="true">drwxr-xr-x</span><span class="ls-name">{{NAV_2_LABEL}}</span></a></li>
|
|
180
|
+
</ul>
|
|
181
|
+
</details>
|
|
182
|
+
<ul class="mobile-nav" role="list">
|
|
183
|
+
<li><a class="mobile-item ls-row" href="{{NAV_1_HREF}}"><span class="ls-perm" aria-hidden="true">drwxr-xr-x</span><span class="ls-name">{{NAV_1_LABEL}}</span></a></li>
|
|
184
|
+
<li><a class="mobile-item ls-row" href="{{NAV_2_HREF}}"><span class="ls-perm" aria-hidden="true">drwxr-xr-x</span><span class="ls-name">{{NAV_2_LABEL}}</span></a></li>
|
|
185
|
+
</ul>
|
|
186
|
+
<div class="mobile-footer">
|
|
187
|
+
<a class="doc-link" href="https://danieldeusing.de">danieldeusing.de</a>
|
|
188
|
+
<details class="mobile-theme">
|
|
189
|
+
<summary>
|
|
190
|
+
<svg width="11" height="11" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6.5" fill="currentColor"></circle></svg>
|
|
191
|
+
<span data-theme-label>warm</span>
|
|
192
|
+
<span class="mf-chev" aria-hidden="true">▾</span>
|
|
193
|
+
</summary>
|
|
194
|
+
<div class="mf-panel">
|
|
195
|
+
<button type="button" class="dropdown-item" data-theme-value="warm">warm</button>
|
|
196
|
+
<button type="button" class="dropdown-item" data-theme-value="green">green</button>
|
|
197
|
+
<button type="button" class="dropdown-item" data-theme-value="mono">mono</button>
|
|
198
|
+
<button type="button" class="dropdown-item" data-theme-value="paper">paper</button>
|
|
199
|
+
</div>
|
|
200
|
+
</details>
|
|
201
|
+
<button type="button" class="anim-toggle" data-anim-toggle aria-pressed="true" title="Toggle animations">
|
|
202
|
+
<span data-anim-box aria-hidden="true">[x]</span>
|
|
203
|
+
<span>anim</span>
|
|
204
|
+
</button>
|
|
205
|
+
</div></nav>
|
|
206
|
+
</header>
|
|
207
|
+
|
|
208
|
+
<main class="wrap" id="top">
|
|
209
|
+
<div class="layout">
|
|
210
|
+
<div class="content">
|
|
211
|
+
<p class="prompt">cat {{FOLDER}}/README.md</p>
|
|
212
|
+
<h1 class="title">{{DOC_TITLE}}</h1>
|
|
213
|
+
<p class="lede">{{TAGLINE}}</p>
|
|
214
|
+
|
|
215
|
+
<!-- one <section> per topic; each id must match a TOC entry. -->
|
|
216
|
+
<section class="doc" id="overview" data-term>
|
|
217
|
+
<p class="prompt">echo overview</p>
|
|
218
|
+
<div class="body" data-term-out>
|
|
219
|
+
<p>{{OVERVIEW}}</p>
|
|
220
|
+
</div>
|
|
221
|
+
</section>
|
|
222
|
+
|
|
223
|
+
<section class="doc" id="steps" data-term>
|
|
224
|
+
<p class="prompt">cat steps.txt</p>
|
|
225
|
+
<div class="body" data-term-out>
|
|
226
|
+
<ol class="steps">
|
|
227
|
+
<li>{{STEP_1}}</li>
|
|
228
|
+
<li>{{STEP_2}}</li>
|
|
229
|
+
</ol>
|
|
230
|
+
</div>
|
|
231
|
+
</section>
|
|
232
|
+
|
|
233
|
+
<!-- Flow diagrams use Mermaid — never hand-drawn ASCII art.
|
|
234
|
+
IMPORTANT: inside a <pre>, write line breaks in node labels as the
|
|
235
|
+
ESCAPED entity <br/>. A raw <br/> would be parsed as an HTML
|
|
236
|
+
element by the browser, and the renderer (which reads textContent)
|
|
237
|
+
would receive a label with the break silently stripped out. -->
|
|
238
|
+
<section class="doc" id="flow" data-term>
|
|
239
|
+
<p class="prompt">cat flow.mmd</p>
|
|
240
|
+
<div class="body" data-term-out>
|
|
241
|
+
<pre class="mermaid">
|
|
242
|
+
flowchart TD
|
|
243
|
+
A["{{FLOW_START}}"] --> B{"{{FLOW_DECISION}}"}
|
|
244
|
+
B -- yes --> C["{{FLOW_YES}}"]
|
|
245
|
+
B -- no --> D["{{FLOW_NO}}"]
|
|
246
|
+
|
|
247
|
+
class D warn
|
|
248
|
+
classDef warn fill:#a02c2c22,stroke:#a02c2c,stroke-width:2px
|
|
249
|
+
</pre>
|
|
250
|
+
</div>
|
|
251
|
+
</section>
|
|
252
|
+
|
|
253
|
+
<section class="doc" id="notes" data-term>
|
|
254
|
+
<p class="prompt">cat notes.md</p>
|
|
255
|
+
<div class="body" data-term-out>
|
|
256
|
+
<p class="eli5"><span class="eli5-term">Note</span> {{NOTES}}</p>
|
|
257
|
+
</div>
|
|
258
|
+
</section>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<aside class="toc">
|
|
262
|
+
<div class="toc-inner">
|
|
263
|
+
<p class="toc-label">On this page</p>
|
|
264
|
+
<nav>
|
|
265
|
+
<a href="#overview" data-toc-link="overview">Overview</a>
|
|
266
|
+
<a href="#steps" data-toc-link="steps">Steps</a>
|
|
267
|
+
<a href="#flow" data-toc-link="flow">Flow</a>
|
|
268
|
+
<a href="#notes" data-toc-link="notes">Notes</a>
|
|
269
|
+
</nav>
|
|
270
|
+
</div>
|
|
271
|
+
</aside>
|
|
272
|
+
</div>
|
|
273
|
+
</main>
|
|
274
|
+
|
|
275
|
+
<footer class="status">
|
|
276
|
+
<!-- bottom-left: [folder] + the doc title -->
|
|
277
|
+
<span class="status-left"><span class="glow">[{{FOLDER}}]</span> {{DOC_TITLE}}</span>
|
|
278
|
+
<!-- bottom-right: danieldeusing.de link + theme (page settings) dropdown -->
|
|
279
|
+
<nav class="status-right">
|
|
280
|
+
<a class="doc-link" href="https://danieldeusing.de">danieldeusing.de</a>
|
|
281
|
+
<span class="sep" aria-hidden="true"></span>
|
|
282
|
+
<details class="dropdown">
|
|
283
|
+
<summary aria-label="theme">
|
|
284
|
+
<svg width="11" height="11" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6.5" fill="currentColor"></circle></svg>
|
|
285
|
+
<span data-theme-label>warm</span>
|
|
286
|
+
<span aria-hidden="true">▾</span>
|
|
287
|
+
</summary>
|
|
288
|
+
<ul class="dropdown-panel" role="list">
|
|
289
|
+
<li><button type="button" class="dropdown-item" data-theme-value="warm">warm</button></li>
|
|
290
|
+
<li><button type="button" class="dropdown-item" data-theme-value="green">green</button></li>
|
|
291
|
+
<li><button type="button" class="dropdown-item" data-theme-value="mono">mono</button></li>
|
|
292
|
+
<li><button type="button" class="dropdown-item" data-theme-value="paper">paper</button></li>
|
|
293
|
+
</ul>
|
|
294
|
+
</details>
|
|
295
|
+
<button type="button" class="anim-toggle" data-anim-toggle aria-pressed="true" title="Toggle animations">
|
|
296
|
+
<span data-anim-box aria-hidden="true">[x]</span>
|
|
297
|
+
<span>anim</span>
|
|
298
|
+
</button>
|
|
299
|
+
</nav>
|
|
300
|
+
</footer>
|
|
301
|
+
|
|
302
|
+
<script type="module">
|
|
303
|
+
import { applyStoredTheme, initThemeSwitcher, initDropdowns, initTerminal, initAnimToggle, initBurgerNav } from
|
|
304
|
+
"https://cdn.jsdelivr.net/npm/@danieldeusing/design/runtime/index.js";
|
|
305
|
+
applyStoredTheme();
|
|
306
|
+
initThemeSwitcher();
|
|
307
|
+
initDropdowns();
|
|
308
|
+
initTerminal();
|
|
309
|
+
initAnimToggle();
|
|
310
|
+
initBurgerNav();
|
|
311
|
+
|
|
312
|
+
// Mermaid diagrams — themed from the design tokens, re-rendered on theme switch.
|
|
313
|
+
// Delete this block (and the pre.mermaid CSS) if the page has no diagrams.
|
|
314
|
+
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.16.0/dist/mermaid.esm.min.mjs";
|
|
315
|
+
|
|
316
|
+
const diagrams = Array.from(document.querySelectorAll("pre.mermaid"));
|
|
317
|
+
if (diagrams.length) {
|
|
318
|
+
// capture the sources before the first render replaces them with SVG
|
|
319
|
+
for (const node of diagrams) node.dataset.mermaidSource = node.textContent;
|
|
320
|
+
|
|
321
|
+
// Renders are serialized: applyStoredTheme() writes data-theme on startup, so an
|
|
322
|
+
// unguarded observer would kick off a second pass while the first is still
|
|
323
|
+
// running — two passes mutating the same nodes stack every diagram into one box.
|
|
324
|
+
let renderPass = 0;
|
|
325
|
+
let pending = Promise.resolve();
|
|
326
|
+
|
|
327
|
+
const renderDiagrams = () => {
|
|
328
|
+
pending = pending.then(async () => {
|
|
329
|
+
const pass = (renderPass += 1);
|
|
330
|
+
const styles = getComputedStyle(document.documentElement);
|
|
331
|
+
const token = (name, fallback) => styles.getPropertyValue(name).trim() || fallback;
|
|
332
|
+
mermaid.initialize({
|
|
333
|
+
startOnLoad: false,
|
|
334
|
+
securityLevel: "strict",
|
|
335
|
+
fontFamily: token("--font-mono", "monospace"),
|
|
336
|
+
flowchart: { htmlLabels: true, useMaxWidth: true },
|
|
337
|
+
themeVariables: {
|
|
338
|
+
background: token("--background", "#f5efe2"),
|
|
339
|
+
primaryColor: token("--card", "#efe7d4"),
|
|
340
|
+
primaryTextColor: token("--foreground", "#43352a"),
|
|
341
|
+
primaryBorderColor: token("--border", "#d9cdb6"),
|
|
342
|
+
secondaryColor: token("--muted", "#ece3cf"),
|
|
343
|
+
tertiaryColor: token("--muted", "#ece3cf"),
|
|
344
|
+
lineColor: token("--muted-foreground", "#71614e"),
|
|
345
|
+
textColor: token("--foreground", "#43352a"),
|
|
346
|
+
mainBkg: token("--card", "#efe7d4"),
|
|
347
|
+
nodeBorder: token("--border", "#d9cdb6"),
|
|
348
|
+
clusterBkg: token("--muted", "#ece3cf"),
|
|
349
|
+
clusterBorder: token("--border", "#d9cdb6"),
|
|
350
|
+
edgeLabelBackground: token("--background", "#f5efe2"),
|
|
351
|
+
titleColor: token("--primary", "#8a4516"),
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
// render ids must be unique per pass, or a re-render collides with itself
|
|
355
|
+
for (const [index, node] of diagrams.entries()) {
|
|
356
|
+
try {
|
|
357
|
+
const { svg } = await mermaid.render(`mermaid-svg-${index}-${pass}`, node.dataset.mermaidSource);
|
|
358
|
+
node.innerHTML = svg;
|
|
359
|
+
node.dataset.processed = "true";
|
|
360
|
+
} catch {
|
|
361
|
+
// a broken diagram falls back to its source — it must never blank the box
|
|
362
|
+
node.textContent = node.dataset.mermaidSource;
|
|
363
|
+
node.removeAttribute("data-processed");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
return pending;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// Wait for the terminal reveal: a diagram inside a not-yet-revealed
|
|
371
|
+
// [data-term-out] is visibility:hidden, and Mermaid would size it against a
|
|
372
|
+
// container it cannot measure properly. With animation off, render at once.
|
|
373
|
+
if (document.documentElement.classList.contains("anim-off") || window.termContentDone) {
|
|
374
|
+
void renderDiagrams();
|
|
375
|
+
} else {
|
|
376
|
+
window.addEventListener("term:contentdone", () => void renderDiagrams(), { once: true });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// re-render with the new palette, but only when the theme actually changed
|
|
380
|
+
let lastTheme = document.documentElement.dataset.theme;
|
|
381
|
+
new MutationObserver(() => {
|
|
382
|
+
const theme = document.documentElement.dataset.theme;
|
|
383
|
+
if (theme === lastTheme) return;
|
|
384
|
+
lastTheme = theme;
|
|
385
|
+
void renderDiagrams();
|
|
386
|
+
}).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// table-of-contents scroll-spy: highlight the section currently in view
|
|
390
|
+
const tocLinks = Array.from(document.querySelectorAll("[data-toc-link]"));
|
|
391
|
+
const sections = tocLinks
|
|
392
|
+
.map((link) => document.getElementById(link.dataset.tocLink))
|
|
393
|
+
.filter(Boolean);
|
|
394
|
+
if (sections.length) {
|
|
395
|
+
const setActive = (id) => {
|
|
396
|
+
for (const link of tocLinks) link.classList.toggle("active", link.dataset.tocLink === id);
|
|
397
|
+
};
|
|
398
|
+
const visible = new Set();
|
|
399
|
+
const observer = new IntersectionObserver(
|
|
400
|
+
(entries) => {
|
|
401
|
+
for (const entry of entries) {
|
|
402
|
+
if (entry.isIntersecting) visible.add(entry.target.id);
|
|
403
|
+
else visible.delete(entry.target.id);
|
|
404
|
+
}
|
|
405
|
+
const topmost = sections.find((section) => visible.has(section.id));
|
|
406
|
+
if (topmost) setActive(topmost.id);
|
|
407
|
+
},
|
|
408
|
+
{ rootMargin: "0px 0px -70% 0px" },
|
|
409
|
+
);
|
|
410
|
+
for (const section of sections) observer.observe(section);
|
|
411
|
+
}
|
|
412
|
+
</script>
|
|
413
|
+
</body>
|
|
414
|
+
</html>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE STANDARD PAGE CHROME. Copy this; do not re-invent it per surface.
|
|
3
|
+
|
|
4
|
+
Every danieldeusing surface wears the same three pieces, in this order:
|
|
5
|
+
|
|
6
|
+
header.bar brand on the left, the `ls -l` head on the right
|
|
7
|
+
.ls-nav the sticky right RAIL — the site navigation, always the rail,
|
|
8
|
+
never a dropdown
|
|
9
|
+
footer.status fixed, full width, and the home of EVERY control: theme,
|
|
10
|
+
language, animation toggle, links
|
|
11
|
+
|
|
12
|
+
Written down because it drifted. netmon grew its own header with the theme picker
|
|
13
|
+
inside it and no footer at all; the docs pages and danieldeusing.de kept the
|
|
14
|
+
pre-rail dropdown long after cockpit moved on. Each was locally reasonable and the
|
|
15
|
+
estate stopped looking like one thing — which is the entire problem the design
|
|
16
|
+
system exists to prevent. A surface that needs a different arrangement should say
|
|
17
|
+
so here, not solve it privately.
|
|
18
|
+
|
|
19
|
+
── WHY THE RAIL AND NOT A DROPDOWN ──
|
|
20
|
+
A dropdown answers "where can I go?" only while you hold it open, and it covers the
|
|
21
|
+
content you were reading to do it. The rail is the page's table of contents: always
|
|
22
|
+
visible, always in the same place, and hideable when you want the width back (the
|
|
23
|
+
state persists in localStorage under "ls-nav"). It is also why the `ls -l` HEAD sits
|
|
24
|
+
in the header bar rather than in the rail — the toggle must stay put whether the
|
|
25
|
+
rail is open or closed, so opening and closing changes the arrow and nothing else
|
|
26
|
+
moves.
|
|
27
|
+
|
|
28
|
+
── WHY EVERY CONTROL LIVES IN THE FOOTER ──
|
|
29
|
+
The header names the page; the footer operates it. Theme, language and animation are
|
|
30
|
+
session preferences, not navigation, and mixing them into the header put a colour
|
|
31
|
+
picker next to a brand on one surface and nowhere at all on another. One place, on
|
|
32
|
+
every surface, so a reader who has found it once has found it everywhere.
|
|
33
|
+
|
|
34
|
+
── WHAT YOU MUST WIRE UP (the CSS alone is not enough) ──
|
|
35
|
+
1. Pre-paint, INLINE in <head>, before any stylesheet — see the <head> block below.
|
|
36
|
+
Both the theme and the rail state must be applied before first paint, or the page
|
|
37
|
+
visibly flashes the default and jumps.
|
|
38
|
+
2. At the end of <body>, import and call the runtime:
|
|
39
|
+
import { initLsNav, initThemeSwitcher, initDropdowns, initAnimToggle }
|
|
40
|
+
from "@danieldeusing/design/runtime";
|
|
41
|
+
3. `initLsNav()` measures header and footer height and publishes --ls-nav-top /
|
|
42
|
+
--ls-nav-bottom, which is how the rail sits BETWEEN the chrome instead of under
|
|
43
|
+
it. A page with no footer.status still works — it just reserves nothing.
|
|
44
|
+
-->
|
|
45
|
+
|
|
46
|
+
<!-- ─────────────── <head> ─────────────── -->
|
|
47
|
+
<!-- INLINE and FIRST. A module at the end of <body> runs after first paint, so a
|
|
48
|
+
reader who chose "green" or hid the rail would watch the default paint and then
|
|
49
|
+
jump, on every single page load. Both reads are wrapped: a locked-down browser
|
|
50
|
+
throws on localStorage, and a page that cannot remember is still a working page —
|
|
51
|
+
it just starts from the default, which is the right answer when in doubt. -->
|
|
52
|
+
<script>
|
|
53
|
+
(() => {
|
|
54
|
+
try {
|
|
55
|
+
const themes = ["warm", "green", "mono", "paper"];
|
|
56
|
+
const stored = localStorage.getItem("theme");
|
|
57
|
+
if (themes.includes(stored)) document.documentElement.dataset.theme = stored;
|
|
58
|
+
} catch {}
|
|
59
|
+
try {
|
|
60
|
+
if (localStorage.getItem("ls-nav") === "off") document.documentElement.dataset.lsNav = "off";
|
|
61
|
+
} catch {}
|
|
62
|
+
})();
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<!-- ─────────────── header ─────────────── -->
|
|
66
|
+
<header class="bar">
|
|
67
|
+
<a href="/" class="brand"
|
|
68
|
+
><span class="glow">danieldeusing-SURFACE</span><span class="cursor-block" aria-hidden="true"></span
|
|
69
|
+
></a>
|
|
70
|
+
|
|
71
|
+
<button type="button" class="nav-burger" data-nav-toggle aria-label="menu" aria-controls="site-nav" aria-expanded="false">
|
|
72
|
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M3 12h18"/><path d="M3 18h18"/></svg>
|
|
73
|
+
</button>
|
|
74
|
+
|
|
75
|
+
<nav aria-label="site" id="site-nav" class="site-nav">
|
|
76
|
+
<!-- The head lives in the BAR, the rail hangs below it. Keeping `ls -l` at one
|
|
77
|
+
fixed spot is the point: the toggle is in the same place open or closed. -->
|
|
78
|
+
<div class="ls-nav-head">
|
|
79
|
+
<span class="ls-nav-title prompt">ls -l</span>
|
|
80
|
+
<!-- The guillemet is GENERATED by chrome.css (» open, « closed) — do not type a
|
|
81
|
+
character in here, or it will disagree with the state on the next release. -->
|
|
82
|
+
<button type="button" class="ls-nav-toggle" data-ls-nav-toggle aria-controls="nav"
|
|
83
|
+
aria-expanded="true" aria-label="show or hide navigation" title="show or hide navigation"></button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="ls-nav" id="nav">
|
|
87
|
+
<ul class="ls-panel" role="list">
|
|
88
|
+
<!-- One <li> per entry. `ls-row--dir` marks a directory (it takes the accent
|
|
89
|
+
and the weight); `ls-row--sub` / `ls-row--sub2` draw the nesting. The
|
|
90
|
+
permission string is decoration that carries meaning: drwxr-xr-x has
|
|
91
|
+
children, -rw-r--r-- does not. -->
|
|
92
|
+
<li><span class="ls-group"># group</span></li>
|
|
93
|
+
<li class="ls-row"><span class="ls-perm">-rw-r--r--</span><a class="ls-name" href="/page">page</a></li>
|
|
94
|
+
<li class="ls-row ls-row--dir"><span class="ls-perm">drwxr-xr-x</span><a class="ls-name" href="/section/">section/</a></li>
|
|
95
|
+
<li class="ls-row ls-row--sub"><span class="ls-perm">-rw-r--r--</span><a class="ls-name" href="/section/child">child</a></li>
|
|
96
|
+
</ul>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- Below 64rem the rail hides and the burger opens this instead. -->
|
|
100
|
+
<ul class="mobile-nav" role="list"></ul>
|
|
101
|
+
</nav>
|
|
102
|
+
</header>
|
|
103
|
+
|
|
104
|
+
<!-- ─────────────── content ─────────────── -->
|
|
105
|
+
<!-- .wrap is the shared column: --content-w wide, --content-pad from the edges. Set
|
|
106
|
+
padding-BLOCK here for the page's own vertical rhythm — never the `padding`
|
|
107
|
+
shorthand, which resets padding-inline and silently drops the shared margin. -->
|
|
108
|
+
<main class="wrap" style="padding-block: 1.1rem 6rem">
|
|
109
|
+
<!-- page content -->
|
|
110
|
+
</main>
|
|
111
|
+
|
|
112
|
+
<!-- ─────────────── footer ─────────────── -->
|
|
113
|
+
<!-- Fixed and full width. It is not "the bottom of the page", it is the control
|
|
114
|
+
surface, so it stays on screen and the rail stops above it. -->
|
|
115
|
+
<footer class="status">
|
|
116
|
+
<span class="status-left"><span class="glow">[surface]</span> what this page is</span>
|
|
117
|
+
|
|
118
|
+
<nav class="status-right">
|
|
119
|
+
<a class="doc-link" href="https://danieldeusing.de">danieldeusing.de</a>
|
|
120
|
+
<span class="sep" aria-hidden="true"></span>
|
|
121
|
+
|
|
122
|
+
<details class="dropdown">
|
|
123
|
+
<summary>
|
|
124
|
+
<span class="visually-hidden">theme </span>
|
|
125
|
+
<svg width="11" height="11" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6.5" fill="currentColor"></circle></svg>
|
|
126
|
+
<span data-theme-label>warm</span>
|
|
127
|
+
<span aria-hidden="true">▾</span>
|
|
128
|
+
</summary>
|
|
129
|
+
<ul class="dropdown-panel" role="list">
|
|
130
|
+
<li><button type="button" class="dropdown-item" data-theme-value="warm">warm</button></li>
|
|
131
|
+
<li><button type="button" class="dropdown-item" data-theme-value="green">green</button></li>
|
|
132
|
+
<li><button type="button" class="dropdown-item" data-theme-value="mono">mono</button></li>
|
|
133
|
+
<li><button type="button" class="dropdown-item" data-theme-value="paper">paper</button></li>
|
|
134
|
+
</ul>
|
|
135
|
+
</details>
|
|
136
|
+
|
|
137
|
+
<span class="sep" aria-hidden="true"></span>
|
|
138
|
+
<button type="button" class="anim-toggle" data-anim-toggle aria-pressed="true">[x] anim</button>
|
|
139
|
+
</nav>
|
|
140
|
+
</footer>
|