From d438438e3347fe2cc6328fbd0b293558a2ef00ba Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 11 Mar 2026 13:58:58 +0100 Subject: [PATCH] Add configurable white-label theming via instance.yaml Extend theming from 3 CSS variables (primary colors only) to 14 configurable properties covering colors, fonts, borders, and shape. All values are optional with sensible defaults. - New _theme.html include replaces duplicated inline injection - Wire theme include into all 7 templates (base, login, dashboard, catalog, admin_tables, activity_center, corporate_memory) - Conditional font loading: skip default Inter when custom font_url set - Config.theme_overrides() classmethod generates CSS variable dict - Visual theme-reference.html guide for instance configurators - Document all theme keys in instance.yaml.example --- docs/theme-reference.html | 597 +++++++++++++++++++++++++ webapp/config.py | 33 +- webapp/templates/_theme.html | 16 + webapp/templates/activity_center.html | 3 + webapp/templates/admin_tables.html | 3 + webapp/templates/base.html | 10 +- webapp/templates/base_login.html | 10 +- webapp/templates/catalog.html | 3 + webapp/templates/corporate_memory.html | 3 + webapp/templates/dashboard.html | 3 + 10 files changed, 662 insertions(+), 19 deletions(-) create mode 100644 docs/theme-reference.html create mode 100644 webapp/templates/_theme.html diff --git a/docs/theme-reference.html b/docs/theme-reference.html new file mode 100644 index 0000000..a50ab3d --- /dev/null +++ b/docs/theme-reference.html @@ -0,0 +1,597 @@ + + + + + +Theme Configuration Reference - AI Data Analyst + + + + + + + + +
+ + +
+

Header Area

+

The top navigation bar with logo, title, and subtitle.

+ +
+
App Header
+
+ surface #FFFFFF + font_primary Inter +
+ +
+

AI Data Analyst

+ Ask questions about your data in natural language +
+
+
+ text_secondary #6B7280 + border #E5E7EB +
+
+
+ + +
+

Page Layout

+

Background, cards, buttons, and text hierarchy.

+ +
+
Page Structure
+
+
+ background #F5F7FA + radius 6px +
+ surface #FFF +

Analysis Results

+

Revenue grew 12% quarter over quarter, driven by the enterprise segment.

+
+ Run Query + Export +
+ primary #0073D1 +
+
+

Primary heading text

+

Secondary descriptive text shown below.

+ text_primary #1A253C +
+
+
+
+
+ + +
+

Chat / Query Interface

+

The conversational query area with input fields and response bubbles.

+ +
+
Chat Panel
+
+
+
Show me monthly revenue for 2025 broken down by region.
+
Here is the revenue breakdown. The top region is North America at $4.2M.
+
+ + border #E5E7EB + primary (focus) #0073D1 +
+
+ primary_light rgba(0,115,209,0.1) +
+
+
+ + +
+

Status Indicators

+

Success, warning, and error states used in badges, alerts, and inline messages.

+ +
+
Status Badges
+
+
+ Query completed — success: #10B77F + Slow response — warning: #F59F0A + Connection lost — error: #EA580C +
+
+
+
+ + +
+

Color Palette

+

Every configurable color with its YAML key, CSS variable, and default value.

+ +
+
+
+
+
primary
+
--primary
+
#0073D1
+
+
+
+
+
+
primary_dark
+
--primary-dark
+
#005BA3
+
+
+
+
+
+
primary_light
+
--primary-light
+
rgba(0, 115, 209, 0.1)
+
+
+
+
+
+
text_primary
+
--text-primary
+
#1A253C
+
+
+
+
+
+
text_secondary
+
--text-secondary
+
#6B7280
+
+
+
+
+
+
background
+
--background
+
#F5F7FA
+
+
+
+
+
+
surface
+
--surface
+
#FFFFFF
+
+
+
+
+
+
border
+
--border
+
#E5E7EB
+
+
+
+
+
+
success
+
--success
+
#10B77F
+
+
+
+
+
+
warning
+
--warning
+
#F59F0A
+
+
+
+
+
+
error
+
--error
+
#EA580C
+
+
+
+
+ + +
+

Typography

+

+ Controlled by font_primary + (the font-family stack) and + font_url + (a Google Fonts URL to load the typeface). +

+ +
+
Font Weight Samples — Inter
+
+
+ 400 + The quick brown fox jumps over the lazy dog. 0123456789 +
+
+ 500 + The quick brown fox jumps over the lazy dog. 0123456789 +
+
+ 600 + The quick brown fox jumps over the lazy dog. 0123456789 +
+
+ 700 + The quick brown fox jumps over the lazy dog. 0123456789 +
+
+
+ +
+ Tip: To use a different font, set + font_url + to the Google Fonts stylesheet URL and + font_primary + to the corresponding family name with fallbacks, e.g. + 'Roboto', system-ui, sans-serif. +
+
+ + +
+

Shape & Border Radius

+

+ The radius + key sets --radius-md, + the base border-radius for cards, buttons, inputs, and badges. +

+ +
+
+ 4px + Sharp / Compact +
+
+ 6px + Default +
+
+ 8px + Moderate +
+
+ 12px + Rounded +
+
+ 16px + Pill-like +
+
+
+ + +
+

Quick Start YAML

+

+ Copy this block into your instance.yaml + and adjust values to match your brand. +

+ +
+ +# instance.yaml - Theme configuration +# All keys are optional; defaults are shown below. + +theme: + # -- Brand colors -- + primary: "#0073D1" # Main accent: buttons, links, focus rings + primary_dark: "#005BA3" # Hover / active state for primary elements + primary_light: "rgba(0, 115, 209, 0.1)" # Tinted backgrounds (chat bubbles, highlights) + + # -- Text -- + text_primary: "#1A253C" # Headings and body text + text_secondary: "#6B7280" # Descriptions, labels, meta text + + # -- Surfaces -- + background: "#F5F7FA" # Page background + surface: "#FFFFFF" # Cards, panels, header, modals + border: "#E5E7EB" # Borders and dividers + + # -- Typography -- + font_primary: "'Inter', system-ui, sans-serif" + font_url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" + + # -- Shape -- + radius: "6px" # Base border-radius for UI elements + + # -- Status colors -- + success: "#10B77F" # Completed, positive states + warning: "#F59F0A" # Slow queries, caution states + error: "#EA580C" # Failures, validation errors +
+
+ + +
+

Full Key Reference

+

Complete mapping of YAML keys to CSS custom properties.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
YAML KeyCSS VariableDefaultPreview
primary--primary#0073D1
primary_dark--primary-dark#005BA3
primary_light--primary-lightrgba(0,115,209,0.1)
text_primary--text-primary#1A253C
text_secondary--text-secondary#6B7280
background--background#F5F7FA
surface--surface#FFFFFF
border--border#E5E7EB
font_primary--font-primary'Inter', system-ui, sans-seriffont stack
font_urlfont_urlGoogle Fonts URLexternal URL
radius--radius-md6px
success--success#10B77F
warning--warning#F59F0A
error--error#EA580C
+
+
+ +
+ + + + + diff --git a/webapp/config.py b/webapp/config.py index 9f18f8b..c72a08a 100644 --- a/webapp/config.py +++ b/webapp/config.py @@ -115,10 +115,21 @@ class Config: '' )) - # Theme colors (optional overrides from instance config) + # Theme (optional overrides from instance config) THEME_PRIMARY = _get(_instance, "theme", "primary", default="") THEME_PRIMARY_DARK = _get(_instance, "theme", "primary_dark", default="") THEME_PRIMARY_LIGHT = _get(_instance, "theme", "primary_light", default="") + THEME_TEXT_PRIMARY = _get(_instance, "theme", "text_primary", default="") + THEME_TEXT_SECONDARY = _get(_instance, "theme", "text_secondary", default="") + THEME_BACKGROUND = _get(_instance, "theme", "background", default="") + THEME_SURFACE = _get(_instance, "theme", "surface", default="") + THEME_BORDER = _get(_instance, "theme", "border", default="") + THEME_FONT_PRIMARY = _get(_instance, "theme", "font_primary", default="") + THEME_FONT_URL = _get(_instance, "theme", "font_url", default="") + THEME_RADIUS = _get(_instance, "theme", "radius", default="") + THEME_SUCCESS = _get(_instance, "theme", "success", default="") + THEME_WARNING = _get(_instance, "theme", "warning", default="") + THEME_ERROR = _get(_instance, "theme", "error", default="") # Auth providers to disable (list of provider names, e.g., ["email", "password"]) AUTH_DISABLED_PROVIDERS = _get(_instance, "auth", "disabled_providers", default=[]) @@ -142,6 +153,26 @@ class Config: JIRA_CLOUD_ID = os.environ.get("JIRA_CLOUD_ID", "") JIRA_DATA_DIR = Path(os.environ.get("JIRA_DATA_DIR", "/data/src_data/raw/jira")) + @classmethod + def theme_overrides(cls) -> dict: + """Return non-empty theme CSS variable overrides.""" + mapping = { + "--primary": cls.THEME_PRIMARY, + "--primary-dark": cls.THEME_PRIMARY_DARK, + "--primary-light": cls.THEME_PRIMARY_LIGHT, + "--text-primary": cls.THEME_TEXT_PRIMARY, + "--text-secondary": cls.THEME_TEXT_SECONDARY, + "--background": cls.THEME_BACKGROUND, + "--surface": cls.THEME_SURFACE, + "--border": cls.THEME_BORDER, + "--font-primary": cls.THEME_FONT_PRIMARY, + "--radius-md": cls.THEME_RADIUS, + "--success": cls.THEME_SUCCESS, + "--warning": cls.THEME_WARNING, + "--error": cls.THEME_ERROR, + } + return {k: v for k, v in mapping.items() if v} + @classmethod def validate(cls) -> list[str]: """Validate that required configuration is present.""" diff --git a/webapp/templates/_theme.html b/webapp/templates/_theme.html new file mode 100644 index 0000000..8786409 --- /dev/null +++ b/webapp/templates/_theme.html @@ -0,0 +1,16 @@ +{# Theme override injection - include in ALL page sections #} +{% if config.THEME_FONT_URL %} + + + +{% endif %} +{% set overrides = config.theme_overrides() %} +{% if overrides %} + +{% endif %} diff --git a/webapp/templates/activity_center.html b/webapp/templates/activity_center.html index 9e3c87b..72425d7 100644 --- a/webapp/templates/activity_center.html +++ b/webapp/templates/activity_center.html @@ -4,9 +4,11 @@ Activity Center - Data Analyst Portal + {% if not config.THEME_FONT_URL %} + {% endif %} + {% include '_theme.html' %} diff --git a/webapp/templates/admin_tables.html b/webapp/templates/admin_tables.html index 0295cbd..5c9d2df 100644 --- a/webapp/templates/admin_tables.html +++ b/webapp/templates/admin_tables.html @@ -4,9 +4,11 @@ Table Management - {{ config.INSTANCE_NAME }} + {% if not config.THEME_FONT_URL %} + {% endif %} + {% include '_theme.html' %} diff --git a/webapp/templates/base.html b/webapp/templates/base.html index 5fca2f1..4f9c8ac 100644 --- a/webapp/templates/base.html +++ b/webapp/templates/base.html @@ -6,15 +6,7 @@ {% block title %}Data Analyst Portal{% endblock %} - {% if config.THEME_PRIMARY %} - - {% endif %} + {% include '_theme.html' %}
diff --git a/webapp/templates/base_login.html b/webapp/templates/base_login.html index 7eeae1f..7946638 100644 --- a/webapp/templates/base_login.html +++ b/webapp/templates/base_login.html @@ -6,15 +6,7 @@ {% block title %}Data Analyst Portal{% endblock %} - {% if config.THEME_PRIMARY %} - - {% endif %} + {% include '_theme.html' %} {% with messages = get_flashed_messages(with_categories=true) %} diff --git a/webapp/templates/catalog.html b/webapp/templates/catalog.html index 5bba0aa..cb2a570 100644 --- a/webapp/templates/catalog.html +++ b/webapp/templates/catalog.html @@ -4,9 +4,11 @@ Data Catalog - Data Analyst Portal + {% if not config.THEME_FONT_URL %} + {% endif %} + {% include '_theme.html' %}
diff --git a/webapp/templates/dashboard.html b/webapp/templates/dashboard.html index c061401..d78df46 100644 --- a/webapp/templates/dashboard.html +++ b/webapp/templates/dashboard.html @@ -4,9 +4,11 @@ Dashboard - Data Analyst Portal + {% if not config.THEME_FONT_URL %} + {% endif %} + {% include '_theme.html' %}