The SSR server injects fresh color CSS custom properties as an inline
<style> block before </head>, but _themes.scss also set hardcoded
color defaults on :root at build time. The two :root blocks compete
in the cascade — the compiled stylesheet (loaded after the inline
<style>) overwrites the server-injected colors, causing a visible
flash where wrong colors render briefly before ThemeService.apply()
corrects them.
Removing the color properties from :root in _themes.scss makes the
server-injected <style> the sole source of truth for colors. Non-color
properties (fonts, radii, shadows) remain as build-time defaults since
they don't change between deploys.
The reviewer noted the static sitemap.xml had a hardcoded domain in a
white-label app. Instead of writing files to disk, we now serve
sitemap.xml and robots.txt as dynamic API endpoints that read
KMTN_WEBSITE_URL at runtime.
- Nginx proxies /sitemap.xml and /robots.txt to the backend API so
search crawlers reach them at root paths.
- Removed the startup filesystem-write that would fail in Docker
(backend can't write to Nginx container's static dir).
- Simplified sitemap_export.py to pure generation functions (no I/O).
- Added /api/sitemap/regenerate admin endpoint for consistency.
- CI workflow: replace hardcoded branch list with ['**'] wildcard so
pipeline runs on every branch push, not just 4 named branches.
- Sitemap/robots.txt: moved from static files to dynamic generation.
New sitemap_export.py module (follows theme_export.py pattern) uses
KMTN_WEBSITE_URL to generate tenant-aware sitemap.xml and robots.txt
on app startup. Atomic writes to STATIC_FILES_DIR so Nginx serves them.
- Added /api/sitemap endpoint with /status (public) and /regenerate
(admin-only) for runtime control.
Addresses reviewer comments on PR #4:
1. 'more general way to ensure this runs on any branch on push'
2. 'whitelabel app - this file may need to be dynamically generated'