From e7fc03bce2c2577a9c5331ce5b2fa43a66ce7773 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Jul 2022 20:27:43 +0900 Subject: [PATCH] Allow specifying multiple CSS files in themes (#10465) Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- CHANGES | 3 +++ doc/development/theming.rst | 11 +++++---- doc/templating.rst | 18 +++++++++++++++ sphinx/builders/html/__init__.py | 23 ++++++++++++------- .../_themes/mytheme/_static/extra.css | 0 .../_themes/mytheme/_static/mytheme.css | 0 .../_themes/mytheme/theme.conf | 3 +++ .../conf.py | 2 ++ .../index.rst | 2 ++ tests/test_build_html.py | 9 ++++++++ 10 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/_static/extra.css create mode 100644 tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/_static/mytheme.css create mode 100644 tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/theme.conf create mode 100644 tests/roots/test-build-html-theme-having-multiple-stylesheets/conf.py create mode 100644 tests/roots/test-build-html-theme-having-multiple-stylesheets/index.rst diff --git a/CHANGES b/CHANGES index c939f56345d..e5a29a31f01 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,9 @@ Deprecated Features added -------------- +* #10444: html theme: Allow specifying multiple CSS files through the ``stylesheet`` + setting in ``theme.conf`` or by setting ``html_style`` to an iterable of strings. + Bugs fixed ---------- diff --git a/doc/development/theming.rst b/doc/development/theming.rst index fcbeb030a61..b46ba0362e6 100644 --- a/doc/development/theming.rst +++ b/doc/development/theming.rst @@ -56,10 +56,10 @@ Python :mod:`ConfigParser` module) and has the following structure: want to also inherit the stylesheet, include it via CSS' ``@import`` in your own. -* The **stylesheet** setting gives the name of a CSS file which will be - referenced in the HTML header. If you need more than one CSS file, either - include one from the other via CSS' ``@import``, or use a custom HTML template - that adds ```` tags as necessary. Setting the +* The **stylesheet** setting gives a list of CSS filenames separated commas which + will be referenced in the HTML header. You can also use CSS' ``@import`` + technique to include one from the other, or use a custom HTML template that + adds ```` tags as necessary. Setting the :confval:`html_style` config value will override this setting. * The **pygments_style** setting gives the name of a Pygments style to use for @@ -82,6 +82,9 @@ Python :mod:`ConfigParser` module) and has the following structure: .. versionadded:: 1.7 sidebar settings +.. versionchanged:: 5.1 + + The stylesheet setting accepts multiple CSS filenames .. _distribute-your-theme: diff --git a/doc/templating.rst b/doc/templating.rst index e174adbec78..27f56eb4499 100644 --- a/doc/templating.rst +++ b/doc/templating.rst @@ -392,11 +392,29 @@ in the future. .. versionadded:: 5.0.2 +.. data:: styles + + A list of the names of the main stylesheets as given by the theme or + :confval:`html_style`. + + .. versionadded:: 5.1 + .. data:: style The name of the main stylesheet, as given by the theme or :confval:`html_style`. + .. versionchanged:: 5.1 + + The theme or :confval:`html_style` are now able to specify multiple + stylesheets, the ``style`` key returns the last stylesheet when more than + one is specified. + + .. deprecated:: 5.1 + + Use the :data:`styles` key instead, as there is no longer a single main + stylesheet. The ``style`` key will be removed in Sphinx 7.0. + .. data:: title The title of the current document, as used in the ```` tag. diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 4a6bc6ce0ab..c9832d1c59a 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -279,13 +279,16 @@ def _get_translations_js(self) -> str: return jsfile return None - def _get_style_filename(self) -> str: - if self.config.html_style is not None: - return self.config.html_style + def _get_style_filenames(self) -> Iterator[str]: + if isinstance(self.config.html_style, str): + yield self.config.html_style + elif self.config.html_style is not None: + yield from self.config.html_style elif self.theme: - return self.theme.get_config('theme', 'stylesheet') + stylesheet = self.theme.get_config('theme', 'stylesheet') + yield from map(str.strip, stylesheet.split(',')) else: - return 'default.css' + yield 'default.css' def get_theme_config(self) -> Tuple[str, Dict]: return self.config.html_theme, self.config.html_theme_options @@ -324,7 +327,9 @@ def init_highlighter(self) -> None: def init_css_files(self) -> None: self.css_files = [] self.add_css_file('pygments.css', priority=200) - self.add_css_file(self._get_style_filename(), priority=200) + + for filename in self._get_style_filenames(): + self.add_css_file(filename, priority=200) for filename, attrs in self.app.registry.css_files: self.add_css_file(filename, **attrs) @@ -525,6 +530,7 @@ def prepare_writing(self, docnames: Set[str]) -> None: # back up script_files and css_files to allow adding JS/CSS files to a specific page. self._script_files = list(self.script_files) self._css_files = list(self.css_files) + styles = list(self._get_style_filenames()) self.globalcontext = { 'embedded': self.embedded, @@ -552,7 +558,8 @@ def prepare_writing(self, docnames: Set[str]) -> None: 'sphinx_version': __display_version__, 'sphinx_version_tuple': sphinx_version, 'docutils_version_info': docutils.__version_info__[:5], - 'style': self._get_style_filename(), + 'styles': styles, + 'style': styles[-1], # xref RemovedInSphinx70Warning 'rellinks': rellinks, 'builder': self.name, 'parents': [], @@ -1356,7 +1363,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: lambda self: _('%s %s documentation') % (self.project, self.release), 'html', [str]) app.add_config_value('html_short_title', lambda self: self.html_title, 'html') - app.add_config_value('html_style', None, 'html', [str]) + app.add_config_value('html_style', None, 'html', [list, str]) app.add_config_value('html_logo', None, 'html', [str]) app.add_config_value('html_favicon', None, 'html', [str]) app.add_config_value('html_css_files', [], 'html') diff --git a/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/_static/extra.css b/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/_static/extra.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/_static/mytheme.css b/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/_static/mytheme.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/theme.conf b/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/theme.conf new file mode 100644 index 00000000000..c87296e7eb8 --- /dev/null +++ b/tests/roots/test-build-html-theme-having-multiple-stylesheets/_themes/mytheme/theme.conf @@ -0,0 +1,3 @@ +[theme] +inherit = basic +stylesheet = mytheme.css, extra.css diff --git a/tests/roots/test-build-html-theme-having-multiple-stylesheets/conf.py b/tests/roots/test-build-html-theme-having-multiple-stylesheets/conf.py new file mode 100644 index 00000000000..3cb43d6110e --- /dev/null +++ b/tests/roots/test-build-html-theme-having-multiple-stylesheets/conf.py @@ -0,0 +1,2 @@ +html_theme_path = ['_themes'] +html_theme = 'mytheme' diff --git a/tests/roots/test-build-html-theme-having-multiple-stylesheets/index.rst b/tests/roots/test-build-html-theme-having-multiple-stylesheets/index.rst new file mode 100644 index 00000000000..b8b81f9d3f9 --- /dev/null +++ b/tests/roots/test-build-html-theme-having-multiple-stylesheets/index.rst @@ -0,0 +1,2 @@ +test-build-html-theme-having-multiple-stylesheets +================================================= diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 70141497bbf..2b74690a14e 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1782,3 +1782,12 @@ def test_theme_options_with_override(app, status, warning): result = (app.outdir / '_static' / 'documentation_options.js').read_text(encoding='utf8') assert 'NAVIGATION_WITH_KEYS: true' in result assert 'ENABLE_SEARCH_SHORTCUTS: false' in result + + +@pytest.mark.sphinx('html', testroot='build-html-theme-having-multiple-stylesheets') +def test_theme_having_multiple_stylesheets(app): + app.build() + content = (app.outdir / 'index.html').read_text(encoding='utf-8') + + assert '<link rel="stylesheet" type="text/css" href="_static/mytheme.css" />' in content + assert '<link rel="stylesheet" type="text/css" href="_static/extra.css" />' in content