Skip to content

Commit 3c11db8

Browse files
Remove double-linking of theme CSS (#598)
Co-authored-by: Damian Avila <[email protected]>
1 parent 66f491d commit 3c11db8

File tree

7 files changed

+88
-84
lines changed

7 files changed

+88
-84
lines changed

CONTRIBUTING.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ Contributions are very welcome! Installing the development version, building
44
the demo docs and developing the css/js of the theme, etc, is explained in
55
more detail in the contributing section of the documentation:
66

7-
- [Contributing source files](docs/contributing.md)
8-
- [Contributing rendered docs](https://pydata-sphinx-theme.readthedocs.io/en/latest/contributing.html)
7+
- [Contributing source files](docs/contribute/index.md)
8+
- [Contributing rendered docs](https://pydata-sphinx-theme.readthedocs.io/en/latest/contribute/index.html)

docs/contribute/index.md

-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ The CSS and JS for this theme are built for the browser from `src/pydata_sphinx_
3636

3737
- the main part of the theme assets
3838
- customizes [Bootstrap](https://getbootstrap.com/) with [Sass](https://sass-lang.com)
39-
- points to the `font-face` of vendored web fonts, but does not include their
40-
CSS `@font-face` declaration
4139

4240
- JS: `src/pydata_sphinx_theme/assets/scripts/index.js`
4341

docs/contribute/topics.md

+49-61
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,62 @@ $ pre-commit run --all-files
7575
$ pre-commit run -a
7676
```
7777

78-
## Web asset compiling and bundling
78+
## Web assets (CSS/JS/Fonts)
7979

80-
All of the CSS and JS assets stored in `src/pydata_sphinx_theme/assets` will be compiled and bundled with the theme when you build it locally.
81-
These bundled assets will be placed in `src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static`.
80+
This theme includes several web assets to ease development and design.
81+
The configuration for our asset compilation is in `webpack.config.js`.
8282

83-
The configuration that defines what happens upon compilation is at `webpack.config.js`.
83+
### Compile and bundle assets
8484

85-
When assets are compiled, a `<hash>` is generated for each, and appended to the end of the asset's reference in the HTML templates of the theme.
85+
When assets are compiled, static versions are placed in various places in the theme's static folder:
86+
87+
```
88+
src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static
89+
```
90+
91+
For many assets, a `<hash>` is generated and appended to the end of its reference in the HTML templates of the theme.
8692
This ensures the correct asset versions are served when viewers return to your
8793
site after upgrading the theme.
8894

89-
Web fonts, and their supporting CSS, are copied into
90-
`src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static/vendor/<font name>/<font version>/`.
95+
To compile the assets and bundle them with the theme, run this command:
96+
97+
```console
98+
$ nox -s compile
99+
```
100+
101+
### Styles (SCSS) and Scripts (JS)
102+
103+
There are two relevant places for CSS/JS assets:
104+
105+
- `src/pydata_sphinx_theme/assets/styles` has source files for SCSS assets. These will be compiled to CSS.
106+
- `src/pydata_sphinx_theme/assets/scripts` has source files for JS assets. These will be compiled to JS and import several vendored libraries (like Bootstrap).
107+
- `src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static` has compiled versions of these assets (e.g. CSS files). This folder is not tracked in `.git` history, but it is bundled with the theme's distribution.
91108

92-
The links to these unique file names are captured as Jinja2 macros that are defined in HTML templates bundled with the theme.
109+
### Vendored scripts
110+
111+
We vendor several packages in addition to our own CSS and JS.
112+
For example, Bootstrap, JQuery, and Popper.
113+
This is configured in the `webpack.config.js` file, and imported in the respective `SCSS` or `JS` file in our assets folder.
114+
115+
### FontAwesome icons
116+
117+
Three "styles" of the [FontAwesome 5 Free](https://fontawesome.com/icons?m=free)
118+
icon font are used for {ref}`icon links <icon-links>` and admonitions, and is
119+
the only `vendored` font.
120+
121+
- It is managed as a dependency in `package.json`
122+
- Copied directly into the site statics at compilation, including licenses
123+
- Partially preloaded to reduce flicker and artifacts of early icon renders
124+
- Configured in `webpack.config.js`
125+
126+
### Jinja macros
127+
128+
Our Webpack build generates a collection of [Jinja macros](https://jinja.palletsprojects.com/en/3.0.x/templates/#macros) in the `static/webpack-macros.html` file.
129+
130+
These macros are imported in the main `layout.html` file, and then inserted at various places in the page to link the static assets.
131+
132+
Some of the assets [are "preloaded"](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload), meaning that the browser begins requesting these resources before they're actually needed.
133+
In particular, our JavaScript assets are preloaded in `<head>`, and the scripts are actually loaded at the end of `<body>`.
93134

94135
## Accessibility checks
95136

@@ -171,59 +212,6 @@ The output of the last command includes:
171212
- a short summary of the current state of the accessibility rules we are trying to maintain
172213
- local paths to JSON and HTML reports which contain all of the issues found
173214

174-
## Change fonts
175-
176-
Three "styles" of the [FontAwesome 5 Free](https://fontawesome.com/icons?m=free)
177-
icon font are used for {ref}`icon links <icon-links>` and admonitions, and is
178-
the only `vendored` font. Further font choices are described in the {ref}`customizing`
179-
section of the user guide, and require some knowledge of HTML and CSS.
180-
181-
The remaining vendored font selection is:
182-
183-
- managed as a dependency in `package.json`
184-
185-
- allowing the version to be managed centrally
186-
187-
- copied directly into the site statics, including licenses
188-
189-
- allowing the chosen font to be replaced (or removed entirely) with minimal
190-
templating changes: practically, changing the icon font is difficult at this
191-
point.
192-
193-
- partially preloaded
194-
195-
- reducing flicker and re-layout artifacts of early icon renders
196-
197-
- mostly managed in `webpack.js`
198-
199-
- allowing upgrades to be handled in a relatively sane, manageable way, to
200-
ensure the most recent icons
201-
202-
### Upgrade a font
203-
204-
If _only_ the version of the `existing` font must change, for example to enable
205-
new icons, edit the appropriate font version in `package.json`.
206-
Then re-compile the theme with:
207-
208-
```console
209-
$ nox -s compile
210-
```
211-
212-
### Change a font
213-
214-
If the above doesn't work, for example if file names for an existing font change,
215-
or a new font variant altogether is being added, hand-editing of `webpack.config.js`
216-
is required. The steps are roughly:
217-
218-
- install the new font, as above
219-
- in `webpack.config.js`:
220-
- add the new font to `vendorVersions` and `vendorPaths`
221-
- add new `link` tags to the appropriate macro in `macroTemplate`
222-
- add the new font files (including the license) to `CopyPlugin`
223-
- remove references to the font being replaced/removed, if applicable
224-
- see the `font-awesome` sections of this configuration to see what the end-result configuration looks like.
225-
- re-compile the theme's assets: `nox -s compile`
226-
227215
## Update our kitchen sink documents
228216

229217
The [kitchen sink reference](../demo/kitchen-sink/index.rst) is for demonstrating as much syntax and style for Sphinx builds as possible.

src/pydata_sphinx_theme/__init__.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def update_config(app, env):
4545

4646

4747
def update_templates(app, pagename, templatename, context, doctree):
48-
"""Update template names for page build."""
48+
"""Update template names and assets for page build."""
49+
# Allow for more flexibility in template names
4950
template_sections = [
5051
"theme_navbar_start",
5152
"theme_navbar_center",
@@ -55,7 +56,6 @@ def update_templates(app, pagename, templatename, context, doctree):
5556
"theme_left_sidebar_end",
5657
"sidebars",
5758
]
58-
5959
for section in template_sections:
6060
if context.get(section):
6161
# Break apart `,` separated strings so we can use , in the defaults
@@ -69,6 +69,14 @@ def update_templates(app, pagename, templatename, context, doctree):
6969
if not os.path.splitext(template)[1]:
7070
context[section][ii] = template + ".html"
7171

72+
# Remove a duplicate entry of the theme CSS. This is because it is in both:
73+
# - theme.conf
74+
# - manually linked in `webpack-macros.html`
75+
if "css_files" in context:
76+
theme_css_name = "_static/styles/pydata-sphinx-theme.css"
77+
if theme_css_name in context["css_files"]:
78+
context["css_files"].remove(theme_css_name)
79+
7280

7381
def add_toctree_functions(app, pagename, templatename, context, doctree):
7482
"""Add functions so Jinja templates can add toctree objects."""

src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22
{%- import "static/webpack-macros.html" as _webpack with context %}
33

44
{%- block css %}
5-
{{ _webpack.head_pre_bootstrap() }}
5+
{{ _webpack.head_pre_assets() }}
66
{{ _webpack.head_pre_icons() }}
7-
{% block fonts %}
8-
{{ _webpack.head_pre_fonts() }}
9-
{% endblock %}
107
{{- css() }}
118
{{ _webpack.head_js_preload() }}
129
{%- endblock %}

src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
[theme]
22
inherit = basic
3+
# Note that we don't link the CSS file via Sphinx
4+
# instead we manually link it in `webpack-macros.html`
35
stylesheet = styles/pydata-sphinx-theme.css
46
pygments_style = tango
57
sidebars = search-field.html, sidebar-nav-bs.html

webpack.config.js

+24-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
// Webpack configuration for pydata-sphinx-theme.
2-
//
3-
// There's a decent amount of complexity here, arising from the fact that we
4-
// have a fairly non-standard "JS application" here.
1+
/**
2+
* Webpack configuration for pydata-sphinx-theme.
3+
*
4+
* This script does a few primary things:
5+
*
6+
* - Generates a `webpack-macros.html` file that defines macros used
7+
* to insert CSS / JS at various places in the main `layout.html` template.
8+
* - Compiles our SCSS and JS and places them in the _static/ folder
9+
* - Downloads and links FontAwesome and some JS libraries (Bootstrap, jQuery, etc)
10+
*/
511

612
const { resolve } = require("path");
713
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
@@ -31,26 +37,31 @@ const vendorPaths = {
3137
//
3238
function macroTemplate({ compilation }) {
3339
const hash = compilation.hash;
40+
// We load these files into the theme via HTML templates
3441
const css_files = ["styles/theme.css", "styles/pydata-sphinx-theme.css"];
3542
const js_files = ["scripts/pydata-sphinx-theme.js"];
3643

44+
// Load a CSS script with a digest for cache busting.
3745
function stylesheet(css) {
3846
return `<link href="{{ pathto('_static/${css}', 1) }}?digest=${hash}" rel="stylesheet">`;
3947
}
4048

49+
// Pre-load a JS script (script will need to be loaded later on in the page)
4150
function preload(js) {
4251
return `<link rel="preload" as="script" href="{{ pathto('_static/${js}', 1) }}?digest=${hash}">`;
4352
}
4453

54+
// Load a JS script with a digest for cache busting.
4555
function script(js) {
4656
return `<script src="{{ pathto('_static/${js}', 1) }}?digest=${hash}"></script>`;
4757
}
4858

4959
return dedent(`\
5060
<!--
51-
All these macros are auto-generated and must **NOT** be edited by hand.
52-
See the webpack.config.js file, to learn more about how this is generated.
61+
AUTO-GENERATED from webpack.config.js, do **NOT** edit by hand.
62+
These are re-used in layout.html
5363
-->
64+
{# Load FontAwesome icons #}
5465
{% macro head_pre_icons() %}
5566
<link rel="stylesheet"
5667
href="{{ pathto('_static/vendor/fontawesome/${
@@ -66,19 +77,19 @@ function macroTemplate({ compilation }) {
6677
}/webfonts/fa-brands-400.woff2', 1) }}">
6778
{% endmacro %}
6879
69-
{% macro head_pre_fonts() %}
70-
{% endmacro %}
71-
72-
{% macro head_pre_bootstrap() %}
73-
${css_files.map(stylesheet).join("\n ")}
80+
{% macro head_pre_assets() %}
81+
<!-- Loaded before other Sphinx assets -->
82+
${css_files.map(stylesheet).join("\n")}
7483
{% endmacro %}
7584
7685
{% macro head_js_preload() %}
77-
${js_files.map(preload).join("\n ")}
86+
<!-- Pre-loaded scripts that we'll load fully later -->
87+
${js_files.map(preload).join("\n")}
7888
{% endmacro %}
7989
8090
{% macro body_post() %}
81-
${js_files.map(script).join("\n ")}
91+
<!-- Scripts loaded after <body> so the DOM is not blocked -->
92+
${js_files.map(script).join("\n")}
8293
{% endmacro %}
8394
`);
8495
}

0 commit comments

Comments
 (0)