asterion-sphinx-style 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asterion_sphinx_style-0.1.0/.github/workflows/publish.yml +82 -0
- asterion_sphinx_style-0.1.0/.gitignore +8 -0
- asterion_sphinx_style-0.1.0/LICENSE +202 -0
- asterion_sphinx_style-0.1.0/PKG-INFO +87 -0
- asterion_sphinx_style-0.1.0/README.md +68 -0
- asterion_sphinx_style-0.1.0/pyproject.toml +48 -0
- asterion_sphinx_style-0.1.0/src/asterion_sphinx_style/__init__.py +114 -0
- asterion_sphinx_style-0.1.0/src/asterion_sphinx_style/py.typed +0 -0
- asterion_sphinx_style-0.1.0/src/asterion_sphinx_style/static/asterion-favicon.png +0 -0
- asterion_sphinx_style-0.1.0/src/asterion_sphinx_style/static/asterion-logo.png +0 -0
- asterion_sphinx_style-0.1.0/src/asterion_sphinx_style/static/asterion.css +173 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Build on everything, publish only on a tag.
|
|
2
|
+
#
|
|
3
|
+
# The version is never written down: hatch-vcs derives it from the git tag, so a
|
|
4
|
+
# release is `git tag vX.Y.Z && git push --tags` and nothing can drift. Two
|
|
5
|
+
# consequences shape this file -- the checkout must be unshallow (a shallow one
|
|
6
|
+
# hides tags and silently builds 0.0.0), and an off-tag or dirty build produces a
|
|
7
|
+
# PEP 440 *local* version (`0.1.0.dev2+g1234abc`) which PyPI rejects. The guard
|
|
8
|
+
# below turns that into a clear failure instead of a confusing upload error.
|
|
9
|
+
name: publish
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
branches: [main]
|
|
14
|
+
tags: ["v*"]
|
|
15
|
+
pull_request:
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: publish-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
with:
|
|
27
|
+
fetch-depth: 0 # hatch-vcs reads tags
|
|
28
|
+
|
|
29
|
+
- name: Install uv
|
|
30
|
+
uses: astral-sh/setup-uv@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
|
|
34
|
+
- name: Build sdist and wheel
|
|
35
|
+
run: uv build
|
|
36
|
+
|
|
37
|
+
# A sphinx extension that fails to import is a broken release, and nothing
|
|
38
|
+
# else here would catch it: there is no test suite to run.
|
|
39
|
+
- name: Smoke-test the wheel
|
|
40
|
+
run: |
|
|
41
|
+
uv run --isolated --no-project --with dist/*.whl \
|
|
42
|
+
python -c "import asterion_sphinx_style as s; print(s.__version__); assert s.LOGO.is_file()"
|
|
43
|
+
|
|
44
|
+
- name: Check the version is publishable
|
|
45
|
+
run: |
|
|
46
|
+
version=$(ls dist/*.whl | head -1 | cut -d- -f2)
|
|
47
|
+
echo "built version: $version"
|
|
48
|
+
case "$version" in
|
|
49
|
+
*+*|*dev*)
|
|
50
|
+
echo "::error::'$version' is a local or dev version; PyPI will reject it."
|
|
51
|
+
echo "Tag a clean tree: git tag vX.Y.Z && git push --tags"
|
|
52
|
+
exit 1 ;;
|
|
53
|
+
esac
|
|
54
|
+
if [ "${GITHUB_REF_TYPE}" = "tag" ] && [ "${GITHUB_REF_NAME#v}" != "$version" ]; then
|
|
55
|
+
echo "::error::tag ${GITHUB_REF_NAME} and built version $version disagree."
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
- uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
if-no-files-found: error
|
|
64
|
+
|
|
65
|
+
# Split from `build` on purpose: this job has no checkout and runs no code of
|
|
66
|
+
# ours, so the OIDC token that can publish is exposed to as little as possible.
|
|
67
|
+
publish:
|
|
68
|
+
needs: build
|
|
69
|
+
if: github.ref_type == 'tag'
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
environment:
|
|
72
|
+
name: pypi
|
|
73
|
+
url: https://pypi.org/p/asterion-sphinx-style
|
|
74
|
+
permissions:
|
|
75
|
+
id-token: write # trusted publishing; no API token to store or rotate
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/download-artifact@v4
|
|
78
|
+
with:
|
|
79
|
+
name: dist
|
|
80
|
+
path: dist/
|
|
81
|
+
|
|
82
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asterion-sphinx-style
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Asterion Robotics' sphinx style: a plain sphinx extension, usable in any sphinx project
|
|
5
|
+
Author-email: Thibault Poignonec <t.poignonec@asterion-robotics.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: asterion,documentation,sphinx,theme
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: Sphinx :: Extension
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: sphinx-rtd-theme>=3.0
|
|
17
|
+
Requires-Dist: sphinx>=7.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# asterion_sphinx_style
|
|
21
|
+
|
|
22
|
+
Sphinx style for Asterion public documentation.
|
|
23
|
+
|
|
24
|
+
A plain sphinx extension, usable in any sphinx project:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# conf.py
|
|
28
|
+
extensions = ["asterion_sphinx_style"]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It configures [sphinx-rtd-theme](https://sphinx-rtd-theme.readthedocs.io/) rather than shipping a theme of its own, so everything a reader already knows about Read the Docs navigation still holds.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install asterion-sphinx-style
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If a project already builds its `extensions` list elsewhere, rebind rather than mutate:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
extensions = [*extensions, "asterion_sphinx_style"]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Overriding
|
|
46
|
+
|
|
47
|
+
Everything is applied **only when the project left it unset**, so your `conf.py` always wins:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
html_logo = "_static/my-logo.png" # yours, not Asterion's
|
|
51
|
+
html_theme = "furo" # the style stops configuring the theme
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
One caveat: `html_theme` reads as `alabaster` when a project has not chosen one, so a project that deliberately picks `alabaster` is indistinguishable from one that picked nothing, and the style will switch it to `sphinx_rtd_theme`. Any other theme is respected.
|
|
55
|
+
|
|
56
|
+
## Palette
|
|
57
|
+
|
|
58
|
+
Sampled from the logo wordmark and `AsterionReport.cls`. Contrast is against white, WCAG 2.1.
|
|
59
|
+
|
|
60
|
+
| Role | Hex | Ratio | |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| Brand blue | `#0832F6` | 7.51:1 | AAA — safe for links and body text |
|
|
63
|
+
| Deep blue | `#030189` | 15.33:1 | navigation header, admonition titles |
|
|
64
|
+
| Navy | `#010042` | 19.45:1 | hover and visited states |
|
|
65
|
+
| Alert red | `#DC0000` | 5.19:1 | danger and error admonitions only |
|
|
66
|
+
|
|
67
|
+
## What it does, and what it deliberately does not
|
|
68
|
+
|
|
69
|
+
Sets, when unset: `html_theme`, `html_logo`, `html_favicon`, and the theme's `style_nav_header_background`. Adds one stylesheet and its own static directory (appended, so your `_static` keeps precedence).
|
|
70
|
+
|
|
71
|
+
It does **not** repaint the theme's navigation machinery — the current-page tree, expand/collapse states, light/dark alternation. That is a tuned system, and a style that rewrites it ends up fighting the theme on every release. Only surfaces that read as *brand* are touched.
|
|
72
|
+
|
|
73
|
+
## Design rules
|
|
74
|
+
|
|
75
|
+
A style is typically installed once and applied to every version of a site, including releases cut years earlier. Three rules follow:
|
|
76
|
+
|
|
77
|
+
1. **Fill, never clobber** — a project's own settings always win.
|
|
78
|
+
2. **Absolute paths, appended** — packaged assets by absolute path; `html_static_path` appended to on `builder-inited`, never assigned.
|
|
79
|
+
3. **Never warn** — doc builds commonly run under `-W`, where one warning on one old page fails an entire site.
|
|
80
|
+
|
|
81
|
+
## Versioning
|
|
82
|
+
|
|
83
|
+
The git tag is the only version that exists; `pyproject.toml` is `dynamic` via `hatch-vcs`. A release is `git tag vX.Y.Z && git push --tags`.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
Apache-2.0. See [LICENSE](LICENSE). The Asterion Robotics logo and name are trademarks of Asterion Robotics and are not covered by that grant.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# asterion_sphinx_style
|
|
2
|
+
|
|
3
|
+
Sphinx style for Asterion public documentation.
|
|
4
|
+
|
|
5
|
+
A plain sphinx extension, usable in any sphinx project:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
# conf.py
|
|
9
|
+
extensions = ["asterion_sphinx_style"]
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
It configures [sphinx-rtd-theme](https://sphinx-rtd-theme.readthedocs.io/) rather than shipping a theme of its own, so everything a reader already knows about Read the Docs navigation still holds.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install asterion-sphinx-style
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If a project already builds its `extensions` list elsewhere, rebind rather than mutate:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
extensions = [*extensions, "asterion_sphinx_style"]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Overriding
|
|
27
|
+
|
|
28
|
+
Everything is applied **only when the project left it unset**, so your `conf.py` always wins:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
html_logo = "_static/my-logo.png" # yours, not Asterion's
|
|
32
|
+
html_theme = "furo" # the style stops configuring the theme
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
One caveat: `html_theme` reads as `alabaster` when a project has not chosen one, so a project that deliberately picks `alabaster` is indistinguishable from one that picked nothing, and the style will switch it to `sphinx_rtd_theme`. Any other theme is respected.
|
|
36
|
+
|
|
37
|
+
## Palette
|
|
38
|
+
|
|
39
|
+
Sampled from the logo wordmark and `AsterionReport.cls`. Contrast is against white, WCAG 2.1.
|
|
40
|
+
|
|
41
|
+
| Role | Hex | Ratio | |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| Brand blue | `#0832F6` | 7.51:1 | AAA — safe for links and body text |
|
|
44
|
+
| Deep blue | `#030189` | 15.33:1 | navigation header, admonition titles |
|
|
45
|
+
| Navy | `#010042` | 19.45:1 | hover and visited states |
|
|
46
|
+
| Alert red | `#DC0000` | 5.19:1 | danger and error admonitions only |
|
|
47
|
+
|
|
48
|
+
## What it does, and what it deliberately does not
|
|
49
|
+
|
|
50
|
+
Sets, when unset: `html_theme`, `html_logo`, `html_favicon`, and the theme's `style_nav_header_background`. Adds one stylesheet and its own static directory (appended, so your `_static` keeps precedence).
|
|
51
|
+
|
|
52
|
+
It does **not** repaint the theme's navigation machinery — the current-page tree, expand/collapse states, light/dark alternation. That is a tuned system, and a style that rewrites it ends up fighting the theme on every release. Only surfaces that read as *brand* are touched.
|
|
53
|
+
|
|
54
|
+
## Design rules
|
|
55
|
+
|
|
56
|
+
A style is typically installed once and applied to every version of a site, including releases cut years earlier. Three rules follow:
|
|
57
|
+
|
|
58
|
+
1. **Fill, never clobber** — a project's own settings always win.
|
|
59
|
+
2. **Absolute paths, appended** — packaged assets by absolute path; `html_static_path` appended to on `builder-inited`, never assigned.
|
|
60
|
+
3. **Never warn** — doc builds commonly run under `-W`, where one warning on one old page fails an entire site.
|
|
61
|
+
|
|
62
|
+
## Versioning
|
|
63
|
+
|
|
64
|
+
The git tag is the only version that exists; `pyproject.toml` is `dynamic` via `hatch-vcs`. A release is `git tag vX.Y.Z && git push --tags`.
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
Apache-2.0. See [LICENSE](LICENSE). The Asterion Robotics logo and name are trademarks of Asterion Robotics and are not covered by that grant.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "asterion-sphinx-style"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Asterion Robotics' sphinx style: a plain sphinx extension, usable in any sphinx project"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [{ name = "Thibault Poignonec", email = "t.poignonec@asterion-robotics.com" }]
|
|
13
|
+
keywords = ["sphinx", "theme", "documentation", "asterion"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Framework :: Sphinx :: Extension",
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# A sphinx extension and nothing more. It carries the theme it configures, so a
|
|
24
|
+
# bare `pip install` is enough.
|
|
25
|
+
dependencies = [
|
|
26
|
+
"sphinx>=7.0",
|
|
27
|
+
"sphinx-rtd-theme>=3.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
packages = ["src/asterion_sphinx_style"]
|
|
32
|
+
|
|
33
|
+
# The git tag is the only version that exists; `fallback-version` keeps a
|
|
34
|
+
# .git-less source export buildable.
|
|
35
|
+
[tool.hatch.version]
|
|
36
|
+
source = "vcs"
|
|
37
|
+
fallback-version = "0.0.0"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.version.raw-options]
|
|
40
|
+
version_scheme = "no-guess-dev"
|
|
41
|
+
local_scheme = "node-and-date"
|
|
42
|
+
|
|
43
|
+
[tool.ruff]
|
|
44
|
+
line-length = 100
|
|
45
|
+
target-version = "py311"
|
|
46
|
+
|
|
47
|
+
[tool.ruff.lint]
|
|
48
|
+
select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM", "RUF", "ANN", "PTH"]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Copyright 2026 Asterion Robotics
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
# Author: Thibault Poignonec <t.poignonec@asterion-robotics.com>
|
|
17
|
+
|
|
18
|
+
"""Asterion Robotics' sphinx style — a plain sphinx extension.
|
|
19
|
+
|
|
20
|
+
Add it to any project's ``conf.py``::
|
|
21
|
+
|
|
22
|
+
extensions = ["asterion_sphinx_style"]
|
|
23
|
+
|
|
24
|
+
It configures :mod:`sphinx_rtd_theme` rather than shipping a theme of its own,
|
|
25
|
+
so everything a reader knows about Read the Docs' navigation still holds.
|
|
26
|
+
|
|
27
|
+
Three rules govern everything here, because a style is typically installed once
|
|
28
|
+
and applied to *every* version of a documentation site, including releases cut
|
|
29
|
+
years earlier:
|
|
30
|
+
|
|
31
|
+
1. **Fill, never clobber.** Every setting is applied only when the project left
|
|
32
|
+
it unset, so a repo's own ``conf.py`` always wins. That is what makes
|
|
33
|
+
overriding the logo a one-liner rather than a style option.
|
|
34
|
+
2. **Absolute paths, appended.** Packaged assets are handed to sphinx by
|
|
35
|
+
absolute path and ``html_static_path`` is appended to on ``builder-inited``,
|
|
36
|
+
never assigned, so a project's own ``_static`` survives.
|
|
37
|
+
3. **Never warn.** Documentation builds run under ``-W`` more often than not,
|
|
38
|
+
where a single warning on a single old page fails an entire site.
|
|
39
|
+
|
|
40
|
+
The palette is the brand's, sampled from the logo and the report template.
|
|
41
|
+
Every colour clears WCAG AA against both white and its own foreground; the
|
|
42
|
+
brand blue clears AAA on white.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from __future__ import annotations
|
|
46
|
+
|
|
47
|
+
from pathlib import Path
|
|
48
|
+
from typing import TYPE_CHECKING, Any
|
|
49
|
+
|
|
50
|
+
if TYPE_CHECKING: # pragma: no cover - typing only, keeps import cost at zero
|
|
51
|
+
from sphinx.application import Sphinx
|
|
52
|
+
from sphinx.config import Config
|
|
53
|
+
|
|
54
|
+
try: # pragma: no cover - trivial, and untestable from a source checkout
|
|
55
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
56
|
+
|
|
57
|
+
__version__ = version("asterion-sphinx-style")
|
|
58
|
+
except PackageNotFoundError: # pragma: no cover
|
|
59
|
+
__version__ = "0.0.0"
|
|
60
|
+
|
|
61
|
+
BRAND_BLUE = "#0832F6"
|
|
62
|
+
"""The wordmark blue. 7.51:1 on white — AAA for body text, so it is safe for links."""
|
|
63
|
+
|
|
64
|
+
DEEP_BLUE = "#030189"
|
|
65
|
+
"""``AsterionBlueInner``, the report cover's gradient centre. Brand surfaces."""
|
|
66
|
+
|
|
67
|
+
NAVY = "#010042"
|
|
68
|
+
"""``AsterionBlueOuter``, the gradient edge. Deeper surfaces and hover states."""
|
|
69
|
+
|
|
70
|
+
ALERT_RED = "#DC0000"
|
|
71
|
+
"""The report's confidential stamp. Danger admonitions only."""
|
|
72
|
+
|
|
73
|
+
THEME = "sphinx_rtd_theme"
|
|
74
|
+
SPHINX_DEFAULT_THEME = "alabaster"
|
|
75
|
+
"""What ``html_theme`` reads as when a project has not chosen one."""
|
|
76
|
+
|
|
77
|
+
STATIC_DIR = Path(__file__).parent / "static"
|
|
78
|
+
STYLESHEET = "asterion.css"
|
|
79
|
+
LOGO = STATIC_DIR / "asterion-logo.png"
|
|
80
|
+
"""The white wordmark: the navigation header it sits on is a dark brand block."""
|
|
81
|
+
FAVICON = STATIC_DIR / "asterion-favicon.png"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _apply_defaults(_app: Sphinx, config: Config) -> None:
|
|
85
|
+
"""Fill in what the project left unset. Never overwrite a deliberate choice."""
|
|
86
|
+
if config.html_theme in ("", SPHINX_DEFAULT_THEME):
|
|
87
|
+
config.html_theme = THEME
|
|
88
|
+
if not config.html_logo:
|
|
89
|
+
config.html_logo = str(LOGO)
|
|
90
|
+
if not config.html_favicon:
|
|
91
|
+
config.html_favicon = str(FAVICON)
|
|
92
|
+
|
|
93
|
+
# Theme options are per-theme, so only touch them when the theme is ours.
|
|
94
|
+
# setdefault, not assignment: a project may have set some of these already.
|
|
95
|
+
if config.html_theme == THEME:
|
|
96
|
+
options: dict[str, Any] = dict(config.html_theme_options or {})
|
|
97
|
+
options.setdefault("style_nav_header_background", DEEP_BLUE)
|
|
98
|
+
config.html_theme_options = options
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _register_static(app: Sphinx) -> None:
|
|
102
|
+
"""Append, so a project's own ``_static`` keeps precedence."""
|
|
103
|
+
app.config.html_static_path.append(str(STATIC_DIR))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def setup(app: Sphinx) -> dict[str, Any]:
|
|
107
|
+
app.connect("config-inited", _apply_defaults)
|
|
108
|
+
app.connect("builder-inited", _register_static)
|
|
109
|
+
app.add_css_file(STYLESHEET)
|
|
110
|
+
return {
|
|
111
|
+
"version": __version__,
|
|
112
|
+
"parallel_read_safe": True,
|
|
113
|
+
"parallel_write_safe": True,
|
|
114
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/* Copyright 2026 Asterion Robotics
|
|
2
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
*
|
|
4
|
+
* Asterion's brand layer over sphinx-rtd-theme.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately small. The theme's navigation mechanics -- the current-page
|
|
7
|
+
* tree, its expand/collapse states, its light/dark alternation -- are a tuned
|
|
8
|
+
* system, and repainting them wholesale is how a style ends up fighting the
|
|
9
|
+
* theme on every release. This restyles the surfaces that read as *brand* (the
|
|
10
|
+
* header block, the mobile bar, links) and leaves the machinery alone.
|
|
11
|
+
*
|
|
12
|
+
* Palette, sampled from the logo and the report class:
|
|
13
|
+
* #0832F6 brand blue wordmark 7.51:1 on white (AAA)
|
|
14
|
+
* #030189 deep blue gradient centre 15.33:1
|
|
15
|
+
* #010042 navy gradient edge 19.45:1
|
|
16
|
+
* #DC0000 alert red confidential 5.19:1
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
:root {
|
|
20
|
+
--asterion-brand: #0832f6;
|
|
21
|
+
--asterion-deep: #030189;
|
|
22
|
+
--asterion-navy: #010042;
|
|
23
|
+
--asterion-alert: #dc0000;
|
|
24
|
+
|
|
25
|
+
/* Sidebar tints. The theme builds its navigation out of neutral greys; these
|
|
26
|
+
* are the same lightness steps pulled toward the brand hue, so the structure
|
|
27
|
+
* survives and only the colour changes. Every pairing below clears WCAG AA. */
|
|
28
|
+
/* Both are kept light and near-neutral on purpose. A saturated blue on a
|
|
29
|
+
* saturated navy reads flat however good its contrast ratio looks: hue
|
|
30
|
+
* similarity undercuts luminance separation. Hierarchy therefore comes from
|
|
31
|
+
* tint (white caption over tinted links) rather than from dimming anything. */
|
|
32
|
+
--asterion-nav-text: #dde3ff; /* 15.28:1 on navy */
|
|
33
|
+
--asterion-nav-caption: #ffffff; /* 19.45:1 on navy, and bold + uppercase */
|
|
34
|
+
--asterion-nav-current: #eef1ff; /* the "you are here" block */
|
|
35
|
+
--asterion-nav-current-2: #dce2ff; /* one level deeper */
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* --- brand surfaces ------------------------------------------------------ */
|
|
39
|
+
|
|
40
|
+
/* Also set via `style_nav_header_background`; kept here so the block still
|
|
41
|
+
looks right if a project overrides html_theme_options wholesale. */
|
|
42
|
+
.wy-side-nav-search,
|
|
43
|
+
.wy-nav-top {
|
|
44
|
+
background-color: var(--asterion-deep);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* The wordmark is wide and the theme sizes logos for square marks. */
|
|
48
|
+
.wy-side-nav-search > a img.logo {
|
|
49
|
+
max-width: 12rem;
|
|
50
|
+
padding: 0.35rem 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* The project name under the logo duplicates the wordmark. */
|
|
54
|
+
.wy-side-nav-search > div.version {
|
|
55
|
+
color: rgba(255, 255, 255, 0.75);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.wy-side-nav-search input[type="text"] {
|
|
59
|
+
border-color: var(--asterion-navy);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* --- sidebar navigation -------------------------------------------------- */
|
|
63
|
+
|
|
64
|
+
/* The theme's nav is a grey system: a dark panel, and a light block marking the
|
|
65
|
+
* section you are in. That structure is good and is kept; every grey in it is
|
|
66
|
+
* re-tinted to the brand. Brand blue cannot be used as *text* on the dark panel
|
|
67
|
+
* (1.72:1 against the theme's #343131, and no better on navy), so it appears
|
|
68
|
+
* where it is legible: as the L1 heading on the light block, and as fills. */
|
|
69
|
+
|
|
70
|
+
.wy-nav-side {
|
|
71
|
+
background: var(--asterion-navy);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.wy-menu-vertical a {
|
|
75
|
+
color: var(--asterion-nav-text);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.wy-menu-vertical a:hover {
|
|
79
|
+
background-color: var(--asterion-deep);
|
|
80
|
+
color: #fff;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.wy-menu-vertical header,
|
|
84
|
+
.wy-menu-vertical p.caption {
|
|
85
|
+
color: var(--asterion-nav-caption);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* The current section's block, and its L1 heading in Asterion blue. */
|
|
89
|
+
.wy-menu-vertical li.current {
|
|
90
|
+
background: var(--asterion-nav-current);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.wy-menu-vertical li.current > a,
|
|
94
|
+
.wy-menu-vertical li.on a {
|
|
95
|
+
color: var(--asterion-brand);
|
|
96
|
+
background: #fff;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.wy-menu-vertical li.current > a:hover,
|
|
100
|
+
.wy-menu-vertical li.on a:hover {
|
|
101
|
+
background: var(--asterion-nav-current);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Descendants sit on the light block, so they take the darkest brand tone. */
|
|
105
|
+
.wy-menu-vertical li.current a {
|
|
106
|
+
color: var(--asterion-navy);
|
|
107
|
+
border-right-color: var(--asterion-nav-current-2);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.wy-menu-vertical li.current a:hover {
|
|
111
|
+
background: var(--asterion-nav-current-2);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.wy-menu-vertical li.toctree-l2.current > a,
|
|
115
|
+
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a {
|
|
116
|
+
background: var(--asterion-nav-current-2);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Expand arrows inherit from their row rather than the theme's grey ramp. */
|
|
120
|
+
.wy-menu-vertical li button.toctree-expand {
|
|
121
|
+
color: var(--asterion-nav-text);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.wy-menu-vertical li.current > a button.toctree-expand,
|
|
125
|
+
.wy-menu-vertical li.on a button.toctree-expand,
|
|
126
|
+
.wy-menu-vertical li.current a button.toctree-expand {
|
|
127
|
+
color: var(--asterion-brand);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* --- links --------------------------------------------------------------- */
|
|
131
|
+
|
|
132
|
+
.rst-content a,
|
|
133
|
+
.wy-nav-content a {
|
|
134
|
+
color: var(--asterion-brand);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.rst-content a:hover,
|
|
138
|
+
.wy-nav-content a:hover {
|
|
139
|
+
color: var(--asterion-deep);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* The theme ships purple (#9b59b6) here, which belongs to a different palette.
|
|
143
|
+
* Navy keeps the "where have I been" affordance inside the brand. */
|
|
144
|
+
.rst-content a:visited,
|
|
145
|
+
.wy-nav-content a:visited {
|
|
146
|
+
color: var(--asterion-navy);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Headerlinks are decoration; brand blue on hover is enough. */
|
|
150
|
+
.rst-content .headerlink {
|
|
151
|
+
color: var(--asterion-brand);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* --- accents ------------------------------------------------------------- */
|
|
155
|
+
|
|
156
|
+
.rst-content .admonition-title {
|
|
157
|
+
background-color: var(--asterion-deep);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.rst-content .danger .admonition-title,
|
|
161
|
+
.rst-content .error .admonition-title {
|
|
162
|
+
background-color: var(--asterion-alert);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.rst-content .note,
|
|
166
|
+
.rst-content .admonition {
|
|
167
|
+
border-left: 3px solid var(--asterion-deep);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.rst-content .danger,
|
|
171
|
+
.rst-content .error {
|
|
172
|
+
border-left-color: var(--asterion-alert);
|
|
173
|
+
}
|