From 1dc14e86ce4ccf16d6919633bc91b3a6e36de2d0 Mon Sep 17 00:00:00 2001 From: Derek Lewis Date: Sun, 23 Aug 2026 01:27:47 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=96=E2=9C=A8=EF=BC=9Agive=20the=20docs?= =?UTF-8?q?=20pages=20the=20site's=20design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The index was rebuilt and the fourteen pages under it were not, so every link out of it landed on a 48ch column wrapped in `.grid`, `.unit`, `.four-fifths` and `.improve`, none of which are styled anywhere. Four things now render that never have. The Key Point each handbook page carries, which only the index was reading. The attribution the colons page owes Google, whose only renderer was a layout no page loads. A way back to the index, which the pages had no link to. And the recommended and not-recommended examples as a distinguishable pair, having until now been the same plain text twice. The terms each page states come from the licence files beside it: CC BY-SA 4.0 under the handbook, CC BY-NC-SA 4.0 elsewhere. Signed-off-by: Derek Lewis Assisted-by: Claude-Code:claude-opus-5 Assisted-by: Claude-Code:claude-fable-5 --- .github/workflows/deploy.yml | 4 + _assets/styles/_docs-page.scss | 498 ++++++++++++++++++ _assets/styles/main.scss | 1 + _data/sections.json | 6 + .../assets}/img/cc/COPYING.md | 0 .../assets}/img/cc/cc-by.svg | 0 .../assets}/img/cc/cc-heart-filled.svg | 0 .../assets}/img/cc/cc-heart.svg | 0 .../assets}/img/cc/cc-logo.svg | 0 .../assets}/img/cc/cc-nc-eu.svg | 0 .../assets}/img/cc/cc-nc-jp.svg | 0 .../assets}/img/cc/cc-nc.svg | 0 .../assets}/img/cc/cc-nd.svg | 0 .../assets}/img/cc/cc-pd.svg | 0 .../assets}/img/cc/cc-pdm.svg | 0 .../assets}/img/cc/cc-remix.svg | 0 .../assets}/img/cc/cc-sa.svg | 0 .../assets}/img/cc/cc-sampling-plus.svg | 0 .../assets}/img/cc/cc-sampling.svg | 0 .../assets}/img/cc/cc-share.svg | 0 .../assets}/img/cc/cc-zero.svg | 0 _includes/assets/img/octicons/COPYING.md | 44 ++ _includes/assets/img/octicons/pencil.svg | 10 + _includes/breadcrumb.liquid | 38 ++ _includes/key-point.liquid | 3 - _layouts/deep-page.liquid | 26 +- _layouts/docs.liquid | 138 ++++- .../handbook-content-g-devdocs-style.liquid | 120 ----- collections/_docs/README.md | 37 +- .../_docs/handbook/style/capitalization.md | 3 +- .../_docs/handbook/style/code-in-text.md | 3 +- .../_docs/handbook/style/code-samples.md | 3 +- .../_docs/handbook/style/code-syntax.md | 1 + collections/_docs/handbook/style/colons.md | 3 +- .../_docs/handbook/style/commit-messages.md | 3 +- collections/_docs/handbook/style/dashes.md | 3 +- collections/_docs/handbook/style/lists.md | 3 +- .../style/people-person-first-language.md | 3 +- eleventy.config.mjs | 82 +++ package.json | 1 + pnpm-lock.yaml | 12 + 41 files changed, 888 insertions(+), 157 deletions(-) create mode 100644 _assets/styles/_docs-page.scss create mode 100644 _data/sections.json rename {_assets => _includes/assets}/img/cc/COPYING.md (100%) rename {_assets => _includes/assets}/img/cc/cc-by.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-heart-filled.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-heart.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-logo.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-nc-eu.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-nc-jp.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-nc.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-nd.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-pd.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-pdm.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-remix.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-sa.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-sampling-plus.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-sampling.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-share.svg (100%) rename {_assets => _includes/assets}/img/cc/cc-zero.svg (100%) create mode 100644 _includes/assets/img/octicons/COPYING.md create mode 100644 _includes/assets/img/octicons/pencil.svg create mode 100644 _includes/breadcrumb.liquid delete mode 100644 _includes/key-point.liquid delete mode 100644 _layouts/handbook-content-g-devdocs-style.liquid diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6400155e4..f2e52791d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -25,6 +25,10 @@ jobs: steps: - name: Check out project repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # The docs pages date themselves from when git last touched them, + # which a single-commit checkout cannot answer. + fetch-depth: 0 - name: Set up Node.js runtime uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/_assets/styles/_docs-page.scss b/_assets/styles/_docs-page.scss new file mode 100644 index 000000000..595fe0ccb --- /dev/null +++ b/_assets/styles/_docs-page.scss @@ -0,0 +1,498 @@ +@use 'sass:color'; + +// A documentation page: the head band and the foot read at the same 1200px +// width as the rest of the site; the prose between them sits at a reading +// measure instead, because these fourteen pages are for reading. Covers both +// docs.liquid's thirteen pages and deep-page.liquid's /about/vision/, which +// carries the same doc-page body class but renders no breadcrumb, no Key +// Point, and no foot. + +// Colours come from _sublime-theme.scss and reuse the exact tones +// _news-item.scss and _docs.scss give a reading page and a quiet section, so +// a handbook page agrees with the rest of the site on what a link, a muted +// caption, and an aside look like. + +$doc-wrap: 1200px; +$doc-gutter: clamp(1rem, 4vw, 3rem); +$doc-link: shade-color($primary, 25%); +$doc-muted: #4b433c; +$doc-hairline: rgb(167 151 139 / 55%); + +// Raw $tertiary is too pale to serve as a rule on parchment; shaded, it +// still reads as the same neutral and holds up as one. +$doc-rule: shade-color($tertiary, 20%); + +// The wash _docs.scss and _news.scss give a quiet section, reused here at +// prose scale: a callout's fill, an inline code chip, and the flash a +// heading gets when it is the page's `:target`. +$doc-quiet: color.mix($tertiary, $body-bg, 12%); + +// $quaternary as solid text on parchment fails AA; shaded, it clears it. The +// unshaded colour still suits the "not recommended" rule below, which only +// has to clear the lower non-text minimum. +$doc-bad-text: shade-color($quaternary, 25%); + +// Scoped to `main` to keep the resets off the navbar and footer, which are +// its siblings under the same body class. The padding replaces what +// `main > .container` gives the footer elsewhere. +.doc-page main { + padding-bottom: 104px; +} + +.doc-page main a { + color: $doc-link; +} + +.doc-page main :focus-visible { + outline: 2px solid $body-color; + outline-offset: 2px; +} + +.doc-page .wrap { + max-width: $doc-wrap; + padding-inline: $doc-gutter; + margin-inline: auto; +} + +/* --- Head: breadcrumb, title, Key Point --------------------------------- + A flat stack, not a grid: unlike the masthead, there is no second panel + beside it. +*/ + +// Ink, so it carries on from the navbar rather than leaving a pale band +// between the two. +.doc-page .doc-head { + padding-top: calc(#{$navbar-height} + clamp(2rem, 5vw, 3.5rem)); + padding-bottom: clamp(2.25rem, 5vw, 3.5rem); + color: $body-bg; + background-color: $body-color; +} + +// The band opens flush, with no borrowed margin above its first element. +.doc-page .doc-head .wrap > :first-child { + margin-top: 0; +} + +.doc-page .breadcrumb-list { + display: flex; + flex-wrap: wrap; + gap: 0.25rem 0; + padding: 0; + margin: 0; + font-family: $font-family-monospace; + font-size: 0.8125rem; + list-style: none; +} + +.doc-page .breadcrumb-step + .breadcrumb-step::before { + margin-inline: 0.4375em; + content: '›'; +} + +// Links on the ink. `.doc-page main a` is (0,1,2), so this needs `main` too. +.doc-page main .breadcrumb-step a { + color: rgb(231 231 214 / 82%); +} + +.doc-page main .breadcrumb-step a:hover { + color: $body-bg; +} + +// _custom.scss makes every h1 a flex row and draws a rule after it, which is +// right for a page heading and wrong at this scale. +.doc-page .doc-title { + display: block; + max-width: 40ch; + margin-top: 1rem; + font-size: clamp(1.875rem, 4.5vw, 2.75rem); + font-weight: 700; + line-height: 1.08; + letter-spacing: -0.02em; +} + +.doc-page .doc-title::after { + content: none; +} + +// The measure sits on the element that carries the font-size, so the `ch` it +// reads is the one the Key Point is actually set in. +.doc-page .doc-key { + max-width: 42ch; + margin-top: 1rem; + font-size: clamp(1rem, 1.6vw, 1.0625rem); + line-height: 1.6; + color: rgb(231 231 214 / 76%); +} + +/* --- Body: the prose ------------------------------------------------------ + The measure sits on `.doc-prose` itself, alongside its own font-size, for + the same reason. +*/ + +.doc-page .doc-body { + padding-block: clamp(2.5rem, 6vw, 4rem); +} + +// contributing.md autolinks a couple of bare GitHub label URLs; as visible +// link text they are one unbroken token long enough to push past 390px. +.doc-page .doc-prose { + max-width: 65ch; + margin-inline: auto; + font-size: 1.0625rem; + line-height: 1.75; + overflow-wrap: break-word; +} + +.doc-page .doc-prose > :first-child { + margin-top: 0; +} + +.doc-page .doc-prose > :last-child { + margin-bottom: 0; +} + +.doc-page .doc-prose p, +.doc-page .doc-prose ul, +.doc-page .doc-prose ol, +.doc-page .doc-prose table, +.doc-page .doc-prose pre { + margin-block: 1.375em; +} + +.doc-page .doc-prose h2, +.doc-page .doc-prose h3 { + font-weight: 700; + line-height: 1.3; +} + +.doc-page .doc-prose h2 { + margin-top: 2.5em; + margin-bottom: 0.75em; + font-size: 1.5rem; + letter-spacing: -0.01em; +} + +.doc-page .doc-prose h3 { + margin-top: 1.75em; + margin-bottom: 0.625em; + font-size: 1.1875rem; +} + +// Headings carry an id and no visible affordance; a cross-page anchor link +// (the handbook is full of them) needs something to confirm on arrival. +.doc-page .doc-prose :is(h2, h3):target { + padding-inline-start: 0.625em; + margin-inline-start: -0.625em; + background-color: $doc-quiet; +} + +.doc-page .doc-prose ul, +.doc-page .doc-prose ol { + padding-inline-start: 1.25em; +} + +.doc-page .doc-prose li + li { + margin-top: 0.5em; +} + +// contributing.md's Developer Certificate of Origin list is loose (each item +// is its own paragraph); the item, not the paragraph inside it, carries the +// rhythm. +.doc-page .doc-prose li > p { + margin-block: 0; +} + +.doc-page .doc-prose li > ul, +.doc-page .doc-prose li > ol { + margin-block: 0.5em 0; +} + +.doc-page .doc-prose strong { + font-weight: 700; +} + +.doc-page .doc-prose em { + font-style: italic; +} + +.doc-page .doc-prose a { + text-decoration: underline; +} + +.doc-page .doc-prose abbr[title] { + text-decoration: underline dotted; + cursor: help; +} + +// Reboot gives bare `code` Bootstrap's own pink (`--bs-code-color`); this is +// prose, not a diff, so it reads in the page's own ink instead. +.doc-page .doc-prose code { + padding: 0.15em 0.375em; + font-family: $font-family-monospace; + font-size: 0.875em; + color: $body-color; + background-color: $doc-quiet; +} + +.doc-page .doc-prose pre { + padding: 1rem; + overflow-x: auto; + background-color: $secondary; +} + +.doc-page .doc-prose pre code { + padding: 0; + font-size: 0.875em; + background-color: transparent; +} + +/* --- Examples: Google's recommended / not-recommended pairs -------------- + `.example` always wraps exactly one `.compare-better` or `.compare-worse` + span; `:has()` reads which, so the rule and the label agree without a + second class on the paragraph. +*/ + +.doc-page .doc-prose .example { + padding-inline-start: 1.25em; + margin-block: 1.375em; + border-inline-start: 3px solid $doc-rule; +} + +// A pair reads as one comparison, not two paragraphs in the ordinary rhythm. +.doc-page .doc-prose .example + .example { + margin-block-start: 0.625em; +} + +.doc-page .doc-prose .example:has(.compare-better) { + border-inline-start-color: $primary; +} + +.doc-page .doc-prose .example:has(.compare-worse) { + border-inline-start-color: $quaternary; +} + +.doc-page .doc-prose .compare-better, +.doc-page .doc-prose .compare-worse { + font-weight: 700; +} + +.doc-page .doc-prose .compare-better { + color: $doc-link; +} + +.doc-page .doc-prose .compare-worse { + color: $doc-bad-text; +} + +/* --- Callouts ------------------------------------------------------------- + markdown-it-github-alerts turns `> [!NOTE]` into a titled block. The rule + and the title carry the kind, so the wash stays the same for all of them + and the text keeps one contrast against one ground. +*/ + +.doc-page .doc-prose .markdown-alert { + padding: 0.875rem 1.125rem; + margin-block: 1.375em; + color: $doc-muted; + background-color: $doc-quiet; + border-inline-start: 3px solid $doc-rule; +} + +.doc-page .doc-prose .markdown-alert > :first-child { + margin-top: 0; +} + +.doc-page .doc-prose .markdown-alert > :last-child { + margin-bottom: 0; +} + +.doc-page .doc-prose .markdown-alert-title { + display: flex; + gap: 0.4375rem; + align-items: center; + margin-bottom: 0.375em; + font-family: $font-family-monospace; + font-size: 0.75rem; + font-weight: 700; + color: $body-color; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +// The octicons carry no fill of their own, so they take the title's colour +// and change with it. +.doc-page .doc-prose .markdown-alert-title svg { + flex: none; + width: 1rem; + height: 1rem; + fill: currentcolor; +} + +// Warning and caution are the two that mean stop and read. They take the +// same red the not-recommended examples use. +.doc-page .doc-prose .markdown-alert-warning, +.doc-page .doc-prose .markdown-alert-caution { + border-inline-start-color: $quaternary; +} + +.doc-page .doc-prose .markdown-alert-warning .markdown-alert-title, +.doc-page .doc-prose .markdown-alert-caution .markdown-alert-title { + color: $doc-bad-text; +} + +// A pure-parchment chip on the callout's wash reads as a cutout rather than +// a second, muddier wash stacked on the first. +.doc-page .doc-prose .markdown-alert code { + background-color: $body-bg; +} + +/* --- Tables --------------------------------------------------------------- + `display: block` hands the table its own scroll container without a + wrapper element markdown never gives it; the internal table layout is + unaffected, since `border-collapse` still reaches `th`/`td`. +*/ + +.doc-page .doc-prose table { + display: block; + width: max-content; + max-width: 100%; + margin-block: 1.375em; + overflow-x: auto; + font-size: 0.9375rem; + border-collapse: collapse; +} + +.doc-page .doc-prose th, +.doc-page .doc-prose td { + padding: 0.5rem 0.875rem; + text-align: left; + border-bottom: 1px solid $doc-hairline; +} + +.doc-page .doc-prose th { + font-weight: 700; + border-bottom: 2px solid $body-color; +} + +/* --- Footnotes -------------------------------------------------------- */ + +.doc-page .doc-prose .footnote-ref a { + text-decoration: none; +} + +.doc-page .doc-prose .footnotes-sep { + height: 0; + margin-block: 2.25em 1.25em; + border: 0; + border-top: 1px solid $doc-hairline; +} + +.doc-page .doc-prose .footnotes { + font-size: 0.9375rem; + color: $doc-muted; +} + +.doc-page .doc-prose .footnotes-list { + padding-inline-start: 1.25em; +} + +.doc-page .doc-prose .footnote-backref { + margin-inline-start: 0.375em; + text-decoration: none; +} + +/* --- Foot: licence, attribution, edit link -------------------------------- + Absent on /about/vision/: deep-page.liquid renders no foot at all. +*/ + +.doc-page .doc-foot { + padding-block: clamp(1.5rem, 3vw, 2rem); + border-top: 1px solid $doc-hairline; +} + +.doc-page .doc-foot-grid { + display: flex; + flex-wrap: wrap; + gap: 0.75rem 1.5rem; + align-items: baseline; + justify-content: space-between; +} + +// The buttons name the same terms the sentence under them states, so they +// sit at caption weight rather than as a badge. +.doc-page .doc-cc { + display: flex; + gap: 0.3125rem; + align-items: center; + margin-bottom: 0.5rem; + color: $doc-muted; +} + +.doc-page .doc-cc svg { + width: 1.375rem; + height: 1.375rem; +} + +.doc-page .doc-terms { + max-width: 58ch; + font-size: 0.8125rem; + line-height: 1.6; + color: $doc-muted; +} + +.doc-page .doc-foot-aside { + display: flex; + flex-wrap: wrap; + gap: 0.25rem 1.25rem; + align-items: baseline; +} + +.doc-page .doc-updated { + font-family: $font-family-monospace; + font-size: 0.75rem; + color: $doc-muted; +} + +// The one thing in the foot a reader can act on, so it takes a button's +// shape while the terms beside it stay at caption weight. +.doc-page .doc-edit a { + display: inline-flex; + gap: 0.4375rem; + align-items: center; + padding: 0.4375rem 0.875rem; + font-family: $font-family-monospace; + font-size: 0.8125rem; + font-weight: 700; + text-decoration: none; + border: 1px solid $doc-link; +} + +.doc-page .doc-edit a:hover { + color: $body-bg; + background-color: $doc-link; +} + +.doc-page .doc-edit svg { + flex: none; + width: 0.9375rem; + height: 0.9375rem; + fill: currentcolor; +} + +.doc-page .doc-edit { + flex: none; + font-family: $font-family-monospace; + font-size: 0.8125rem; +} + +/* --- Narrow --------------------------------------------------------------- + Below 480px the foot's two columns no longer both fit on one line even + wrapped; `space-between` would otherwise justify a single stacked item + away from the text it follows. +*/ + +@media (width < 480px) { + .doc-page .doc-foot-grid { + justify-content: flex-start; + } +} diff --git a/_assets/styles/main.scss b/_assets/styles/main.scss index 4f2ea309c..42c93e07d 100644 --- a/_assets/styles/main.scss +++ b/_assets/styles/main.scss @@ -18,6 +18,7 @@ @import '../../node_modules/bootstrap/scss/pagination'; @import 'about'; @import 'docs'; +@import 'docs-page'; @import 'error'; @import 'home'; @import 'nav'; diff --git a/_data/sections.json b/_data/sections.json new file mode 100644 index 000000000..8b19302eb --- /dev/null +++ b/_data/sections.json @@ -0,0 +1,6 @@ +{ + "handbook": { + "name": "Style handbook", + "url": "/docs/#handbook" + } +} diff --git a/_assets/img/cc/COPYING.md b/_includes/assets/img/cc/COPYING.md similarity index 100% rename from _assets/img/cc/COPYING.md rename to _includes/assets/img/cc/COPYING.md diff --git a/_assets/img/cc/cc-by.svg b/_includes/assets/img/cc/cc-by.svg similarity index 100% rename from _assets/img/cc/cc-by.svg rename to _includes/assets/img/cc/cc-by.svg diff --git a/_assets/img/cc/cc-heart-filled.svg b/_includes/assets/img/cc/cc-heart-filled.svg similarity index 100% rename from _assets/img/cc/cc-heart-filled.svg rename to _includes/assets/img/cc/cc-heart-filled.svg diff --git a/_assets/img/cc/cc-heart.svg b/_includes/assets/img/cc/cc-heart.svg similarity index 100% rename from _assets/img/cc/cc-heart.svg rename to _includes/assets/img/cc/cc-heart.svg diff --git a/_assets/img/cc/cc-logo.svg b/_includes/assets/img/cc/cc-logo.svg similarity index 100% rename from _assets/img/cc/cc-logo.svg rename to _includes/assets/img/cc/cc-logo.svg diff --git a/_assets/img/cc/cc-nc-eu.svg b/_includes/assets/img/cc/cc-nc-eu.svg similarity index 100% rename from _assets/img/cc/cc-nc-eu.svg rename to _includes/assets/img/cc/cc-nc-eu.svg diff --git a/_assets/img/cc/cc-nc-jp.svg b/_includes/assets/img/cc/cc-nc-jp.svg similarity index 100% rename from _assets/img/cc/cc-nc-jp.svg rename to _includes/assets/img/cc/cc-nc-jp.svg diff --git a/_assets/img/cc/cc-nc.svg b/_includes/assets/img/cc/cc-nc.svg similarity index 100% rename from _assets/img/cc/cc-nc.svg rename to _includes/assets/img/cc/cc-nc.svg diff --git a/_assets/img/cc/cc-nd.svg b/_includes/assets/img/cc/cc-nd.svg similarity index 100% rename from _assets/img/cc/cc-nd.svg rename to _includes/assets/img/cc/cc-nd.svg diff --git a/_assets/img/cc/cc-pd.svg b/_includes/assets/img/cc/cc-pd.svg similarity index 100% rename from _assets/img/cc/cc-pd.svg rename to _includes/assets/img/cc/cc-pd.svg diff --git a/_assets/img/cc/cc-pdm.svg b/_includes/assets/img/cc/cc-pdm.svg similarity index 100% rename from _assets/img/cc/cc-pdm.svg rename to _includes/assets/img/cc/cc-pdm.svg diff --git a/_assets/img/cc/cc-remix.svg b/_includes/assets/img/cc/cc-remix.svg similarity index 100% rename from _assets/img/cc/cc-remix.svg rename to _includes/assets/img/cc/cc-remix.svg diff --git a/_assets/img/cc/cc-sa.svg b/_includes/assets/img/cc/cc-sa.svg similarity index 100% rename from _assets/img/cc/cc-sa.svg rename to _includes/assets/img/cc/cc-sa.svg diff --git a/_assets/img/cc/cc-sampling-plus.svg b/_includes/assets/img/cc/cc-sampling-plus.svg similarity index 100% rename from _assets/img/cc/cc-sampling-plus.svg rename to _includes/assets/img/cc/cc-sampling-plus.svg diff --git a/_assets/img/cc/cc-sampling.svg b/_includes/assets/img/cc/cc-sampling.svg similarity index 100% rename from _assets/img/cc/cc-sampling.svg rename to _includes/assets/img/cc/cc-sampling.svg diff --git a/_assets/img/cc/cc-share.svg b/_includes/assets/img/cc/cc-share.svg similarity index 100% rename from _assets/img/cc/cc-share.svg rename to _includes/assets/img/cc/cc-share.svg diff --git a/_assets/img/cc/cc-zero.svg b/_includes/assets/img/cc/cc-zero.svg similarity index 100% rename from _assets/img/cc/cc-zero.svg rename to _includes/assets/img/cc/cc-zero.svg diff --git a/_includes/assets/img/octicons/COPYING.md b/_includes/assets/img/octicons/COPYING.md new file mode 100644 index 000000000..bc41ff0c7 --- /dev/null +++ b/_includes/assets/img/octicons/COPYING.md @@ -0,0 +1,44 @@ +# Copying + +The icons in this directory are [Octicons][], by GitHub, taken verbatim from +[`@primer/octicons`][] 19.33.0 and used under the MIT License reproduced below. + +They are vendored rather than installed because the site draws a handful of +them. [`markdown-it-github-alerts`][] carries its own copies of the alert icons +and does not read from here. + +
+ +```text +MIT License + +Copyright (c) 2026 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + + + + +[Octicons]: https://primer.style/octicons/ +[`@primer/octicons`]: https://www.npmjs.com/package/@primer/octicons +[`markdown-it-github-alerts`]: https://www.npmjs.com/package/markdown-it-github-alerts + + + diff --git a/_includes/assets/img/octicons/pencil.svg b/_includes/assets/img/octicons/pencil.svg new file mode 100644 index 000000000..1eb3d917a --- /dev/null +++ b/_includes/assets/img/octicons/pencil.svg @@ -0,0 +1,10 @@ + diff --git a/_includes/breadcrumb.liquid b/_includes/breadcrumb.liquid new file mode 100644 index 000000000..718cf172d --- /dev/null +++ b/_includes/breadcrumb.liquid @@ -0,0 +1,38 @@ +{%- assign trail = page.url | breadcrumb: collections.all, sections -%} +{%- if trail.size > 0 -%} + + {%- comment -%} + The page itself closes the list here, though not above: search results + want the whole trail, while on the page its title is the heading a line + below and would only be read twice. + {%- endcomment -%} + +{%- endif -%} diff --git a/_includes/key-point.liquid b/_includes/key-point.liquid deleted file mode 100644 index 63207961a..000000000 --- a/_includes/key-point.liquid +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/_layouts/deep-page.liquid b/_layouts/deep-page.liquid index df3126e4a..030cd3eba 100644 --- a/_layouts/deep-page.liquid +++ b/_layouts/deep-page.liquid @@ -1,12 +1,24 @@ --- layout: default -editable: true +body_classes: + - doc-page --- +{%- comment -%} + A page below the top level: it opens on the trail that leads to it. +{%- endcomment -%}
-
- {%- if page.permalink != '/' -%} -

{{ title }}

- {%- endif -%} - {{ content }} -
+
+
+
+ {% include 'breadcrumb.liquid' %} +

{{ title }}

+
+
+ +
+
+
{{ content }}
+
+
+
diff --git a/_layouts/docs.liquid b/_layouts/docs.liquid index 48b01246c..63373ea77 100644 --- a/_layouts/docs.liquid +++ b/_layouts/docs.liquid @@ -1,20 +1,126 @@ --- -layout: deep-page.liquid +layout: default body_classes: - docs + - doc-page +editable: true --- -
-
-
- {% if editable != false %} -
- Edit -
- {% endif %} - {{ content }} -
-
-
+{%- comment -%} + Which licence the page states: the handbook is share-alike, the rest of + the documentation is share-alike and non-commercial. +{%- endcomment -%} +{%- assign inHandbook = false -%} +{%- if page.url contains '/docs/handbook/' -%} + {%- assign inHandbook = true -%} +{%- endif -%} +{%- comment -%} + A page adapted from Google's style guide says so with `google: true`, which + reads the source page off its own filename. Where the two differ, name + Google's slug instead: `google: inclusive-documentation`. +{%- endcomment -%} +{%- if google -%} + {%- assign googleSlug = page.fileSlug -%} + {%- unless google == true -%} + {%- assign googleSlug = google -%} + {%- endunless -%} + {%- assign googleUrl = 'https://developers.google.com/style/' + | append: googleSlug + -%} +{%- endif -%} +
+
+
+
+ {% include 'breadcrumb.liquid' %} +

{{ title }}

+ {%- if key_point %} +

{{ key_point }}

+ {%- endif %} +
+
+ +
+
+
{{ content }}
+
+
+
+ +
+
+
+ {%- comment -%} + The buttons name the terms the sentence below spells out, so they + are decorative here and the sentence carries the link. + {%- endcomment -%} + +

+ {%- if google %} + Portions of this page are reproduced from work + shared by Google + under + CC BY 4.0, from + the original page. + {%- endif %} + Except where noted, this page is published under + {%- if inHandbook %} + CC BY-SA 4.0. + {%- else %} + CC BY-NC-SA 4.0. + {%- endif %} +

+
+ +
+ {%- assign updated = page.inputPath | gitLastUpdated -%} + {%- if updated %} +

+ Last updated + + UTC +

+ {%- endif %} + {%- if editable != false %} +

+ + {% include 'assets/img/octicons/pencil.svg' %} + Improve this page + +

+ {%- endif %} +
+
+
+
diff --git a/_layouts/handbook-content-g-devdocs-style.liquid b/_layouts/handbook-content-g-devdocs-style.liquid deleted file mode 100644 index 330a73da2..000000000 --- a/_layouts/handbook-content-g-devdocs-style.liquid +++ /dev/null @@ -1,120 +0,0 @@ -
-
-
- {%- if editable != false -%} -
- Edit -
- {%- endif -%} -

{{ title }}

-
- {% include key-point.html %} - {{ content }} -
-
- -
-
diff --git a/collections/_docs/README.md b/collections/_docs/README.md index a3e124cdc..a6bb29b0b 100644 --- a/collections/_docs/README.md +++ b/collections/_docs/README.md @@ -1,12 +1,14 @@ ## `open.inf.is` Documentation -> [!WARNING]\ +> [!WARNING] +> > This documentation is still in **draft** stage. All information may be > incomplete, inaccurate, outdated, or even **completely wrong**.
-> [!NOTE]\ +> [!NOTE] +> > If one is using VS Code, it is recommended to use the provided devcontainer > available via the official [Remote Development extension][]. > @@ -68,6 +70,35 @@ nps build

+### Handbook pages adapted from Google + +
+ +Much of the style handbook is adapted from the [Google developer documentation +style guide][], which is offered under [CC BY 4.0][]. That license asks for +credit, so a page carrying any of it says where it came from: + +```yaml +--- +title: Colons +key_point: A colon indicates that closely-related information follows. +google: true +--- +``` + +`google: true` reads the source page off the filename, so `colons.md` credits +`developers.google.com/style/colons`. Where the two do not line up, name +Google's own slug instead, and the credit follows it: + +```yaml +google: inclusive-documentation +``` + +Leave the key off a page that is ours, and no credit is shown. The footer builds +itself either way. + +

+ ### License
@@ -78,7 +109,9 @@ Commons Attribution-NonCommercial-ShareAlike 4.0 International License][]. +[CC BY 4.0]: https://creativecommons.org/licenses/by/4.0/ [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License]: https://creativecommons.org/licenses/by-nc-sa/4.0/ +[Google developer documentation style guide]: https://developers.google.com/style [Remote Development extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack diff --git a/collections/_docs/handbook/style/capitalization.md b/collections/_docs/handbook/style/capitalization.md index 0a5d60fff..e3296f53d 100644 --- a/collections/_docs/handbook/style/capitalization.md +++ b/collections/_docs/handbook/style/capitalization.md @@ -4,7 +4,8 @@ key_point: When to capitalize, and when to leave a word lowercase. stub: true --- -> [!NOTE]\ +> [!NOTE] +> > This page is a placeholder. It is linked from elsewhere in the handbook and is > yet to be written. diff --git a/collections/_docs/handbook/style/code-in-text.md b/collections/_docs/handbook/style/code-in-text.md index 3f9268546..74a57eb1e 100644 --- a/collections/_docs/handbook/style/code-in-text.md +++ b/collections/_docs/handbook/style/code-in-text.md @@ -4,7 +4,8 @@ key_point: What to set in code font, and how to explain placeholders. stub: true --- -> [!NOTE]\ +> [!NOTE] +> > This page is a placeholder. The sections below are linked from elsewhere in > the handbook and are yet to be written. diff --git a/collections/_docs/handbook/style/code-samples.md b/collections/_docs/handbook/style/code-samples.md index d4b0449dc..bb58d5b1b 100644 --- a/collections/_docs/handbook/style/code-samples.md +++ b/collections/_docs/handbook/style/code-samples.md @@ -4,7 +4,8 @@ key_point: How to introduce and present a code sample. stub: true --- -> [!NOTE]\ +> [!NOTE] +> > This page is a placeholder. The section below is linked from elsewhere in the > handbook and is yet to be written. diff --git a/collections/_docs/handbook/style/code-syntax.md b/collections/_docs/handbook/style/code-syntax.md index c47fdefc1..a260860bb 100644 --- a/collections/_docs/handbook/style/code-syntax.md +++ b/collections/_docs/handbook/style/code-syntax.md @@ -1,6 +1,7 @@ --- title: Documenting Command-Line Syntax key_point: Recommendations for documenting command-line tools. +google: true --- ## Placeholder variables diff --git a/collections/_docs/handbook/style/colons.md b/collections/_docs/handbook/style/colons.md index 042f85710..f5ebf2a45 100644 --- a/collections/_docs/handbook/style/colons.md +++ b/collections/_docs/handbook/style/colons.md @@ -1,8 +1,7 @@ --- title: Colons key_point: A colon indicates that closely-related information follows. -original_url: https://developers.google.com/style/colons -original_title: Colons | Google developer documentation style guide +google: true --- ## Introductory Phrase Preceding Colon diff --git a/collections/_docs/handbook/style/commit-messages.md b/collections/_docs/handbook/style/commit-messages.md index d6b416f37..8b4a0311f 100644 --- a/collections/_docs/handbook/style/commit-messages.md +++ b/collections/_docs/handbook/style/commit-messages.md @@ -144,7 +144,8 @@ Certificate of Origin][], and saying a tool helped is not a transfer of responsibility. You are answerable for every line in your pull request, whatever wrote it. -> [!WARNING]\ +> [!WARNING] +> > git only looks for trailers in the **last** paragraph of the message, and only > if every line in it is a trailer. Three consequences, each of which has > silently cost this project its metadata: diff --git a/collections/_docs/handbook/style/dashes.md b/collections/_docs/handbook/style/dashes.md index 3cb6664bf..1c0441755 100644 --- a/collections/_docs/handbook/style/dashes.md +++ b/collections/_docs/handbook/style/dashes.md @@ -4,7 +4,8 @@ key_point: Which dash to use, and how it differs from a colon. stub: true --- -> [!NOTE]\ +> [!NOTE] +> > This page is a placeholder. The section below is linked from elsewhere in the > handbook and is yet to be written. diff --git a/collections/_docs/handbook/style/lists.md b/collections/_docs/handbook/style/lists.md index 2817ff974..fa9ab7c9a 100644 --- a/collections/_docs/handbook/style/lists.md +++ b/collections/_docs/handbook/style/lists.md @@ -4,7 +4,8 @@ key_point: How to introduce and punctuate lists. stub: true --- -> [!NOTE]\ +> [!NOTE] +> > This page is a placeholder. The section below is linked from elsewhere in the > handbook and is yet to be written. diff --git a/collections/_docs/handbook/style/people-person-first-language.md b/collections/_docs/handbook/style/people-person-first-language.md index 536c80c82..239851a88 100644 --- a/collections/_docs/handbook/style/people-person-first-language.md +++ b/collections/_docs/handbook/style/people-person-first-language.md @@ -6,7 +6,8 @@ key_point: stub: true --- -> [!NOTE]\ +> [!NOTE] +> > This page is a placeholder. The rule below stands; the guidance that would > follow it is yet to be written. diff --git a/eleventy.config.mjs b/eleventy.config.mjs index 762a46180..1873c9d49 100644 --- a/eleventy.config.mjs +++ b/eleventy.config.mjs @@ -1,3 +1,4 @@ +import { execFileSync } from 'node:child_process'; import { parse as pathParse } from 'node:path'; import { EleventyI18nPlugin } from '@11ty/eleventy'; import { PATHS } from '@openinf/portal/build/constants'; @@ -5,6 +6,7 @@ import autoprefixer from 'autoprefixer'; import cssnano from 'cssnano'; import markdownItAnchor from 'markdown-it-anchor'; import markdownItFootnote from 'markdown-it-footnote'; +import markdownItGitHubAlerts from 'markdown-it-github-alerts'; import postcss from 'postcss'; import { compileString } from 'sass'; @@ -15,6 +17,81 @@ export default async function (eleventyConfig) { eleventyConfig.amendLibrary('md', (md) => { md.use(markdownItAnchor); md.use(markdownItFootnote); + // `> [!NOTE]` and the rest become a titled callout rather than a + // blockquote opening on the literal marker. The icons it ships with come + // through as well; _docs-page.scss draws them in the title's own colour. + md.use(markdownItGitHubAlerts); + }); + + // When a page last changed, read from git rather than the file's mtime, + // which a checkout sets to the moment it ran. A shallow clone holds one + // commit and would date every page to the build, so it reports nothing + // instead and the layouts leave the line off. + const gitIsShallow = (() => { + try { + return ( + execFileSync('git', ['rev-parse', '--is-shallow-repository'], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim() === 'true' + ); + } catch { + return true; + } + })(); + + const lastChanged = new Map(); + eleventyConfig.addFilter('gitLastUpdated', (inputPath) => { + if (gitIsShallow || !inputPath) return null; + const file = String(inputPath).replace(/^\.\//, ''); + if (!lastChanged.has(file)) { + let iso = ''; + try { + iso = execFileSync('git', ['log', '-1', '--format=%cI', '--', file], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + } catch { + iso = ''; + } + lastChanged.set(file, iso ? new Date(iso) : null); + } + + return lastChanged.get(file); + }); + + // The trail to a page, built from somewhere a reader can actually go + // rather than from the slashes in its URL. `/docs/handbook/style/colons/` + // has no page at `/docs/handbook/` or `/docs/handbook/style/`, so a trail + // split off the URL would link twice into nothing, and a post's date + // directories would do it three times. A step appears here only if a page + // sits at that URL, or `_data/sections.json` names somewhere else it can + // point. The trail always terminates at the front page, and stops short of + // the page it is on, whose title is the heading directly below it. + eleventyConfig.addFilter('breadcrumb', (url, all, sections) => { + if (typeof url !== 'string' || url === '/') return []; + + const titles = new Map( + (all ?? []) + .filter((item) => item?.url && item.data?.title) + .map((item) => [item.url, item.data.title]) + ); + // The front page is titled for its own heading, not for this list. + const trail = [{ title: 'Home', url: '/' }]; + const parts = url.split('/').filter(Boolean); + + for (let index = 0; index < parts.length - 1; index += 1) { + const ancestor = `/${parts.slice(0, index + 1).join('/')}/`; + const named = sections?.[parts[index]]; + + if (titles.has(ancestor)) { + trail.push({ title: titles.get(ancestor), url: ancestor }); + } else if (named?.name && named?.url) { + trail.push({ title: named.name, url: named.url }); + } + } + + return trail; }); // Configure Eleventy. @@ -99,6 +176,11 @@ export default async function (eleventyConfig) { 'Aggregate, curate, disseminate, and apply information derived from diverse sources.' ); eleventyConfig.addGlobalData('siteUrl', 'https://open.inf.is'); + // The branch an "Improve this page" link opens against. Fixed rather than + // read from the checkout, which during a pull request build is the branch + // under review; GitHub's editor also wants a branch it can commit to, and + // takes no symbolic ref. + eleventyConfig.addGlobalData('repoBranch', 'live'); // What Jekyll called `site.time`: the moment the site was generated. The // footer still asks for it through a `date` filter, so this is the instant // rather than a formatted year, and anything else wanting a build date can diff --git a/package.json b/package.json index c0776113b..9626662ed 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "markdown-it-abbr": "2.0.0", "markdown-it-anchor": "9.2.1", "markdown-it-footnote": "4.0.0", + "markdown-it-github-alerts": "1.0.1", "markdown-it-sub": "2.0.0", "markdown-it-sup": "2.0.0", "markdown-it-task-lists": "2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7750d5121..fee17a966 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: markdown-it-footnote: specifier: 4.0.0 version: 4.0.0 + markdown-it-github-alerts: + specifier: 1.0.1 + version: 1.0.1(markdown-it@14.3.0) markdown-it-sub: specifier: 2.0.0 version: 2.0.0 @@ -2163,6 +2166,11 @@ packages: markdown-it-footnote@4.0.0: resolution: {integrity: sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==} + markdown-it-github-alerts@1.0.1: + resolution: {integrity: sha512-NNATF4QdoGI07hyCitoB2YqJ1YcNVCKT89ut2VtfFY9rkeFCXe/V2lOonKQLpJiq5DjiZZepf97BJx5xOjFIAw==} + peerDependencies: + markdown-it: '>= 13.0.0' + markdown-it-sub@2.0.0: resolution: {integrity: sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==} @@ -5568,6 +5576,10 @@ snapshots: markdown-it-footnote@4.0.0: {} + markdown-it-github-alerts@1.0.1(markdown-it@14.3.0): + dependencies: + markdown-it: 14.3.0 + markdown-it-sub@2.0.0: {} markdown-it-sup@2.0.0: {}