Skip to content

Commit 9e3fb56

Browse files
committed
Add scientific-python-myst-theme
1 parent 6c54a4c commit 9e3fb56

10 files changed

Lines changed: 336 additions & 5 deletions

File tree

assets/css/custom.css

Lines changed: 0 additions & 3 deletions
This file was deleted.

content/.copier-answers.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2+
_commit: e390239
3+
_src_path: gh:scientific-python/scientific-python-myst-theme
4+
favicon: https://raw.githubusercontent.com/scientific-python/scientific-python.org/main/static/favicon.ico
5+
project_name: Learn Scientific Python
6+
project_url: learn.scientific-python.org
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
body {
2+
display: flex;
3+
flex-direction: column;
4+
min-height: 100vh;
5+
}
6+
7+
main {
8+
min-height: 0;
9+
flex: 1 0 auto;
10+
}
11+
12+
.article.content {
13+
/* Override 100vh from myst-theme:styles/typography.css so content div
14+
* doesn't grow <main> to push the footer offscreen.
15+
*/
16+
min-height: 0;
17+
}
18+
19+
.footer {
20+
/* Make footer "sticky" to page bottom (also the above flex rules), per
21+
* the flexbox strategy described here:
22+
* https://css-tricks.com/couple-takes-sticky-footer/#aa-there-is-flexbox
23+
* and here:
24+
* https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/
25+
* This solution does not require hardcoding a fixed footer height in the
26+
* style rules.
27+
*/
28+
flex-shrink: 0;
29+
background: #013243;
30+
color: white;
31+
padding-left: 2rem;
32+
padding-right: 2rem;
33+
34+
padding-left: 3.5rem;
35+
padding-right: 3.5rem;
36+
37+
/* Outer content grid */
38+
& .outer-grid {
39+
/* spacer, project description, spacer, link columns, spacer */
40+
grid-template-columns: 3fr 3fr 4fr;
41+
align-items: center;
42+
margin-bottom: 0rem;
43+
44+
& li {
45+
list-style: none;
46+
}
47+
}
48+
49+
@media (max-width: 640px) {
50+
& .outer-grid {
51+
grid-template-columns: 1fr;
52+
justify-items: start;
53+
}
54+
}
55+
56+
/* Heading colours */
57+
& a,
58+
h1,
59+
h2,
60+
h3,
61+
h4,
62+
h5,
63+
h6 {
64+
color: white;
65+
}
66+
67+
& h1 {
68+
font-size: 1.25rem;
69+
font-weight: bold;
70+
}
71+
}
72+
73+
/* Hide download button */
74+
.myst-fm-downloads-button {
75+
display: none;
76+
}
77+
78+
/* Set site title bold */
79+
.myst-home-link {
80+
font-weight: bold;
81+
}
82+
83+
/* Unsure where this CSS came from, but keeping it for now */
84+
/* --- Make layout feel less "book-ish" and closer to a website --- */
85+
.bd-main .bd-content .bd-article-container {
86+
max-width: 900px; /* keep readable, less wide than default "docs" feel */
87+
}
88+
89+
h1,
90+
h2,
91+
h3,
92+
h4 {
93+
letter-spacing: -0.01em;
94+
}
95+
96+
/* Slightly tighten the top spacing so it feels more like a marketing/docs site */
97+
.bd-header {
98+
box-shadow: none;
99+
}
100+
101+
/* If the left sidebar feels too dominant, reduce its visual weight */
102+
.bd-sidebar-primary {
103+
border-right: 0;
104+
}

content/assets/images/favicon.ico

14.7 KB
Binary file not shown.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: 1
2+
project:
3+
plugins:
4+
- team-grid.mjs
5+
6+
site:
7+
template: book-theme
8+
parts:
9+
primary_sidebar_footer: sidebar-footer.md
10+
footer: footer.md
11+
options:
12+
style: assets/css/scientific-python.css
13+
hide_toc: true
14+
hide_footer_links: true
15+
folders: true
16+
hide_myst_branding: true

content/footer.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
% This defines the footer of the site, and is not parsed as a regular "page"
2+
% We point to it with the following in `myst.yml`:
3+
% site:
4+
% parts:
5+
% footer: footer.md
6+
7+
% Here we use `grid` to add a basic grid structure to the HTML,
8+
% but the formatting column sizes are defined manually in css/footer.css
9+
% see the `grid-template-columns` line.
10+
:::::{grid} 3 3 5 5
11+
:class: outer-grid col-screen
12+
13+
<!-- Project description -->
14+
15+
::::{div}
16+
17+
```{image} assets/images/logo.svg
18+
:width: 60px
19+
:align: left
20+
```
21+
22+
Community-driven and community-owned initiative dedicated to building a robust, sustainable ecosystem for statistical software in Python.
23+
::::
24+
25+
<!-- Spacer between project description and links columns -->
26+
27+
::::{div}
28+
::::
29+
30+
<!-- Link columns -->
31+
32+
% This a _second_ grid embedded within the first one, to create nicer
33+
% responsive design experience. This grid will have a single column on narrow screens,
34+
% and fan out into three columns on wide screens. However, it always remains within
35+
% its parent grid column.
36+
::::{grid} 1 1 3 3
37+
38+
:::{div}
39+
40+
- [About](/about)
41+
:::
42+
43+
::::
44+
45+
:::::

content/myst.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
version: 1
2+
# Inherit shared Scientific Python theme (template, footer, plugins, CSS).
3+
# Vendored from scientific-python/scientific-python-myst-theme via copier;
4+
# see docs/decisions/0001-myst-migration/0009-myst-theme-integration.md.
5+
extends: config/scientific-python.yml
26

37
project:
48
id: 97f4bf9c-5845-4aac-aa8d-1051725d9067 # fixed UUID; MyST regenerates on each build without this, breaking caching
@@ -52,5 +56,9 @@ site:
5256
url: https://tools.scientific-python.org
5357
options:
5458
domain: learn.scientific-python.org
55-
logo: ../assets/images/logo.svg
56-
folders: true
59+
logo: assets/images/logo.svg
60+
favicon: assets/images/favicon.ico
61+
# template, folders, hide_*, and style inherited from
62+
# config/scientific-python.yml (style: assets/css/scientific-python.css).
63+
# No local style override: mystmd >=1.10 requires style to be a single
64+
# string, so the former theme-CSS + custom.css layering is not possible.

content/team-grid.mjs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { readFileSync } from 'node:fs';
2+
import { resolve, dirname } from 'node:path';
3+
4+
function parseColumns(str) {
5+
const cols = (str ?? '1 2 2 3')
6+
.trim()
7+
.split(/\s+/)
8+
.map(Number)
9+
.filter((n) => !isNaN(n) && n >= 1 && n <= 12);
10+
return cols.length ? cols : [1, 2, 2, 3];
11+
}
12+
13+
/** @type {import('myst-common').DirectiveSpec} */
14+
const teamGridDirective = {
15+
name: 'team-grid',
16+
doc: 'Display a team member grid from a JSON file.',
17+
options: {
18+
file: {
19+
type: String,
20+
required: true,
21+
doc: 'Path to the JSON data file, relative to the page file.',
22+
},
23+
columns: {
24+
type: String,
25+
doc: 'Column counts for xs/sm/md/lg breakpoints, e.g. "2 3 4 5".',
26+
},
27+
},
28+
run(data, vfile) {
29+
const pageDir = dirname(vfile.path);
30+
const filePath = resolve(pageDir, data.options.file);
31+
const members = JSON.parse(readFileSync(filePath, 'utf-8'));
32+
33+
const cards = members.map((member) => ({
34+
type: 'card',
35+
url: member.url,
36+
children: [
37+
{
38+
type: 'image',
39+
url: member.avatarUrl,
40+
alt: `Avatar of ${member.name}`,
41+
},
42+
{
43+
type: 'paragraph',
44+
children: [{ type: 'text', value: member.name }],
45+
},
46+
],
47+
}));
48+
49+
return [
50+
{
51+
type: 'grid',
52+
columns: parseColumns(data.options?.columns),
53+
children: cards,
54+
},
55+
];
56+
},
57+
};
58+
59+
/** @type {import('myst-common').MystPlugin} */
60+
const plugin = {
61+
name: 'Team Grid',
62+
directives: [teamGridDirective],
63+
};
64+
65+
export default plugin;
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# ADR 0009 — Integrate scientific-python-myst-theme via copier (committed files)
2+
3+
Date: 2026-05-28
4+
Status: Proposed
5+
Branch: lb/myst-migration
6+
7+
## Context
8+
9+
`scientific-python/scientific-python-myst-theme` provides shared MyST styling,
10+
config, plugins (`team-grid.mjs`), assets (logo, favicon, CSS), and a footer
11+
template. It is distributed as a **copier template**, not a pip/npm package: the
12+
intended consumption pattern is for a site's `myst.yml` to `extends:` a
13+
`config/scientific-python.yml` shipped in the template.
14+
15+
The theme is brand-new (created 2026-05-18, 0 tagged releases), so upstream
16+
`main` HEAD is the only pinning target. We need a strategy for pulling theme
17+
files into `content/` that is reproducible on Netlify CI (`make html-all`), on a
18+
fresh `git clone` + `myst start`, and across maintainer machines.
19+
20+
Constraint: MyST resolves `myst.yml` paths (`extends:`, `style:`, `logo:`,
21+
plugins) relative to the `myst.yml` location, which is `content/`. Theme files
22+
must land at predictable paths under `content/`.
23+
24+
## Decision
25+
26+
Adopt **committed theme files, vendored selectively from a copier render** (a
27+
refinement of Option 2).
28+
29+
Upstream is a _scaffold-a-new-site_ template: a full `copier copy` renders
30+
`index.md`, `myst.yml`, `about.md`, `news.md`, `Makefile`, `.gitignore`
31+
alongside the theme infrastructure. Our `content/` already holds a populated
32+
site, so a direct copy would clobber `index.md`/`myst.yml` and add unwanted
33+
pages. We therefore render to a throwaway dir and copy in only the theme files.
34+
35+
Implementation (as performed):
36+
37+
- Render once to a temp dir via `pixi exec --spec copier copier copy --trust
38+
--defaults gh:scientific-python/scientific-python-myst-theme <tmp>` (copier is
39+
run ephemerally; it is not a project dependency).
40+
- Vendor only these into `content/`, committed: `config/scientific-python.yml`,
41+
`assets/css/scientific-python.css`, `assets/images/logo.svg`,
42+
`assets/images/favicon.ico`, `team-grid.mjs`, `footer.md`, and
43+
`.copier-answers.yml` (provenance: records upstream `_commit`).
44+
- Add `extends: config/scientific-python.yml` to `content/myst.yml`; `style`
45+
(`assets/css/scientific-python.css`) is inherited from it. No local style
46+
override: mystmd >=1.10 requires `style` to be a single string, and MyST does
47+
not bundle a stylesheet's CSS `@import`s, so theme CSS plus a separate local
48+
override file cannot coexist. The former local `custom.css` (a navbar Lato
49+
font tweak) was dropped.
50+
- Remove the previously duplicated top-level `assets/` (logo/favicon byte
51+
identical to theme copies); `content/assets/` is now the single source.
52+
- Do not add copier to the Netlify build or `make prepare`; CI consumes the
53+
committed files as-is.
54+
55+
Update path: `copier update` cannot run cleanly (it conflicts on the diverged
56+
scaffold files), so updates are done by re-rendering to a temp dir and diffing
57+
the vendored files by hand. Revisit Option 3 once upstream separates "theme
58+
core" from "site scaffold" and cuts tagged releases.
59+
60+
Known upstream gap: `config/scientific-python.yml` sets
61+
`primary_sidebar_footer: sidebar-footer.md`, which the template does not ship; the
62+
build emits a non-fatal warning. To be raised upstream, not stubbed.
63+
64+
`external-content/cookie` is unaffected (ADR 0004): it is a separately-built
65+
Jekyll site overlaid on `public/development/`, whereas the theme is config plus
66+
assets consumed by the MyST build itself.
67+
68+
## Options considered
69+
70+
1. **Git submodule** (mirror cookie) — introduces transient copied files, dual
71+
source of truth, and a copy step on every `myst start`; a copier template is
72+
not a runnable artifact, so a source submodule is a structural mismatch.
73+
2. **Copier copy, committed files** (chosen) — zero new build-time deps,
74+
`myst start` works on fresh clone. Tradeoff: manual update cadence, drift risk.
75+
3. **Scheduled GitHub Action + copier update** — cron PR on diff; automatic but
76+
adds a workflow and auto-PR conflict maintenance. Premature with no releases.
77+
4. **Pre-commit hook running copier update** — slows every commit, doesn't help
78+
CI on fresh checkout, surprises contributors without copier. Rejected.
79+
5. **Rethink cookie integration** — cookie's Ruby toolchain and release tags keep
80+
submodule + Makefile right for it; this decision does not alter ADR 0004.
81+
82+
## Consequences
83+
84+
- `netlify.toml` and `make prepare` are unchanged; `make html-all` (the
85+
Netlify build path) works because all theme files are on disk after `git
86+
clone`. Fresh clone `git clone && cd content && myst start` needs no setup.
87+
- `pixi.toml` `build`/`serve` tasks (local-only, git-excluded) were fixed to run
88+
with `cwd = content`.
89+
- Manual updates risk upstream drift; pin `.copier-answers.yml` to the first
90+
tagged release when available, and revisit Option 3 then.

0 commit comments

Comments
 (0)