Skip to content

feat: white-label branding support#18

Open
pthmas wants to merge 11 commits intomainfrom
pierrick/white-labeling
Open

feat: white-label branding support#18
pthmas wants to merge 11 commits intomainfrom
pierrick/white-labeling

Conversation

@pthmas
Copy link
Collaborator

@pthmas pthmas commented Feb 25, 2026

Summary

  • Add runtime-configurable branding via 7 optional env vars (CHAIN_NAME, CHAIN_LOGO_URL, ACCENT_COLOR, BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, ERROR_COLOR)
  • New backend GET /api/config endpoint serves branding config from environment variables
  • Frontend BrandingContext fetches config once on load, applies CSS custom properties for accent colors and derives surface palettes from background colors
  • Logo, chain name, favicon, and page title update dynamically
  • All values optional — defaults match current Atlas branding when unset

Test plan

  • Set custom CHAIN_NAME and ACCENT_COLOR in .env, verify title/links/buttons change
  • Set BACKGROUND_COLOR_DARK / BACKGROUND_COLOR_LIGHT, verify surface colors adapt in both themes
  • Set CHAIN_LOGO_URL pointing to mounted branding asset, verify logo in navbar and welcome page
  • Verify with no branding env vars set, UI looks identical to current Atlas
  • Test dark/light theme toggle with custom backgrounds
  • Verify bunx vite build succeeds
  • Verify docker compose build && docker compose up -d works end-to-end

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • White-label your explorer: customize chain names, logos, and color themes via environment variables.
    • New configuration API endpoint exposes branding settings for dynamic application.
    • Color palette generation with support for dark and light themes.
  • Documentation

    • New white-labeling guide with setup instructions and examples.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Warning

Rate limit exceeded

@pthmas has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f0a7045-3793-424e-988e-0bbfdde2a3fa

📥 Commits

Reviewing files that changed from the base of the PR and between 6090c62 and 6c1de97.

📒 Files selected for processing (21)
  • .env.example
  • .gitignore
  • README.md
  • backend/crates/atlas-api/src/handlers/config.rs
  • backend/crates/atlas-api/src/handlers/mod.rs
  • backend/crates/atlas-api/src/main.rs
  • branding/.gitkeep
  • docker-compose.yml
  • docs/WHITE_LABELING.md
  • frontend/.gitignore
  • frontend/nginx.conf
  • frontend/src/App.tsx
  • frontend/src/api/config.ts
  • frontend/src/components/Layout.tsx
  • frontend/src/context/BrandingContext.tsx
  • frontend/src/context/branding-context.ts
  • frontend/src/hooks/useBranding.ts
  • frontend/src/index.css
  • frontend/src/pages/WelcomePage.tsx
  • frontend/src/utils/color.ts
  • frontend/tailwind.config.js
📝 Walkthrough

Walkthrough

This PR introduces white-label branding support across the explorer. Environment variables define chain naming and color customization, a new backend API endpoint (/api/config) exposes branding configuration, and frontend context providers fetch and apply these settings dynamically without requiring rebuilds.

Changes

Cohort / File(s) Summary
Configuration & Documentation
.env.example, README.md, docs/WHITE_LABELING.md
Adds branding environment variables with defaults and comprehensive documentation for white-label configuration, Docker integration, and custom logo support.
Backend Configuration Handler
backend/crates/atlas-api/src/handlers/config.rs, backend/crates/atlas-api/src/handlers/mod.rs, backend/crates/atlas-api/src/main.rs
Introduces BrandingConfig struct and get_config GET handler; loads branding fields from environment variables into AppState with defaults; registers new /api/config route.
Frontend Branding Context
frontend/src/context/branding-context.ts, frontend/src/context/BrandingContext.tsx, frontend/src/hooks/useBranding.ts, frontend/src/api/config.ts
Establishes BrandingContext and BrandingProvider that fetches /api/config on mount, applies CSS color variables reactively, and provides branding state via useContext hook.
Frontend Components
frontend/src/App.tsx, frontend/src/components/Layout.tsx, frontend/src/pages/WelcomePage.tsx
Wraps app with BrandingProvider; updates Logo and WelcomePage to consume branding hook for dynamic logo URLs and chain names; changes status indicator color to accent-primary.
Frontend Styling & Color Utilities
frontend/src/index.css, frontend/src/utils/color.ts, frontend/tailwind.config.js
Adds CSS variables for accent colors; implements color derivation utility to compute palette shades from base color with HSL-based math; updates button/badge styling to use dynamic CSS variables.
Infrastructure
docker-compose.yml, frontend/nginx.conf
Adds branding environment variables to atlas-api service; mounts branding directory as read-only volume for frontend; configures Nginx to serve branding assets at /branding/ with 1-hour cache.

Sequence Diagram

sequenceDiagram
    participant App as Frontend App
    participant BP as BrandingProvider
    participant API as Backend /api/config
    participant AppState as Server AppState
    participant CSS as Document Root

    App->>BP: Mount & Render
    BP->>API: GET /api/config
    API->>AppState: Read branding fields
    AppState-->>API: Return BrandingConfig
    API-->>BP: JSON response
    BP->>BP: deriveSurfaceShades(accent_color, theme)
    BP->>CSS: applyPalette(derived colors)
    CSS->>CSS: Set CSS custom properties
    BP->>BP: Provide via BrandingContext
    App->>App: useContext(BrandingContext)
    App->>App: Render with dynamic branding
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~40 minutes

Possibly related PRs

  • feat: light mode #12: Modifies frontend theming by wrapping App.tsx with a provider, updating CSS/Tailwind tokens in index.css and tailwind.config.js, and refactoring Layout and WelcomePage components to consume dynamic branding context, creating overlapping patterns in the theming/styling surface.

Suggested reviewers

  • tac0turtle

🐰 Branding colors hop and shine,
White-label themes, now thine and mine,
From env to CSS they flow,
No rebuild needed—watch them glow!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: white-label branding support' directly and clearly summarizes the main objective of the changeset, which adds comprehensive runtime-configurable white-label branding throughout both frontend and backend.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pierrick/white-labeling
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pthmas pthmas marked this pull request as ready for review February 27, 2026 13:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
.env.example (1)

23-27: Consider quoting hex color values for parser compatibility.

Some .env parsers may interpret # as the start of a comment, even without a preceding space. Quoting ensures the full hex value is captured.

🔧 Suggested fix
-ACCENT_COLOR=#dc2626                  # Primary accent color (links, buttons, active states)
-BACKGROUND_COLOR_DARK=#050505         # Dark mode base background
-BACKGROUND_COLOR_LIGHT=#f4ede6        # Light mode base background
-SUCCESS_COLOR=#22c55e                 # Success indicator color
-ERROR_COLOR=#dc2626                   # Error indicator color
+ACCENT_COLOR="#dc2626"                # Primary accent color (links, buttons, active states)
+BACKGROUND_COLOR_DARK="#050505"       # Dark mode base background
+BACKGROUND_COLOR_LIGHT="#f4ede6"      # Light mode base background
+SUCCESS_COLOR="#22c55e"               # Success indicator color
+ERROR_COLOR="#dc2626"                 # Error indicator color
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 23 - 27, The hex color values in the .env example
(ACCENT_COLOR, BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR,
ERROR_COLOR) may be parsed incorrectly due to the leading #; update each
variable to wrap the hex value in quotes (e.g., change ACCENT_COLOR=#dc2626 to
ACCENT_COLOR="#dc2626") and apply the same quoting to any other color vars in
the file so parsers don't treat # as a comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Line 51: The CHAIN_NAME env handling currently accepts an empty string; change
the retrieval logic for chain_name (the variable initialized where
std::env::var("CHAIN_NAME") is called) to treat empty or whitespace-only values
as unset by checking the env var result and falling back to "Atlas" when the
value is Err or when the Ok string is empty/only whitespace (e.g., trim and test
is_empty), so chain_name uses the default branding unless a non-empty value is
provided.

In `@docs/WHITE_LABELING.md`:
- Around line 5-13: The docs currently conflict on the default branding: the
intro claims the explorer falls back to "ev-node" branding while the CHAIN_NAME
table lists the default as "Atlas"; pick one canonical default and make the text
consistent by updating either the opening paragraph (replace "ev-node" with
"Atlas") or the table default (replace "`Atlas`" with "`ev-node`"), and ensure
the CHAIN_NAME entry and any other references to default branding in
WHITE_LABELING.md use that same value.

In `@frontend/src/context/BrandingContext.tsx`:
- Line 76: The forEach callback currently implicitly returns the result of
root.style.removeProperty(v) (vars.forEach(v => root.style.removeProperty(v));)
which violates the useIterableCallbackReturn rule; change the callback to a
block body so it returns void (e.g., vars.forEach(v => {
root.style.removeProperty(v); });) making sure the callback for vars.forEach
does not return any value.

In `@frontend/src/utils/color.ts`:
- Around line 34-40: Normalize and validate the input inside hexToRgb: trim and
ensure it starts with or without '#', accept 3- or 6-character hex forms (expand
3-char shorthand to 6-char), verify the final cleaned string is exactly 6 hex
digits (0-9a-f/A-F), and if validation fails either throw a clear error or
return a safe default RGB; then parse r/g/b with parseInt on the validated
string to avoid NaN channels. Ensure these checks are implemented in hexToRgb so
downstream CSS variable generation never receives invalid channel values.

---

Nitpick comments:
In @.env.example:
- Around line 23-27: The hex color values in the .env example (ACCENT_COLOR,
BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, ERROR_COLOR) may
be parsed incorrectly due to the leading #; update each variable to wrap the hex
value in quotes (e.g., change ACCENT_COLOR=#dc2626 to ACCENT_COLOR="#dc2626")
and apply the same quoting to any other color vars in the file so parsers don't
treat # as a comment.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a488e55 and bc045f9.

📒 Files selected for processing (18)
  • .env.example
  • README.md
  • backend/crates/atlas-api/src/handlers/config.rs
  • backend/crates/atlas-api/src/handlers/mod.rs
  • backend/crates/atlas-api/src/main.rs
  • docker-compose.yml
  • docs/WHITE_LABELING.md
  • frontend/nginx.conf
  • frontend/src/App.tsx
  • frontend/src/api/config.ts
  • frontend/src/components/Layout.tsx
  • frontend/src/context/BrandingContext.tsx
  • frontend/src/context/branding-context.ts
  • frontend/src/hooks/useBranding.ts
  • frontend/src/index.css
  • frontend/src/pages/WelcomePage.tsx
  • frontend/src/utils/color.ts
  • frontend/tailwind.config.js

@pthmas pthmas requested a review from tac0turtle February 27, 2026 14:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/crates/atlas-api/src/main.rs (1)

17-30: ⚠️ Potential issue | 🟠 Major

AppState.chain_id bootstrap is still missing.

Line 17-Line 30 has no chain_id: u64 field, and Line 45-Line 94 does not fetch eth_chainId once at startup. This leaves the main.rs startup contract incomplete.

Based on learnings: "Applies to /atlas-api//main.rs : Fetch chain ID from RPC using eth_chainId once at startup and store in AppState.chain_id as u64".

Also applies to: 45-94

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/crates/atlas-api/src/main.rs` around lines 17 - 30, Add a chain_id:
u64 field to the AppState struct and populate it once during startup by calling
eth_chainId on the configured RPC URL; specifically, update the AppState
definition to include pub chain_id: u64, then in main (the
startup/initialization block where PgPool, rpc_url, solc_path, and admin_api_key
are created) perform a single JSON-RPC call to eth_chainId using the rpc_url,
parse the hex result to a u64, and assign that value to AppState.chain_id before
constructing the AppState instance (handle RPC errors gracefully and bubble them
up or log and exit).
🧹 Nitpick comments (1)
backend/crates/atlas-api/src/main.rs (1)

55-68: Normalize whitespace-only optional branding vars as unset.

Line 57, Line 58, Line 61, Line 64, Line 67, and Line 68 only check is_empty(), so values like " " are treated as configured. Consider trimming like CHAIN_NAME for consistent optional behavior.

Proposed refactor
 let chain_logo_url = std::env::var("CHAIN_LOGO_URL")
     .ok()
-    .filter(|s| !s.is_empty());
-let accent_color = std::env::var("ACCENT_COLOR").ok().filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
+let accent_color = std::env::var("ACCENT_COLOR")
+    .ok()
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
 let background_color_dark = std::env::var("BACKGROUND_COLOR_DARK")
     .ok()
-    .filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
 let background_color_light = std::env::var("BACKGROUND_COLOR_LIGHT")
     .ok()
-    .filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
 let success_color = std::env::var("SUCCESS_COLOR")
     .ok()
-    .filter(|s| !s.is_empty());
-let error_color = std::env::var("ERROR_COLOR").ok().filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
+let error_color = std::env::var("ERROR_COLOR")
+    .ok()
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/crates/atlas-api/src/main.rs` around lines 55 - 68, The optional
branding env vars (chain_logo_url, accent_color, background_color_dark,
background_color_light, success_color, error_color) currently only check
.is_empty(), so whitespace-only values remain treated as set; update each
std::env::var(...).ok().filter(|s| !s.is_empty()) to trim the string before
emptiness check (e.g., .filter(|s| !s.trim().is_empty()) or map to
s.trim().to_string() then check), mirroring the handling used for CHAIN_NAME, so
whitespace-only values are normalized to unset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Around line 51-54: The chain_name fallback is incorrect: update the code that
sets chain_name (the
std::env::var("CHAIN_NAME").ok().filter(...).unwrap_or_else(...) expression) to
return "Unknown" instead of "Atlas" so the project uses the required default
when CHAIN_NAME is not provided.

In `@docs/WHITE_LABELING.md`:
- Around line 27-34: The fenced code block that starts with ``` in
WHITE_LABELING.md is untyped and triggers MD040; update the opening fence to
include a language tag (e.g., change ``` to ```text) so the block becomes a
typed fenced code block and resolves the lint warning for the snippet showing
the atlas/ directory tree.

---

Outside diff comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Around line 17-30: Add a chain_id: u64 field to the AppState struct and
populate it once during startup by calling eth_chainId on the configured RPC
URL; specifically, update the AppState definition to include pub chain_id: u64,
then in main (the startup/initialization block where PgPool, rpc_url, solc_path,
and admin_api_key are created) perform a single JSON-RPC call to eth_chainId
using the rpc_url, parse the hex result to a u64, and assign that value to
AppState.chain_id before constructing the AppState instance (handle RPC errors
gracefully and bubble them up or log and exit).

---

Nitpick comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Around line 55-68: The optional branding env vars (chain_logo_url,
accent_color, background_color_dark, background_color_light, success_color,
error_color) currently only check .is_empty(), so whitespace-only values remain
treated as set; update each std::env::var(...).ok().filter(|s| !s.is_empty()) to
trim the string before emptiness check (e.g., .filter(|s| !s.trim().is_empty())
or map to s.trim().to_string() then check), mirroring the handling used for
CHAIN_NAME, so whitespace-only values are normalized to unset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0a0d972-42cd-42be-b6dd-536c88ac6b04

📥 Commits

Reviewing files that changed from the base of the PR and between bc045f9 and 451a21d.

📒 Files selected for processing (3)
  • backend/crates/atlas-api/src/main.rs
  • docs/WHITE_LABELING.md
  • frontend/src/utils/color.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/utils/color.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Line 22: Replace the whitespace placeholder assignment for CHAIN_LOGO_URL with
a clean empty value (use CHAIN_LOGO_URL=) and move the explanatory text into a
commented line (e.g., # URL or path to logo (e.g., /branding/logo.png). Default:
bundled ev-node logo) so the env var is empty by default and the description
remains for contributors; update the CHAIN_LOGO_URL line in the example file
accordingly.
- Around line 23-27: Update the .env.example so the hex color values are quoted
to avoid parser/linter issues: wrap the values for ACCENT_COLOR,
BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, and ERROR_COLOR in
quotes (e.g., " #... " or ' #... ') while keeping the existing inline comments
intact; this ensures cross-tool compatibility for dotenv parsers and linters
without changing the variable names or comments.

In `@backend/crates/atlas-api/src/main.rs`:
- Around line 55-68: Env vars for optional branding (chain_logo_url,
accent_color, background_color_dark, background_color_light, success_color,
error_color) should be normalized by trimming whitespace before empty-checking;
update each initializer to trim the retrieved String (e.g., via mapping to
s.trim().to_string() or using filter_map with s.trim()) and then filter out
empty results so whitespace-only values are treated as empty and not passed to
the frontend.
- Around line 51-68: Add a #[cfg(test)] mod tests block in the same file that
exercises the new env-parsing logic by setting and unsetting environment
variables and asserting the resulting values of chain_name, chain_logo_url,
accent_color, background_color_dark, background_color_light, success_color, and
error_color; specifically write tests for (1) unset vars producing None or the
default "Unknown" for chain_name, (2) empty-string vars treated as missing, and
(3) whitespace-only chain_name trimmed to default, using std::env::set_var and
std::env::remove_var to control state and re-evaluating the parsing code (call
the same expressions or extract the parsing into a small helper you can invoke
in tests), and run via cargo test --workspace.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b624a402-1e4c-4f38-9404-4e900ecf6480

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2e1b5 and 6090c62.

📒 Files selected for processing (5)
  • .env.example
  • backend/crates/atlas-api/src/main.rs
  • docker-compose.yml
  • docs/WHITE_LABELING.md
  • frontend/src/context/branding-context.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/context/branding-context.ts
  • docker-compose.yml
  • docs/WHITE_LABELING.md

Copy link
Contributor

@tac0turtle tac0turtle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does it look on a slow internets, the default color would throw things off before the api config is loaded or is there a loading screen?

pthmas and others added 9 commits March 15, 2026 21:32
Add runtime-configurable branding via environment variables so each
chain deployment can customize name, logo, colors without rebuilding.

Backend: new /api/config endpoint serving branding config from env vars
(CHAIN_NAME, CHAIN_LOGO_URL, ACCENT_COLOR, BACKGROUND_COLOR_DARK,
BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, ERROR_COLOR).

Frontend: BrandingContext fetches config once on load and applies CSS
custom properties for accent, success/error, and derived surface
palettes. Logo, chain name, favicon, and page title update dynamically.
All values are optional with sensible defaults matching current Atlas
branding.
- Run cargo fmt to wrap long env var lines in main.rs
- Remove unused rgbToHex function and mode parameter in color.ts
- Fix react-refresh/only-export-components by separating BrandingContext
  definition into branding-context.ts and useBranding hook into its own file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Explain how to customize chain name, logo, and color scheme via
environment variables. Includes examples for blue, green, and
minimal configurations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The default branding comes from ev-node, not Atlas.
- Treat empty/whitespace CHAIN_NAME as unset, falling back to "Atlas"
- Validate hex input in hexToRgb: trim, support 3-char shorthand, reject
  malformed values to avoid NaN CSS variables
Make the table header and intro consistent so operators understand
that "Atlas" is the default ev-node branding, not a separate brand.
- Use block body in forEach to avoid implicit return (Biome rule)
- Add language tag to fenced code block in docs (MD040)
- Update CLAUDE.md to reflect "Atlas" as CHAIN_NAME default
Atlas is the indexer name, not a chain name. Deployers should always
set CHAIN_NAME to their chain's actual name.
- Fix Tailwind accent colors to use rgb(var(...) / <alpha-value>) pattern
  so opacity modifiers (e.g. accent-primary/40) work correctly
- Store accent CSS vars as RGB triplets instead of hex values
- Convert hex to RGB triplets in BrandingContext via hexToRgbTriplet helper
- Fix symlink frontend/public/branding to use relative path ../../branding
- Add unit tests for BrandingConfig serialization in config.rs
- Extract env-parsing into testable parse_chain_name/parse_optional_env helpers
- Add unit tests for env-parsing (unset, empty, whitespace, valid values)
- Trim whitespace on all optional branding env vars before filtering empties
- Quote hex color values in .env.example for cross-tool compatibility
- Clean up CHAIN_LOGO_URL whitespace placeholder in .env.example
@pthmas pthmas force-pushed the pierrick/white-labeling branch from 6090c62 to 610df4a Compare March 15, 2026 20:35
pthmas added 2 commits March 15, 2026 21:37
- Fix cargo fmt: collapse parse_optional_env chain to single line
- Remove frontend/public/branding symlink from git — it breaks CI and
  Docker builds where the target doesn't exist. Add to .gitignore
  so developers can create it locally per docs/WHITE_LABELING.md
The branding/ folder is needed for the docker-compose volume mount
but its contents are chain-specific and should not be committed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants