Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/docs-preview-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
'requirements-docs.txt',
'scripts/build-docs.sh',
'scripts/render-dev-notes.py',
'tests/test_docs_404.py',
'tests/test_render_dev_notes.py',
'zensical.toml',
]);
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/docs-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
'requirements-docs.txt',
'scripts/build-docs.sh',
'scripts/render-dev-notes.py',
'tests/test_docs_404.py',
'tests/test_render_dev_notes.py',
'zensical.toml',
]);
Expand Down
4 changes: 3 additions & 1 deletion docs/development/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ Verify branded surfaces in both the `default` and `slate` palette schemes. Prefe
CSS variables in `docs/stylesheets/dev-notes.css` over one-off hard-coded colors.
Keep asset paths relative to `docs_dir`. Theme templates live in `overrides/`;
keep the custom `404.html` useful for ordinary missing pages as well as expired
pull request previews.
pull request previews. Keep that fallback self-contained: GitHub Pages serves it
for arbitrary paths where root-relative theme assets do not resolve beneath the
project site prefix.

## Validate and preview

Expand Down
158 changes: 130 additions & 28 deletions overrides/404.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,130 @@
{% extends "main.html" %}

{% block htmltitle %}
<title>Page not found - {{ config.site_name }}</title>
{% endblock %}

{% block content %}
<h1>Page not found</h1>
<p id="not-found-message">The requested documentation page does not exist.</p>
<p>
<a id="not-found-home" href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}">
Return to OpenShell Research
</a>
</p>
<script>
(() => {
const previewPath = window.location.pathname.match(
/^(.*\/)pr-preview\/pr-[1-9][0-9]*(?:\/|$)/,
);
if (previewPath) {
document.title = `Preview unavailable - {{ config.site_name }}`;
document.getElementById("not-found-message").textContent =
"This pull request preview has expired or was removed.";
document.getElementById("not-found-home").href = previewPath[1];
}
})();
</script>
{% endblock %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<meta name="color-scheme" content="light dark">
<title>Page not found - {{ config.site_name }}</title>
<style>
:root {
color-scheme: light dark;
--accent: #5a8f00;
--background: #f6f7f8;
--border: #d9dde3;
--button-text: #111315;
--card: #ffffff;
--muted: #52606d;
--text: #171a1f;
}

@media (prefers-color-scheme: dark) {
:root {
--accent: #76b900;
--background: #111315;
--border: #343a40;
--card: #1b1f23;
--muted: #b6bec8;
--text: #f4f6f8;
}
}

* {
box-sizing: border-box;
}

body {
align-items: center;
background: var(--background);
color: var(--text);
display: flex;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", sans-serif;
justify-content: center;
margin: 0;
min-height: 100vh;
padding: 2rem;
}

main {
background: var(--card);
border: 1px solid var(--border);
border-radius: 1rem;
box-shadow: 0 1rem 3rem rgb(0 0 0 / 12%);
max-width: 38rem;
padding: clamp(2rem, 7vw, 4rem);
text-align: center;
width: 100%;
}

svg {
display: block;
height: 5rem;
margin: 0 auto 1.5rem;
width: 5rem;
}

h1 {
font-size: clamp(2rem, 8vw, 3.5rem);
letter-spacing: -0.04em;
line-height: 1;
margin: 0 0 1rem;
}

p {
color: var(--muted);
font-size: 1.05rem;
line-height: 1.6;
margin: 0 auto 2rem;
max-width: 30rem;
}

a {
background: var(--accent);
border-radius: 0.5rem;
color: var(--button-text);
display: inline-block;
font-weight: 700;
padding: 0.8rem 1.15rem;
text-decoration: none;
}

a:hover {
filter: brightness(0.9);
}

a:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 4px;
}
</style>
</head>
<body>
<main aria-labelledby="not-found-title">
<svg viewBox="0 0 100 100" role="img" aria-label="OpenShell Research">
<path d="M50 9 84 23v28c0 21-15 35-34 41-19-6-34-20-34-41V23Z" fill="#76b900"/>
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
<path d="m37 41 12 9-12 9"/>
<path d="M55 59h12"/>
</g>
</svg>
<h1 id="not-found-title">Page not found</h1>
<p id="not-found-message">The requested documentation page does not exist.</p>
<a id="not-found-home" href="/OpenShell-Research/">Return to OpenShell Research</a>
</main>
<script>
(() => {
const previewPath = window.location.pathname.match(
/^(.*\/)pr-preview\/pr-[1-9][0-9]*(?:\/|$)/,
);
if (previewPath) {
document.title = "Preview unavailable - {{ config.site_name }}";
document.getElementById("not-found-title").textContent = "Preview unavailable";
document.getElementById("not-found-message").textContent =
"This pull request preview has expired or was removed.";
document.getElementById("not-found-home").href = previewPath[1];
}
})();
</script>
</body>
</html>
1 change: 1 addition & 0 deletions scripts/build-docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ python -m pip install -r requirements-docs.txt

python scripts/render-dev-notes.py
zensical build --clean --strict
REQUIRE_RENDERED_404=1 python tests/test_docs_404.py
51 changes: 51 additions & 0 deletions tests/test_docs_404.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

import os
from pathlib import Path
import re
import unittest


ROOT = Path(__file__).resolve().parents[1]
TEMPLATE = ROOT / "overrides" / "404.html"
RENDERED = ROOT / "site" / "404.html"


class Docs404Tests(unittest.TestCase):
def test_template_is_standalone_and_self_contained(self) -> None:
html = TEMPLATE.read_text(encoding="utf-8")

self.assertTrue(html.lstrip().lower().startswith("<!doctype html>"))
self.assertNotIn("{% extends", html)
self.assertNotRegex(html, r"<(?:img|script)[^>]+src=")
self.assertNotRegex(html, r"<link[^>]+rel=[\"']stylesheet")
self.assertNotRegex(html, r"https?://")
self.assertIn("<style>", html)
self.assertIn("<main aria-labelledby=", html)
self.assertIn("prefers-color-scheme: dark", html)

def test_template_handles_expired_preview_paths(self) -> None:
html = TEMPLATE.read_text(encoding="utf-8")

self.assertIn("pr-preview", html)
self.assertIn("Preview unavailable", html)
self.assertIn("This pull request preview has expired or was removed.", html)

def test_rendered_page_has_no_root_asset_dependencies(self) -> None:
if os.environ.get("REQUIRE_RENDERED_404") != "1":
self.skipTest("rendered output is checked after the documentation build")
if not RENDERED.exists():
self.fail("site/404.html was not generated")

html = RENDERED.read_text(encoding="utf-8")
asset_reference = re.compile(
r"(?:href|src)=[\"']/(?:assets|stylesheets|javascripts)/",
)
self.assertNotRegex(html, asset_reference)
self.assertIn("<style>", html)
self.assertIn("This pull request preview has expired or was removed.", html)


if __name__ == "__main__":
unittest.main()