commit e8a71dcb92cd5a29dec16fc9ee9a4f84680d667e Author: jasa Date: Wed Sep 23 15:09:14 2026 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ff4c88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.log +demo-dist/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ba2552 --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +# tlig_css + +The TLIG stylesheet rebuilt on SASS tokens and Vite, as a replacement for +`Avada-Child-Theme/css/tlig_en.css` (7,082 lines) and the compiled Avada +`style.css` it fights with. + +This first pass covers the common foundation only: fonts, colour, type scale, +links, buttons, form fields and the footer. Page-specific styling (messages, +bookstore, menus, the wood and scroll backgrounds) is not here yet. + +## Commands + + npm install + npm run dev # demo with live reload, at localhost:5173 + npm run preview # demo as a static page, at localhost:4173 + npm run build # the deliverable: dist/tlig.css + dist/fonts/ + +There is no way to open `src/index.html` from disk — it is an ES module that +imports `.scss`, so it needs one of the two servers above. `file://` gives a +blank page. + +**`npm run dev`** compiles the SASS in memory and injects the CSS through +JavaScript. Fast, hot-reloading, and how you work. One caveat: because the CSS +arrives after first paint, anything with a `transition` animates up from its +unstyled state, so for a few milliseconds links are browser-blue and buttons are +nearly transparent. That is a dev-server artifact, not the stylesheet. + +**`npm run preview`** builds the demo to `demo-dist/` and serves it. The page +there loads the compiled stylesheet through a real `` in +``, so it paints once, exactly as a browser will on the real site. Use +this when judging how something actually looks, or when screenshotting. + +**`npm run build`** emits only the deliverable — `dist/tlig.css` and the fonts, +no HTML and no demo markup. The demo is built from `src/index.html` and the +deliverable from `src/main.js`, so demo chrome cannot reach `dist`. + +## The demo page + +`npm run dev` serves `src/index.html`: the foundation rendered on the real +ground, inside the wood background and the papyrus scroll, with the real footer +component at the bottom. It shows the type scale, every link and button +variant, the dark-ground mechanism, and a colour grid generated at runtime from +the `--tlig-color-*` properties the stylesheet actually emits — so the grid +cannot drift from the code. + +The toolbar has three controls: + +- **Script** — switches the sample heading between Latin, Hungarian, Cyrillic + and Greek. No CSS changes when you do; the browser picks the font file by + codepoint. Also addressable as `?script=bg` for linking or screenshotting. + Greek is included deliberately: no papyrus cut exists for it, so it shows the + fallback face honestly. +- **Show boxes** — outlines each component so the structure is visible. +- A live viewport width and the active breakpoint. + +The wood and the scroll are **demo chrome**, in `src/demo/demo.scss`, not in the +stylesheet: page layout is still on the to-do list below, and a demo is not the +place to quietly ship it. The demo is not a build input, so none of it can reach +`dist/tlig.css` — the build is checked for exactly that. + +The frame is there anyway because a foundation only tells the truth on the +ground it has to work on. Brown ink at 86% looks arbitrary on white and +obviously right on papyrus. + +## Layout + + src/sass/ + abstracts/ compile-time only; emits no CSS + _palette.scss every literal colour the live site uses + _tokens.scss semantic tokens: what a colour is *for* + _functions.scss token accessors + _mixins.scss media queries, type, patterns + base/ + _root.scss emits every token as a CSS custom property + _fonts.scss @font-face + _reset.scss + _typography.scss + _links.scss + components/ + _button.scss _field.scss _footer.scss + src/fonts/ papyrus-bold (4 scripts) and Lato, self-hosted + src/demo/ demo page chrome and behaviour — never built + src/index.html the demo page + +## The three rules that replace the jigsaw + +**One source of truth per value.** No rule writes a hex code. `_palette.scss` +holds the literals, `_tokens.scss` names them by role, `base/_root.scss` emits +them as `--tlig-*` custom properties, and every rule reads a token. `#482020` +appears 74 times in `tlig_en.css`; it appears once here. + +**Context re-points tokens, it does not restate rules.** The `dark-ground` +mixin re-points nine custom properties; `.site-footer` and `.on-dark` both +include it and declare no colours of their own. Every link, heading and button +inside them is then correct — where the live CSS needs 24 id selectors to say +the same thing. The list of what "dark" means exists once, so the two cannot +drift: building the demo caught exactly that bug, an `.on-dark` that re-pointed +the links but not `--tlig-color-heading`, rendering a heading dark-brown on +dark-brown. + +**Specificity goes one way.** Layer order in `main.scss` is tokens → base → +components, and nothing later needs to outrank anything earlier. Every +`!important` in the build — four of them — is in the single +`prefers-reduced-motion` block, where overriding author animation is the point. +There are none anywhere else. `tlig_en.css` has 518. + +## What the tokens were taken from + +Values are the ones the site renders today, not new choices: + +| Token | Value | Source | +|---|---|---| +| `--tlig-color-page-ink` | `rgba(72,32,32,0.86)` | Avada `--body_typography-color` | +| `--tlig-color-link` | `#810008` | Avada `--link_color` | +| `--tlig-color-link-hover` | `#ca4a1f` | `tlig_en.css` global `a:hover` | +| `--tlig-color-heading` | `#482020` | Avada `--h1_typography-color` | +| `--tlig-color-heading-message` | `#7a0012` | `tlig_en.css` global `h2` | +| `--tlig-color-footer-ink` | `#fbefc9` | `tlig_en.css` `#footer-container` | +| `--tlig-color-button-bg` | `#8f0b06` | `tlig_en.css` `.btnSubmitNewsletter` | +| `--tlig-size-base` | `18px` | Avada `--body_typography-font-size` | + +Two deliberate departures, both noted in the source: + +- **h2** is encoded as Trebuchet / `#7a0012` / bold. Avada's own token says Lato + / `#333333`, but `tlig_en.css` overrides it globally with four `!important`s, + so the second is what actually renders. +- **Spacing** is snapped to a 4px scale. The live CSS uses 6px, 23px, 35px and + 46px in adjacent rules; those become the nearest step. + +## Fonts + +`papyrus-bold` is declared four times under one family name, split by +`unicode-range` across the Latin, Latin-Extended (Hungarian), Cyrillic and +Cyrillic-Extended (Bulgarian) files. The browser picks the file by codepoint, so +a Bulgarian heading needs no language class and no second stylesheet. + +All font files are project assets. The live site points `@font-face` at absolute +`ww3.tlig.org` URLs, which is why a staging copy renders in the fallback face; +here Vite emits them into `dist/fonts/` and the CSS references them relatively, +so the bundle resolves at whatever path it is dropped. + +Lato is self-hosted from the three files already in the child theme, replacing +the 33 `@font-face` blocks Avada generates from its Google Fonts cache. + +## Not done yet + +- Page-level layout: header, nav, the wood and papyrus backgrounds, `#main` +- Message typography (`.tbody`, `.vassula`, `.jc`, `.god`, the bible-reference + asides) +- Menus, bookstore, search, testimonies, downloads +- Enqueuing the built CSS from the theme, and retiring `tlig_en.css` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..df75bc7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1175 @@ +{ + "name": "tlig_css", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "sass": "^1.105.0", + "vite": "^8.3.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.150.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.150.0.tgz", + "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz", + "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz", + "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz", + "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz", + "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz", + "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz", + "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz", + "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz", + "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz", + "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz", + "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz", + "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz", + "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz", + "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz", + "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz", + "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rolldown": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.9.tgz", + "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.150.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.9", + "@rolldown/binding-android-arm64": "1.2.9", + "@rolldown/binding-darwin-arm64": "1.2.9", + "@rolldown/binding-darwin-x64": "1.2.9", + "@rolldown/binding-freebsd-x64": "1.2.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.9", + "@rolldown/binding-linux-arm64-gnu": "1.2.9", + "@rolldown/binding-linux-arm64-musl": "1.2.9", + "@rolldown/binding-linux-ppc64-gnu": "1.2.9", + "@rolldown/binding-linux-s390x-gnu": "1.2.9", + "@rolldown/binding-linux-x64-gnu": "1.2.9", + "@rolldown/binding-linux-x64-musl": "1.2.9", + "@rolldown/binding-openharmony-arm64": "1.2.9", + "@rolldown/binding-win32-arm64-msvc": "1.2.9", + "@rolldown/binding-win32-x64-msvc": "1.2.9" + } + }, + "node_modules/sass": { + "version": "1.105.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.105.0.tgz", + "integrity": "sha512-4EjSuRTyG8M4yRXXY1PT9OUV5zz23xBxJkbIAprohKHqf6HOU4MCpE3U3vAC+iO1gxobBjESNbcj8rYAQZWnyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", + "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.6", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2a72033 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "tlig-css", + "private": true, + "version": "0.1.0", + "type": "module", + "description": "TLIG stylesheet, rebuilt on SASS tokens and Vite.", + "scripts": { + "dev": "vite", + "build": "vite build", + "build:demo": "vite build --mode demo", + "preview": "vite build --mode demo && vite preview --mode demo" + }, + "devDependencies": { + "sass": "^1.105.0", + "vite": "^8.3.0" + } +} diff --git a/site/wp-content/includes/images/background-header9.webp b/site/wp-content/includes/images/background-header9.webp new file mode 100644 index 0000000..b8359f1 Binary files /dev/null and b/site/wp-content/includes/images/background-header9.webp differ diff --git a/site/wp-content/includes/images/background-header9_sticky6.webp b/site/wp-content/includes/images/background-header9_sticky6.webp new file mode 100644 index 0000000..35e41c2 Binary files /dev/null and b/site/wp-content/includes/images/background-header9_sticky6.webp differ diff --git a/site/wp-content/includes/images/background_planche_1920.jpg b/site/wp-content/includes/images/background_planche_1920.jpg new file mode 100644 index 0000000..855456e Binary files /dev/null and b/site/wp-content/includes/images/background_planche_1920.jpg differ diff --git a/site/wp-content/includes/images/banner/Banner_Lent_image002.jpg.webp b/site/wp-content/includes/images/banner/Banner_Lent_image002.jpg.webp new file mode 100644 index 0000000..3313cd8 Binary files /dev/null and b/site/wp-content/includes/images/banner/Banner_Lent_image002.jpg.webp differ diff --git a/site/wp-content/includes/images/banner/Banner_Lent_image003.jpg.webp b/site/wp-content/includes/images/banner/Banner_Lent_image003.jpg.webp new file mode 100644 index 0000000..77ba32f Binary files /dev/null and b/site/wp-content/includes/images/banner/Banner_Lent_image003.jpg.webp differ diff --git a/site/wp-content/includes/images/banner/Banner_home.jpg.webp b/site/wp-content/includes/images/banner/Banner_home.jpg.webp new file mode 100644 index 0000000..d78b2ab Binary files /dev/null and b/site/wp-content/includes/images/banner/Banner_home.jpg.webp differ diff --git a/site/wp-content/includes/images/banner/Banner_home_philippines.jpg.webp b/site/wp-content/includes/images/banner/Banner_home_philippines.jpg.webp new file mode 100644 index 0000000..a2ea1d1 Binary files /dev/null and b/site/wp-content/includes/images/banner/Banner_home_philippines.jpg.webp differ diff --git a/site/wp-content/includes/images/banner/BookIIBannerFinal.jpg.webp b/site/wp-content/includes/images/banner/BookIIBannerFinal.jpg.webp new file mode 100644 index 0000000..f8585b7 Binary files /dev/null and b/site/wp-content/includes/images/banner/BookIIBannerFinal.jpg.webp differ diff --git a/site/wp-content/includes/images/banner/FarewellVassulaBanner2.jpg.webp b/site/wp-content/includes/images/banner/FarewellVassulaBanner2.jpg.webp new file mode 100644 index 0000000..a1fde5f Binary files /dev/null and b/site/wp-content/includes/images/banner/FarewellVassulaBanner2.jpg.webp differ diff --git a/site/wp-content/includes/images/footerTopBorder.jpg b/site/wp-content/includes/images/footerTopBorder.jpg new file mode 100644 index 0000000..93a5276 Binary files /dev/null and b/site/wp-content/includes/images/footerTopBorder.jpg differ diff --git a/site/wp-content/includes/images/goldenBar.png b/site/wp-content/includes/images/goldenBar.png new file mode 100644 index 0000000..024209a Binary files /dev/null and b/site/wp-content/includes/images/goldenBar.png differ diff --git a/site/wp-content/includes/images/goldenBar.png.webp b/site/wp-content/includes/images/goldenBar.png.webp new file mode 100644 index 0000000..b8503d6 Binary files /dev/null and b/site/wp-content/includes/images/goldenBar.png.webp differ diff --git a/site/wp-content/includes/images/magazine90x128.jpg b/site/wp-content/includes/images/magazine90x128.jpg new file mode 100644 index 0000000..9252263 Binary files /dev/null and b/site/wp-content/includes/images/magazine90x128.jpg differ diff --git a/site/wp-content/includes/images/map_braun.png b/site/wp-content/includes/images/map_braun.png new file mode 100644 index 0000000..2f52c9a Binary files /dev/null and b/site/wp-content/includes/images/map_braun.png differ diff --git a/site/wp-content/includes/images/menu/Menu-activities-2.jpg.webp b/site/wp-content/includes/images/menu/Menu-activities-2.jpg.webp new file mode 100644 index 0000000..17b67db Binary files /dev/null and b/site/wp-content/includes/images/menu/Menu-activities-2.jpg.webp differ diff --git a/site/wp-content/includes/images/menu/Menu-interreligious.jpg.webp b/site/wp-content/includes/images/menu/Menu-interreligious.jpg.webp new file mode 100644 index 0000000..3cff563 Binary files /dev/null and b/site/wp-content/includes/images/menu/Menu-interreligious.jpg.webp differ diff --git a/site/wp-content/includes/images/menu/Menu-news.jpg.webp b/site/wp-content/includes/images/menu/Menu-news.jpg.webp new file mode 100644 index 0000000..25704be Binary files /dev/null and b/site/wp-content/includes/images/menu/Menu-news.jpg.webp differ diff --git a/site/wp-content/includes/images/menu/Menu-pilgrimage-1.jpg.webp b/site/wp-content/includes/images/menu/Menu-pilgrimage-1.jpg.webp new file mode 100644 index 0000000..9358dbf Binary files /dev/null and b/site/wp-content/includes/images/menu/Menu-pilgrimage-1.jpg.webp differ diff --git a/site/wp-content/includes/images/menu/Menu-retreats-1.jpg.webp b/site/wp-content/includes/images/menu/Menu-retreats-1.jpg.webp new file mode 100644 index 0000000..71fa3a4 Binary files /dev/null and b/site/wp-content/includes/images/menu/Menu-retreats-1.jpg.webp differ diff --git a/site/wp-content/includes/images/menu/ange-dessin-vassula.png.webp b/site/wp-content/includes/images/menu/ange-dessin-vassula.png.webp new file mode 100644 index 0000000..58a0b28 Binary files /dev/null and b/site/wp-content/includes/images/menu/ange-dessin-vassula.png.webp differ diff --git a/site/wp-content/includes/images/menu/ange_daniel2.png.webp b/site/wp-content/includes/images/menu/ange_daniel2.png.webp new file mode 100644 index 0000000..f97435c Binary files /dev/null and b/site/wp-content/includes/images/menu/ange_daniel2.png.webp differ diff --git a/site/wp-content/includes/images/menu/books-onebook-500-2.png.webp b/site/wp-content/includes/images/menu/books-onebook-500-2.png.webp new file mode 100644 index 0000000..90e5f3c Binary files /dev/null and b/site/wp-content/includes/images/menu/books-onebook-500-2.png.webp differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_309.png b/site/wp-content/includes/images/menu/papyrus_submenu_309.png new file mode 100644 index 0000000..341cbab Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_309.png differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_351_cnd.png b/site/wp-content/includes/images/menu/papyrus_submenu_351_cnd.png new file mode 100644 index 0000000..6fe9466 Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_351_cnd.png differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_362_cnd.png b/site/wp-content/includes/images/menu/papyrus_submenu_362_cnd.png new file mode 100644 index 0000000..ccf08c5 Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_362_cnd.png differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_366_cnd.png b/site/wp-content/includes/images/menu/papyrus_submenu_366_cnd.png new file mode 100644 index 0000000..09a54c9 Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_366_cnd.png differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_399_cnd.png b/site/wp-content/includes/images/menu/papyrus_submenu_399_cnd.png new file mode 100644 index 0000000..bf8e7d2 Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_399_cnd.png differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_402_cnd.png b/site/wp-content/includes/images/menu/papyrus_submenu_402_cnd.png new file mode 100644 index 0000000..291c16a Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_402_cnd.png differ diff --git a/site/wp-content/includes/images/menu/papyrus_submenu_450_cnd.png b/site/wp-content/includes/images/menu/papyrus_submenu_450_cnd.png new file mode 100644 index 0000000..6857258 Binary files /dev/null and b/site/wp-content/includes/images/menu/papyrus_submenu_450_cnd.png differ diff --git a/site/wp-content/includes/images/menu/unity5-cnd.png.webp b/site/wp-content/includes/images/menu/unity5-cnd.png.webp new file mode 100644 index 0000000..3c4b217 Binary files /dev/null and b/site/wp-content/includes/images/menu/unity5-cnd.png.webp differ diff --git a/site/wp-content/includes/images/page/Scroll3-min.png.webp b/site/wp-content/includes/images/page/Scroll3-min.png.webp new file mode 100644 index 0000000..61f5704 Binary files /dev/null and b/site/wp-content/includes/images/page/Scroll3-min.png.webp differ diff --git a/site/wp-content/includes/images/page/background-message-entete_cnd.png.webp b/site/wp-content/includes/images/page/background-message-entete_cnd.png.webp new file mode 100644 index 0000000..fb59435 Binary files /dev/null and b/site/wp-content/includes/images/page/background-message-entete_cnd.png.webp differ diff --git a/site/wp-content/includes/images/page/background-message-footer-1200x114_cnd.png.webp b/site/wp-content/includes/images/page/background-message-footer-1200x114_cnd.png.webp new file mode 100644 index 0000000..d349946 Binary files /dev/null and b/site/wp-content/includes/images/page/background-message-footer-1200x114_cnd.png.webp differ diff --git a/site/wp-content/includes/images/page/background-message-milieux1200x190-4_cnd.png.webp b/site/wp-content/includes/images/page/background-message-milieux1200x190-4_cnd.png.webp new file mode 100644 index 0000000..0d71206 Binary files /dev/null and b/site/wp-content/includes/images/page/background-message-milieux1200x190-4_cnd.png.webp differ diff --git a/site/wp-content/includes/images/page/papyrus_background_v-min.png.webp b/site/wp-content/includes/images/page/papyrus_background_v-min.png.webp new file mode 100644 index 0000000..f58fc21 Binary files /dev/null and b/site/wp-content/includes/images/page/papyrus_background_v-min.png.webp differ diff --git a/site/wp-content/includes/images/pen-min.png b/site/wp-content/includes/images/pen-min.png new file mode 100644 index 0000000..9e3b852 Binary files /dev/null and b/site/wp-content/includes/images/pen-min.png differ diff --git a/site/wp-content/includes/images/signupEmpty2.png b/site/wp-content/includes/images/signupEmpty2.png new file mode 100644 index 0000000..1c30a26 Binary files /dev/null and b/site/wp-content/includes/images/signupEmpty2.png differ diff --git a/src/demo/demo.js b/src/demo/demo.js new file mode 100644 index 0000000..b02c22d --- /dev/null +++ b/src/demo/demo.js @@ -0,0 +1,128 @@ +// Demo page behaviour. Not a build input — see src/demo/demo.scss. +import '../sass/main.scss' +import './demo.scss' + +// -- Colour swatches --------------------------------------------------------- +// +// Read from the emitted custom properties rather than from a hand-kept list, so +// the demo cannot drift from the stylesheet: delete a token and its swatch +// disappears on reload. + +function emittedTokens (group) { + const names = new Set() + for (const sheet of document.styleSheets) { + let rules + try { rules = sheet.cssRules } catch { continue } // cross-origin + for (const rule of rules) { + if (rule.selectorText !== ':root') continue + for (const prop of rule.style) { + if (prop.startsWith(`--tlig-${group}-`)) names.add(prop) + } + } + } + return [...names] +} + +function renderSwatches () { + const root = getComputedStyle(document.documentElement) + const html = emittedTokens('color').map((name) => { + const value = root.getPropertyValue(name).trim() + const short = name.replace('--tlig-color-', '') + return `
+
+
+
${short}
+
${value}
+
+
` + }).join('') + document.querySelector('#swatches').innerHTML = html + document.querySelector('#swatch-count').textContent = emittedTokens('color').length +} + +// -- Script switcher --------------------------------------------------------- +// +// The point of this control: all four papyrus files share one family name and +// are separated by unicode-range, so switching scripts changes no CSS at all. +// The browser picks the font file by codepoint. + +const SAMPLES = { + en: { + label: 'English', + heading: 'True Life in God', + line: 'Peace be with you — I am the Alpha and the Omega.', + note: 'Latin — PapyrusLETBold, the original file.', + }, + hu: { + // The pangram, not the site's own title: "Igaz Élet Istenben" contains no + // codepoint above U+00FF, so it would be served by the Latin file and + // would demonstrate nothing. ő (U+0151) and ű (U+0171) are the test. + label: 'Magyar', + heading: 'Árvíztűrő tükörfúrógép', + line: 'Igaz Élet Istenben — ő and ű are what the Latin-Extended cut is for.', + note: 'Latin Extended-A — the hu file. The original PapyrusLETBold has no ő or ű.', + }, + bg: { + label: 'Български', + heading: 'Истински живот в Бога', + line: 'Мир на вас — Аз съм Алфа и Омега.', + note: 'Cyrillic — the cyrillic and bulgarian cuts.', + }, + ru: { + label: 'Русский', + heading: 'Истинная жизнь в Боге', + line: 'Мир вам — Я есмь Альфа и Омега.', + note: 'Cyrillic — same file as Bulgarian, no extra rule.', + }, + el: { + label: 'Ελληνικά', + heading: 'Αληθινή εν Θεώ Ζωή', + line: 'Ειρήνη υμίν — Εγώ ειμι το Άλφα και το Ωμέγα.', + note: 'No papyrus cut exists for Greek — this is the fallback face, shown honestly.', + }, +} + +function setScript (code) { + const sample = SAMPLES[code] + if (!sample) return + document.querySelector('#script-heading').textContent = sample.heading + document.querySelector('#script-line').textContent = sample.line + document.querySelector('#script-note').textContent = sample.note + document.querySelector('#script-heading').lang = code + for (const button of document.querySelectorAll('[data-script]')) { + button.setAttribute('aria-pressed', String(button.dataset.script === code)) + } +} + +// -- Breakpoint readout ------------------------------------------------------ + +function trackViewport () { + const output = document.querySelector('#viewport') + const update = () => { + const width = window.innerWidth + const name = width >= 1200 ? 'wide' : width >= 800 ? 'tablet' : 'phone' + output.textContent = `${width}px · ${name}` + } + window.addEventListener('resize', update, { passive: true }) + update() +} + +// -- Wire up ----------------------------------------------------------------- + +renderSwatches() +trackViewport() + +// `?script=bg` opens straight onto a given script, so a particular cut can be +// linked to or screenshotted without clicking. +const requested = new URLSearchParams(location.search).get('script') +setScript(requested in SAMPLES ? requested : 'en') + +for (const button of document.querySelectorAll('[data-script]')) { + button.addEventListener('click', () => setScript(button.dataset.script)) +} + +const boxes = document.querySelector('#toggle-boxes') +boxes.addEventListener('click', () => { + const on = document.body.classList.toggle('show-boxes') + boxes.setAttribute('aria-pressed', String(on)) +}) diff --git a/src/demo/demo.scss b/src/demo/demo.scss new file mode 100644 index 0000000..77e6faf --- /dev/null +++ b/src/demo/demo.scss @@ -0,0 +1,256 @@ +// ----------------------------------------------------------------------------- +// Demo chrome — NOT part of the shipped stylesheet. +// +// Everything here exists to frame the foundation in something that looks like +// the real site (wood ground, papyrus scroll) and to give the page its toolbar +// and swatch grid. It is reached only from src/index.html, which is not a build +// input, so none of it can reach dist/tlig.css. +// +// The scroll frame is deliberately here and not in components/: page layout is +// still on the "not done" list, and a demo is not the place to quietly ship it. +// ----------------------------------------------------------------------------- + +@use '../sass/abstracts' as *; + +$img: '/wp-content/includes/images'; + +// -- The page ground --------------------------------------------------------- + +html { + background: url('#{$img}/background_planche_1920.jpg') top center / contain fixed; +} + +body { + min-height: 100vh; +} + +// The scroll: a head image, a vertically repeating middle, a foot image. +.scroll { + max-width: 1200px; + margin-inline: auto; +} + +.scroll__head, +.scroll__foot { + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.scroll__head { + height: 140px; + background-image: url('#{$img}/page/background-message-entete_cnd.png.webp'); + background-position: bottom; +} + +.scroll__body { + margin-block-start: -1px; + padding: 0 clamp(#{space-raw('md')}, 5vw, 5rem) space('xl'); + background: url('#{$img}/page/background-message-milieux1200x190-4_cnd.png.webp') repeat-y top / 100% auto; +} + +.scroll__foot { + // The top 18px of this image ramp from alpha 15 up to its 242 plateau — it + // fades in. Butted against the body, that ramp composites over bare wood and + // reads as a hard line at the body's bottom edge. The overlap below is the + // measured ramp height plus a little, so the whole fade lands on papyrus. + // + // `top` cannot do this: the element is static, and only a positioned box + // moves. `position`/`z-index` pin the paint order the effect depends on — + // tree order already gives it, but silently. + position: relative; + z-index: 1; + margin-block-start: -20px; + height: 114px; + background-image: url('#{$img}/page/background-message-footer-1200x114_cnd.png.webp'); + background-position: top; +} + +// Below the tablet breakpoint the site swaps in narrower scroll images, which +// are not mirrored into this project. Rather than stretch the wide ones, the +// demo falls back to a plain parchment panel. +@include down('tablet') { + .scroll__head, + .scroll__foot { display: none; } + + .scroll__body { + margin: space('md'); + padding: space('lg'); + border-radius: radius('md'); + background: rgba(247, 240, 224, 0.92); + } +} + +// -- Section scaffolding ----------------------------------------------------- + +.demo-section { + padding-block: space('xl'); + + & + & { + border-block-start: 1px solid rgba(145, 116, 85, 0.35); + } +} + +.demo-label { + display: flex; + align-items: baseline; + gap: space('sm'); + margin-block-end: space('lg'); + font-family: font('body'); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; + color: $brown-600; + + &::after { + content: ''; + flex: 1; + height: 1px; + background: currentColor; + opacity: 0.35; + } +} + +.demo-note { + margin-block-start: space('sm'); + font-family: font('body'); + font-size: 13px; + font-style: italic; + color: $brown-600; +} + +// A row of specimens that should sit side by side and wrap sanely. +.demo-row { + display: flex; + flex-wrap: wrap; + gap: space('md'); + align-items: center; +} + +// -- Swatches ---------------------------------------------------------------- + +.swatch-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: space('sm'); +} + +.swatch { + overflow: hidden; + border: 1px solid rgba(72, 32, 32, 0.15); + border-radius: radius('sm'); + background: $white; + font-family: font('body'); + font-size: 11px; + line-height: 1.5; + + // Checkerboard, so an alpha token reads as translucent rather than as a + // slightly different flat colour. + &__chip { + height: 56px; + background-image: + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%), + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%); + background-size: 12px 12px; + background-position: 0 0, 6px 6px; + } + + &__fill { height: 100%; } + + &__meta { padding: space('2xs') space('xs'); } + &__name { font-weight: 700; color: $brown-900; } + &__value { color: $grey-500; font-variant-numeric: tabular-nums; } +} + +// -- Type specimen table ----------------------------------------------------- + +.specimen { + display: grid; + grid-template-columns: 8rem 1fr; + gap: space('sm') space('lg'); + align-items: baseline; + + @include down('phone') { + grid-template-columns: 1fr; + gap: space('2xs'); + } +} + +.specimen__key { + font-family: font('body'); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: $brown-600; +} + +// -- Dark-ground demo -------------------------------------------------------- + +.demo-dark { + padding: space('lg'); + border-radius: radius('md'); + background: $brown-900; +} + +// -- Toolbar ----------------------------------------------------------------- + +.toolbar { + position: sticky; + top: 0; + z-index: 10; + display: flex; + flex-wrap: wrap; + gap: space('md'); + align-items: center; + padding: space('xs') space('md'); + background: rgba(30, 14, 14, 0.94); + color: #f0e6d2; + font-family: font('body'); + font-size: 12px; + backdrop-filter: blur(6px); + + &__brand { + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; + } + + &__group { + display: flex; + gap: space('2xs'); + align-items: center; + } + + &__spacer { flex: 1; } + + button { + padding: 3px 10px; + border: 1px solid rgba(240, 230, 210, 0.3); + border-radius: radius('pill'); + background: transparent; + color: inherit; + font-size: 12px; + + &[aria-pressed='true'] { + background: #f0e6d2; + color: #1e0e0e; + border-color: #f0e6d2; + } + } + + code { + font-variant-numeric: tabular-nums; + opacity: 0.8; + } +} + +// "Show the boxes" — outlines every component so the structure is visible. +.show-boxes { + .site-footer__col, + .demo-row > *, + .field-group, + .scroll__body > * { + outline: 1px dashed rgba(202, 74, 31, 0.6); + outline-offset: 2px; + } +} diff --git a/src/fonts/lato/Lato-Bold.woff b/src/fonts/lato/Lato-Bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/src/fonts/lato/Lato-Bold.woff differ diff --git a/src/fonts/lato/Lato-Bold.woff2 b/src/fonts/lato/Lato-Bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/src/fonts/lato/Lato-Bold.woff2 differ diff --git a/src/fonts/lato/Lato-Medium.woff b/src/fonts/lato/Lato-Medium.woff new file mode 100644 index 0000000..39d53fd Binary files /dev/null and b/src/fonts/lato/Lato-Medium.woff differ diff --git a/src/fonts/lato/Lato-Medium.woff2 b/src/fonts/lato/Lato-Medium.woff2 new file mode 100644 index 0000000..01792cb Binary files /dev/null and b/src/fonts/lato/Lato-Medium.woff2 differ diff --git a/src/fonts/lato/Lato-MediumItalic.woff b/src/fonts/lato/Lato-MediumItalic.woff new file mode 100644 index 0000000..724962b Binary files /dev/null and b/src/fonts/lato/Lato-MediumItalic.woff differ diff --git a/src/fonts/lato/Lato-MediumItalic.woff2 b/src/fonts/lato/Lato-MediumItalic.woff2 new file mode 100644 index 0000000..1cc7965 Binary files /dev/null and b/src/fonts/lato/Lato-MediumItalic.woff2 differ diff --git a/src/fonts/papyrus/papyrus-bold-bulgarian.woff b/src/fonts/papyrus/papyrus-bold-bulgarian.woff new file mode 100644 index 0000000..1f1d89b Binary files /dev/null and b/src/fonts/papyrus/papyrus-bold-bulgarian.woff differ diff --git a/src/fonts/papyrus/papyrus-bold-cyrillic.woff b/src/fonts/papyrus/papyrus-bold-cyrillic.woff new file mode 100644 index 0000000..a4ac37f Binary files /dev/null and b/src/fonts/papyrus/papyrus-bold-cyrillic.woff differ diff --git a/src/fonts/papyrus/papyrus-bold-cyrillic.woff2 b/src/fonts/papyrus/papyrus-bold-cyrillic.woff2 new file mode 100644 index 0000000..fd30e19 Binary files /dev/null and b/src/fonts/papyrus/papyrus-bold-cyrillic.woff2 differ diff --git a/src/fonts/papyrus/papyrus-bold-hu.woff2 b/src/fonts/papyrus/papyrus-bold-hu.woff2 new file mode 100644 index 0000000..b546f26 Binary files /dev/null and b/src/fonts/papyrus/papyrus-bold-hu.woff2 differ diff --git a/src/fonts/papyrus/papyrus-bold-latin.woff b/src/fonts/papyrus/papyrus-bold-latin.woff new file mode 100644 index 0000000..f07bd23 Binary files /dev/null and b/src/fonts/papyrus/papyrus-bold-latin.woff differ diff --git a/src/fonts/papyrus/papyrus-bold-latin.woff2 b/src/fonts/papyrus/papyrus-bold-latin.woff2 new file mode 100644 index 0000000..7bbed3c Binary files /dev/null and b/src/fonts/papyrus/papyrus-bold-latin.woff2 differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..29e1817 --- /dev/null +++ b/src/index.html @@ -0,0 +1,228 @@ + + + + + + TLIG — stylesheet demo + + + + +
+ tlig.css + + + Script: + + + + + + + + + + + +
+ +
+
+
+ + +
+

A stylesheet without the jigsaw

+
+

Everything below is rendered by src/sass/main.scss — 10 kB + compiled, and every !important in it (four) sits in the + one prefers-reduced-motion block, where overriding author + animation is the point. tlig_en.css has 518. The wood ground and the papyrus scroll around this + text are demo chrome, not part of the stylesheet: page layout + is still on the to-do list, and a demo is not the place to quietly + ship it.

+

The frame is here anyway because a foundation only tells you the + truth when you see it on the ground it has to work on. Brown ink at + 86% opacity looks arbitrary on white and obviously right on papyrus.

+
+
+ + +
+

One family name, four files

+

True Life in God

+

+

+

Use the toolbar to switch script. No CSS changes, no + language class, no second stylesheet — the four papyrus cuts share the + family name papyrus-bold and are separated by + unicode-range, so the browser picks the file by codepoint.

+
+ + +
+

Type scale

+
+ h1 · 34px +

Papyrus-bold, tight leading

+ + h2 · 22px +

Trebuchet, message red, bold

+ + h3 · 20px +

Lato bold, heading ink

+ + h4 · 25px +

Papyrus-bold, the section mark

+ + h5 · 14px +
Lato bold, small heading
+ + h6 · 12px +
Lato bold, uppercase
+ + body · 18px +

Lato at 18px on a 1.5 line height, in + --tlig-color-page-ink: a brown at 86% rather than black, + so it settles onto the papyrus instead of sitting on top of it. Here + is bold text, italic text, and + small print in the muted token.

+ + blockquote +
The translucent panel — --tlig-color-page-panel + at 30% white — with the message-red rule and the asymmetric corner the + site has always had.
+
+

h2 is encoded as Trebuchet / #7a0012 / bold. Avada's + own token says Lato / #333333, but tlig_en.css overrides it + globally with four !importants — so the override is what + actually renders, and the override is what got written down.

+
+ + +
+

Links

+
+

A default link carries no underline — that is for + navigation and cards. Inside .prose, a link + underlines itself without needing a class. The explicit + .link-underline variant is kept + for templates that ask for it, and .link-plain + inherits its colour, for a link wrapping a whole card.

+
+

The live rule is + a:hover { color: #ca4a1f !important } — one global + !important that every later link style then has to fight. + That is where most of the 518 in tlig_en.css start.

+
+ + +
+

Buttons and fields

+
+ View the magazine + Donate + +
+ +
+ + +
+ +

Four ids and classes in the live CSS — + #footer-viewMagazineSpan, .btnSubmitNewsletter, + .btnSubmitDailyMessage, .footer-donateSpan — + repeating the same six declarations with different corner radii. One + class with modifiers replaces them.

+
+ + +
+

Context re-points tokens

+
+

The same markup on a dark ground

+

This block adds one class. It declares no colours of its own: the + dark-ground mixin re-points nine tokens, and this + heading, this body copy, this link, its hover and the + button below are all correct as a result. No dark-specific rule for + any of them exists in the stylesheet.

+

A button, unchanged

+
+

This is the mechanism that replaces the footer's 24 + id selectors: a region declares what its tokens mean, and every + component inside it is already right. The footer below includes the same + mixin — the list of what "dark" means exists once, so the two cannot + drift. An earlier draft of this page caught the bug that proves the + point: .on-dark had re-pointed the links but not + --tlig-color-heading, and the heading above rendered + dark-brown on dark-brown.

+
+ + +
+

Colour tokens (0)

+
+

Generated at runtime from the --tlig-color-* + properties the stylesheet actually emits, not from a hand-kept list — so + this grid cannot drift. Delete a token and its swatch disappears.

+
+ +
+
+
+ + + + + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..bb10af0 --- /dev/null +++ b/src/main.js @@ -0,0 +1,3 @@ +// Entry point. Its only job today is to pull the stylesheet into the build; +// behaviour belongs in src/js/ and gets imported here as it is written. +import './sass/main.scss' diff --git a/src/sass/abstracts/_functions.scss b/src/sass/abstracts/_functions.scss new file mode 100644 index 0000000..2343be6 --- /dev/null +++ b/src/sass/abstracts/_functions.scss @@ -0,0 +1,37 @@ +// ----------------------------------------------------------------------------- +// Token accessors. +// +// These return `var(--tlig-…)` rather than the literal, so a token stays +// overridable at runtime. Use `*-raw()` only where a custom property cannot go +// — inside a media query condition, or in a `@font-face` block. +// ----------------------------------------------------------------------------- + +@use 'sass:map'; +@use 'tokens' as *; + +@function _var($group, $key, $map) { + @if not map.has-key($map, $key) { + @error 'Unknown #{$group} token "#{$key}". Known: #{map.keys($map)}.'; + } + @return var(--#{$prefix}-#{$group}-#{$key}); +} + +@function color($key) { @return _var('color', $key, $colors); } +@function font($key) { @return _var('font', $key, $fonts); } +@function size($key) { @return _var('size', $key, $font-sizes); } +@function leading($key){ @return _var('leading', $key, $line-heights); } +@function weight($key) { @return _var('weight', $key, $font-weights); } +@function space($key) { @return _var('space', $key, $space); } +@function radius($key) { @return _var('radius', $key, $radii); } + +// Compile-time literals, for the few places a custom property is not allowed. +@function color-raw($key) { @return map.get($colors, $key); } +@function font-raw($key) { @return map.get($fonts, $key); } +@function space-raw($key) { @return map.get($space, $key); } + +@function breakpoint($key) { + @if not map.has-key($breakpoints, $key) { + @error 'Unknown breakpoint "#{$key}". Known: #{map.keys($breakpoints)}.'; + } + @return map.get($breakpoints, $key); +} diff --git a/src/sass/abstracts/_index.scss b/src/sass/abstracts/_index.scss new file mode 100644 index 0000000..2fb6dd9 --- /dev/null +++ b/src/sass/abstracts/_index.scss @@ -0,0 +1,5 @@ +// Single import surface for the abstract layer: `@use '../abstracts' as *;` +@forward 'palette'; +@forward 'tokens'; +@forward 'functions'; +@forward 'mixins'; diff --git a/src/sass/abstracts/_mixins.scss b/src/sass/abstracts/_mixins.scss new file mode 100644 index 0000000..ba5e0f6 --- /dev/null +++ b/src/sass/abstracts/_mixins.scss @@ -0,0 +1,89 @@ +@use 'sass:map'; +@use 'tokens' as *; +@use 'functions' as *; + +// -- Media queries ----------------------------------------------------------- +// +// Mobile-first: `up()` opens a floor, `down()` a ceiling, and `down()` subtracts +// 0.02px so the two never both match on the same viewport. The live CSS has +// exactly that overlap bug at 800px. + +@mixin up($name) { + @media (min-width: #{breakpoint($name)}) { @content; } +} + +@mixin down($name) { + @media (max-width: #{breakpoint($name) - 0.02px}) { @content; } +} + +@mixin between($from, $to) { + @media (min-width: #{breakpoint($from)}) and (max-width: #{breakpoint($to) - 0.02px}) { + @content; + } +} + +// -- Type -------------------------------------------------------------------- + +@mixin type($family: 'body', $size: 'base', $leading: 'normal', $weight: 'normal') { + font-family: font($family); + font-size: size($size); + line-height: leading($leading); + font-weight: weight($weight); +} + +// Headings on this site are the display face at a fixed size and tight leading. +@mixin heading($size: '2xl') { + font-family: font('display'); + font-size: size($size); + line-height: leading('tight'); + font-weight: weight('normal'); + color: color('heading'); +} + +// -- Patterns ---------------------------------------------------------------- + +// The translucent panel that carries text over the papyrus background. +@mixin panel($pad: 'lg', $round: 'md') { + padding: space($pad); + background-color: color('page-panel'); + border-radius: radius($round); +} + +// Visible only to screen readers. +@mixin visually-hidden { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +// A focus ring that survives on both the light page and the dark footer. +@mixin focus-ring($offset: 2px) { + outline: 2px solid currentColor; + outline-offset: $offset; +} + +// Re-point the token set for a dark ground. +// +// This is the whole contract in one place: any region that includes it gets +// correct body copy, headings, links and hovers with no further declarations. +// It must cover *every* token that encodes a light-ground assumption — an +// incomplete list is how a heading ends up dark-brown on dark-brown. +@mixin dark-ground { + --#{$prefix}-color-page-ink: #{map.get($colors, 'footer-ink')}; + --#{$prefix}-color-page-ink-strong: #{map.get($colors, 'footer-ink')}; + --#{$prefix}-color-page-ink-muted: #{map.get($colors, 'footer-rule')}; + --#{$prefix}-color-heading: #{map.get($colors, 'footer-ink')}; + --#{$prefix}-color-heading-message: #{map.get($colors, 'footer-link-hover')}; + --#{$prefix}-color-link: #{map.get($colors, 'footer-link')}; + --#{$prefix}-color-link-hover: #{map.get($colors, 'footer-link-hover')}; + --#{$prefix}-color-link-underlined: #{map.get($colors, 'footer-link')}; + --#{$prefix}-color-page-rule: #{map.get($colors, 'footer-rule')}; + + color: color('page-ink'); +} diff --git a/src/sass/abstracts/_palette.scss b/src/sass/abstracts/_palette.scss new file mode 100644 index 0000000..355f98f --- /dev/null +++ b/src/sass/abstracts/_palette.scss @@ -0,0 +1,66 @@ +// ----------------------------------------------------------------------------- +// Raw palette — every literal colour the live site actually uses. +// +// Nothing outside this file may write a hex value. Rules consume the *semantic* +// tokens in `_tokens.scss`, never these names directly: a raw name says what a +// colour looks like, a semantic name says what it is for, and only the second +// survives a redesign. +// +// Source: `Avada-Child-Theme/style.css` (`:root` fusion variables) and +// `Avada-Child-Theme/css/tlig_en.css`, September 2026. +// ----------------------------------------------------------------------------- + +// Browns — the site's "ink"; the wood background and papyrus scroll sit under +// everything, so the darkest text tone is a brown, never black. +$brown-900: #482020; // 74 uses in tlig_en.css — the most common colour +$brown-800: #511a06; // message body text +$brown-600: #917455; // wood / border tone +$brown-300: #c8b29a; // newsletter input field + +// Reds — link, heading and call-to-action family. +$red-900: #7a0012; // message headings (h2) +$red-800: #810008; // Avada --link_color +$red-780: #7e050c; // .linkUnderline +$red-700: #8f0b06; // primary button fill +$red-650: #770000; // mobile header / menu +$red-600: #951111; // blockquote rule +$red-500: #961a07; // alert +$red-400: #ca4a1f; // link hover (global `a:hover` in tlig_en.css) + +// Golds — footer, accents, hover states on dark ground. +$gold-700: #d8ae0b; // donate button +$gold-600: #ffc600; // sliding-bar link hover +$gold-500: #f9d368; // footer dropdown rules +$gold-400: #ffe683; // hover on dark +$gold-300: #ffe7bf; // mobile menu hover +$gold-200: #fff8c4; // mobile menu text +$gold-100: #fbefc9; // footer text + +// Neutrals. +$white: #ffffff; +$grey-050: #fbfbfb; +$grey-100: #f0f0f0; +$grey-200: #eaeaea; +$grey-300: #d6d6d6; +$grey-400: #bbbbbb; +$grey-500: #989898; +$grey-700: #333333; +$black: #0a0a0a; + +// Semantic accents used by notices and status chips. +$green-600: #53961e; +$green-400: #96d865; +$blue-700: #244c80; +$sand-100: #fcf8e3; + +// Alpha tints. Kept as literals rather than computed, because these exact +// values are what ships today and a rounding difference would be a visible +// change dressed up as a refactor. +$ink-86a: rgba(72, 32, 32, 0.86); // body text +$ink-50a: rgba(72, 32, 32, 0.5); +$wood-50a: rgba(145, 116, 85, 0.5); +$wood-20a: rgba(145, 116, 85, 0.2); +$white-80a: rgba(255, 255, 255, 0.8); +$white-30a: rgba(255, 255, 255, 0.3); // the translucent "paper" panels +$shadow-35a: rgba(0, 0, 0, 0.35); +$shadow-15a: rgba(0, 0, 0, 0.15); diff --git a/src/sass/abstracts/_tokens.scss b/src/sass/abstracts/_tokens.scss new file mode 100644 index 0000000..2c22de3 --- /dev/null +++ b/src/sass/abstracts/_tokens.scss @@ -0,0 +1,139 @@ +// ----------------------------------------------------------------------------- +// Semantic tokens. +// +// One map per concern. Each map is emitted as CSS custom properties by +// `base/_root.scss`, so a token can be re-pointed per language or per section +// at runtime without recompiling — which is how the per-language papyrus and +// the dark footer get handled without a second stylesheet. +// ----------------------------------------------------------------------------- + +@use 'palette' as *; + +$prefix: 'tlig'; + +// -- Colour ------------------------------------------------------------------ + +$colors: ( + // Page + 'page-ink': $ink-86a, // body copy + 'page-ink-strong': $brown-900, // headings, emphasis + 'page-ink-muted': $grey-500, + 'page-panel': $white-30a, // translucent card over the scroll + 'page-rule': $grey-300, + + // Links + 'link': $red-800, + 'link-hover': $red-400, + 'link-underlined': $red-780, // the `.link-underline` variant + + // Headings + 'heading': $brown-900, + 'heading-message': $red-900, // h2 inside a message + + // Message typography (the scroll pages) + 'message-body': $brown-800, + 'message-aside': $brown-600, + + // Footer — the one region on a dark ground + 'footer-bg': $brown-900, + 'footer-ink': $gold-100, + 'footer-link': $white, + 'footer-link-hover': $gold-400, + 'footer-rule': $gold-500, + 'footer-panel': $black, + + // Buttons + 'button-bg': $red-700, + 'button-ink': $white, + 'button-ink-hover': $gold-400, + 'button-donate-bg': $gold-700, + 'button-donate-ink': $white, + + // Forms + 'field-bg': $brown-300, + 'field-ink': $white, + 'field-placeholder': rgba(255, 255, 255, 0.8), + + // Status + 'ok': $green-600, + 'ok-soft': $green-400, + 'alert': $red-500, + 'info': $blue-700, + 'notice-bg': $sand-100, + + // Chrome + 'shadow': $shadow-35a, + 'shadow-soft': $shadow-15a, +); + +// -- Type -------------------------------------------------------------------- + +// `papyrus-bold` is the display face; the per-script files are bound to the +// same family name via unicode-range in `base/_fonts.scss`, so a Bulgarian +// heading needs no separate rule. +$fonts: ( + 'display': (papyrus-bold, 'Papyrus', fantasy), + 'body': (Lato, 'Trebuchet MS', Helvetica, sans-serif), + 'message': ('Trebuchet MS', Verdana, sans-serif), + 'nav': (Oswald, 'Lato', sans-serif), +); + +$font-sizes: ( + 'xs': 12px, + 'sm': 14px, + 'base': 18px, // Avada --body_typography-font-size + 'md': 20px, + 'lg': 22px, + 'xl': 25px, + '2xl': 34px, // h1 +); + +$line-heights: ( + 'tight': 1.2, // headings + 'snug': 1.35, + 'normal': 1.5, // body + 'message': 1.33, // 24px on 18px +); + +$font-weights: ( + 'normal': 400, + 'medium': 600, + 'bold': 700, +); + +// -- Space ------------------------------------------------------------------- + +// A 4px-based scale. The live CSS uses arbitrary values (6px, 23px, 46px…); +// those are rounded to the nearest step here. Every such rounding is invisible +// at a glance and is the point of the exercise — one rhythm, not forty. +$space: ( + '3xs': 2px, + '2xs': 4px, + 'xs': 8px, + 'sm': 12px, + 'md': 16px, + 'lg': 24px, + 'xl': 32px, + '2xl': 48px, + '3xl': 64px, +); + +$radii: ( + 'sm': 5px, + 'md': 7px, + 'lg': 20px, + 'pill': 999px, +); + +// -- Layout ------------------------------------------------------------------ + +$site-width: 1200px; // Avada --site_width + +// Named breakpoints. The live CSS switches at 480 and 799/800 with a one-pixel +// gap between `max-width: 800px` and `min-width: 800px` rules; that gap is +// closed here by driving everything from these two numbers. +$breakpoints: ( + 'phone': 480px, + 'tablet': 800px, + 'wide': 1200px, +); diff --git a/src/sass/base/_fonts.scss b/src/sass/base/_fonts.scss new file mode 100644 index 0000000..8e6f187 --- /dev/null +++ b/src/sass/base/_fonts.scss @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------------- +// @font-face. +// +// The live site declares papyrus-bold once, in the compiled Avada stylesheet, +// pointing at absolute ww3.tlig.org URLs — which is why a staging copy renders +// with the fallback face. Here the files are project assets: Vite fingerprints +// them and rewrites the URL, so the bundle is self-contained wherever it lands. +// +// All four papyrus files share one family name and are separated by +// unicode-range. A Bulgarian or Hungarian heading therefore needs no language +// class and no second stylesheet — the browser picks the file by codepoint. +// ----------------------------------------------------------------------------- + +// Latin — the original PapyrusLETBold. +@font-face { + font-family: papyrus-bold; + src: url('/fonts/papyrus/papyrus-bold-latin.woff2') format('woff2'), + url('/fonts/papyrus/papyrus-bold-latin.woff') format('woff'); + font-weight: 400; + font-style: normal; + font-display: swap; + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, + U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, + U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +// Latin Extended-A — Hungarian needs ő and ű, which the original file lacks. +@font-face { + font-family: papyrus-bold; + src: url('/fonts/papyrus/papyrus-bold-hu.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2C60-2C7F, U+A720-A7FF; +} + +// Cyrillic. +@font-face { + font-family: papyrus-bold; + src: url('/fonts/papyrus/papyrus-bold-cyrillic.woff2') format('woff2'), + url('/fonts/papyrus/papyrus-bold-cyrillic.woff') format('woff'); + font-weight: 400; + font-style: normal; + font-display: swap; + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +// Cyrillic Extended — the Bulgarian cut covers the forms the base file misses. +@font-face { + font-family: papyrus-bold; + src: url('/fonts/papyrus/papyrus-bold-bulgarian.woff') format('woff'); + font-weight: 400; + font-style: normal; + font-display: swap; + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F; +} + +// Lato — self-hosted. The live site pulls this from Google via Avada's +// fusion-gfonts cache, 33 @font-face blocks for what is three files. +@font-face { + font-family: Lato; + src: url('/fonts/lato/Lato-Medium.woff2') format('woff2'), + url('/fonts/lato/Lato-Medium.woff') format('woff'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: Lato; + src: url('/fonts/lato/Lato-MediumItalic.woff2') format('woff2'), + url('/fonts/lato/Lato-MediumItalic.woff') format('woff'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: Lato; + src: url('/fonts/lato/Lato-Bold.woff2') format('woff2'), + url('/fonts/lato/Lato-Bold.woff') format('woff'); + font-weight: 700; + font-style: normal; + font-display: swap; +} diff --git a/src/sass/base/_index.scss b/src/sass/base/_index.scss new file mode 100644 index 0000000..153de82 --- /dev/null +++ b/src/sass/base/_index.scss @@ -0,0 +1,5 @@ +@forward 'root'; +@forward 'fonts'; +@forward 'reset'; +@forward 'typography'; +@forward 'links'; diff --git a/src/sass/base/_links.scss b/src/sass/base/_links.scss new file mode 100644 index 0000000..e2e7ae5 --- /dev/null +++ b/src/sass/base/_links.scss @@ -0,0 +1,51 @@ +// ----------------------------------------------------------------------------- +// Links. +// +// The live rule is `a:hover { color: #ca4a1f !important; }` — one global +// `!important` that every later link style then has to fight, which is where +// most of the `!important` inflation in `tlig_en.css` starts. Here the hover +// colour is inherited from a token, so a context that needs a different hover +// (the footer, a button) simply re-points `--tlig-color-link-hover`. +// ----------------------------------------------------------------------------- + +@use '../abstracts' as *; + +a { + color: color('link'); + text-decoration: none; + transition: color 120ms ease-in-out; + + &:hover, + &:focus-visible { + color: color('link-hover'); + } +} + +// Body copy inside prose gets an underline; navigation and cards do not. +.prose a, +.link-underline { + color: color('link-underlined'); + text-decoration: underline; + text-underline-offset: 0.15em; + + &:hover, + &:focus-visible { + color: color('link-hover'); + } +} + +// A link wrapping a card or an image should not tint its contents. +.link-plain { + color: inherit; + + &:hover, + &:focus-visible { + color: inherit; + } +} + +// Any region that re-points the token set gets correct links for free — this +// is what the footer does, via the same mixin. +.on-dark { + @include dark-ground; +} diff --git a/src/sass/base/_reset.scss b/src/sass/base/_reset.scss new file mode 100644 index 0000000..641bf4c --- /dev/null +++ b/src/sass/base/_reset.scss @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------------- +// Reset. Deliberately small — it normalises box model and defaults, and stops. +// ----------------------------------------------------------------------------- + +@use '../abstracts' as *; + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; +} + +body { + margin: 0; +} + +// Headings and paragraphs carry their spacing from the type scale, not from +// whatever the user agent thinks. +h1, h2, h3, h4, h5, h6, +p, figure, blockquote, dl, dd { + margin: 0; +} + +ul, ol { + margin: 0; + padding: 0; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; + height: auto; +} + +input, +button, +textarea, +select { + font: inherit; + color: inherit; +} + +button { + cursor: pointer; +} + +:focus-visible { + @include focus-ring; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/src/sass/base/_root.scss b/src/sass/base/_root.scss new file mode 100644 index 0000000..e520938 --- /dev/null +++ b/src/sass/base/_root.scss @@ -0,0 +1,23 @@ +// ----------------------------------------------------------------------------- +// Emit every token as a CSS custom property. +// +// This is the only place the SASS maps become CSS. Everything downstream reads +// `var(--tlig-…)`, which means a language, a section or a future dark mode can +// re-point a token in one declaration block instead of re-stating the rules. +// ----------------------------------------------------------------------------- + +@use 'sass:map'; +@use 'sass:list'; +@use '../abstracts' as *; + +:root { + @each $key, $value in $colors { --#{$prefix}-color-#{$key}: #{$value}; } + @each $key, $value in $fonts { --#{$prefix}-font-#{$key}: #{list.join((), $value, $separator: comma)}; } + @each $key, $value in $font-sizes { --#{$prefix}-size-#{$key}: #{$value}; } + @each $key, $value in $line-heights{ --#{$prefix}-leading-#{$key}: #{$value}; } + @each $key, $value in $font-weights{ --#{$prefix}-weight-#{$key}: #{$value}; } + @each $key, $value in $space { --#{$prefix}-space-#{$key}: #{$value}; } + @each $key, $value in $radii { --#{$prefix}-radius-#{$key}: #{$value}; } + + --#{$prefix}-site-width: #{$site-width}; +} diff --git a/src/sass/base/_typography.scss b/src/sass/base/_typography.scss new file mode 100644 index 0000000..6af22be --- /dev/null +++ b/src/sass/base/_typography.scss @@ -0,0 +1,103 @@ +// ----------------------------------------------------------------------------- +// Element typography. +// +// Sizes and colours are the ones the site renders today; the difference is that +// each is stated once, from a token, with no `!important`. `tlig_en.css` sets +// h2 globally with four `!important`s to beat Avada's own h2 rule — here there +// is only one h2 rule, so nothing needs beating. +// ----------------------------------------------------------------------------- + +@use '../abstracts' as *; + +body { + @include type('body', 'base', 'normal'); + color: color('page-ink'); + letter-spacing: 0; +} + +h1 { + @include heading('2xl'); + margin-block: space('md') space('xs'); +} + +// Trebuchet, deep red, bold: the message-heading look, applied globally today. +h2 { + font-family: font('message'); + font-size: size('lg'); + line-height: leading('normal'); + font-weight: weight('bold'); + color: color('heading-message'); + margin-block-end: space('sm'); +} + +h3 { + @include type('body', 'md', 'normal', 'bold'); + color: color('heading'); + margin-block-end: space('xs'); +} + +h4 { + @include heading('xl'); + margin-block: space('lg'); +} + +h5 { + @include type('body', 'sm', 'normal', 'bold'); + color: color('heading'); +} + +h6 { + @include type('body', 'xs', 'normal', 'bold'); + color: color('heading'); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +p { + margin-block-end: space('md'); + + &:last-child { + margin-block-end: 0; + } +} + +strong, b { font-weight: weight('bold'); } +em, i { font-style: italic; } + +small { + font-size: size('sm'); + color: color('page-ink-muted'); +} + +hr { + height: 1px; + margin-block: space('xl'); + border: 0; + background-color: color('page-rule'); +} + +blockquote { + @include panel('lg', 'lg'); + border-inline-start: 4px solid color('heading-message'); + border-end-start-radius: space('2xl'); + font-style: italic; +} + +// -- Utility classes --------------------------------------------------------- +// +// The live CSS carries `.font-papyrus`, `.font34`, `.centre`, `.bold`, `.ita` +// and friends, each an `!important` one-liner. Those are template-facing, so +// they are kept — but as plain, unforced declarations. + +.font-display { font-family: font('display'); } +.font-body { font-family: font('body'); } +.font-message { font-family: font('message'); } + +.text-center { text-align: center; } +.text-start { text-align: start; } +.text-justify { text-align: justify; } + +.text-muted { color: color('page-ink-muted'); } +.text-strong { color: color('page-ink-strong'); } + +.visually-hidden { @include visually-hidden; } diff --git a/src/sass/components/_button.scss b/src/sass/components/_button.scss new file mode 100644 index 0000000..6b45090 --- /dev/null +++ b/src/sass/components/_button.scss @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------------- +// Buttons. +// +// The live site has `#footer-viewMagazineSpan`, `.btnSubmitNewsletter`, +// `.btnSubmitDailyMessage` and `.footer-donateSpan` — four ids and classes +// repeating the same six declarations with different corner radii. One class +// with modifiers replaces them. +// ----------------------------------------------------------------------------- + +@use '../abstracts' as *; + +.button { + display: inline-block; + padding: space('xs') space('md'); + border: 1px solid transparent; + border-radius: radius('sm'); + background-color: color('button-bg'); + color: color('button-ink'); + font-family: font('body'); + font-size: size('sm'); + font-weight: weight('bold'); + line-height: 1.2; + letter-spacing: 2px; + text-align: center; + text-decoration: none; + transition: color 120ms ease-in-out, background-color 120ms ease-in-out; + + &:hover, + &:focus-visible { + color: color('button-ink-hover'); + } + + // Donate: gold ground, ink on hover rather than gold-on-gold. + &--donate { + background-color: color('button-donate-bg'); + color: color('button-donate-ink'); + + &:hover, + &:focus-visible { + color: color('page-ink-strong'); + } + } + + // The half of a joined field + button pair. + &--attached { + border-start-start-radius: 0; + border-end-start-radius: 0; + } + + &--block { + display: block; + width: 100%; + } + + &[disabled] { + opacity: 0.5; + cursor: not-allowed; + } +} diff --git a/src/sass/components/_field.scss b/src/sass/components/_field.scss new file mode 100644 index 0000000..70d9f91 --- /dev/null +++ b/src/sass/components/_field.scss @@ -0,0 +1,36 @@ +// ----------------------------------------------------------------------------- +// Text inputs. Modelled on the footer newsletter field (`.inputEmail`), which +// today carries five `!important`s to override Avada's form styling. +// ----------------------------------------------------------------------------- + +@use '../abstracts' as *; + +.field { + min-width: 0; + padding: space('2xs') space('xs'); + border: 1px solid color('field-bg'); + border-radius: radius('sm'); + background-color: color('field-bg'); + color: color('field-ink'); + font-family: font('body'); + font-size: size('sm'); + letter-spacing: 2px; + + &::placeholder { + color: color('field-placeholder'); + opacity: 1; + } + + &--attached { + border-start-end-radius: 0; + border-end-end-radius: 0; + } +} + +// Field + button as one control. +.field-group { + display: flex; + justify-content: center; + + .field { flex: 1 1 auto; max-width: 18rem; } +} diff --git a/src/sass/components/_footer.scss b/src/sass/components/_footer.scss new file mode 100644 index 0000000..4b53898 --- /dev/null +++ b/src/sass/components/_footer.scss @@ -0,0 +1,116 @@ +// ----------------------------------------------------------------------------- +// Footer. +// +// The live footer is 24 id selectors (`#footer-magazine`, `#footer-rightblock`, +// `#footer-whatOptionLink1`…) with hand-tuned percentage widths and a media +// query that resets each one to 100%. This is the same layout expressed as two +// flex containers, and it re-points the link tokens once via `.on-dark` instead +// of colouring each link by hand. +// ----------------------------------------------------------------------------- + +@use '../abstracts' as *; + +.site-footer { + // Scope the dark-ground tokens to the whole region. Everything inside — + // links, headings, buttons — reads the right colour without a single + // footer-specific colour declaration. Shared with `.on-dark` so the two can + // never drift apart. + @include dark-ground; + + padding-block: space('xl'); + background-color: color('footer-bg'); + font-family: font('display'); +} + +.site-footer__inner { + width: min(100% - 2 * #{space-raw('lg')}, var(--#{$prefix}-site-width)); + margin-inline: auto; +} + +// Two rows: magazine + newsletter above, the three link columns below. +.site-footer__row { + display: flex; + flex-wrap: wrap; + gap: space('xl'); + justify-content: space-between; + + & + & { + margin-block-start: space('2xl'); + } +} + +.site-footer__col { + flex: 1 1 18rem; + text-align: center; +} + +// The magazine block is the wide one at desktop; below the tablet breakpoint +// every column is simply full width, which the flex-basis already gives us. +.site-footer__col--wide { + flex: 1 1 28rem; +} + +.site-footer__title { + font-family: font('display'); + font-size: size('md'); + line-height: leading('tight'); + color: color('footer-ink'); + margin-block-end: space('sm'); +} + +.site-footer__text { + font-size: size('md'); + line-height: leading('snug'); +} + +// Social row. +.site-footer__social { + display: flex; + flex-wrap: wrap; + gap: space('md'); + justify-content: center; + list-style: none; + + a { + font-size: size('xl'); + + &:hover, + &:focus-visible { + color: color('button-donate-bg'); + } + } +} + +// The "what is…" dropdown: a bordered list on the dark panel colour. +.site-footer__menu { + // 80% of the column, as the live `#footer-whatDropdown` has it, but capped so + // it does not stretch on a wide screen. + max-width: min(22rem, 80%); + margin-inline: auto; + list-style: none; + text-align: start; + border: 1px solid color('footer-rule'); + border-radius: radius('sm'); + overflow: hidden; + + li + li { + border-block-start: 1px solid color('footer-rule'); + } + + a { + display: block; + padding: space('xs') space('sm'); + background-color: color('footer-panel'); + } +} + +@include down('tablet') { + .site-footer__row { + gap: space('lg'); + } + + .site-footer__col, + .site-footer__col--wide { + flex-basis: 100%; + } +} diff --git a/src/sass/components/_index.scss b/src/sass/components/_index.scss new file mode 100644 index 0000000..ffa3061 --- /dev/null +++ b/src/sass/components/_index.scss @@ -0,0 +1,3 @@ +@forward 'button'; +@forward 'field'; +@forward 'footer'; diff --git a/src/sass/main.scss b/src/sass/main.scss new file mode 100644 index 0000000..a7755fe --- /dev/null +++ b/src/sass/main.scss @@ -0,0 +1,14 @@ +// ----------------------------------------------------------------------------- +// TLIG stylesheet — entry point. +// +// Layer order is load-bearing: tokens first, then element defaults, then +// components. Nothing later in the file needs higher specificity than anything +// earlier, which is the whole reason there is not a single `!important` here. +// +// abstracts/ compile-time only, emits no CSS +// base/ custom properties, @font-face, reset, element typography, links +// components/ single-purpose blocks +// ----------------------------------------------------------------------------- + +@use 'base'; +@use 'components'; diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..0fbf7c6 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,69 @@ +import { defineConfig } from 'vite' +import { resolve } from 'node:path' + +const here = (...p) => resolve(import.meta.dirname, ...p) + +// Two build targets from one config: +// +// vite build → dist/ the deliverable: tlig.css + fonts, +// no HTML. This is what WordPress +// enqueues. +// vite build --mode demo → demo-dist/ a static demo page that loads that +// stylesheet through a real , +// with the site images beside it. +// +// The second exists because in dev Vite injects CSS through JavaScript, so +// anything with a `transition` animates from its unstyled state on first paint. +// That is a dev artifact, but it makes the demo misrepresent the stylesheet. +// The static build paints once, the way a browser will on the real site. +export default defineConfig(({ mode }) => { + const isDemo = mode === 'demo' + + return { + root: 'src', + + // Relative asset URLs — a url() in CSS resolves against the stylesheet's + // own location, so the output can be dropped at any path under wp-content + // without rebuilding. + base: './', + + // `site/` mirrors the live asset tree, so the demo can reference + // /wp-content/includes/images/… exactly as a WordPress template does. + publicDir: here('site'), + + css: { + devSourcemap: true, + }, + + build: { + outDir: isDemo ? here('demo-dist') : here('dist'), + emptyOutDir: true, + + // The demo needs the wood and scroll images; the deliverable does not — + // WordPress already hosts them. + copyPublicDir: isDemo, + + sourcemap: true, + cssCodeSplit: false, + + rollupOptions: { + // The demo is built from the page; the deliverable from the entry + // module, so no demo markup or demo CSS can reach dist/. + input: isDemo ? here('src/index.html') : here('src/main.js'), + + output: { + // Predictable names: WordPress enqueues these paths, so they must not + // change on every build. Fonts keep their own names for the same + // reason — a preload hint in the theme header has to stay valid. + entryFileNames: isDemo ? 'assets/[name]-[hash].js' : 'tlig.js', + assetFileNames: (info) => { + const name = info.names?.[0] ?? info.name ?? '' + if (name.endsWith('.css')) return 'tlig.css' + if (/\.(woff2?|ttf|eot|svg)$/.test(name)) return 'fonts/[name][extname]' + return 'assets/[name]-[hash][extname]' + }, + }, + }, + }, + } +})