Centralize shared stylesheets used by hero_website_lib #4

Open
opened 2026-05-18 12:37:11 +00:00 by timur · 2 comments
Owner

Context

Meeting feedback: common styling is duplicated across hero_website_framework and various service UIs. Move shared CSS / theme tokens / Bootstrap overrides into hero_web_template so every Hero UI (framework-backed or not) references the same source.

Scope

  1. Audit current CSS surface in hero_website_framework/crates/hero_website_lib/static/css/ (Bootstrap, FontAwesome, CodeMirror themes, highlight.js themes, custom admin/auth/blog CSS).
  2. Identify what is Hero-specific theming (admin tab styling, stat boxes, dark/light tokens, brand colors) vs third-party vendor CSS (Bootstrap, FontAwesome).
  3. Move Hero-specific theming into hero_web_template/webcomponent/ (or a new shared CSS asset crate — propose the layout in a comment).
  4. hero_website_lib references the shared CSS instead of carrying its own copy.
  5. Document the asset layout + how a non-framework service includes the shared CSS.

Out of scope

  • Replacing Bootstrap / FontAwesome / CodeMirror — those vendor assets stay where they are, just centralized once.
  • Restyling — this is a refactor, not a design pass.

Acceptance

  • Shared theme CSS lives in exactly one place under hero_web_template.
  • hero_website_lib pulls it at build (via rust-embed from a shared crate, or via a build-time copy step — propose in a comment).
  • A service that does NOT use hero_website_lib can still include the same shared CSS with a documented snippet.
  • hero_website_framework #hoist-components (parallel work — components and CSS travel together)
  • hero_skills web skills (update to reference new asset locations)
## Context Meeting feedback: common styling is duplicated across hero_website_framework and various service UIs. Move shared CSS / theme tokens / Bootstrap overrides into `hero_web_template` so every Hero UI (framework-backed or not) references the same source. ## Scope 1. Audit current CSS surface in `hero_website_framework/crates/hero_website_lib/static/css/` (Bootstrap, FontAwesome, CodeMirror themes, highlight.js themes, custom admin/auth/blog CSS). 2. Identify what is **Hero-specific theming** (admin tab styling, stat boxes, dark/light tokens, brand colors) vs **third-party vendor CSS** (Bootstrap, FontAwesome). 3. Move Hero-specific theming into `hero_web_template/webcomponent/` (or a new shared CSS asset crate — propose the layout in a comment). 4. `hero_website_lib` references the shared CSS instead of carrying its own copy. 5. Document the asset layout + how a non-framework service includes the shared CSS. ## Out of scope - Replacing Bootstrap / FontAwesome / CodeMirror — those vendor assets stay where they are, just centralized once. - Restyling — this is a refactor, not a design pass. ## Acceptance - Shared theme CSS lives in exactly one place under `hero_web_template`. - `hero_website_lib` pulls it at build (via `rust-embed` from a shared crate, or via a build-time copy step — propose in a comment). - A service that does NOT use `hero_website_lib` can still include the same shared CSS with a documented snippet. ## Related - hero_website_framework #hoist-components (parallel work — components and CSS travel together) - hero_skills web skills (update to reference new asset locations)
Author
Owner

Parent / context tracker: hero_skills#262 — read it before starting work on this issue. Locked decisions, reference materials, and execution order live there. Iterate via comments here; consolidation passes on the body only after feedback settles.

Parent / context tracker: [hero_skills#262](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/262) — read it before starting work on this issue. Locked decisions, reference materials, and execution order live there. Iterate via comments here; consolidation passes on the body only after feedback settles.
Author
Owner

Audit summary

Pulled development on hero_web_template and hero_website_framework. Mapped the CSS surface on both framework crates plus the canonical theme used by demo_website / inline blocks in templates.

What's in each crate today

hero_website_framework/crates/hero_admin_lib/static/css/ — admin-side, all vendor:

File Type
bootstrap.min.css, bootstrap-icons.min.css, fonts/bootstrap-icons.woff[2] Vendor
highlight-github-dark.min.css Vendor (highlight.js theme)
unpoly.min.css Vendor

No Hero theme CSS file exists in hero_admin_lib — the five hoisted Web Components (<hero-api-docs>, <hero-connection-status>, <hero-logs-viewer>, <hero-jobs-viewer>, <hero-markdown-viewer>) are self-contained Shadow-DOM and carry their own styles inline. Confirmed in crates/hero_admin_lib/src/assets.rs.

hero_website_framework/crates/hero_website_lib/static/css/ — website-side, mostly vendor with one Hero file:

File Type
bootstrap.min.css + .map Vendor
codemirror/*.min.css (8 files: core + 3 themes + fold/show-hint/fullscreen) Vendor
fontawesome/*.min.css (5 files) + static/webfonts/fa-* Vendor
highlight/*.min.css (4 themes) Vendor
blog-content.css (96 lines, scoped to .blog-content) Hero-specific

Hero-specific theming actually lives inline in templates, not in .css files. The canonical block is crates/hero_website_lib/default_templates/admin/base.html:22–827 (805 lines) — and matches the meeting brief almost word-for-word (--primary/dark-mode --dm-* tokens, .admin-tabs, .stat-box{.success,.info,.warning,.danger,.error}, .admin-card, table/form/button/badge/alert overrides, [data-theme="dark"] cascade, .log-row-error/.log-row-warn, .theme-toggle, dark scrollbar, transition rules). Other inline <style> blocks of note: admin/login.html (286 lines), admin/blog_edit.html (136), auth/visitor_register.html (88) — these are page-specific and out of scope unless we choose to also extract them.

Three Hero admin themes exist in the wild — open question

I want to flag this before moving anything: there are three distinct Hero admin theme systems today, and they use different token namespaces / dark-mode conventions:

Where Style Tokens Dark-mode selector Lines
hero_website_lib/default_templates/admin/base.html (inline) Gradient navbar, classic admin dashboard --primary, --success, --dm-bg, --dm-surface, --dm-card, --dm-text, ... [data-theme="dark"] (custom attr) 805
hero_proc/crates/hero_proc_admin/static/css/dashboard.css Modern compact IDE-style --bg-primary, --bg-secondary, --accent-green/blue/..., derived from --bs-* [data-bs-theme="dark"] (Bootstrap-native) 1973
hero_web_template/webcomponent/templates/base.html (inline) Dark teal node dashboard --bg-darker, --bg-card, --accent-teal, --text-primary/secondary [data-bs-theme="dark"] (Bootstrap-native) 558

The issue body's vocabulary ("admin tab styling, stat-boxes, dark/light tokens") maps exactly to the hero_website_lib/admin/base.html block — so I'm treating that one as the canonical source for this refactor. Confirm if you'd rather we pick the hero_proc_admin / Bootstrap-native variant ([data-bs-theme] is more aligned with Bootstrap 5.3 and would future-proof us against Bootstrap upgrades). I'd default to keeping the hero_website_lib tokens (matches issue scope, both framework crates already render with it) and file a separate follow-up issue to unify with hero_proc_admin's convention as a design pass — not part of this refactor.

Proposed asset layout

A new top-level directory at hero_web_template/theme/, exposed as a small Rust crate named hero_theme:

hero_web_template/
├── theme/                          # NEW — single source of truth for shared Hero CSS
│   ├── Cargo.toml                  # crate "hero_theme"
│   ├── README.md                   # consumption snippets for framework + non-framework
│   ├── src/
│   │   └── lib.rs                  # rust-embed wrapper + router helper + favicon
│   └── static/
│       ├── css/
│       │   ├── hero-tokens.css     # :root + [data-theme="dark"] custom-property tokens
│       │   ├── hero-admin.css      # navbar / admin-tabs / admin-container / admin-card / stat-box / responsive
│       │   ├── hero-forms.css      # buttons / forms / badges / alerts / pagination / modal / dropdown
│       │   ├── hero-content.css    # code/pre, log-row-*, scrollbar, theme-toggle, transitions
│       │   └── blog-content.css    # moved verbatim from hero_website_lib/static/css/
│       └── favicon.svg             # moved from hero_admin_lib/static/favicon.svg
├── webcomponent/                   # existing demo — keep, eventually consume ../theme
├── backend/                        # existing demo backend
└── ...

Why a sub-crate, not a flat static/ dir:

  • Both hero_admin_lib and hero_website_lib already use rust-embed to bake their assets into the binary. A sibling crate that also exposes a #[derive(Embed)] struct lets them re-export the same assets with zero new mechanism. No build-step copy, no path-juggling, deterministic in CI.
  • A non-framework service can also add hero_theme = { git = "...", branch = "development" } and mount it the same way.

Why split into four CSS files instead of one bundle:

  • hero-tokens.css alone is what a service usually wants if it has its own admin shell — let people opt into just the design tokens.
  • hero-admin.css is the "give me the standard admin dashboard look" file. Pulls in hero-tokens.css.
  • hero-forms.css / hero-content.css are nice-to-haves that polish Bootstrap form controls and code blocks.
  • Five <link> tags or one hero-all.css aggregator — happy to do either. Five named files reads better for the docs snippet.

Inclusion mechanism

Framework crates (hero_admin_lib, hero_website_lib):

In Cargo.toml:

hero_theme = { git = "https://forge.ourworld.tf/lhumina_code/hero_web_template", branch = "development" }

In the admin router (one line):

let app = Router::new()
    .merge(hero_theme::router("/lib-static/theme"))  // mounts /lib-static/theme/css/*, favicon.svg
    .merge(hero_admin_lib::lib_static_files_router())  // existing /static/shared/* unchanged
    // ...

In the consumer's base.html (replaces the 805-line inline block):

<link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-tokens.css">
<link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-admin.css">
<link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-forms.css">
<link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-content.css">

hero_website_lib's default admin/base.html is the first consumer — its inline <style> block is deleted and replaced with the four <link> tags above.

Non-framework service (documented snippet, README.md):

Option A — Cargo dep (preferred, version-locked):

[dependencies]
hero_theme = { git = "https://forge.ourworld.tf/lhumina_code/hero_web_template", branch = "development" }
let app = Router::new().merge(hero_theme::router("/lib-static/theme"));

…then add the four <link> tags in base.html.

Option B — raw <link> to the forge raw URL (zero Cargo touch, no version lock):

<link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-tokens.css">
<link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-admin.css">
<link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-forms.css">
<link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-content.css">

Open questions before I move files

  1. Theme system pick — go with hero_website_lib/admin/base.html tokens ([data-theme="dark"], gradient navbar) for this refactor, and file a separate unify-with-hero_proc_admin issue later? Or pick the Bootstrap-native [data-bs-theme] convention now, which means rewriting the dark-mode cascade?
  2. Crate locationhero_web_template/theme/ (sibling of webcomponent/ and backend/ — what I'm proposing) vs hero_web_template/crates/hero_theme/ (workspace-style)? The repo isn't a Cargo workspace today; either works, sibling is simpler.
  3. Demo-vs-shared concernhero_web_template is also the clone-me starter. Once theme/ lives here, anyone cloning the repo to start a service gets both. Acceptable? Or should theme/ be a separate hero_web_theme repo and hero_web_template just depend on it? (I'd default to keeping it here — fewer repos to wire, and the META locked decision says "Shared CSS / theme tokens live in hero_web_template".)
  4. blog-content.css — also move into theme/static/css/, or leave it in hero_website_lib since only the blog feature uses it? I'd move it; "one place for shared theme CSS" reads cleaner.
  5. Out of scope for this PR — do you also want me to extract the inline <style> blocks from admin/login.html / admin/blog_edit.html / auth/visitor_register.html / the admin/docs/*.html files? They're small (88–286 lines), page-specific, and not in the issue's scope vocabulary, so I'd leave them inline.

Once you confirm #1 and #2 (the rest I can default if you don't have a preference) I'll do the move + framework reference updates in one commit, push to a feature branch, and open a PR per hero_branching.

## Audit summary Pulled `development` on `hero_web_template` and `hero_website_framework`. Mapped the CSS surface on both framework crates plus the canonical theme used by `demo_website` / inline blocks in templates. ### What's in each crate today **`hero_website_framework/crates/hero_admin_lib/static/css/`** — admin-side, all vendor: | File | Type | |---|---| | `bootstrap.min.css`, `bootstrap-icons.min.css`, `fonts/bootstrap-icons.woff[2]` | Vendor | | `highlight-github-dark.min.css` | Vendor (highlight.js theme) | | `unpoly.min.css` | Vendor | No Hero theme CSS file exists in `hero_admin_lib` — the five hoisted Web Components (`<hero-api-docs>`, `<hero-connection-status>`, `<hero-logs-viewer>`, `<hero-jobs-viewer>`, `<hero-markdown-viewer>`) are self-contained Shadow-DOM and carry their own styles inline. Confirmed in `crates/hero_admin_lib/src/assets.rs`. **`hero_website_framework/crates/hero_website_lib/static/css/`** — website-side, mostly vendor with one Hero file: | File | Type | |---|---| | `bootstrap.min.css` + `.map` | Vendor | | `codemirror/*.min.css` (8 files: core + 3 themes + fold/show-hint/fullscreen) | Vendor | | `fontawesome/*.min.css` (5 files) + `static/webfonts/fa-*` | Vendor | | `highlight/*.min.css` (4 themes) | Vendor | | **`blog-content.css`** (96 lines, scoped to `.blog-content`) | **Hero-specific** | **Hero-specific theming actually lives inline in templates, not in `.css` files.** The canonical block is `crates/hero_website_lib/default_templates/admin/base.html:22–827` (805 lines) — and matches the meeting brief almost word-for-word (`--primary`/dark-mode `--dm-*` tokens, `.admin-tabs`, `.stat-box{.success,.info,.warning,.danger,.error}`, `.admin-card`, table/form/button/badge/alert overrides, `[data-theme="dark"]` cascade, `.log-row-error/.log-row-warn`, `.theme-toggle`, dark scrollbar, transition rules). Other inline `<style>` blocks of note: `admin/login.html` (286 lines), `admin/blog_edit.html` (136), `auth/visitor_register.html` (88) — these are page-specific and out of scope unless we choose to also extract them. ### Three Hero admin themes exist in the wild — open question I want to flag this before moving anything: there are three distinct Hero admin theme systems today, and they use different token namespaces / dark-mode conventions: | Where | Style | Tokens | Dark-mode selector | Lines | |---|---|---|---|---| | `hero_website_lib/default_templates/admin/base.html` (inline) | Gradient navbar, classic admin dashboard | `--primary`, `--success`, `--dm-bg`, `--dm-surface`, `--dm-card`, `--dm-text`, ... | `[data-theme="dark"]` (custom attr) | 805 | | `hero_proc/crates/hero_proc_admin/static/css/dashboard.css` | Modern compact IDE-style | `--bg-primary`, `--bg-secondary`, `--accent-green/blue/...`, derived from `--bs-*` | `[data-bs-theme="dark"]` (Bootstrap-native) | 1973 | | `hero_web_template/webcomponent/templates/base.html` (inline) | Dark teal node dashboard | `--bg-darker`, `--bg-card`, `--accent-teal`, `--text-primary/secondary` | `[data-bs-theme="dark"]` (Bootstrap-native) | 558 | The issue body's vocabulary ("admin tab styling, stat-boxes, dark/light tokens") maps **exactly** to the `hero_website_lib/admin/base.html` block — so I'm treating that one as the canonical source for this refactor. Confirm if you'd rather we pick the `hero_proc_admin` / Bootstrap-native variant (`[data-bs-theme]` is more aligned with Bootstrap 5.3 and would future-proof us against Bootstrap upgrades). I'd default to keeping the `hero_website_lib` tokens (matches issue scope, both framework crates already render with it) and file a separate follow-up issue to unify with `hero_proc_admin`'s convention as a design pass — not part of this refactor. ## Proposed asset layout A new top-level directory at `hero_web_template/theme/`, exposed as a small Rust crate named `hero_theme`: ``` hero_web_template/ ├── theme/ # NEW — single source of truth for shared Hero CSS │ ├── Cargo.toml # crate "hero_theme" │ ├── README.md # consumption snippets for framework + non-framework │ ├── src/ │ │ └── lib.rs # rust-embed wrapper + router helper + favicon │ └── static/ │ ├── css/ │ │ ├── hero-tokens.css # :root + [data-theme="dark"] custom-property tokens │ │ ├── hero-admin.css # navbar / admin-tabs / admin-container / admin-card / stat-box / responsive │ │ ├── hero-forms.css # buttons / forms / badges / alerts / pagination / modal / dropdown │ │ ├── hero-content.css # code/pre, log-row-*, scrollbar, theme-toggle, transitions │ │ └── blog-content.css # moved verbatim from hero_website_lib/static/css/ │ └── favicon.svg # moved from hero_admin_lib/static/favicon.svg ├── webcomponent/ # existing demo — keep, eventually consume ../theme ├── backend/ # existing demo backend └── ... ``` **Why a sub-crate, not a flat `static/` dir:** - Both `hero_admin_lib` and `hero_website_lib` already use `rust-embed` to bake their assets into the binary. A sibling crate that also exposes a `#[derive(Embed)]` struct lets them re-export the same assets with zero new mechanism. No build-step copy, no path-juggling, deterministic in CI. - A non-framework service can also add `hero_theme = { git = "...", branch = "development" }` and mount it the same way. **Why split into four CSS files instead of one bundle:** - `hero-tokens.css` alone is what a service usually wants if it has its own admin shell — let people opt into just the design tokens. - `hero-admin.css` is the "give me the standard admin dashboard look" file. Pulls in `hero-tokens.css`. - `hero-forms.css` / `hero-content.css` are nice-to-haves that polish Bootstrap form controls and code blocks. - Five `<link>` tags or one `hero-all.css` aggregator — happy to do either. Five named files reads better for the docs snippet. ## Inclusion mechanism **Framework crates (`hero_admin_lib`, `hero_website_lib`):** In `Cargo.toml`: ```toml hero_theme = { git = "https://forge.ourworld.tf/lhumina_code/hero_web_template", branch = "development" } ``` In the admin router (one line): ```rust let app = Router::new() .merge(hero_theme::router("/lib-static/theme")) // mounts /lib-static/theme/css/*, favicon.svg .merge(hero_admin_lib::lib_static_files_router()) // existing /static/shared/* unchanged // ... ``` In the consumer's `base.html` (replaces the 805-line inline block): ```html <link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-tokens.css"> <link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-admin.css"> <link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-forms.css"> <link rel="stylesheet" href="{{ base_path }}/lib-static/theme/css/hero-content.css"> ``` `hero_website_lib`'s default `admin/base.html` is the first consumer — its inline `<style>` block is deleted and replaced with the four `<link>` tags above. **Non-framework service (documented snippet, README.md):** Option A — Cargo dep (preferred, version-locked): ```toml [dependencies] hero_theme = { git = "https://forge.ourworld.tf/lhumina_code/hero_web_template", branch = "development" } ``` ```rust let app = Router::new().merge(hero_theme::router("/lib-static/theme")); ``` …then add the four `<link>` tags in `base.html`. Option B — raw `<link>` to the forge raw URL (zero Cargo touch, no version lock): ```html <link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-tokens.css"> <link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-admin.css"> <link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-forms.css"> <link rel="stylesheet" href="https://forge.ourworld.tf/lhumina_code/hero_web_template/raw/branch/development/theme/static/css/hero-content.css"> ``` ## Open questions before I move files 1. **Theme system pick** — go with `hero_website_lib/admin/base.html` tokens (`[data-theme="dark"]`, gradient navbar) for this refactor, and file a separate unify-with-`hero_proc_admin` issue later? Or pick the Bootstrap-native `[data-bs-theme]` convention now, which means rewriting the dark-mode cascade? 2. **Crate location** — `hero_web_template/theme/` (sibling of `webcomponent/` and `backend/` — what I'm proposing) vs `hero_web_template/crates/hero_theme/` (workspace-style)? The repo isn't a Cargo workspace today; either works, sibling is simpler. 3. **Demo-vs-shared concern** — `hero_web_template` is also the clone-me starter. Once `theme/` lives here, anyone cloning the repo to start a service gets both. Acceptable? Or should `theme/` be a separate `hero_web_theme` repo and `hero_web_template` just depend on it? (I'd default to keeping it here — fewer repos to wire, and the META locked decision says "Shared CSS / theme tokens live in `hero_web_template`".) 4. **`blog-content.css`** — also move into `theme/static/css/`, or leave it in `hero_website_lib` since only the blog feature uses it? I'd move it; "one place for shared theme CSS" reads cleaner. 5. **Out of scope for this PR** — do you also want me to extract the inline `<style>` blocks from `admin/login.html` / `admin/blog_edit.html` / `auth/visitor_register.html` / the `admin/docs/*.html` files? They're small (88–286 lines), page-specific, and not in the issue's scope vocabulary, so I'd leave them inline. Once you confirm #1 and #2 (the rest I can default if you don't have a preference) I'll do the move + framework reference updates in one commit, push to a feature branch, and open a PR per `hero_branching`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_web_template#4
No description provided.