[dlmnwd]
-Unofficial Character Generator
+Unofficial Character Generator
+ +diff --git a/AGENTS.md b/AGENTS.md index 338369d..986eb44 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,9 +15,9 @@ not embed setting-specific strings; kindreds and their mechanics are _discovered from the book_ (headers like `X NAMES`, `Kindred Type`), never hardcoded. (The class list in `src/parse/class.ts` is a known fixed list, by contrast.) -The repo is public (MIT for its own code). The bundled fonts (`web/fonts/`: Departure -Mono, Basteleur) are SIL OFL 1.1 with their licence texts alongside; `NOTICE.md` -records the scope. Never vendor an asset whose licence forbids redistribution. +The repo is public (MIT for its own code). The bundled font (`web/fonts/`: Departure +Mono) is SIL OFL 1.1 with its licence text alongside; `NOTICE.md` records the +scope. Never vendor an asset whose licence forbids redistribution. ## Commands @@ -163,8 +163,8 @@ reason to exist; never add an upload affordance. prefilled fixture for iterating on the UI without a PDF). Keep sheet.ts free of parsing and file I/O. - `web/index.html` is the entry; `web/app.css` is the implemented visual system - (a light-inverted "textmode" character sheet using Departure Mono, with Basteleur for - display). `DESIGN.md` and `PRODUCT.md` describe the design/product intent as context. + (a light-inverted "textmode" character sheet set entirely in Departure Mono). + `DESIGN.md` and `PRODUCT.md` describe the design/product intent as context. - `scripts/build-single.ts` inlines the bundled JS/CSS/fonts into one self-contained HTML after `web:build`; it throws if any external asset survives. - Deploys to Cloudflare Pages as the static `dist/` build; bundled at dlmnwd.com with an diff --git a/DESIGN.md b/DESIGN.md index 8976cfd..3002efa 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -61,6 +61,26 @@ rhythm; thin rules as section dividers rather than boxes. Responsive: controls wrap to stacked on narrow screens; sheet tables stay legible with horizontal scroll only as a last resort. +## Dark theme + +The Script Wizards black/orange scheme (sampled from the scriptwizards.org +homepage), implemented in `web/app.css` purely as token overrides on the light +"textmode" system. Auto via `prefers-color-scheme` (guarded by +`:root:not([data-theme="light"])`), with explicit `:root[data-theme="dark"]` +overrides so the masthead toggle (auto/dark/light, persisted to localStorage) +wins in both directions. Print always re-asserts the light palette. + +- `--paper` `#111111` ground; `--paper-2` `#1a1a1a` panel fill; `--paper-sunk` + `#0d0d0d` sunken wells and table header bars. +- `--ink` `#ffffff` body text (18.9:1 on ground); `--ink-soft` `#c4c4c4` muted + (10.8:1); `--faint` `#555555` hairlines/placeholder. +- `--ember` `#ffa227` the accent orange: wordmark, headings, filled controls. + Never body text. `--accent-ink` `#1c1b19` is the text on orange fills. + `--ember-2` `#d98d2c` secondary accent (7.0:1); `--moss` `#8b9a55`. +- `--line` `#3d3d3d` frame borders; `--line-soft` `#2a2a2a` inner hairlines. +- `--shade` `0, 0, 0` bevel shadow rgb; `--row-hover` + `rgba(255, 255, 255, 0.06)` lightens instead of darkens. + ## Motion Quiet and intentional. On generate, the sheet sections rise+fade on a short diff --git a/NOTICE.md b/NOTICE.md index 8b13bbf..dbb27af 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -11,15 +11,6 @@ Departure Mono, by Helena Zhang. SIL Open Font License 1.1, copied verbatim to Upstream: https://departuremono.com -## web/fonts/Basteleur-Bold.woff2 - -Basteleur, by Keussel, published by Velvetyne Type Foundry. SIL Open Font -License 1.1, copied verbatim to `web/fonts/Basteleur-OFL.txt`. Basteleur carries -a Reserved Font Name, so the family is never renamed or re-released under -another name. - -Upstream: https://velvetyne.fr/fonts/basteleur - ## Dolmenwood [Dolmenwood](https://necroticgnome.com/pages/about-dolmenwood) is © [Necrotic diff --git a/README.md b/README.md index 5eacff7..70f1e81 100644 --- a/README.md +++ b/README.md @@ -199,9 +199,8 @@ for fuller Hoard/special coverage; `treasure`. Code: MIT (see `LICENSE`). Game data is yours and not covered here. -Fonts: [Departure Mono](https://departuremono.com/) by Helena Zhang and -[Basteleur](https://velvetyne.fr/fonts/basteleur/) by Keussel, both under the SIL -Open Font License 1.1, bundled in `web/fonts/` with their licence texts. See +Font: [Departure Mono](https://departuremono.com/) by Helena Zhang, under the SIL +Open Font License 1.1, bundled in `web/fonts/` with its licence text. See [`NOTICE.md`](NOTICE.md) for the scope. The web app deploys to Cloudflare Pages as a static build (`bun run web:build` → `dist/`). diff --git a/web/app.css b/web/app.css index 52da85e..793803d 100644 --- a/web/app.css +++ b/web/app.css @@ -1,23 +1,17 @@ -/* Dolmenwood tools: a light-mode "textmode" character sheet. Type system follows - the pixel-terminal look: Departure Mono does the workhorse reading, and Basteleur - is reserved for one display moment, the character name. Palette is a light - inversion of that stone/gold: warm parchment, ink, ember. */ - -/* Both faces are SIL Open Font License 1.1. Licence text sits beside them in - web/fonts/, and NOTICE.md records the scope. Basteleur carries a Reserved Font - Name, so the family is never renamed or re-released under another name. */ +/* Dolmenwood tools: a "textmode" character sheet. Type system follows the + pixel-terminal look: Departure Mono for everything, hierarchy from size, + weight, and case alone. The light palette is a light inversion of that + stone/gold: warm parchment, ink, ember. The dark palette is the Script + Wizards black/orange scheme (scriptwizards.org). */ + +/* Departure Mono is SIL Open Font License 1.1. Licence text sits beside it in + web/fonts/, and NOTICE.md records the scope. */ @font-face { font-family: "Departure Mono"; src: url("./fonts/DepartureMono-Regular.woff2") format("woff2"); font-weight: 400; font-display: swap; } -@font-face { - font-family: "Basteleur"; - src: url("./fonts/Basteleur-Bold.woff2") format("woff2"); - font-weight: 700; - font-display: swap; -} :root { --paper: #f1e7cf; /* warm parchment field behind everything */ @@ -30,17 +24,62 @@ --ember: #cf3a26; /* primary accent: red */ --ember-2: #d7642f; /* secondary accent: orange */ --moss: #5f6b34; /* tertiary glyph accent: wood green */ + --accent-ink: #f1e7cf; /* text on accent fills */ --line: #8a7350; /* frame borders, softened warm sepia */ --line-soft: #d3c39c; /* inner hairlines */ - --display: "Basteleur", Georgia, serif; /* character name only */ + --shade: 36, 29, 20; /* bevel shadow rgb */ + --sheen: 255, 255, 255; /* bevel highlight rgb */ + --row-hover: rgba(36, 29, 20, 0.045); /* table row hover tint */ + + --display: "Departure Mono", ui-monospace, monospace; /* character name */ --ui: "Departure Mono", ui-monospace, monospace; /* titles, labels, numbers */ --body: "Departure Mono", ui-monospace, monospace; /* reading text */ color-scheme: light; } +/* Dark: the Script Wizards black/orange scheme. The :not guard lets the OS + preference apply until the user picks a theme; the [data-theme] blocks make + a manual choice win in both directions. */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --paper: #111111; + --paper-2: #1a1a1a; + --paper-sunk: #0d0d0d; + --ink: #ffffff; + --ink-soft: #c4c4c4; + --faint: #555555; + --ember: #ffa227; + --ember-2: #d98d2c; + --moss: #8b9a55; + --accent-ink: #1c1b19; + --line: #3d3d3d; + --line-soft: #2a2a2a; + --shade: 0, 0, 0; + --row-hover: rgba(255, 255, 255, 0.06); + color-scheme: dark; + } +} +:root[data-theme="dark"] { + --paper: #111111; + --paper-2: #1a1a1a; + --paper-sunk: #0d0d0d; + --ink: #ffffff; + --ink-soft: #c4c4c4; + --faint: #555555; + --ember: #ffa227; + --ember-2: #d98d2c; + --moss: #8b9a55; + --accent-ink: #1c1b19; + --line: #3d3d3d; + --line-soft: #2a2a2a; + --shade: 0, 0, 0; + --row-hover: rgba(255, 255, 255, 0.06); + color-scheme: dark; +} + * { box-sizing: border-box; } @@ -129,6 +168,13 @@ body { color: var(--line); } +.masthead__side { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.35rem; +} + .tagline { margin: 0 0 0.2rem; font-family: var(--ui); @@ -138,6 +184,19 @@ body { text-align: right; } +.theme-toggle { + font: 400 0.8rem/1 var(--ui); + text-transform: uppercase; + cursor: pointer; + color: var(--ink-soft); + background: none; + border: 2px solid var(--line); + padding: 0.3rem 0.5rem; +} +.theme-toggle:hover { + color: var(--ember); +} + /* ── Panel (the reusable frame) ────────────────────────────────────────── */ .panel { @@ -194,8 +253,8 @@ input[type="text"] { border: 2px solid var(--line); padding: 0.4rem 0.55rem; box-shadow: - inset 1px 1px 0 rgba(36, 29, 20, 0.22), - inset -1px -1px 0 rgba(255, 255, 255, 0.5); + inset 1px 1px 0 rgba(var(--shade), 0.22), + inset -1px -1px 0 rgba(var(--sheen), 0.5); } select { @@ -227,10 +286,10 @@ select option:disabled { color: var(--faint); } -:where(select, input, .btn):focus-visible { +:where(select, input, .btn, .theme-toggle):focus-visible { outline: none; box-shadow: - inset 1px 1px 0 rgba(36, 29, 20, 0.22), + inset 1px 1px 0 rgba(var(--shade), 0.22), 0 0 0 2px var(--ember); } @@ -240,22 +299,22 @@ select option:disabled { font: 400 1rem/1 var(--ui); text-transform: uppercase; cursor: pointer; - color: var(--paper); + color: var(--accent-ink); background: var(--ember); border: 2px solid var(--line); padding: 0.5rem 0.8rem; text-align: center; box-shadow: - inset 1px 1px 0 rgba(255, 255, 255, 0.35), - inset -1px -1px 0 rgba(36, 29, 20, 0.45); + inset 1px 1px 0 rgba(var(--sheen), 0.35), + inset -1px -1px 0 rgba(var(--shade), 0.45); } .btn:hover:not(:disabled) { background: var(--ember-2); } .btn:active:not(:disabled) { box-shadow: - inset -1px -1px 0 rgba(255, 255, 255, 0.2), - inset 1px 1px 0 rgba(36, 29, 20, 0.5); + inset -1px -1px 0 rgba(var(--sheen), 0.2), + inset 1px 1px 0 rgba(var(--shade), 0.5); } .btn.ghost { color: var(--ink); @@ -361,9 +420,12 @@ select option:disabled { } .sheet-name { margin: 0; - font: 700 clamp(1.7rem, 4vw, 2.6rem) / 0.92 var(--display); + font: 400 clamp(1.7rem, 4vw, 2.6rem) / 0.92 var(--display); text-transform: uppercase; color: var(--ember); + /* Same stroke trick as the wordmark: no synthetic bold on a pixel face. */ + -webkit-text-stroke: 0.035em currentColor; + paint-order: stroke fill; } .sheet-sub-row { display: flex; @@ -516,7 +578,7 @@ select option:disabled { border-bottom: 0; } .sheet tbody tr:hover td { - background: rgba(36, 29, 20, 0.045); + background: var(--row-hover); } .sheet .num { text-align: right; @@ -554,19 +616,19 @@ select option:disabled { border: 2px solid var(--line); padding: 0.08rem 0.4rem; box-shadow: - inset 1px 1px 0 rgba(255, 255, 255, 0.45), - inset -1px -1px 0 rgba(36, 29, 20, 0.22); + inset 1px 1px 0 rgba(var(--sheen), 0.45), + inset -1px -1px 0 rgba(var(--shade), 0.22); } .badge .bv { color: var(--ember); font-variant-numeric: tabular-nums; } .badge--accent { - color: var(--paper); + color: var(--accent-ink); background: var(--ember); } .badge--accent .bv { - color: var(--paper); + color: var(--accent-ink); } .badge--soft { border-color: var(--line-soft); @@ -805,6 +867,25 @@ select option:disabled { @page { margin: 1cm; } + /* Paper is paper: print always renders the light palette, whatever theme is + active on screen. Ties the dark selectors on specificity and wins on order. */ + :root:not([data-theme="light"]) { + --paper: #f1e7cf; + --paper-2: #f6efdb; + --paper-sunk: #e7dabb; + --ink: #241d14; + --ink-soft: #6f6147; + --faint: #c3b48f; + --ember: #cf3a26; + --ember-2: #d7642f; + --moss: #5f6b34; + --accent-ink: #f1e7cf; + --line: #8a7350; + --line-soft: #d3c39c; + --shade: 36, 29, 20; + --row-hover: rgba(36, 29, 20, 0.045); + color-scheme: light; + } .masthead, .rail, .colophon { diff --git a/web/fonts/Basteleur-Bold.woff2 b/web/fonts/Basteleur-Bold.woff2 deleted file mode 100644 index 69b8558..0000000 Binary files a/web/fonts/Basteleur-Bold.woff2 and /dev/null differ diff --git a/web/fonts/Basteleur-OFL.txt b/web/fonts/Basteleur-OFL.txt deleted file mode 100644 index 16b6825..0000000 --- a/web/fonts/Basteleur-OFL.txt +++ /dev/null @@ -1,106 +0,0 @@ -Copyright (c) 2021, Keussel (www.keussel.studio|contact@keussel.studio), -with Reserved Font Name Basteleur. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - -SIL Open Font License v1.1 -==================================================== - - -Preamble ----------- - -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - - -Definitions -------------- - -`"Font Software"` refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -`"Reserved Font Name"` refers to any names specified as such after the -copyright statement(s). - -`"Original Version"` refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -`"Modified Version"` refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -`"Author"` refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - - -Permission & Conditions ------------------------- - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1. Neither the Font Software nor any of its individual components, - in Original or Modified Versions, may be sold by itself. - -2. Original or Modified Versions of the Font Software may be bundled, - redistributed and/or sold with any software, provided that each copy - contains the above copyright notice and this license. These can be - included either as stand-alone text files, human-readable headers or - in the appropriate machine-readable metadata fields within text or - binary files as long as those fields can be easily viewed by the user. - -3. No Modified Version of the Font Software may use the Reserved Font - Name(s) unless explicit written permission is granted by the corresponding - Copyright Holder. This restriction only applies to the primary font name as - presented to the users. - -4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font - Software shall not be used to promote, endorse or advertise any - Modified Version, except to acknowledge the contribution(s) of the - Copyright Holder(s) and the Author(s) or with their explicit written - permission. - -5. The Font Software, modified or unmodified, in part or in whole, - must be distributed entirely under this license, and must not be - distributed under any other license. The requirement for fonts to - remain under this license does not apply to any document created - using the Font Software. - - -Termination ------------ - -This license becomes null and void if any of the above conditions are -not met. - - - DISCLAIMER - - THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE - COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL - DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM - OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/web/index.html b/web/index.html index 9f13d9a..c05ec55 100644 --- a/web/index.html +++ b/web/index.html @@ -24,13 +24,26 @@ + + +
Unofficial Character Generator
+Unofficial Character Generator
+ +