diff --git a/copier.yml b/copier.yml
index b8a2041..4872dab 100644
--- a/copier.yml
+++ b/copier.yml
@@ -8,6 +8,11 @@ project_url:
type: str
help: what is your project url?
+footer_items_json:
+ type: str
+ help: path to JSON file defining links in the footer as a text-to-url mapping.
+ default: assets/json/footer_items.json
+
favicon:
type: str
help: what is your project favicon?
diff --git a/template/assets/css/scientific-python.css b/template/assets/css/scientific-python.css
index f29cdea..71f3166 100644
--- a/template/assets/css/scientific-python.css
+++ b/template/assets/css/scientific-python.css
@@ -31,15 +31,13 @@ main {
padding-left: 2rem;
padding-right: 2rem;
- padding-left: 3.5rem;
- padding-right: 3.5rem;
-
/* Outer content grid */
& .outer-grid {
- /* spacer, project description, spacer, link columns, spacer */
- grid-template-columns: 3fr 3fr 4fr;
+ /* logo, project description, link columns, social icons */
+ grid-template-columns: 2fr 6fr 8fr 3fr;
+ column-gap: 4rem;
align-items: center;
- margin-bottom: 0rem;
+ margin: 0rem 4rem;
& li {
list-style: none;
@@ -68,6 +66,35 @@ main {
font-size: 1.25rem;
font-weight: bold;
}
+ & .footer-links {
+ column-gap: 1.5rem;
+ line-height: normal;
+
+ & a {
+ color: white;
+ text-decoration-color: white;
+ inline-size: min-content;
+ display: inline-block;
+ text-wrap: nowrap;
+ }
+ }
+
+ & .footer-icons {
+ column-gap: 3rem;
+ row-gap: 1rem;
+
+ & img {
+ filter: invert(100%);
+ margin-bottom: 0.1rem;
+ margin-top: 0.1rem;
+ min-width: 1.75rem;
+ width: 1.75rem;
+ }
+ }
+}
+
+.footer-logo {
+ min-width: 60px;
}
/* Hide download button */
@@ -102,3 +129,7 @@ h4 {
.bd-sidebar-primary {
border-right: 0;
}
+
+.copyright {
+ font-size: smaller;
+}
diff --git a/template/assets/icons/README.md b/template/assets/icons/README.md
new file mode 100644
index 0000000..eff88d2
--- /dev/null
+++ b/template/assets/icons/README.md
@@ -0,0 +1,8 @@
+You can find more free icons at:
+
+- https://fonts.google.com/icons (Search and then click the SVG download button at the bottom of the right-hand column.)
+- https://simpleicons.org/
+
+Apache licensed, but with a notice at https://github.com/google/material-design-icons:
+
+> We'd love attribution in your app's about screen, but it's not required.
diff --git a/template/assets/icons/bluesky.svg b/template/assets/icons/bluesky.svg
new file mode 100644
index 0000000..e332981
--- /dev/null
+++ b/template/assets/icons/bluesky.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/discord.svg b/template/assets/icons/discord.svg
new file mode 100644
index 0000000..822322f
--- /dev/null
+++ b/template/assets/icons/discord.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/discourse.svg b/template/assets/icons/discourse.svg
new file mode 100644
index 0000000..28eb5f7
--- /dev/null
+++ b/template/assets/icons/discourse.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/facebook.svg b/template/assets/icons/facebook.svg
new file mode 100644
index 0000000..2f755c3
--- /dev/null
+++ b/template/assets/icons/facebook.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/github.svg b/template/assets/icons/github.svg
new file mode 100644
index 0000000..7840857
--- /dev/null
+++ b/template/assets/icons/github.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/instagram.svg b/template/assets/icons/instagram.svg
new file mode 100644
index 0000000..a117827
--- /dev/null
+++ b/template/assets/icons/instagram.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/linkedin.svg b/template/assets/icons/linkedin.svg
new file mode 100644
index 0000000..8414bf3
--- /dev/null
+++ b/template/assets/icons/linkedin.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/mail.svg b/template/assets/icons/mail.svg
new file mode 100644
index 0000000..c307bed
--- /dev/null
+++ b/template/assets/icons/mail.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/mastodon.svg b/template/assets/icons/mastodon.svg
new file mode 100644
index 0000000..d059845
--- /dev/null
+++ b/template/assets/icons/mastodon.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/rss-feed.svg b/template/assets/icons/rss-feed.svg
new file mode 100644
index 0000000..44b27a8
--- /dev/null
+++ b/template/assets/icons/rss-feed.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/scientific-python.svg b/template/assets/icons/scientific-python.svg
new file mode 100644
index 0000000..d9e0ad0
--- /dev/null
+++ b/template/assets/icons/scientific-python.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/sent.svg b/template/assets/icons/sent.svg
new file mode 100644
index 0000000..40c9490
--- /dev/null
+++ b/template/assets/icons/sent.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/tiktok.svg b/template/assets/icons/tiktok.svg
new file mode 100644
index 0000000..8248f39
--- /dev/null
+++ b/template/assets/icons/tiktok.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/twitter.svg b/template/assets/icons/twitter.svg
new file mode 100644
index 0000000..5e23e61
--- /dev/null
+++ b/template/assets/icons/twitter.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/icons/youtube.svg b/template/assets/icons/youtube.svg
new file mode 100644
index 0000000..eb6089f
--- /dev/null
+++ b/template/assets/icons/youtube.svg
@@ -0,0 +1 @@
+
diff --git a/template/assets/json/footer_items.json b/template/assets/json/footer_items.json
new file mode 100644
index 0000000..4ab433c
--- /dev/null
+++ b/template/assets/json/footer_items.json
@@ -0,0 +1,19 @@
+{
+ "links": {
+ "About": "/about",
+ "Roadmap": "/roadmap",
+ "Code of conduct": "/code_of_conduct",
+ "SPECs": "/specs",
+ "Summits": "/summits",
+ "Calendars": "/calendars",
+ "Press kit": "/press-kit"
+ },
+ "icons": {
+ "github": "https://github.com/scientific-python/",
+ "youtube": "https://www.youtube.com/c/ScientificPython-org",
+ "mastodon": "https://fosstodon.org/@scientific_python",
+ "bluesky": "https://bsky.app/profile/scientific-python.org",
+ "discourse": "https://discuss.scientific-python.org/",
+ "discord": "https://discord.com/invite/vur45CbwMz"
+ }
+}
diff --git a/template/config/scientific-python.yml b/template/config/scientific-python.yml
index 2e1bdd6..f0379e0 100644
--- a/template/config/scientific-python.yml
+++ b/template/config/scientific-python.yml
@@ -2,7 +2,9 @@ version: 1
project:
plugins:
- team-grid.mjs
-
+ - footer-items.mjs
+ exclude:
+ - assets/icons/README.md
site:
template: book-theme
parts:
diff --git a/template/footer-items.mjs b/template/footer-items.mjs
new file mode 100644
index 0000000..fe8c959
--- /dev/null
+++ b/template/footer-items.mjs
@@ -0,0 +1,77 @@
+import { readFileSync } from 'node:fs';
+
+/** @type {import('myst-common').DirectiveSpec} */
+const footerLinksDirective = {
+ name: 'footer-links',
+ doc: 'Display footer links from a JSON file.',
+ options: {
+ file: {
+ type: String,
+ required: true,
+ doc: 'Path to the JSON file, relative to the project root.',
+ },
+ },
+ run(data) {
+ const footerLinks = JSON.parse(readFileSync(data.options.file, 'utf-8'));
+ return Object.entries(footerLinks["links"]).map(
+ ([text, url]) => ({
+ type: 'grid-item',
+ children: [
+ {
+ type: 'link',
+ url: url,
+ children: [
+ {
+ type: 'text',
+ value: text
+ }
+ ]
+ }
+ ]
+ })
+ );
+ }
+};
+
+/** @type {import('myst-common').DirectiveSpec} */
+const footerIconsDirective = {
+ name: 'footer-icons',
+ doc: 'Display footer icons from a JSON file.',
+ options: {
+ file: {
+ type: String,
+ required: true,
+ doc: 'Path to the JSON file, relative to the project root.',
+ },
+ },
+ run(data) {
+ const footerIcons = JSON.parse(readFileSync(data.options.file, 'utf-8'));
+ return Object.entries(footerIcons["icons"]).map(
+ ([text, url]) => ({
+ type: 'grid-item',
+ children: [
+ {
+ type: 'link',
+ url: url,
+ children: [
+ {
+ type: 'image',
+ url: `/assets/icons/${text}.svg`,
+ alt: text
+ }
+ ]
+ }
+ ]
+ })
+ );
+ return IconsGrid;
+ }
+};
+
+/** @type {import('myst-common').MystPlugin} */
+const plugin = {
+ name: 'Footer Items',
+ directives: [footerLinksDirective, footerIconsDirective],
+};
+
+export default plugin;
diff --git a/template/footer.md.jinja b/template/footer.md.jinja
index 494e779..078c6e6 100644
--- a/template/footer.md.jinja
+++ b/template/footer.md.jinja
@@ -4,42 +4,58 @@
% parts:
% footer: footer.md
-% Here we use `grid` to add a basic grid structure to the HTML,
-% but the formatting column sizes are defined manually in css/footer.css
-% see the `grid-template-columns` line.
-:::::{grid} 3 3 5 5
-:class: outer-grid col-screen
-
-
+% Here we use `grid` to add a basic grid structure to the footer, but we don't use
+% :columns: property of the grid-items to set widths; instead they're defined in
+% asserts/css/scientific-python.css using the `grid-template-columns` property.
-::::{div}
+::::::{grid} 1 1 4 4
+:class: outer-grid col-screen
-```{image} assets/images/logo.svg
+
+:::::{grid-item}
+:::{image} assets/images/logo.svg
:width: 60px
-:align: left
-```
-
-Community-driven and community-owned initiative dedicated to building a robust, sustainable ecosystem for statistical software in Python.
-::::
-
-
-
-::::{div}
-::::
+:class: footer-logo
+:::
+:::::
-
+
+:::::{grid-item}
+Community-driven and community-owned initiative dedicated to building a robust, sustainable ecosystem for scientific software in Python.
+:::::
-% This a _second_ grid embedded within the first one, to create nicer
+
+:::::{grid-item}
+% This a grid embedded within the outer one, to create nicer
% responsive design experience. This grid will have a single column on narrow screens,
% and fan out into three columns on wide screens. However, it always remains within
% its parent grid column.
-::::{grid} 1 1 3 3
+::::{grid} 1 1 2 3
+:class: footer-links
+
+:::{footer-links}
+:file: {{ footer_items_json }}
+:::
+
+::::
+:::::
-:::{div}
+
+:::::{grid-item}
+:class: socials
-- [About](/about)
- :::
+::::{grid} 2 2 3 6
+:class: footer-icons
+
+:::{footer-icons}
+:file: {{ footer_items_json }}
+:::
::::
+:::{div .copyright}
+© 2026 {{ project_name }} team. All rights reserved.
+:::
+
:::::
+::::::