diff --git a/Makefile b/Makefile index 080be0268b0..6bdc65fa1a4 100644 --- a/Makefile +++ b/Makefile @@ -65,15 +65,11 @@ pep_rss: $(PYTHON) generate_rss.py pages: pep_rss - $(SPHINX_BUILD) --index-file + $(SPHINX_BUILD) --build-dirs sphinx: $(SPHINX_BUILD) -# for building Sphinx without a web-server -sphinx-local: - $(SPHINX_BUILD) --build-files - fail-warning: $(SPHINX_BUILD) --fail-on-warning diff --git a/build.py b/build.py old mode 100644 new mode 100755 index 784fbea547d..decb84e71eb --- a/build.py +++ b/build.py @@ -1,3 +1,7 @@ +#!/usr/bin/env python3 +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + """Build script for Sphinx documentation""" import argparse @@ -9,17 +13,29 @@ def create_parser(): parser = argparse.ArgumentParser(description="Build PEP documents") # alternative builders: - parser.add_argument("-l", "--check-links", action="store_true") - parser.add_argument("-f", "--build-files", action="store_true") - parser.add_argument("-d", "--build-dirs", action="store_true") + builders = parser.add_mutually_exclusive_group() + builders.add_argument("-l", "--check-links", action="store_const", + dest="builder", const="linkcheck", + help='Check validity of links within PEP sources. ' + 'Cannot be used with "-f" or "-d".') + builders.add_argument("-f", "--build-files", action="store_const", + dest="builder", const="html", + help='Render PEPs to "pep-NNNN.html" files (default). ' + 'Cannot be used with "-d" or "-l".') + builders.add_argument("-d", "--build-dirs", action="store_const", + dest="builder", const="dirhtml", + help='Render PEPs to "index.html" files within "pep-NNNN" directories. ' + 'Cannot be used with "-f" or "-l".') # flags / options - parser.add_argument("-w", "--fail-on-warning", action="store_true") - parser.add_argument("-n", "--nitpicky", action="store_true") - parser.add_argument("-j", "--jobs", type=int, default=1) - - # extra build steps - parser.add_argument("-i", "--index-file", action="store_true") # for PEP 0 + parser.add_argument("-w", "--fail-on-warning", action="store_true", + help="Fail the Sphinx build on any warning.") + parser.add_argument("-n", "--nitpicky", action="store_true", + help="Run Sphinx in 'nitpicky' mode, " + "warning on every missing reference target.") + parser.add_argument("-j", "--jobs", type=int, default=1, + help="How many parallel jobs to run (if supported). " + "Integer, default 1.") return parser.parse_args() @@ -45,15 +61,11 @@ def create_index_file(html_root: Path, builder: str) -> None: doctree_directory = build_directory / ".doctrees" # builder configuration - if args.build_files: - sphinx_builder = "html" - elif args.build_dirs: - sphinx_builder = "dirhtml" - elif args.check_links: - sphinx_builder = "linkcheck" + if args.builder is not None: + sphinx_builder = args.builder else: # default builder - sphinx_builder = "dirhtml" + sphinx_builder = "html" # other configuration config_overrides = {} @@ -71,6 +83,5 @@ def create_index_file(html_root: Path, builder: str) -> None: parallel=args.jobs, ) app.build() - - if args.index_file: - create_index_file(build_directory, sphinx_builder) + + create_index_file(build_directory, sphinx_builder) diff --git a/conf.py b/conf.py index b7b37405520..a508a96f533 100644 --- a/conf.py +++ b/conf.py @@ -1,3 +1,6 @@ +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + """Configuration for building PEPs using Sphinx.""" from pathlib import Path @@ -43,7 +46,6 @@ # HTML output settings html_math_renderer = "maths_to_html" # Maths rendering -html_title = "peps.python.org" # Set # Theme settings html_theme_path = ["pep_sphinx_extensions"] diff --git a/contents.rst b/contents.rst index 658655e4044..94295408341 100644 --- a/contents.rst +++ b/contents.rst @@ -1,9 +1,11 @@ +.. This file is placed in the public domain or under the + CC0-1.0-Universal license, whichever is more permissive. Python Enhancement Proposals (PEPs) *********************************** -This is an internal Sphinx page, please go to the :doc:`PEP Index<pep-0000>`. +This is an internal Sphinx page, please go to the :doc:`PEP Index <pep-0000>`. .. toctree:: diff --git a/docs/build.rst b/docs/build.rst new file mode 100644 index 00000000000..0d7aa144dcd --- /dev/null +++ b/docs/build.rst @@ -0,0 +1,106 @@ +.. + Author: Adam Turner + + +Building PEPs Locally +===================== + +Whilst editing a PEP, it is useful to review the rendered output locally. +This can also be used to check that the PEP is valid reStructuredText before +submission to the PEP editors. + +The rest of this document assumes you are working from a local clone of the +`PEPs repository <https://github.com/python/peps>`__, with Python 3.9 or later +installed. + + +Render PEPs locally +------------------- + +1. Create a virtual environment and install requirements. + + The rest of these instructions assume an active virtual environment named + ``venv``. + The Python Packaging User Guide contains + `instructions on creating a virtual environment <https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment>`__ + for reference. + + .. code-block:: console + + (venv) $ python -m pip install --upgrade pip + (venv) $ python -m pip install -r requirements.txt + +2. **(Optional)** Delete prior build files. + Generally only needed when making changes to the rendering system itself. + + .. code-block:: console + + $ rm -rf build + +3. Run the build script: + + .. code-block:: console + + (venv) $ make sphinx + + If you don't have access to ``make``, run: + + .. code-block:: ps1con + + (venv) PS> python build.py + + .. note:: + + There may be a series of warnings about unreferenced citations or labels. + Whilst these are valid warnings, they do not impact the build process. + +4. Navigate to the ``build`` directory of your PEPs repo to find the HTML pages. + PEP 0 provides a formatted index, and may be a useful reference. + + +``build.py`` tools +------------------ + +Several additional tools can be run through ``build.py``, or the Makefile. + + +Check links +''''''''''' + +Check the validity of links within PEP sources (runs the `Sphinx linkchecker +<https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.linkcheck.CheckExternalLinksBuilder>`__). + +.. code-block:: console + + (venv) $ python build.py --check-links + (venv) $ make check-links + + +Stricter rendering +'''''''''''''''''' + +Run in `nit-picky <https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-nitpicky>`__ +mode. +This generates warnings for all missing references. + +.. code-block:: console + + (venv) $ python build.py --nitpicky + +Fail the build on any warning. +As of January 2022, there are around 250 warnings when building the PEPs. + +.. code-block:: console + + (venv) $ python build.py --fail-on-warning + (venv) $ make fail-warning + + +``build.py`` usage +------------------ + +For details on the command-line options to the ``build.py`` script, run: + +.. code-block:: console + + (venv) $ python build.py --help diff --git a/docs/rendering_system.rst b/docs/rendering_system.rst new file mode 100644 index 00000000000..bf793d5142e --- /dev/null +++ b/docs/rendering_system.rst @@ -0,0 +1,242 @@ +.. + Author: Adam Turner + + We can't use :pep:`N` references in this document, as they use links relative + to the current file, which doesn't work in a subdirectory like this one. + + +An Overview of the PEP Rendering System +======================================= + +This document provides an overview of the PEP rendering system, as a companion +to :doc:`PEP 676 <../pep-0676>`. + + +1. Configuration +---------------- + +Configuration is stored in three files: + +- ``conf.py`` contains the majority of the Sphinx configuration +- ``contents.rst`` creates the Sphinx-mandated table of contents directive +- ``pep_sphinx_extensions/pep_theme/theme.conf`` sets the Pygments themes + +The configuration: + +- registers the custom Sphinx extension +- sets both ``.txt`` and ``.rst`` suffixes to be parsed as PEPs +- tells Sphinx which source files to use +- registers the PEP theme, maths renderer, and template +- disables some default settings that are covered in the extension +- sets the default and "dark mode" code formatter styles + + +2. Orchestration +---------------- + +``build.py`` manages the rendering process. +Usage is covered in :doc:`build`. + + +3. Extension +------------ + +The Sphinx extension and theme are contained in the ``pep_sphinx_extensions`` +directory. +The following is a brief overview of the stages of the PEP rendering process, +and how the extension functions at each point. + + +3.1 Extension setup +''''''''''''''''''' + +The extension registers several objects: + +- ``FileBuilder`` and ``DirectoryBuilder`` run the build process for file- and + directory-based building, respectively. +- ``PEPParser`` registers the custom document transforms and parses PEPs to + a Docutils document. +- ``PEPTranslator`` converts a Docutils document into HTML. +- ``PEPRole`` handles ``:pep:`` roles in the reStructuredText source. + +The extension also patches default behaviour: + +- updating the default settings +- updating the Docutils inliner +- using HTML maths display over MathJax + + +3.2 Builder initialised +''''''''''''''''''''''' + +After the Sphinx builder object is created and initialised, we ensure the +configuration is correct for the builder chosen. + +Currently this involves updating the relative link template. +See ``_update_config_for_builder`` in ``pep_sphinx_extensions/__init__.py``. + + +3.3 Before documents are read +''''''''''''''''''''''''''''' + +The ``create_pep_zero`` hook is called. See `5. PEP 0`_. + + +3.4 Read document +''''''''''''''''' + +Parsing the document is handled by ``PEPParser`` +(``pep_sphinx_extensions.pep_processor.parsing.pep_parser.PEPParser``), a +lightweight wrapper over ``sphinx.parsers.RSTParser``. + +``PEPParser`` reads the document with leading :rfc:`2822` headers and registers +the transforms we want to apply. +These are: + +- ``PEPHeaders`` +- ``PEPTitle`` +- ``PEPContents`` +- ``PEPFooter`` + +Transforms are then applied in priority order. + + +3.4.1 ``PEPRole`` role +********************** + +This overrides the built-in ``:pep:`` role to return the correct URL. + + +3.4.2 ``PEPHeaders`` transform +****************************** + +PEPs start with a set of :rfc:`2822` headers, per :doc:`PEP 1 <../pep-0001>`. +This transform validates that the required headers are present and of the +correct data type, and removes headers not for display. +It must run before the ``PEPTitle`` transform. + + +3.4.3 ``PEPTitle`` transform +**************************** + +We generate the title node from the parsed title in the PEP headers, and make +all nodes in the document children of the new title node. +This transform must also handle parsing reStructuredText markup within PEP +titles, such as :doc:`PEP 604 <../pep-0604>`. + + +3.4.4 ``PEPContents`` transform +******************************* + +The automatic table of contents (TOC) is inserted in this transform in a +two-part process. + +First, the transform inserts a placeholder for the TOC and a horizontal rule +after the document title and PEP headers. +A callback transform then recursively walks the document to create the TOC, +starting from after the placeholder node. +Whilst walking the document, all reference nodes in the titles are removed, and +titles are given a self-link. + + +3.4.5 ``PEPFooter`` transform +***************************** + +This first builds a map of file modification times from a single git call, as +a speed-up. This will return incorrect results on a shallow checkout of the +repository, as is the default on continuous integration systems. + +We then attempt to remove any empty references sections, and append metadata in +the footer (source link and last modified timestamp). + + +3.5 Prepare for writing +'''''''''''''''''''''''' + +``pep_html_builder.FileBuilder.prepare_writing`` initialises the bare miniumum +of the Docutils writer and the settings for writing documents. +This provides a significant speed-up over the base Sphinx implementation, as +most of the data automatically initialised was unused. + + +3.6 Translate Docutils to HTML +''''''''''''''''''''''''''''''' + +``PEPTranslator`` overrides paragraph and reference logic to replicate +processing from the previous ``docutils.writers.pep``-based system. +Paragraphs are made compact where possible by omitting ``<p>`` tags, and +footnote references are be enclosed in square brackets. + + +3.7 Prepare for export to Jinja +''''''''''''''''''''''''''''''' + +Finally in ``pep_html_builder``, we gather all the parts to be passed to the +Jinja template. +This is also where we create the sidebar table of contents. + +The HTML files are then written out to the build directory. + + +4. Theme +-------- + +The theme is comprised of the HTML template in +``pep_sphinx_extensions/pep_theme/templates/page.html`` and the stylesheets in +``pep_sphinx_extensions/pep_theme/static``. + +The template is entirely self-contained, not relying on any default behaviour +from Sphinx. +It specifies the CSS files to include, the favicon, and basic semantic +information for the document structure. + +The styles are defined in two parts: + +- ``style.css`` handles the meat of the layout +- ``mq.css`` adds media queries for a responsive design + + +5. \PEP 0 +--------- + +The generation of the index, PEP 0, happens in three phases. +The reStructuredText source file is generated, it is then added to Sphinx, and +finally the data is post processed. + + +5.1 File creation +''''''''''''''''' + +``pep-0000.rst`` is created during a callback, before documents are loaded by +Sphinx. + +We first parse the individual PEP files to get the :rfc:`2822` header, and then +parse and validate that metadata. + +After collecting and validating all the PEP data, the index itself is created in +three steps: + + 1. Output the header text + 2. Output the category and numerical indices + 3. Output the author index + +The ``AUTHOR_OVERRIDES.csv`` file can be used to override an author's name in +the PEP 0 output. + +We then add the newly created PEP 0 file to two Sphinx variables so that it will +be processed as a normal source document. + + +5.2 Post processing +''''''''''''''''''' + +The ``PEPHeaders`` transform schedules the \PEP 0 post-processing code. +This serves two functions: masking email addresses and linking numeric +PEP references to the actual documents. + + +6. RSS Feed +----------- + +The RSS feed is created by extracting the header metadata and abstract from the +ten most recent PEPs. diff --git a/generate_rss.py b/generate_rss.py old mode 100644 new mode 100755 index 88798cb798a..efeeb3c4d6b --- a/generate_rss.py +++ b/generate_rss.py @@ -1,3 +1,7 @@ +#!/usr/bin/env python3 +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + import datetime import email.utils from pathlib import Path diff --git a/pep-0009.txt b/pep-0009.txt index a94b4f5917f..4a9f3d6681c 100644 --- a/pep-0009.txt +++ b/pep-0009.txt @@ -11,6 +11,7 @@ Post-History: Resolution: https://mail.python.org/mailman/private/peps/2016-January/001165.html :: + Update As of 05-Jan-2016, this PEP is officially deprecated and replaced @@ -226,10 +227,11 @@ Resolution: https://mail.python.org/mailman/private/peps/2016-January/001165.htm -Local Variables: -mode: indented-text -indent-tabs-mode: nil -sentence-end-double-space: t -fill-column: 70 -coding: utf-8 -End: +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: diff --git a/pep-0011.txt b/pep-0011.txt index dba6ec7b364..b867018c80c 100644 --- a/pep-0011.txt +++ b/pep-0011.txt @@ -287,7 +287,8 @@ Copyright This document has been placed in the public domain. - .. + +.. Local Variables: mode: indented-text indent-tabs-mode: nil diff --git a/pep-0610.rst b/pep-0610.rst index f41e4616c65..d4aacffa44d 100644 --- a/pep-0610.rst +++ b/pep-0610.rst @@ -568,7 +568,8 @@ This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. - .. + +.. Local Variables: mode: indented-text indent-tabs-mode: nil diff --git a/pep-0625.rst b/pep-0625.rst index 4b39fba1d7f..dcebad94e63 100644 --- a/pep-0625.rst +++ b/pep-0625.rst @@ -158,7 +158,8 @@ This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive. - .. + +.. Local Variables: mode: indented-text indent-tabs-mode: nil diff --git a/pep-0676.rst b/pep-0676.rst index b86dbc9a42c..b955e1f920a 100644 --- a/pep-0676.rst +++ b/pep-0676.rst @@ -106,6 +106,9 @@ The proposed specification for rendering the PEP files to HTML is as per the The rendered PEPs MUST be available at `peps.python.org`_. These SHOULD be hosted as static files, and MAY be behind a content delivery network (CDN). +A service to render previews of pull requests SHOULD be provided. This service +MAY be integrated with the hosting and deployment solution. + The following redirect rules MUST be created for the `python.org`_ domain: * ``/peps/`` -> https://peps.python.org/ diff --git a/pep-3139.txt b/pep-3139.txt index b2a19e006ab..d6b79fffff9 100644 --- a/pep-3139.txt +++ b/pep-3139.txt @@ -185,10 +185,11 @@ Copyright -Local Variables: -mode: indented-text -indent-tabs-mode: nil -sentence-end-double-space: t -fill-column: 70 -coding: utf-8 -End: +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: diff --git a/pep_sphinx_extensions/LICENCE.rst b/pep_sphinx_extensions/LICENCE.rst new file mode 100644 index 00000000000..f147a8023e3 --- /dev/null +++ b/pep_sphinx_extensions/LICENCE.rst @@ -0,0 +1,2 @@ +This files in this directory are placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. \ No newline at end of file diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index f302b14b7b9..5479a1dc132 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -39,7 +39,7 @@ def _depart_maths(): def _update_config_for_builder(app: Sphinx): if app.builder.name == "dirhtml": - environment.default_settings["pep_url"] = "../pep-{:0>4}" + app.env.settings["pep_url"] = "../pep-{:0>4}" def setup(app: Sphinx) -> dict[str, bool]: @@ -50,12 +50,17 @@ def setup(app: Sphinx) -> dict[str, bool]: # Register plugin logic app.add_builder(pep_html_builder.FileBuilder, override=True) app.add_builder(pep_html_builder.DirectoryBuilder, override=True) + app.add_source_parser(pep_parser.PEPParser) # Add PEP transforms - app.add_role("pep", pep_role.PEPRole(), override=True) # Transform PEP references to links + app.set_translator("html", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (html builder) app.set_translator("dirhtml", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (dirhtml builder) - app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook + + app.add_role("pep", pep_role.PEPRole(), override=True) # Transform PEP references to links + + # Register event callbacks app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used + app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook # Mathematics rendering inline_maths = HTMLTranslator.visit_math, _depart_maths diff --git a/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py b/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py index 703aa3af36e..5af7a49b86c 100644 --- a/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py +++ b/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py @@ -16,12 +16,12 @@ class FileBuilder(StandaloneHTMLBuilder): indexer = None relations = {} _script_files = _css_files = [] + globalcontext = {"script_files": [], "css_files": []} def prepare_writing(self, _doc_names: set[str]) -> None: self.docwriter = HTMLWriter(self) _opt_parser = OptionParser([self.docwriter], defaults=self.env.settings, read_config_files=True) self.docsettings = _opt_parser.get_default_values() - self.globalcontext = {"docstitle": self.config.html_title, "script_files": [], "css_files": []} def get_doc_context(self, docname: str, body: str, _metatags: str) -> dict: """Collect items for the template context of a page.""" @@ -36,9 +36,13 @@ def get_doc_context(self, docname: str, body: str, _metatags: str) -> dict: # local table of contents toc_tree = self.env.tocs[docname].deepcopy() - for node in toc_tree.traverse(nodes.reference): - node["refuri"] = node["anchorname"] or '#' # fix targets - toc = self.render_partial(toc_tree)["fragment"] + if len(toc_tree[0]) > 1: + toc_tree = toc_tree[0][1] # don't include document title + for node in toc_tree.traverse(nodes.reference): + node["refuri"] = node["anchorname"] or '#' # fix targets + toc = self.render_partial(toc_tree)["fragment"] + else: + toc = "" # PEPs with no sections -- 9, 210 return {"title": title, "sourcename": source_name, "toc": toc, "body": body} diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py index 1f907dfb701..7f8c0f01535 100644 --- a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py @@ -18,8 +18,8 @@ class PEPFooter(transforms.Transform): """ - # Set low priority so ref targets aren't removed before they are needed - default_priority = 999 + # Uses same priority as docutils.transforms.TargetNotes + default_priority = 520 def apply(self) -> None: pep_source_path = Path(self.document["source"]) @@ -34,13 +34,21 @@ def apply(self) -> None: if "references" in title_words: # Remove references section if there are no displayed # footnotes (it only has title & link target nodes) - if all(isinstance(ref_node, (nodes.title, nodes.target)) - for ref_node in section): + to_hoist = [] + types = set() + for node in section: + types.add(type(node)) + if isinstance(node, nodes.target): + to_hoist.append(node) + if types <= {nodes.title, nodes.target}: + section.parent.extend(to_hoist) section.parent.remove(section) break # Add link to source text and last modified date if pep_source_path.stem != "pep-0000": + if pep_source_path.stem != "pep-0210": # 210 is entirely empty, skip + self.document += nodes.transition() self.document += _add_source_link(pep_source_path) self.document += _add_commit_history_info(pep_source_path) diff --git a/pep_sphinx_extensions/pep_theme/static/colour_scheme.js b/pep_sphinx_extensions/pep_theme/static/colour_scheme.js new file mode 100644 index 00000000000..2cb6e9fabda --- /dev/null +++ b/pep_sphinx_extensions/pep_theme/static/colour_scheme.js @@ -0,0 +1,33 @@ +const dark = document.getElementById("css-dark"); +const pygmentsNormal = document.getElementById("pyg"); +const pygmentsDark = document.getElementById("pyg-dark"); + +const makeLight = () => { + dark.media = pygmentsNormal.media = pygmentsDark.media = "" + dark.disabled = pygmentsDark.disabled = true + pygmentsNormal.disabled = false + +} + +const makeDark = () => { + dark.media = pygmentsNormal.media = pygmentsDark.media = "" + dark.disabled = pygmentsDark.disabled = false + pygmentsNormal.disabled = true +} + + +const toggleColourScheme = () => { + if (localStorage.getItem("colour_scheme") === "dark") { + makeLight() + localStorage.setItem("colour_scheme", "light") + } else { + makeDark() + localStorage.setItem("colour_scheme", "dark") + } +} + +/* set colour scheme from local storage */ +document.addEventListener("DOMContentLoaded", () => { + if (localStorage.getItem("colour_scheme") === "light") makeLight() + if (localStorage.getItem("colour_scheme") === "dark") makeDark() +}) diff --git a/pep_sphinx_extensions/pep_theme/static/colour_scheme.svg b/pep_sphinx_extensions/pep_theme/static/colour_scheme.svg new file mode 100644 index 00000000000..78fef233c12 --- /dev/null +++ b/pep_sphinx_extensions/pep_theme/static/colour_scheme.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke-width="2" stroke="#777" fill="none" stroke-linecap="round" stroke-linejoin="round"> +<circle cx="12" cy="12" r="9"/> +<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"/> +</svg> diff --git a/pep_sphinx_extensions/pep_theme/static/dark.css b/pep_sphinx_extensions/pep_theme/static/dark.css new file mode 100644 index 00000000000..314192d94c9 --- /dev/null +++ b/pep_sphinx_extensions/pep_theme/static/dark.css @@ -0,0 +1,18 @@ +@charset "UTF-8"; +/* + * This stylesheet is separate from `style.css` to allow toggling dark mode with + * a button, with no duplication of information + */ + +/* Set master 'dark-mode' colours */ +:root { + --colour-background: #011; + --colour-background-accent: #333; + --colour-text: #ccc; + --colour-links: #8bf; + --colour-scrollbar: #333; + --colour-rule-strong: #777; + --colour-rule-light: #222; + --colour-inline-code: #333; + --colour-warning: #900; +} diff --git a/pep_sphinx_extensions/pep_theme/static/style.css b/pep_sphinx_extensions/pep_theme/static/style.css index a23a9ae600c..4b446d3dded 100644 --- a/pep_sphinx_extensions/pep_theme/static/style.css +++ b/pep_sphinx_extensions/pep_theme/static/style.css @@ -1,40 +1,22 @@ @charset "UTF-8"; -/* Styles for PEPs +/* Styles for PEPs */ -Colours: -white: - background - footnotes/references vertical border -#333 - body text -#888 - blockquote left line - header breadcrumbs separator - link underline (hovered/focused) -#ccc: - scrollbar -#ddd - header bottom border - horizontal rule - table vertical border -#eee: - link underline - table rows & top/bottom border - PEP header rows - footnotes/references rows - admonition note background -#f8f8f8: - inline code background - -#0072aa: - links -# fee: - admonition warning background - -*/ +/* Set master 'light-mode' colours (default) */ +:root { + --colour-background: white; + --colour-background-accent: #eee; + --colour-text: #333; + --colour-links: #0072aa; + --colour-scrollbar: #ccc; + --colour-rule-strong: #888; + --colour-rule-light: #ddd; + --colour-inline-code: #f8f8f8; + --colour-warning: #fee; +} /* Set master rules */ * {box-sizing: border-box} +:root {color-scheme: light dark} html { overflow-y: scroll; -webkit-font-smoothing: antialiased; @@ -46,8 +28,8 @@ html { } body { margin: 0; - color: #333; - background-color: white; + color: var(--colour-text); + background-color: var(--colour-background); } section#pep-page-section { padding: 0.25rem 0.25rem 0; @@ -58,7 +40,7 @@ section#pep-page-section { p {margin: .5rem 0} /* Header rules */ -h1.page-title { +h1 { line-height: 2.3rem; font-size: 2rem; font-weight: bold; @@ -95,19 +77,19 @@ h6 { a, a:active, a:visited { - color: #0072aa; - text-decoration-color: #eee; + color: var(--colour-links); + text-decoration-color: var(--colour-background-accent); display: inline; } a:hover, a:focus { - text-decoration-color: #888; + text-decoration-color: var(--colour-rule-strong); } /* Blockquote rules */ blockquote { font-style: italic; - border-left: 1px solid #888; + border-left: 1px solid var(--colour-rule-strong); margin: .5rem; padding: .5rem 1rem; } @@ -130,7 +112,7 @@ code { } code.literal { font-size: .8em; - background-color: #f8f8f8; + background-color: var(--colour-inline-code); } pre { padding: .5rem .75rem; @@ -147,7 +129,7 @@ dl dd { /* Horizontal rule rule */ hr { border: 0; - border-top: 1px solid #ddd; + border-top: 1px solid var(--colour-rule-light); margin: 1.75rem 0; } /*Image rules */ @@ -187,14 +169,14 @@ sub {bottom: -0.25em} table { width: 100%; border-collapse: collapse; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + border-top: 1px solid var(--colour-background-accent); + border-bottom: 1px solid var(--colour-background-accent); } table caption { margin: 1rem 0 .75rem; } table tbody tr:nth-of-type(odd) { - background-color: #eee; + background-color: var(--colour-background-accent); } table th, table td { @@ -202,19 +184,20 @@ table td { padding: 0.25rem 0.5rem 0.2rem; } table td + td { - border-left: 1px solid #ddd; + border-left: 1px solid var(--colour-rule-light); } /* Breadcrumbs rules */ section#pep-page-section > header { - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--colour-rule-light); } section#pep-page-section > header > h1 { font-size: 1.1rem; + line-height: 1.4rem; margin: 0; display: inline-block; padding-right: .6rem; - border-right: 1px solid #888; + border-right: 1px solid var(--colour-rule-strong); } ul.breadcrumbs { margin: 0; @@ -229,6 +212,17 @@ ul.breadcrumbs a { text-decoration: none; } +/* Dark mode toggle rules */ +section#pep-page-section > header > button { + background: transparent url(colour_scheme.svg) 0/contain; + border: none; + cursor: pointer; + width: 1.2rem; + height: 1.2rem; + float: right; + translate: 0 50%; +} + /* Admonitions rules */ div.note, div.warning { @@ -237,10 +231,10 @@ div.warning { margin-bottom: 1rem; } div.note { - background-color: #eee; + background-color: var(--colour-background-accent); } div.warning { - background-color: #fee; + background-color: var(--colour-warning); } p.admonition-title { font-weight: bold; @@ -253,8 +247,8 @@ dl.footnote { grid-template-columns: fit-content(30%) auto; line-height: 1.875; width: 100%; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + border-top: 1px solid var(--colour-background-accent); + border-bottom: 1px solid var(--colour-background-accent); } dl.rfc2822 > dt, dl.rfc2822 > dd, dl.footnote > dt, dl.footnote > dd { @@ -262,34 +256,42 @@ dl.footnote > dt, dl.footnote > dd { } dl.rfc2822 > dt:nth-of-type(even), dl.rfc2822 > dd:nth-of-type(even), dl.footnote > dt:nth-of-type(even), dl.footnote > dd:nth-of-type(even) { - background-color: #eee; + background-color: var(--colour-background-accent); } dl.footnote > dt { font-weight: normal; - border-right: 1px solid white; + border-right: 1px solid var(--colour-background); } /* Sidebar formatting */ -nav#pep-sidebar { +#pep-sidebar { overflow-y: scroll; position: sticky; top: 0; height: 100vh; scrollbar-width: thin; /* CSS Standards, not *yet* widely supported */ - scrollbar-color: #ccc transparent; + scrollbar-color: var(--colour-scrollbar) transparent; } -nav#pep-sidebar::-webkit-scrollbar {width: 6px} -nav#pep-sidebar::-webkit-scrollbar-track {background: transparent} -nav#pep-sidebar::-webkit-scrollbar-thumb {background: #ccc} -nav#pep-sidebar > h2 { +#pep-sidebar::-webkit-scrollbar {width: 6px} +#pep-sidebar::-webkit-scrollbar-track {background: transparent} +#pep-sidebar::-webkit-scrollbar-thumb {background: var(--colour-scrollbar)} +#pep-sidebar > h2 { font-size: 1.4rem; } -nav#pep-sidebar ul { +#pep-sidebar ul { margin-left: 1rem; } -nav#pep-sidebar ul a { +#pep-sidebar ul a { text-decoration: none; } +#toc-title { + font-weight: bold; +} #source { padding-bottom: 2rem; + font-weight: bold; +} + +.reference.external > strong { + font-weight: normal; /* Fix strong links for :pep: and :rfc: roles */ } diff --git a/pep_sphinx_extensions/pep_theme/templates/page.html b/pep_sphinx_extensions/pep_theme/templates/page.html index d47644a73c8..e3034448716 100644 --- a/pep_sphinx_extensions/pep_theme/templates/page.html +++ b/pep_sphinx_extensions/pep_theme/templates/page.html @@ -4,11 +4,14 @@ <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>{{ title + " | "|safe + docstitle }} + + {{ title + " | peps.python.org"|safe }} - + + + @@ -21,16 +24,19 @@

Python Enhancement Proposals

  • PEP Index »
  • {{ title }}
  • +
    {{ body }}
    + diff --git a/pep_sphinx_extensions/pep_theme/theme.conf b/pep_sphinx_extensions/pep_theme/theme.conf index 8150ef720fa..bf410226aca 100644 --- a/pep_sphinx_extensions/pep_theme/theme.conf +++ b/pep_sphinx_extensions/pep_theme/theme.conf @@ -2,3 +2,4 @@ # Theme options inherit = none pygments_style = tango +pygments_dark_style = native