From b108b985ea7649f55191dfbb6176d7f992bd3e5b Mon Sep 17 00:00:00 2001 From: cproudlock Date: Sat, 19 Sep 2026 12:36:16 -0400 Subject: [PATCH] Let the API docs follow the system theme The app has a dark theme. /api/docs was the one page that stayed white when the rest of the site went dark, which is exactly the page somebody has open beside the app while they work. Redoc does not read prefers-color-scheme, and the element it was using takes no theme at all - so the page now initialises through Redoc.init, which does. Light keeps the default look; dark names the colours Redoc otherwise derives from the background and gets wrong: body text, the sidebar, and the right-hand sample panel, which came out dark-on-dark. It re-initialises on a live scheme change rather than restyling, because Redoc bakes its colours into styled-components at init - a CSS override reaches the chrome and misses the content. The listener has the addListener fallback for the older MediaQueryList, since a shop-floor kiosk browser is exactly where this gets read. Nothing new is loaded: same vendored bundle, same spec, no font, no CDN. A test pins that, because theming is the kind of change that quietly introduces one. The try-it page is left light. Swagger UI 5 ships no dark theme, so it would mean vendoring a community stylesheet that fights upstream on every bump - and it is a development tool that is off in production anyway. --- shopdb/core/api/docs.py | 59 +++++++++++++++++++++++--- tests/test_core/test_api_docs_tryit.py | 34 +++++++++++++++ 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/shopdb/core/api/docs.py b/shopdb/core/api/docs.py index cc266f4e..cbda186b 100644 --- a/shopdb/core/api/docs.py +++ b/shopdb/core/api/docs.py @@ -78,22 +78,71 @@ def docs_index(): tryit_note = '' if _tryit_is_mounted(): tryit_note = ( - '

' + '

' 'This page is read-only. To send a real request against this ' 'server, use the try-it view.

' % url_for(_TRYIT_ENDPOINT) ) + # DARK MODE. Redoc does not read prefers-color-scheme on its own, and + # the element takes no theme - so the page goes through + # Redoc.init instead, which does. The app has a dark theme and this was + # the one page that stayed white when the rest of the site went dark. + # + # The page RE-INITIALISES when the OS scheme flips rather than restyling: + # Redoc bakes its colours into styled-components at init, so a CSS + # override reaches the chrome and misses the content. page = ( '' 'ShopDB API' '' '' - '' + '' '%s' - '' + '
' '' - '' % (tryit_note, spec_url, redoc_url) + '' + '' % (tryit_note, redoc_url, json.dumps(spec_url)) ) return Response(page, mimetype='text/html') diff --git a/tests/test_core/test_api_docs_tryit.py b/tests/test_core/test_api_docs_tryit.py index 40145aef..44b07933 100644 --- a/tests/test_core/test_api_docs_tryit.py +++ b/tests/test_core/test_api_docs_tryit.py @@ -247,3 +247,37 @@ def test_the_spec_declares_the_mount_it_is_served_from(enabled_client): # and duplicating it into every path would double it under a subpath. assert '/api/apitokens' in mounted['paths'] assert '/shopdb/api/apitokens' not in mounted['paths'] + + +# --------------------------------------------------------------------------- +# The read-only page follows the OS theme +# --------------------------------------------------------------------------- + + +def test_the_redoc_page_follows_the_system_theme(client): + """The app has a dark theme; /api/docs was the one page that stayed white. + + Redoc ignores prefers-color-scheme and the element takes no + theme, so the page has to go through Redoc.init - which is also why this + test pins the mechanism and not just the CSS. A future edit that puts the + element back would lose dark mode without changing a single colour. + """ + page = client.get('/api/docs/').get_data(as_text=True) + + assert 'Redoc.init' in page + assert '