From cac02601404419d4ef97017b8175a15d808d8dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Seidel?= Date: Mon, 26 Oct 2026 11:24:40 +0100 Subject: [PATCH 1/2] Pages: theme 0.8.1 --- site/vendor/pages-theme/README.md | 8 +- site/vendor/pages-theme/SOURCE | 4 +- .../pages-theme/components/Header.astro | 153 +++++++++++------- site/vendor/pages-theme/components/Hero.astro | 21 ++- .../pages-theme/components/Install.astro | 21 ++- site/vendor/pages-theme/package.json | 2 +- 6 files changed, 135 insertions(+), 74 deletions(-) diff --git a/site/vendor/pages-theme/README.md b/site/vendor/pages-theme/README.md index 9884935..9a23edb 100644 --- a/site/vendor/pages-theme/README.md +++ b/site/vendor/pages-theme/README.md @@ -123,7 +123,7 @@ Import from `@casoon/pages-theme/components` in `.astro` files; available direct | Component | Use | | --- | --- | | `Hero` | Title, lead, default slot (install, badges), `output` slot (real output); `layout`, `command` and `tone` pick the variant | -| `Install` | Install command per ecosystem, CSS-only tabs, copy button | +| `Install` | Install command per ecosystem, CSS-only tabs, copy button; long commands wrap | | `Badge`, `Badges` | Static badges (no external badge services) | | `Facts` | Verifiable key figures | | `Features` | 3–5 core points | @@ -159,6 +159,9 @@ files. | Colour field | `tone="invert"` on any layout | `Terminal`, or whatever the project shows | | Editorial | `layout="editorial"` | `FileSet view="rail"` — names and one line each | +The split layout gives the text column a little more than half from 900 px up, with a title +size that fits about 40 characters on two lines at 1440 px; below 900 px text and output stack. + The stack layout reads as a pipeline from top to bottom: the `source` slot (usually a `CodeBlock` with the configuration), the `command` pill, then the output — each over the full width of the container. Side by side the three do not fit inside `--max`: the source is cut off @@ -272,4 +275,5 @@ Grundlage ist das Claude-Design-Projekt „CASOON Pages Template“. Bewusst ge - Footer mit echtem CASOON-Logo (hell/dunkel) statt Farbquadrat; alle `#`-Links durch echte Ziele ersetzt. - Tabs ohne JavaScript (Radio-Buttons); „Last updated“ in der Doku entfällt vorerst. -- Auf schmalen Bildschirmen rückt die Navigation in eine zweite Zeile. +- Auf schmalen Bildschirmen rückt die Navigation in eine zweite, einzeilige Zeile, die seitlich + scrollt, wenn sie nicht passt; der Theme-Umschalter bleibt neben dem Namen. diff --git a/site/vendor/pages-theme/SOURCE b/site/vendor/pages-theme/SOURCE index 54ba735..0a12e41 100644 --- a/site/vendor/pages-theme/SOURCE +++ b/site/vendor/pages-theme/SOURCE @@ -1,5 +1,5 @@ -@casoon/pages-theme 0.8.0 -source: gh-pages-template@a03f9ef +@casoon/pages-theme 0.8.1 +source: gh-pages-template@0df964f synced: 2026-10-26 Vendored copy. Do not edit here: change the theme in gh-pages-template, diff --git a/site/vendor/pages-theme/components/Header.astro b/site/vendor/pages-theme/components/Header.astro index 0b5f315..512c10d 100644 --- a/site/vendor/pages-theme/components/Header.astro +++ b/site/vendor/pages-theme/components/Header.astro @@ -14,44 +14,46 @@ const nav = [ ]; --- -
+
{config.name} {config.version && v{config.version}} - { - websiteHost && ( - - {websiteHost} - - ) - } - + +
@@ -102,11 +104,19 @@ const nav = [ border-radius: 999px; color: var(--text); font-size: 13.5px; + white-space: nowrap; } .website:hover { border-color: var(--accent); color: var(--heading); } + .links { + display: flex; + flex: 1; + align-items: center; + gap: 20px; + min-width: 0; + } nav { display: flex; align-items: center; @@ -126,6 +136,7 @@ const nav = [ border-radius: 8px; color: var(--text); font-size: 14.5px; + white-space: nowrap; } nav a:hover { background: var(--bg-deep); @@ -147,7 +158,8 @@ const nav = [ justify-content: center; width: 32px; height: 32px; - margin-left: 4px; + /* Close to the GitHub link, as if it were the last nav entry: 20px gap - 14px. */ + margin-left: -14px; border: 1px solid var(--border); border-radius: 9px; background: none; @@ -172,21 +184,35 @@ const nav = [ :global([data-theme='dark']) .icon-moon { display: none; } - /* Narrow screens: navigation moves to a second row. */ + /* Narrow screens: name and theme toggle on the first row, the links on a second row that + stays one line and scrolls sideways when it does not fit — no wrapped labels, nothing cut + off. A header with a website pill or a demo entry needs the second row earlier. */ @media (max-width: 640px) { :global(:root) { - --header-h: 100px; + --header-h: 92px; } .inner { flex-wrap: wrap; - gap: 4px 8px; - height: auto; - min-height: var(--header-h); - padding: 10px 16px; + align-content: center; + gap: 2px 8px; + height: var(--header-h); + padding: 0 16px; + } + .links { + flex: 0 0 calc(100% + 32px); + order: 3; + gap: 8px; + margin: 0 -16px; + /* Room for the focus outline, which the scroll container would clip. */ + padding: 3px 8px; + overflow-x: auto; + scrollbar-width: none; + } + .theme { + margin-left: auto; } nav { - width: 100%; - margin-left: -8px; + margin-left: 0; } .sep { display: none; @@ -194,34 +220,39 @@ const nav = [ nav a { padding: 7px 8px; } - .theme { - margin-left: auto; - } } - /* With the website pill one row needs more room, so the second row starts earlier. */ @media (max-width: 900px) { - :global(:root:has(.header[data-website])) { - --header-h: 100px; + :global(:root:has(.header[data-wide])) { + --header-h: 92px; } - [data-website] .inner { + [data-wide] .inner { flex-wrap: wrap; - gap: 4px 8px; - height: auto; - min-height: var(--header-h); - padding: 10px 16px; + align-content: center; + gap: 2px 8px; + height: var(--header-h); + padding: 0 16px; + } + [data-wide] .links { + flex: 0 0 calc(100% + 32px); + order: 3; + gap: 8px; + margin: 0 -16px; + /* Room for the focus outline, which the scroll container would clip. */ + padding: 3px 8px; + overflow-x: auto; + scrollbar-width: none; } - [data-website] nav { - width: 100%; - margin-left: -8px; + [data-wide] .theme { + margin-left: auto; + } + [data-wide] nav { + margin-left: 0; } - [data-website] .sep { + [data-wide] .sep { display: none; } - [data-website] nav a { + [data-wide] nav a { padding: 7px 8px; } - [data-website] .theme { - margin-left: auto; - } } diff --git a/site/vendor/pages-theme/components/Hero.astro b/site/vendor/pages-theme/components/Hero.astro index e94c5b5..def2aa9 100644 --- a/site/vendor/pages-theme/components/Hero.astro +++ b/site/vendor/pages-theme/components/Hero.astro @@ -141,12 +141,22 @@ const hasSource = Astro.slots.has('source'); min-width: 0; } + /* Text gets a little more than half and a slightly smaller title, so a title of about 40 + characters fits on two lines at desktop width; in two equal halves it took three. */ .is-split .inner { display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); - gap: 56px; + gap: 44px; align-items: center; } + @media (min-width: 900px) { + .is-split .inner { + grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); + gap: 48px; + } + .is-split h1 { + font-size: clamp(36px, 3.4vw, 48px); + } + } .is-stack .head { text-align: center; @@ -215,10 +225,15 @@ const hasSource = Astro.slots.has('source'); gap: 32px; margin-bottom: 44px; } + /* Lead and actions share the row about evenly; the actions keep the install box's width, so + the badges go below it instead of squeezing the lead into a narrow column. */ .is-editorial .lead { - flex: 1 1 300px; + flex: 1 1 380px; margin: 0; } + .is-editorial .actions { + flex: 0 1 470px; + } /* Dark brand ground. Child components follow through the tokens. */ .invert { diff --git a/site/vendor/pages-theme/components/Install.astro b/site/vendor/pages-theme/components/Install.astro index 1d83592..426ebc7 100644 --- a/site/vendor/pages-theme/components/Install.astro +++ b/site/vendor/pages-theme/components/Install.astro @@ -37,8 +37,16 @@ const uid = `install-${commands.map((c) => c.label.replace(/\W/g, '')).join('-') - {/* Long commands scroll horizontally; tabindex keeps the region keyboard-reachable. */} - {c.command} + {/* Long commands wrap instead of being cut off, so all of it stays visible and selectable. + after each slash lets a URL break between its segments. */} + + {c.command.split(/(?<=\/)(?!\/)/).map((part, j) => ( + <> + {j > 0 && } + {part} + + ))} + @@ -82,7 +90,7 @@ const uid = `install-${commands.map((c) => c.label.replace(/\W/g, '')).join('-') } .panel { display: none; - align-items: center; + align-items: baseline; gap: 12px; padding: 14px 16px; } @@ -103,12 +111,15 @@ const uid = `install-${commands.map((c) => c.label.replace(/\W/g, '')).join('-') font-family: var(--font-mono); font-size: 13px; } + /* Breaks at spaces first; a URL longer than the line breaks anywhere. */ code { flex: 1; - overflow: auto; + min-width: 0; color: var(--heading); font-size: 13.5px; - white-space: nowrap; + line-height: 1.6; + white-space: pre-wrap; + overflow-wrap: anywhere; } .copy { flex-shrink: 0; diff --git a/site/vendor/pages-theme/package.json b/site/vendor/pages-theme/package.json index 1035e42..783014b 100644 --- a/site/vendor/pages-theme/package.json +++ b/site/vendor/pages-theme/package.json @@ -1,6 +1,6 @@ { "name": "@casoon/pages-theme", - "version": "0.8.0", + "version": "0.8.1", "private": true, "description": "Shared Astro theme for the GitHub Pages sites of CASOON open-source projects", "type": "module", From 9e20912ef1bac9fefdafe035185fc5ad9d89433b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Seidel?= Date: Mon, 26 Oct 2026 11:50:44 +0100 Subject: [PATCH 2/2] Pages: theme 0.8.2 --- site/vendor/pages-theme/README.md | 2 +- site/vendor/pages-theme/SOURCE | 4 +-- .../pages-theme/components/Install.astro | 29 ++++++++++++++----- site/vendor/pages-theme/package.json | 2 +- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/site/vendor/pages-theme/README.md b/site/vendor/pages-theme/README.md index 9a23edb..0aed3a4 100644 --- a/site/vendor/pages-theme/README.md +++ b/site/vendor/pages-theme/README.md @@ -123,7 +123,7 @@ Import from `@casoon/pages-theme/components` in `.astro` files; available direct | Component | Use | | --- | --- | | `Hero` | Title, lead, default slot (install, badges), `output` slot (real output); `layout`, `command` and `tone` pick the variant | -| `Install` | Install command per ecosystem, CSS-only tabs, copy button; long commands wrap | +| `Install` | Install command per ecosystem, CSS-only tabs, copy button; long commands wrap at spaces and after a `/` | | `Badge`, `Badges` | Static badges (no external badge services) | | `Facts` | Verifiable key figures | | `Features` | 3–5 core points | diff --git a/site/vendor/pages-theme/SOURCE b/site/vendor/pages-theme/SOURCE index 0a12e41..3ad3dab 100644 --- a/site/vendor/pages-theme/SOURCE +++ b/site/vendor/pages-theme/SOURCE @@ -1,5 +1,5 @@ -@casoon/pages-theme 0.8.1 -source: gh-pages-template@0df964f +@casoon/pages-theme 0.8.2 +source: gh-pages-template@d535677 synced: 2026-10-26 Vendored copy. Do not edit here: change the theme in gh-pages-template, diff --git a/site/vendor/pages-theme/components/Install.astro b/site/vendor/pages-theme/components/Install.astro index 426ebc7..d4e58ae 100644 --- a/site/vendor/pages-theme/components/Install.astro +++ b/site/vendor/pages-theme/components/Install.astro @@ -11,6 +11,17 @@ interface Props { const s = t(config.lang); const { commands, label = s.installLabel } = Astro.props; const uid = `install-${commands.map((c) => c.label.replace(/\W/g, '')).join('-')}`; + +/** Splits a command into the units a line may break between: words, and inside a word the path + segments after each "/" ("https://" is one). "./", "../", "~/" and a leading "/" stay with + what follows. Spaces are kept as separate parts. */ +const units = (command: string) => + command + .split(/( +)/) + .filter(Boolean) + .map((word) => + word.startsWith(' ') ? word : word.split(/(?<=\/)(?!\/)(? @@ -38,14 +49,12 @@ const uid = `install-${commands.map((c) => c.label.replace(/\W/g, '')).join('-') $ {/* Long commands wrap instead of being cut off, so all of it stays visible and selectable. - after each slash lets a URL break between its segments. */} + Each unit is an inline-block, so a line breaks only at spaces and after a "/", never at a + hyphen; a unit wider than the box still breaks inside as a last resort. */} - {c.command.split(/(?<=\/)(?!\/)/).map((part, j) => ( - <> - {j > 0 && } - {part} - - ))} + {units(c.command).map((part) => + typeof part === 'string' ? part : part.map((unit) => {unit}), + )}