diff --git a/CHANGES b/CHANGES
index c939f5634..e5a29a31f 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 fcbeb030a..b46ba0362 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 e174adbec..27f56eb44 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 4a6bc6ce0..c9832d1c5 100644
--- a/sphinx/builders/html/__init__.py
+++ b/sphinx/builders/html/__init__.py
@@ -279,13 +279,16 @@ class StandaloneHTMLBuilder(Builder):
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 @@ class StandaloneHTMLBuilder(Builder):
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 @@ class StandaloneHTMLBuilder(Builder):
# 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 @@ class StandaloneHTMLBuilder(Builder):
'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 000000000..e69de29bb
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 000000000..e69de29bb
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 000000000..c87296e7e
--- /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 000000000..3cb43d611
--- /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 000000000..b8b81f9d3
--- /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 70141497b..2b74690a1 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 '' in content
+ assert '' in content