diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ce2669e..95449d4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -54,11 +54,6 @@ jobs: with: python-version: '3.x' - # The social plugin renders the share cards through cairo, which cairosvg - # loads at runtime rather than bundling. - - name: Install cairo - run: sudo apt-get install -y --no-install-recommends libcairo2 - - name: Build site run: pipx run nox -s docs diff --git a/README.md b/README.md index a1e9001..a79efda 100644 --- a/README.md +++ b/README.md @@ -35,21 +35,17 @@ Or through nox, which manages the environment for you: $ nox -s docs-live ``` -Building the share cards needs cairo on the system (`libcairo2` on Debian and -Ubuntu, `cairo` from Homebrew on macOS). To skip them while writing: - -```console -$ SOCIAL_CARDS=false mkdocs serve -``` +The site uses [Sumi](https://sumi.mkdocs-ng.org/), the MkDocs NG theme. It +needs no system libraries and fetches nothing at build time. ## Layout | Path | Contents | | --- | --- | -| `docs/index.md` | Landing page. Renders through `docs/overrides/home.html`. | +| `docs/index.md` | Landing page. Renders through `docs/overrides/landing.html`. | | `docs/getting-started/`, `docs/guides/` | Tutorials and how-to guides. | | `docs/rules.md`, `docs/configuration.md` | Reference. | -| `docs/blog/` | Blog, published with Material's blog plugin. | +| `docs/blog/` | Blog, published by Sumi. A post's file name, `YYYY-MM-DD-slug.md`, sets its URL. | | `docs/overrides/`, `docs/stylesheets/`, `docs/assets/` | Theme, styles, brand assets. | | `scripts/mkdocs_hooks.py` | Redirect stubs for the URLs the old Sphinx site served. | diff --git a/docs/assets/extra.css b/docs/assets/extra.css index 72aa9a9..8f882e6 100644 --- a/docs/assets/extra.css +++ b/docs/assets/extra.css @@ -1,10 +1,10 @@ /* Brand palette ----------------------------------------------------------- - Material's "custom" primary/accent read these variables. + Sumi reads every colour, font and size from its --sumi-* variables, so the + brand is applied by pointing those at the Commit Check palette. - Ink is the brand's dark and Signal Blue its accent. The header and footer - are Ink on every page, so the docs and the landing page share one frame; - Signal Blue is the same #2c9ccd the README badge has always used, so badges - already in the wild still match. */ + Ink is the brand's dark and Signal Blue its accent. Signal Blue is the same + #2c9ccd the README badge has always used, so badges already in the wild + still match. Paper is the cool grey of the tinted bands. */ :root { --cc-ink: #0b1620; --cc-ink-raised: #13212c; @@ -14,113 +14,162 @@ /* The brand blue only reaches 3.1:1 on white; links need 4.5:1. */ --cc-link: #176b89; - --md-primary-fg-color: var(--cc-ink); - --md-primary-fg-color--light: var(--cc-ink-raised); - --md-primary-fg-color--dark: #060d13; - --md-accent-fg-color: var(--cc-brand-dark); - - /* A literal stack: the theme defines --md-text-font-family below :root, - so a var() of it here would resolve to nothing. */ + /* Literal stacks rather than var()s of each other, so each one can be read + on its own. */ --cc-display-font: "Bricolage Grotesque", "Instrument Sans", -apple-system, "Segoe UI", sans-serif; + --sumi-font: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", "PingFang SC", + "Microsoft YaHei", sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + --sumi-font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + + --sumi-accent: var(--cc-brand); + --sumi-accent-ink: var(--cc-link); + --sumi-bg: #ffffff; + --sumi-bg-elev: #ffffff; + --sumi-bg-soft: #f4f6f7; + --sumi-bg-sunken: #eef2f4; + --sumi-fg: #0e1b24; + --sumi-fg-2: #4f6270; + --sumi-fg-3: #6a7c89; + --sumi-line: #dce3e8; + --sumi-line-2: #c9d3da; + --sumi-link: var(--cc-link); + --sumi-link-hover: #0f5570; + --sumi-header-bg: rgba(255, 255, 255, 0.9); + --sumi-code-bg: #f6f8f9; + --sumi-code-head: #eef2f4; + --sumi-code-inline: #eef2f4; + + /* The page width the site has always had: a 1220px column with the header + aligned to it, and both sidebars at the same width. */ + --cc-page-w: 1220px; + --sumi-side-w: 242px; + --sumi-rail-w: 242px; + --sumi-header-h: 48px; } -/* The theme sets the footer colours per scheme, so they are overridden per - scheme too; on :root alone they would lose to the theme's own. */ -[data-md-color-scheme="default"], -[data-md-color-scheme="slate"] { - --md-footer-bg-color: #081119; - --md-footer-bg-color--dark: #060d13; +[data-theme="dark"] { + --sumi-accent: var(--cc-brand); + --sumi-accent-ink: var(--cc-brand-light); + --sumi-bg: #0e1a23; + --sumi-bg-elev: #13212c; + --sumi-bg-soft: #13212c; + --sumi-bg-sunken: #0b1620; + --sumi-fg: #e8eef2; + --sumi-fg-2: #b9c7d1; + --sumi-fg-3: #8fa3b1; + --sumi-line: #22323f; + --sumi-line-2: #2e4252; + --sumi-link: var(--cc-brand-light); + --sumi-link-hover: #7fc6e6; + --sumi-header-bg: rgba(11, 22, 32, 0.9); + --sumi-code-bg: #0b1620; + --sumi-code-head: #13212c; + --sumi-code-inline: #13212c; } -[data-md-color-scheme="slate"] { - /* Material tints every slate grey from one hue; 205 is Ink's. */ - --md-hue: 205; - --md-default-bg-color: #0e1a23; - --md-primary-fg-color: var(--cc-ink); - --md-accent-fg-color: var(--cc-brand-light); - --md-typeset-a-color: var(--cc-brand-light); +/* Page frame ---------------------------------------------------------------- */ +.sumi-header-inner, +.sumi-shell, +.sumi-footer-inner { + max-width: var(--cc-page-w); } -[data-md-color-scheme="default"] { - --md-typeset-a-color: var(--cc-link); - /* A light header in the light scheme, Ink in the dark one, so the header - answers the theme toggle like the rest of the page. */ - --md-primary-fg-color: #ffffff; - --md-primary-fg-color--light: #f4f6f7; - --md-primary-fg-color--dark: #eef2f4; - --md-primary-bg-color: var(--cc-ink); - --md-primary-bg-color--light: #4f6270; +.sumi-shell { + column-gap: 32px; } -[data-md-color-scheme="default"] .md-header { - box-shadow: inset 0 -1px 0 #dce3e8; +/* The footer is Ink on every page and in both schemes, so the docs and the + landing page share one frame. */ +.sumi-footer { + border-top: 0; + background: #060d13; } -/* The theme's search field is a translucent black meant for a coloured - header; on a white one it needs a solid tint instead. */ -[data-md-color-scheme="default"] .md-search__form { - background-color: #eef2f4; +.sumi-footer-inner { + color: #8fa3b1; } -[data-md-color-scheme="default"] .md-search__form:hover { - background-color: #e4e9ed; +.sumi-footer a, +.sumi-footer .sumi-icon-btn { + color: #b9c7d1; } -/* A primary button is Signal Blue with Ink text: white on this blue is 3.1:1, - under the 4.5:1 a button label needs; Ink on it is 5.9:1. */ -.md-typeset .md-button--primary { - background-color: var(--cc-brand); - border-color: var(--cc-brand); - color: var(--cc-ink); +.sumi-footer a:hover, +.sumi-footer .sumi-icon-btn:hover { + color: #ffffff; + background: none; } -.md-typeset .md-button--primary:focus, -.md-typeset .md-button--primary:hover { - background-color: var(--cc-brand-light); - border-color: var(--cc-brand-light); - color: var(--cc-ink); +/* Text the way the site has always set it: Instrument Sans without the + stylistic sets Sumi turns on for its own face, at the same line height, and + with no lead paragraph or rule above each section. */ +body { + font-feature-settings: normal; +} + +.sumi-prose { + line-height: 1.6; +} + +.sumi-prose h1 + p { + font-size: inherit; + line-height: inherit; + color: inherit; +} + +.sumi-prose h2 { + padding-top: 0; + border-top: 0; } /* Display face for page titles, section headings and the site name. Body text - stays in the theme's text font. */ -.md-typeset h1, -.md-typeset h2 { + stays in the text font. */ +.sumi-prose h1, +.sumi-prose h2, +.sumi-brand-name { font-family: var(--cc-display-font); font-weight: 700; letter-spacing: -0.02em; } -/* The theme draws page titles in a light grey at weight 300; in the display - face they read as headings only at full strength. */ -.md-typeset h1 { - color: var(--md-default-fg-color); +.sumi-brand-name { + letter-spacing: -0.01em; } -.md-header__title { - font-family: var(--cc-display-font); - font-weight: 700; - letter-spacing: -0.01em; +/* Buttons ------------------------------------------------------------------- + A primary button is Signal Blue with Ink text: white on this blue is 3.1:1, + under the 4.5:1 a button label needs; Ink on it is 5.9:1. */ +.sumi-prose .md-button--primary, +.sumi-btn--primary { + background-color: var(--cc-brand); + border-color: var(--cc-brand); + color: var(--cc-ink); +} + +.sumi-prose .md-button--primary:focus, +.sumi-prose .md-button--primary:hover, +.sumi-btn--primary:hover { + background-color: var(--cc-brand-light); + border-color: var(--cc-brand-light); + color: var(--cc-ink); } -/* Feature grid: uses Material's card grid, tightened up a little. */ -.md-typeset .grid.cards > ul > li { - border-radius: 0.4rem; +/* Feature grid: the card hover picks up the brand colour. */ +.sumi-prose .grid.cards > ul > li, +.sumi-prose ul.sumi-cards > li { transition: border-color 125ms, box-shadow 125ms; } -.md-typeset .grid.cards > ul > li:hover { +.sumi-prose .grid.cards > ul > li:hover, +.sumi-prose ul.sumi-cards > li:hover { border-color: var(--cc-brand); box-shadow: 0 0 0 1px var(--cc-brand); } -.md-typeset .grid.cards > ul > li > hr { - margin: 0.6rem 0; -} - /* Tables ------------------------------------------------------------------- Cells align to the top so short values line up with a wrapped message. */ -.md-typeset table:not([class]) td, -.md-typeset table:not([class]) th { +.sumi-prose table:not([class]) td, +.sumi-prose table:not([class]) th { vertical-align: top; } @@ -129,12 +178,6 @@ break across two lines even on a wide screen. Other tables hold long regexes and allow-lists that must stay wrappable, hence the wrapper class rather than a rule for every table. */ -.md-typeset .rules-index td code { +.sumi-prose .rules-index td code { white-space: nowrap; } - -/* Sidebar section headings ("Getting started", "Reference", ...) */ -.md-nav__item--section > .md-nav__link { - font-weight: 700; - color: var(--md-default-fg-color); -} diff --git a/docs/assets/social.png b/docs/assets/social.png new file mode 100644 index 0000000..02cc592 Binary files /dev/null and b/docs/assets/social.png differ diff --git a/docs/blog/posts/ai-native-commit-check.md b/docs/blog/posts/2026-06-21-ai-native-json-output-and-a-python-api.md similarity index 100% rename from docs/blog/posts/ai-native-commit-check.md rename to docs/blog/posts/2026-06-21-ai-native-json-output-and-a-python-api.md diff --git a/docs/blog/posts/configuring-commit-check.md b/docs/blog/posts/2026-06-21-from-zero-config-to-org-wide-policy.md similarity index 100% rename from docs/blog/posts/configuring-commit-check.md rename to docs/blog/posts/2026-06-21-from-zero-config-to-org-wide-policy.md diff --git a/docs/blog/posts/introducing-commit-check.md b/docs/blog/posts/2026-06-21-one-policy-file-for-your-git-history.md similarity index 100% rename from docs/blog/posts/introducing-commit-check.md rename to docs/blog/posts/2026-06-21-one-policy-file-for-your-git-history.md diff --git a/docs/blog/posts/ai-attribution-governance.md b/docs/blog/posts/2026-07-06-ai-attribution-governance-enforcing-ai-disclosure-policies-at-the-ci-level.md similarity index 100% rename from docs/blog/posts/ai-attribution-governance.md rename to docs/blog/posts/2026-07-06-ai-attribution-governance-enforcing-ai-disclosure-policies-at-the-ci-level.md diff --git a/docs/blog/posts/ai-commit-attribution-policy.md b/docs/blog/posts/2026-10-11-ai-commit-attribution-policy.md similarity index 100% rename from docs/blog/posts/ai-commit-attribution-policy.md rename to docs/blog/posts/2026-10-11-ai-commit-attribution-policy.md diff --git a/docs/blog/posts/no-llm-co-authors.md b/docs/blog/posts/2026-10-24-no-llm-co-authors.md similarity index 98% rename from docs/blog/posts/no-llm-co-authors.md rename to docs/blog/posts/2026-10-24-no-llm-co-authors.md index adc0d37..05118d5 100644 --- a/docs/blog/posts/no-llm-co-authors.md +++ b/docs/blog/posts/2026-10-24-no-llm-co-authors.md @@ -128,5 +128,5 @@ day. If your project's AI policy says something different, such as "disclose it with `Assisted-by:`" or "no AI trailers at all", -[this earlier post](ai-commit-attribution-policy.md) shows the setting for +[this earlier post](2026-10-11-ai-commit-attribution-policy.md) shows the setting for each. diff --git a/docs/getting-started.md b/docs/getting-started.md index 91f4e8d..17100cb 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -127,9 +127,9 @@ repository root — or in `.github/` — to make the policy explicit: ```toml title="cchk.toml" [commit] conventional_commits = true -subject_imperative = true # (1)! +subject_imperative = true # (1) subject_max_length = 72 -allow_wip_commits = false # (2)! +allow_wip_commits = false # (2) [branch] conventional_branch = true @@ -158,23 +158,23 @@ belongs:
-- :material-git:{ .lg .middle } __Before the commit lands__ +- :lucide-git-commit-horizontal:{ .lg .middle } __Before the commit lands__ --- A pre-commit hook rejects the message as you write it, so nothing bad reaches the branch in the first place. - [:octicons-arrow-right-24: Pre-commit hook](guides/integrations.md#as-a-pre-commit-hook) + [:lucide-arrow-right: Pre-commit hook](guides/integrations.md#as-a-pre-commit-hook) -- :material-github:{ .lg .middle } __On every pull request__ +- :brand-github:{ .lg .middle } __On every pull request__ --- A GitHub Action checks every commit in the PR and can comment on the PR with what needs fixing. - [:octicons-arrow-right-24: GitHub Actions](guides/integrations.md#in-github-actions) + [:lucide-arrow-right: GitHub Actions](guides/integrations.md#in-github-actions)
diff --git a/docs/guides/github-actions.md b/docs/guides/github-actions.md index 82f037e..4c4f335 100644 --- a/docs/guides/github-actions.md +++ b/docs/guides/github-actions.md @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 # (1)! + fetch-depth: 0 # (1) - uses: commit-check/commit-check-action@v2 with: message: true diff --git a/docs/index.md b/docs/index.md index b391280..fa5bb91 100644 --- a/docs/index.md +++ b/docs/index.md @@ -448,7 +448,7 @@ conventional_branch = true -
+

Questions

diff --git a/docs/overrides/home.html b/docs/overrides/home.html deleted file mode 100644 index 8ef0f34..0000000 --- a/docs/overrides/home.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "main.html" %} - -{% block hero %} -
-

Commit Check

-

- Clean commits. Clear standards. -

-

- - Get started - - - - - - - - - Star on GitHub - -

-
-{% endblock %} diff --git a/docs/overrides/landing.html b/docs/overrides/landing.html index db57b1b..96fe3b3 100644 --- a/docs/overrides/landing.html +++ b/docs/overrides/landing.html @@ -1,18 +1,14 @@ {% extends "main.html" %} {# - The landing page takes over the content area but deliberately leaves the - header alone. + The landing page is raw HTML laid out by stylesheets/landing.css, so it + takes over the whole area below the header: no sidebar, no table of + contents, no page actions. The header and footer stay Sumi's, the same as + on every other page. - Instant navigation swaps a fixed list of elements between documents - (see `bs()` in the theme bundle): announce, container, header-topic, - outdated, logo, skip. `{% block header %}` renders *outside* - `[data-md-component=container]`, so a header overridden here would not be - swapped on navigation: it would follow the reader onto every docs page, - and the docs header would follow them back here. The landing look is - applied to the stock header from `landing.css` instead, which is added to - and removed from `` as the reader arrives and leaves — instant - navigation diffs the head, so that part does work. + The content sits inside `.md-typeset`, the class landing.css scopes its + rules to, as well as Sumi's own `.sumi-prose`, so Sumi's styles for the + plain Markdown parts (the FAQ) still apply. #} {% block extrahead %} @@ -20,19 +16,17 @@ {% endblock %} -{# - `site_nav` is left to the theme. It holds the navigation that the header's - menu button opens on phones; `hide: navigation` already keeps it out of the - wide-screen layout. Emptying the block left that button with nothing to - open. -#} +{% block layout %} +
+
+ {{ page.content }} +
+
+{% endblock %} -{% block container %} -
-
- {% block content %} - {% include "partials/content.html" %} - {% endblock %} -
-
+{# The two animated demos. Only this page has them, and Sumi loads every page + in full, so the script is linked here rather than site-wide. #} +{% block scripts %} + {{ super() }} + {% endblock %} diff --git a/docs/overrides/main.html b/docs/overrides/main.html index 49c6161..3d6b4ac 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -1,24 +1,25 @@ {% extends "base.html" %} {# The landing page sets `title: Commit Check` so its social card and search - entry are not labelled "Home". Material appends the site name to any page - that carries a meta title, which on this one page repeats it. - - The site name alone says nothing to someone who has not heard of it, and - it is what Google shows as the result's headline, so the landing page can - give its document title separately as `seo_title`. #} + entry are not labelled "Home". The site name alone says nothing to someone + who has not heard of it, and it is what Google shows as the result's + headline, so the landing page can give its document title separately as + `seo_title`. #} {% block htmltitle %} - {% if page.is_homepage %} - {{ page.meta.seo_title or config.site_name }} + {% if page and page.is_homepage and page.meta.seo_title %} + {{ page.meta.seo_title }} {% else %} {{ super() }} {% endif %} {% endblock %} {% block extrahead %} - {# The display face for headings and the site name (--cc-display-font in - assets/extra.css). The theme loads the text and code faces itself. #} - + {# The brand faces: Instrument Sans for text, JetBrains Mono for code and + Bricolage Grotesque for headings and the site name (see assets/extra.css, + which points Sumi's font variables at them). #} + + + {% endblock %} diff --git a/docs/requirements.txt b/docs/requirements.txt index 548f780..7636cc1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,8 +1,7 @@ -# The community fork of MkDocs and Material, which the site moved to when +# MkDocs NG, the community fork of MkDocs, which the site moved to when # upstream MkDocs 2.0 announced it would remove the plugin system this site -# depends on (social cards, blog) with no migration path. -# -# The `imaging` extra pulls in cairosvg and pillow, which the social plugin -# needs to render the share cards. +# depends on, and Sumi, its documentation theme. Sumi brings its own blog, +# search palette and fonts, so nothing else is needed. mkdocs-ng==1.11.0 -mkdocs-ng-material[imaging]==9.8.0 +mkdocs-sumi==0.3.0 +pymdown-extensions==12.3 diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css deleted file mode 100644 index e33c3ea..0000000 --- a/docs/stylesheets/extra.css +++ /dev/null @@ -1,259 +0,0 @@ -/* Landing-page layout and a few typographic corrections. - - The brand palette lives in assets/extra.css, which loads first and is the - single source for --md-primary-*, --md-accent-* and the link colour. */ - -th { - background-color: var(--md-default-fg-color--lightest); -} - -/* Hero -------------------------------------------------------------------- - Rendered by overrides/home.html, which puts it outside the width-capped - content column so the background reaches the window edges on its own. Moving - it into the page content caps it at the column and needs a `calc(50% - 50vw)` - full-bleed hack to escape again; keeping it in the template avoids that. - - Its

is the page's visible title; .cc-page-title below is the hidden - counterpart that keeps Material from injecting a second one. */ -.hero { - text-align: center; - padding: 4rem 1rem; - background: var(--md-primary-fg-color); - color: var(--md-primary-bg-color); -} - -/* Material renders a title into any page whose content has none, using the nav - label — "Home" here, competing with the hero above it. The page carries this - heading purely to suppress that. */ -.md-typeset .cc-page-title { - display: none; -} - -/* With the title hidden, the "edit this page" pencil is left floating alone in - the corner. A landing page is not one anybody edits. */ -.md-content__inner:has(> .cc-page-title) .md-content__button { - display: none; -} - -/* Scales down rather than overflowing the viewport: at a flat 3rem the title - was wider than a phone screen and clipped at both ends. */ -.hero h1 { - font-size: clamp(2rem, 8vw, 3rem); - font-weight: 700; - margin-bottom: 1rem; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - text-align: center; - color: unset; -} - -/* The permalink anchor every heading gets is noise on a hero title. */ -.hero h1 .headerlink { - display: none; -} - -.hero p { - font-size: 1.2rem; - margin-bottom: 2rem; - opacity: 0.9; - width: min(96%, 34rem); - margin-left: auto; - margin-right: auto; -} - -.hero .twemoji { - --md-icon-size: 1.125em; - - display: inline-flex; - height: var(--md-icon-size); - vertical-align: text-top; -} - -.hero .md-button { - background: var(--cc-hero-button-bg); - color: black; - border: none; - border-radius: unset; - display: inline-block; -} - -/* The hero's two calls to action. As inline-blocks they carried no gap, so a - viewport too narrow to hold them side by side stacked them flush against - each other — two square-cornered boxes of different widths touching, which - reads as one broken element rather than two buttons. */ -.hero .cc-hero-actions { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 0.8rem; -} - -/* Once stacked they are a column, and a column of two different widths still - looks like a mistake. Give them one width the whole way down. */ -@media screen and (max-width: 32rem) { - .hero .cc-hero-actions { - flex-direction: column; - align-items: center; - } - - .hero .cc-hero-actions .md-button { - width: 100%; - max-width: 18rem; - } -} - -.hero .md-button:hover { - /* Was --commit-check-logo-color-green, which is not defined anywhere: the - hover simply did nothing. */ - background: var(--cc-brand-light); - color: black; - border: none; -} - -.md-typeset .md-button:hover { - color: black; -} - -/* Community section -------------------------------------------------------- */ -.community-section { - background: var(--md-default-fg-color--lightest); - padding: 3rem 2rem; - margin: 3rem -1.5rem 0 -1.5rem; - text-align: center; - border-radius: 1rem 1rem 0 0; -} - -.community-section h2 { - color: var(--md-primary-fg-color); - margin-bottom: 1rem; -} - -/* Code block enhancements */ -.md-typeset pre > code { - border-radius: 0.5rem; -} - -/* Button styling improvements */ -.md-button { - border-radius: 2rem; - padding: 0.5rem 1.5rem; - font-weight: 500; - transition: all 0.3s ease; -} - -.md-button:hover { - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -/* Trusted-by section ------------------------------------------------------- */ -.trusted-by { - text-align: center; - padding: 3rem 0; - background: var(--md-default-fg-color--lightest); - margin: 3rem -1.5rem; - border-radius: 1rem; -} - -.trusted-by p { - font-size: 1.1rem; - color: var(--md-default-fg-color--light); - margin-bottom: 2rem; -} - -.logo-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - gap: 2rem; - margin-bottom: 3rem; - padding: 0 2rem; -} - -.logo-item { - display: flex; - flex-direction: column; - align-items: center; - transition: all 0.3s ease; - padding: 1rem; - border-radius: 0.5rem; -} - -.logo-item:hover { - transform: translateY(-4px); - background: var(--md-default-bg-color); - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); -} - -.logo-item img { - width: 60px; - height: 60px; - border-radius: 50%; - margin-bottom: 0.5rem; - filter: grayscale(100%); - transition: filter 0.3s ease; -} - -.logo-item:hover img { - filter: grayscale(0%); -} - -.logo-item span { - font-size: 0.9rem; - font-weight: 500; - color: var(--md-default-fg-color--light); -} - -@media screen and (max-width: 768px) { - .logo-grid { - grid-template-columns: repeat(2, 1fr); - gap: 1rem; - padding: 0 1rem; - } -} - -/* Both tinted bands above bleed past the content column with a negative - margin. Material sets the root font to 125%, so -1.5rem is 30px — but below - its 76.25em breakpoint the column only has a 16px gutter to give back. The - bands hung 14px off the right edge, which made the whole page scroll - sideways on every phone and tablet. Match the gutter there instead. - - (76.25em is Material's own breakpoint. Media-query em units resolve against - the initial 16px font size rather than the 125% root, so this is 1220px.) */ -@media screen and (max-width: 76.25em) { - .trusted-by, - .community-section { - margin-left: -0.8rem; - margin-right: -0.8rem; - } -} - -/* Mobile navigation drawer ------------------------------------------------- - Material leaves this at its own default indigo, which clashes with the - header right above it. */ -@media screen and (max-width: 76.2344em) { - .md-nav--primary .md-nav__title[for="__drawer"] { - background-color: var(--md-primary-fg-color); - } -} - -/* "important" admonition, which Material ships without a colour of its own. */ -.md-typeset .admonition.important, -.md-typeset details.important { - border-color: #00b8d4; -} - -.md-typeset .important > .admonition-title::before, -.md-typeset .important > summary::before { - background-color: #00b8d4; - -webkit-mask-image: var(--md-admonition-icon--info); - mask-image: var(--md-admonition-icon--info); -} - -.md-typeset .important > .admonition-title, -.md-typeset .important > summary { - background-color: #00b8d41a; -} - -/* Annotation markers are round, matching the numbered badges they pair with. */ -a.md-annotation__index { - border-radius: 2.2ch; -} diff --git a/docs/stylesheets/landing.css b/docs/stylesheets/landing.css index 95cb5ef..c329b24 100644 --- a/docs/stylesheets/landing.css +++ b/docs/stylesheets/landing.css @@ -2,16 +2,13 @@ * Landing page only. * * This file is linked from docs/overrides/landing.html rather than listed in - * mkdocs.yml's extra_css, so the documentation pages never load it. Instant - * navigation diffs between documents, so the link is added when a - * reader arrives here and removed when they leave -- which is also why rules - * below may safely reach outside the content area, to .md-header and - * .md-main. A body class could not do that: instant navigation does not - * touch . + * mkdocs.yml's extra_css, so the documentation pages never load it. Every + * page is a full page load, so rules below may safely reach outside the + * content area, to the header and the page frame. * - * Sizes are in px, not the theme's rem: the theme scales its root font size - * up on wide screens, and the landing page is laid out to fixed measures - * (the demo window, the five-column row) that should not grow with it. + * Sizes are in px, not rem: the landing page is laid out to fixed measures + * (the demo window, the five-column row) that should not grow with the + * reader's font size. * * The brand tokens are Ink, Signal Blue and Paper; see the brand kit. The * demo window is drawn light in both colour schemes on purpose -- it stands @@ -47,7 +44,7 @@ --cc-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } -[data-md-color-scheme="slate"] { +[data-theme="dark"] { --cc-paper: #0e1a23; --cc-card: #13212c; --cc-line: #263744; @@ -59,49 +56,35 @@ /* ---------------------------------------------------------------- chrome -- */ -/* The brand is small and out of the way in the header: nobody arrives caring - about it. The search box is what competes with the headline, and a reader - who wants search is one click into the docs. */ -.md-header .md-search { - display: none; -} - -@media screen and (min-width: 76.25em) { - .md-header .md-search { - display: block; - } -} - -/* The hero is a dark band that meets the header, so the theme's gap above the - content, and the spacer it draws inside it, go. */ -.md-main__inner { - margin-top: 0; -} - -.cc-landing .md-content__inner { - padding-top: 0; - margin-bottom: 0; -} - -.cc-landing .md-content__inner::before { - display: none; -} - -/* The "edit this page" pencil points at index.md, which is not what a - first-time reader wants to do next. */ -.cc-landing .md-content__button { - display: none; -} - -.md-main { +/* The hero is a band that meets the header, so the theme's gap above the + content goes. The page is one centred column, the same width as the header, + and the bands below reach past it to the window edges. */ +.sumi-main.cc-landing { + padding: 0; background: var(--cc-paper); /* The bands below span the window with `margin-inline: calc(50% - 50vw)`. 100vw includes a classic scrollbar, so they can end a few pixels wide; - `clip` rather than `hidden`, because `hidden` would make .md-main a - scroll container and break the sticky header and in-page anchors. */ + `clip` rather than `hidden`, because `hidden` would make this a scroll + container and break the sticky header and in-page anchors. */ overflow-x: clip; } +.cc-landing > .md-typeset { + max-width: var(--cc-page-w, 1220px); + margin: 0 auto; + padding: 0 16px; + font-family: var(--sumi-font); + font-size: 16px; + line-height: 1.6; +} + +/* The cards were laid out with half an em above and below a paragraph inside + a list item, which the page's first theme gave them. Component rules below + are more specific and still override it. */ +.md-typeset :is(ul, ol) li :is(p, blockquote) { + margin: 0.5em 0; +} + /* ---------------------------------------------------------------- resets -- */ /* The theme styles bare elements inside .md-typeset. These resets use @@ -112,14 +95,14 @@ margin: 0; } -/* The theme indents lists with [dir=ltr]-prefixed rules, hence [dir] here. */ -[dir] .md-typeset :where(.cc-band:not(.cc-faq)) :is(ul, ol) { +/* :root keeps these ahead of the theme's own list rules. */ +:root .md-typeset :where(.cc-band:not(.cc-faq)) :is(ul, ol) { margin: 0; padding: 0; list-style: none; } -[dir] .md-typeset :where(.cc-band:not(.cc-faq)) :is(ul, ol) > li { +:root .md-typeset :where(.cc-band:not(.cc-faq)) :is(ul, ol) > li { margin: 0; } @@ -150,7 +133,7 @@ /* Each band spans the window while its text stays in the content column: the negative margin pulls the edges out, the padding puts the text back. - Percentages, not viewport units, for the margin: .md-content is a flex + Percentages, not viewport units, for the margin: the column is a grid item, and a `-100vw` margin counts towards its min-content width, which once stretched the column to 2754px on a 1440px screen. */ .md-typeset .cc-band { @@ -2046,7 +2029,7 @@ color: #5b6d7a; } -[data-md-color-scheme="slate"] .md-typeset .cc-users a { +[data-theme="dark"] .md-typeset .cc-users a { color: #a3b4c0; } @@ -2086,7 +2069,7 @@ background: #f5fafd; } -[data-md-color-scheme="slate"] .md-typeset .cc-plan--soon { +[data-theme="dark"] .md-typeset .cc-plan--soon { border-color: #2e5467; background: #10222d; } @@ -2139,20 +2122,25 @@ .md-typeset .cc-faq .cc-h2 { margin: 0 0 28px; + padding-top: 0; + border-top: 0; } .md-typeset .cc-faq details { margin: 0 0 10px; border: 1px solid var(--cc-line); border-radius: 12px; + padding: 0; background: var(--cc-card); box-shadow: none; font-size: 15px; } .md-typeset .cc-faq details > summary { - padding: 14px 44px 14px 18px; + margin: 0; + padding: 14px 18px; background: none; + color: var(--cc-text); font-size: 16px; font-weight: 600; } @@ -2161,9 +2149,10 @@ display: none; } +/* The question admonition's green, on the chevron only. */ .md-typeset .cc-faq details > summary::after { - top: 16px; - right: 14px; + border-color: #64dd17; + opacity: 1; } .md-typeset .cc-faq details > p { @@ -2474,7 +2463,7 @@ /* In the dark scheme the cards are already near Ink; an outline keeps the organization card from dissolving into its neighbours. */ -[data-md-color-scheme="slate"] .md-typeset .cc-group--ink { +[data-theme="dark"] .md-typeset .cc-group--ink { border-color: #22404f; } @@ -2485,51 +2474,51 @@ stay dark: they stand for a terminal, which is dark in either theme. The Action band and the closing band stay Ink in both schemes as a stage for the demo. */ -[data-md-color-scheme="default"] .md-typeset .cc-hero { +[data-theme="light"] .md-typeset .cc-hero { background: #ffffff; color: var(--cc-text); box-shadow: inset 0 -1px 0 var(--cc-line); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-display { +[data-theme="light"] .md-typeset .cc-hero .cc-display { color: var(--cc-text); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-accent { +[data-theme="light"] .md-typeset .cc-hero .cc-accent { /* Signal Blue is 3.1:1 on white, enough for display type (3:1 from 24px). */ color: var(--cc-blue); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-lede { +[data-theme="light"] .md-typeset .cc-hero .cc-lede { color: var(--cc-muted); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-lede code { +[data-theme="light"] .md-typeset .cc-hero .cc-lede code { color: var(--cc-text); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-pill { +[data-theme="light"] .md-typeset .cc-hero .cc-pill { border-color: #b9dcec; background: var(--cc-tint); color: #135a74; } -[data-md-color-scheme="default"] .md-typeset .cc-hero ul.cc-facts { +[data-theme="light"] .md-typeset .cc-hero ul.cc-facts { border-top-color: var(--cc-line); color: var(--cc-muted); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-install { +[data-theme="light"] .md-typeset .cc-hero .cc-install { border-color: var(--cc-ink); background: var(--cc-ink); } -[data-md-color-scheme="default"] .md-typeset .cc-hero .cc-term { +[data-theme="light"] .md-typeset .cc-hero .cc-term { box-shadow: 0 24px 60px rgba(11, 22, 32, 0.22); } /* The history-line motif's nodes are filled with the ground colour so the lines pass behind them; on the light hero that ground is white. */ -[data-md-color-scheme="default"] .md-typeset .cc-hero__graph circle { +[data-theme="light"] .md-typeset .cc-hero__graph circle { fill: #ffffff; } diff --git a/mkdocs.yml b/mkdocs.yml index 713556a..63f041b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,65 +29,26 @@ validation: anchors: warn theme: - name: material - language: en + name: sumi + locale: en custom_dir: docs/overrides logo: assets/logo.svg favicon: assets/favicon.svg - icon: - repo: fontawesome/brands/github - # Instrument Sans for text and JetBrains Mono for code; the display face, - # Bricolage Grotesque, is loaded in overrides/main.html because the theme - # only takes two families. - font: - text: Instrument Sans - code: JetBrains Mono - # Two states, not three. The first visit still follows the system through - # the media queries; after that every click visibly changes the page. A - # third "follow system" state made one click in three do nothing a reader - # could see, which read as a broken toggle. - palette: - - media: "(prefers-color-scheme: light)" - scheme: default - primary: custom - accent: custom - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - media: "(prefers-color-scheme: dark)" - scheme: slate - primary: custom - accent: custom - toggle: - icon: material/brightness-4 - name: Switch to light mode - features: - # Navigation lives in the left sidebar, grouped into sections — deliberately - # not navigation.tabs, which moves the top level up into the header. - - navigation.sections - - navigation.top - - navigation.tracking - - navigation.instant - - navigation.instant.progress - - navigation.footer - - toc.follow - - search.suggest - - search.highlight - - search.share - - content.code.copy - - content.code.annotate - - content.tabs.link - - content.action.edit + # Follows the system on the first visit; the header toggle then switches + # between light and dark and remembers the choice. + palette: auto + # Signal Blue, the brand accent (see assets/extra.css for the full palette). + accent: "#2c9ccd" + # Navigation lives in the left sidebar, grouped into sections, not in + # header tabs. + navigation_tabs: false + # The preview image for pages that do not set their own `image:`. + social_image: assets/social.png extra_css: - # Brand colours and typography, then the landing-page-specific layout. + # Brand colours and typography. The landing page adds its own layout in + # overrides/landing.html. - assets/extra.css - - stylesheets/extra.css - -# The landing page's two demos. Loaded everywhere, because instant navigation -# does not run scripts that arrive with a page; it does nothing elsewhere. -extra_javascript: - - javascripts/landing.js extra: social: @@ -113,9 +74,6 @@ markdown_extensions: permalink: true permalink_title: Link to this section - pymdownx.details - - pymdownx.emoji: - emoji_index: !!python/name:material.extensions.emoji.twemoji - emoji_generator: !!python/name:material.extensions.emoji.to_svg - pymdownx.highlight: anchor_linenums: true line_spans: __span @@ -135,18 +93,9 @@ markdown_extensions: plugins: - search - - blog - # Generates the preview image shown when a page is shared on Slack, X or - # LinkedIn. Needs cairo and pillow, which the docs requirements pull in. - # - # Deploy previews turn this off (see netlify.toml): nobody shares a preview - # link for its preview image, and skipping the cards keeps those builds fast - # and free of the system cairo dependency. - - social: - enabled: !ENV [SOCIAL_CARDS, true] - cards_layout_options: - background_color: "#0b1620" - color: "#ffffff" + # Sumi's plugin: components, the blog (index, tags, Atom feed) under + # docs/blog/, and a Markdown copy of each page. + - sumi hooks: - scripts/mkdocs_hooks.py diff --git a/netlify.toml b/netlify.toml index 8478346..006348d 100644 --- a/netlify.toml +++ b/netlify.toml @@ -20,15 +20,11 @@ # those values are literals, so "${DEPLOY_PRIME_URL}" would reach MkDocs # unexpanded and be rejected as a URL without a scheme. The build command runs # in a shell, where the variable actually expands. -# -# SOCIAL_CARDS=false because nobody shares a preview link for its preview -# image, and skipping the cards keeps previews fast and free of the system -# cairo dependency. [context.deploy-preview] - command = "pip install -r docs/requirements.txt && SITE_URL=\"$DEPLOY_PRIME_URL/\" SOCIAL_CARDS=false mkdocs build --strict" + command = "pip install -r docs/requirements.txt && SITE_URL=\"$DEPLOY_PRIME_URL/\" mkdocs build --strict" [context.branch-deploy] - command = "pip install -r docs/requirements.txt && SITE_URL=\"$DEPLOY_PRIME_URL/\" SOCIAL_CARDS=false mkdocs build --strict" + command = "pip install -r docs/requirements.txt && SITE_URL=\"$DEPLOY_PRIME_URL/\" mkdocs build --strict" # The URLs the original Sphinx site served. The build does not emit redirect # stubs on Netlify (see scripts/mkdocs_hooks.py): a stub at rules.html would be diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 19419ff..0c99412 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -54,6 +54,12 @@ # An orphan: nothing linked to it, it was not in the nav, and it matched no # key in .authors.yml, so the blog never treated it as an author page. "blog/author/team": "blog/", + # Material's blog plugin wrote an archive page and one page per category. + # Sumi's blog index lists every post and filters by tag in place, so these + # all land on it. + "blog/archive/2026": "blog/", + "blog/category/announcements": "blog/", + "blog/category/updates": "blog/", } # Redirect stubs for the URLs the Sphinx site served.