diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc66f8dbb..55e6f3d8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,5 +4,5 @@ Contributions are very welcome! Installing the development version, building the demo docs and developing the css/js of the theme, etc, is explained in more detail in the contributing section of the documentation: -- [Contributing source files](docs/contributing.md) -- [Contributing rendered docs](https://pydata-sphinx-theme.readthedocs.io/en/latest/contributing.html) +- [Contributing source files](docs/contribute/index.md) +- [Contributing rendered docs](https://pydata-sphinx-theme.readthedocs.io/en/latest/contribute/index.html) diff --git a/docs/contribute/index.md b/docs/contribute/index.md index b397ec748..dbd9a7ef2 100644 --- a/docs/contribute/index.md +++ b/docs/contribute/index.md @@ -36,8 +36,6 @@ The CSS and JS for this theme are built for the browser from `src/pydata_sphinx_ - the main part of the theme assets - customizes [Bootstrap](https://getbootstrap.com/) with [Sass](https://sass-lang.com) - - points to the `font-face` of vendored web fonts, but does not include their - CSS `@font-face` declaration - JS: `src/pydata_sphinx_theme/assets/scripts/index.js` diff --git a/docs/contribute/topics.md b/docs/contribute/topics.md index c09ea2cf8..4ebefdfe4 100644 --- a/docs/contribute/topics.md +++ b/docs/contribute/topics.md @@ -75,21 +75,62 @@ $ pre-commit run --all-files $ pre-commit run -a ``` -## Web asset compiling and bundling +## Web assets (CSS/JS/Fonts) -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. -These bundled assets will be placed in `src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static`. +This theme includes several web assets to ease development and design. +The configuration for our asset compilation is in `webpack.config.js`. -The configuration that defines what happens upon compilation is at `webpack.config.js`. +### Compile and bundle assets -When assets are compiled, a `` is generated for each, and appended to the end of the asset's reference in the HTML templates of the theme. +When assets are compiled, static versions are placed in various places in the theme's static folder: + +``` +src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static +``` + +For many assets, a `` is generated and appended to the end of its reference in the HTML templates of the theme. This ensures the correct asset versions are served when viewers return to your site after upgrading the theme. -Web fonts, and their supporting CSS, are copied into -`src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static/vendor///`. +To compile the assets and bundle them with the theme, run this command: + +```console +$ nox -s compile +``` + +### Styles (SCSS) and Scripts (JS) + +There are two relevant places for CSS/JS assets: + +- `src/pydata_sphinx_theme/assets/styles` has source files for SCSS assets. These will be compiled to CSS. +- `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). +- `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. -The links to these unique file names are captured as Jinja2 macros that are defined in HTML templates bundled with the theme. +### Vendored scripts + +We vendor several packages in addition to our own CSS and JS. +For example, Bootstrap, JQuery, and Popper. +This is configured in the `webpack.config.js` file, and imported in the respective `SCSS` or `JS` file in our assets folder. + +### FontAwesome icons + +Three "styles" of the [FontAwesome 5 Free](https://fontawesome.com/icons?m=free) +icon font are used for {ref}`icon links ` and admonitions, and is +the only `vendored` font. + +- It is managed as a dependency in `package.json` +- Copied directly into the site statics at compilation, including licenses +- Partially preloaded to reduce flicker and artifacts of early icon renders +- Configured in `webpack.config.js` + +### Jinja macros + +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. + +These macros are imported in the main `layout.html` file, and then inserted at various places in the page to link the static assets. + +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. +In particular, our JavaScript assets are preloaded in ``, and the scripts are actually loaded at the end of ``. ## Accessibility checks @@ -171,59 +212,6 @@ The output of the last command includes: - a short summary of the current state of the accessibility rules we are trying to maintain - local paths to JSON and HTML reports which contain all of the issues found -## Change fonts - -Three "styles" of the [FontAwesome 5 Free](https://fontawesome.com/icons?m=free) -icon font are used for {ref}`icon links ` and admonitions, and is -the only `vendored` font. Further font choices are described in the {ref}`customizing` -section of the user guide, and require some knowledge of HTML and CSS. - -The remaining vendored font selection is: - -- managed as a dependency in `package.json` - - - allowing the version to be managed centrally - -- copied directly into the site statics, including licenses - - - allowing the chosen font to be replaced (or removed entirely) with minimal - templating changes: practically, changing the icon font is difficult at this - point. - -- partially preloaded - - - reducing flicker and re-layout artifacts of early icon renders - -- mostly managed in `webpack.js` - - - allowing upgrades to be handled in a relatively sane, manageable way, to - ensure the most recent icons - -### Upgrade a font - -If _only_ the version of the `existing` font must change, for example to enable -new icons, edit the appropriate font version in `package.json`. -Then re-compile the theme with: - -```console -$ nox -s compile -``` - -### Change a font - -If the above doesn't work, for example if file names for an existing font change, -or a new font variant altogether is being added, hand-editing of `webpack.config.js` -is required. The steps are roughly: - -- install the new font, as above -- in `webpack.config.js`: - - add the new font to `vendorVersions` and `vendorPaths` - - add new `link` tags to the appropriate macro in `macroTemplate` - - add the new font files (including the license) to `CopyPlugin` - - remove references to the font being replaced/removed, if applicable - - see the `font-awesome` sections of this configuration to see what the end-result configuration looks like. -- re-compile the theme's assets: `nox -s compile` - ## Update our kitchen sink documents The [kitchen sink reference](../demo/kitchen-sink/index.rst) is for demonstrating as much syntax and style for Sphinx builds as possible. diff --git a/src/pydata_sphinx_theme/__init__.py b/src/pydata_sphinx_theme/__init__.py index 639e591a7..acfe9c02f 100644 --- a/src/pydata_sphinx_theme/__init__.py +++ b/src/pydata_sphinx_theme/__init__.py @@ -45,7 +45,8 @@ def update_config(app, env): def update_templates(app, pagename, templatename, context, doctree): - """Update template names for page build.""" + """Update template names and assets for page build.""" + # Allow for more flexibility in template names template_sections = [ "theme_navbar_start", "theme_navbar_center", @@ -55,7 +56,6 @@ def update_templates(app, pagename, templatename, context, doctree): "theme_left_sidebar_end", "sidebars", ] - for section in template_sections: if context.get(section): # Break apart `,` separated strings so we can use , in the defaults @@ -69,6 +69,14 @@ def update_templates(app, pagename, templatename, context, doctree): if not os.path.splitext(template)[1]: context[section][ii] = template + ".html" + # Remove a duplicate entry of the theme CSS. This is because it is in both: + # - theme.conf + # - manually linked in `webpack-macros.html` + if "css_files" in context: + theme_css_name = "_static/styles/pydata-sphinx-theme.css" + if theme_css_name in context["css_files"]: + context["css_files"].remove(theme_css_name) + def add_toctree_functions(app, pagename, templatename, context, doctree): """Add functions so Jinja templates can add toctree objects.""" diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html index b0bc069bc..cff38d03c 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html @@ -2,11 +2,8 @@ {%- import "static/webpack-macros.html" as _webpack with context %} {%- block css %} - {{ _webpack.head_pre_bootstrap() }} + {{ _webpack.head_pre_assets() }} {{ _webpack.head_pre_icons() }} - {% block fonts %} - {{ _webpack.head_pre_fonts() }} - {% endblock %} {{- css() }} {{ _webpack.head_js_preload() }} {%- endblock %} diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf index 140692947..1de2beafa 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf @@ -1,5 +1,7 @@ [theme] inherit = basic +# Note that we don't link the CSS file via Sphinx +# instead we manually link it in `webpack-macros.html` stylesheet = styles/pydata-sphinx-theme.css pygments_style = tango sidebars = search-field.html, sidebar-nav-bs.html diff --git a/webpack.config.js b/webpack.config.js index ef6221ae5..24d166edb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,7 +1,13 @@ -// Webpack configuration for pydata-sphinx-theme. -// -// There's a decent amount of complexity here, arising from the fact that we -// have a fairly non-standard "JS application" here. +/** + * Webpack configuration for pydata-sphinx-theme. + * + * This script does a few primary things: + * + * - Generates a `webpack-macros.html` file that defines macros used + * to insert CSS / JS at various places in the main `layout.html` template. + * - Compiles our SCSS and JS and places them in the _static/ folder + * - Downloads and links FontAwesome and some JS libraries (Bootstrap, jQuery, etc) + */ const { resolve } = require("path"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); @@ -31,26 +37,31 @@ const vendorPaths = { // function macroTemplate({ compilation }) { const hash = compilation.hash; + // We load these files into the theme via HTML templates const css_files = ["styles/theme.css", "styles/pydata-sphinx-theme.css"]; const js_files = ["scripts/pydata-sphinx-theme.js"]; + // Load a CSS script with a digest for cache busting. function stylesheet(css) { return ``; } + // Pre-load a JS script (script will need to be loaded later on in the page) function preload(js) { return ``; } + // Load a JS script with a digest for cache busting. function script(js) { return ``; } return dedent(`\ + {# Load FontAwesome icons #} {% macro head_pre_icons() %} {% endmacro %} - {% macro head_pre_fonts() %} - {% endmacro %} - - {% macro head_pre_bootstrap() %} - ${css_files.map(stylesheet).join("\n ")} + {% macro head_pre_assets() %} + + ${css_files.map(stylesheet).join("\n")} {% endmacro %} {% macro head_js_preload() %} - ${js_files.map(preload).join("\n ")} + + ${js_files.map(preload).join("\n")} {% endmacro %} {% macro body_post() %} - ${js_files.map(script).join("\n ")} + + ${js_files.map(script).join("\n")} {% endmacro %} `); }