Support CSS_FILE in Simple theme

This commit is contained in:
Sam Bull 2026-04-14 11:05:32 +01:00 committed by GitHub
commit 691995c142
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 134 additions and 33 deletions

Binary file not shown.

View file

@ -589,38 +589,12 @@ using the ``{% extends %}`` directive as in the following example:
Example
-------
With this system, it is possible to create a theme with just two files.
With this system, it is possible to create a theme with just one file.
base.html
"""""""""
main.css
""""""""
The first file is the ``templates/base.html`` template:
.. code-block:: html+jinja
{% extends "!simple/base.html" %}
{% block head %}
{{ super() }}
<link rel="stylesheet" type="text/css" href="{{ SITEURL }}/theme/css/style.css" />
{% endblock %}
1. On the first line, we extend the ``base.html`` template from the ``simple``
theme, so we don't have to rewrite the entire file.
2. On the third line, we open the ``head`` block which has already been defined
in the ``simple`` theme.
3. On the fourth line, the function ``super()`` keeps the content previously
inserted in the ``head`` block.
4. On the fifth line, we append a stylesheet to the page.
5. On the last line, we close the ``head`` block.
This file will be extended by all the other templates, so the stylesheet will
be linked from all pages.
style.css
"""""""""
The second file is the ``static/css/style.css`` CSS stylesheet:
The file is ``static/css/main.css`` (the default ``CSS_FILE``):
.. code-block:: css
@ -663,10 +637,32 @@ The second file is the ``static/css/style.css`` CSS stylesheet:
margin-top : 1em ;
}
Download
""""""""
Custom templates
""""""""""""""""
You can download this example theme :download:`here <_static/theme-basic.zip>`.
If you want to customise the templates as well, you can extend them. For
example, to add a custom footer, create a ``templates/base.html``:
.. code-block:: html+jinja
{% extends "!simple/base.html" %}
{% block footer %}
<p>Custom footer</p>
{{ super() }}
{% endblock %}
1. On the first line, we extend the ``base.html`` template from the ``simple``
theme, so we don't have to rewrite the entire file.
2. On the third line, we open the ``footer`` block which has already been
defined in the ``simple`` theme.
3. On the fourth line, we insert the extra footer content.
4. On the fifth line, the function ``super()`` keeps the content previously
inserted in the ``head`` block.
5. On the last line, we close the ``footer`` block.
This file will be extended by all the other templates, so the custom footer
will appear on all pages.
.. Links

View file

@ -592,6 +592,17 @@ def configure_settings(settings: Settings) -> Settings:
else:
raise Exception("Could not find the theme {}".format(settings["THEME"]))
# Clear CSS_FILE if the file doesn't exist in the theme
css_file = settings.get("CSS_FILE")
if css_file:
theme = settings["THEME"]
static_paths = settings.get("THEME_STATIC_PATHS", ["static"])
if not any(
os.path.isfile(os.path.join(theme, sp, "css", css_file))
for sp in static_paths
):
settings["CSS_FILE"] = ""
# standardize strings to lowercase strings
for key in ["DEFAULT_LANG"]:
if key in settings:

View file

@ -201,6 +201,97 @@ class TestTemplateInheritance(LoggedTestCase):
tag_content = f.read()
self.assertIn('title="Foo Tag Atom Feed"', tag_content)
def test_simple_theme_no_css_link(self):
"""The simple theme has no static/css/ directory, so the CSS_FILE
link should not be rendered."""
settings = read_settings(
path=None,
override={
"THEME": "simple",
"PATH": CONTENT_DIR,
"OUTPUT_PATH": self.temp_output,
"CACHE_PATH": self.temp_cache,
"SITEURL": "http://example.com",
},
)
pelican = Pelican(settings=settings)
mute(True)(pelican.run)()
with open(os.path.join(self.temp_output, "test-md-file.html")) as f:
content = f.read()
self.assertNotIn("/theme/css/main.css", content)
def test_child_theme_with_css_file(self):
"""A child theme that provides static/css/main.css should have the
CSS_FILE link rendered."""
# Add a CSS file to the child theme
css_dir = os.path.join(self.temp_theme, "static", "css")
os.makedirs(css_dir)
with open(os.path.join(css_dir, "main.css"), "w") as f:
f.write("body { margin: 0; }")
settings = read_settings(
path=None,
override={
"THEME": self.temp_theme,
"PATH": CONTENT_DIR,
"OUTPUT_PATH": self.temp_output,
"CACHE_PATH": self.temp_cache,
"SITEURL": "http://example.com",
},
)
pelican = Pelican(settings=settings)
mute(True)(pelican.run)()
with open(os.path.join(self.temp_output, "test-md-file.html")) as f:
content = f.read()
self.assertIn(
'href="http://example.com/theme/css/main.css"',
content,
)
def test_css_only_theme(self):
"""A theme with only a static/css/main.css file (no templates)
should work by falling back to the simple theme's templates."""
css_only_theme = mkdtemp(prefix="pelican_test_css_only_theme.")
css_dir = os.path.join(css_only_theme, "static", "css")
os.makedirs(css_dir)
with open(os.path.join(css_dir, "main.css"), "w") as f:
f.write("body { margin: 0; }")
try:
settings = read_settings(
path=None,
override={
"THEME": css_only_theme,
"PATH": CONTENT_DIR,
"OUTPUT_PATH": self.temp_output,
"CACHE_PATH": self.temp_cache,
"SITEURL": "http://example.com",
},
)
pelican = Pelican(settings=settings)
mute(True)(pelican.run)()
with open(os.path.join(self.temp_output, "test-md-file.html")) as f:
content = f.read()
self.assertIn(
'href="http://example.com/theme/css/main.css"',
content,
)
self.assertIn("Proudly powered by", content)
finally:
rmtree(css_only_theme)
if __name__ == "__main__":
unittest.main()

View file

@ -9,6 +9,9 @@
{% if SITESUBTITLE %}
<meta name="description" content="{{ SITESUBTITLE }}" />
{% endif %}
{% if CSS_FILE %}
<link rel="stylesheet" href="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/css/{{ CSS_FILE }}" />
{% endif %}
{% if STYLESHEET_URL %}
<link rel="stylesheet" type="text/css" href="{{ STYLESHEET_URL }}" />
{% endif %}