toga-ai 1.0.510 → 1.0.511
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/knowledge/1.0/apps/tools/INDEX.md +2 -1
- package/knowledge/1.0/apps/tools/architecture.md +24 -2
- package/knowledge/1.0/apps/tools/features/clickup-sprint-dashboard.md +13 -2
- package/knowledge/1.0/apps/tools/features/design-demo-admin.md +1 -2
- package/knowledge/1.0/apps/tools/features/legacy-email-notifier.md +15 -2
- package/knowledge/1.0/apps/tools/features/talos-pricing-ui.md +20 -0
- package/knowledge/1.0/apps/tools/features/theme-light-dark.md +209 -0
- package/knowledge/1.0/standards/frontend.md +42 -3
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
|
-
| [Tools (1.0 Internal-Tools App) Architecture](architecture.md) | **Tools** is a standalone 1.0 (`App_`) application that houses many small internal tools behind simple interfaces, gated by Client_True staff persona. | tools/index.php, tools/_/app/framework.php, tools/_/app/frameworkindex.php, tools/assets/img/favicon/favicon.ico, tools/assets/img/favicon/favicon-32x32.png, tools/assets/img/favicon/favicon-16x16.png, tools/assets/img/favicon/apple-touch-icon.png, tools/_/app/auth.php, tools/_/app/nav.php, tools/common/header.php, tools/common/footer.php, tools/mvc/get.php, tools/mvc/_TEMPLATE/get.php, tools/docs/ADDING_A_TOOL.md |
|
|
5
|
+
| [Tools (1.0 Internal-Tools App) Architecture](architecture.md) | **Tools** is a standalone 1.0 (`App_`) application that houses many small internal tools behind simple interfaces, gated by Client_True staff persona. | tools/index.php, tools/_/app/framework.php, tools/_/app/frameworkindex.php, tools/assets/img/favicon/favicon.ico, tools/assets/img/favicon/favicon-32x32.png, tools/assets/img/favicon/favicon-16x16.png, tools/assets/img/favicon/apple-touch-icon.png, tools/_/app/auth.php, tools/_/app/nav.php, tools/common/header.php, tools/common/footer.php, tools/mvc/get.php, tools/mvc/_TEMPLATE/get.php, tools/docs/ADDING_A_TOOL.md, tools/assets/css/theme.css, tools/assets/js/theme.js |
|
|
6
6
|
| [ClickUp Sprint Dashboard (tools /clickup/react)](features/clickup-sprint-dashboard.md) | A native **ClickUp sprint dashboard** in the 1.0 `tools` app at route `/clickup/react`, built for a wall-mounted **TV display** ("TOGa IQ" internal sprint metri | tools/_/app/clickup/sprint.php, tools/v2/sprints/tile/index.php, tools/v2/sprints/current/index.php, tools/v2/sprints/status-breakdown/index.php, tools/v2/sprints/worktype-breakdown/index.php, tools/v2/sprints/points-by-dev/index.php, tools/v2/sprints/burndown/index.php, tools/assets/clickup/sprint-dashboard.html, tools/mvc/clickup/react/get.php, tools/mvc/login/get.php, tools/_/app/nav.php |
|
|
7
7
|
| [CloudFront Client Setup](features/cloudfront-client-setup.md) | An SSO-gated admin tool at **`/devops/cloudfront-clients`** in the Tools 1.0 app that onboards a client onto **CloudFront + Route 53 across multiple AWS account | tools/_/app/devops/cloudfront.php, tools/mvc/devops/cloudfront-clients/get.php, tools/mvc/devops/cloudfront-clients/post.php, tools/assets/js/cloudfront-clients.js, tools/assets/css/cloudfront-clients.css, tools/_/app/nav.php, tools/_/app/frameworkindex.php, tools/config.production.ini |
|
|
8
8
|
| [Design Demo Admin](features/design-demo-admin.md) | A self-serve admin UI at **`/design`** in the SSO-protected **Tools** app that lets the design team publish self-contained "Claude Design" HTML exports as **ver | tools/_/app/design/github.php, tools/mvc/design/get.php, tools/mvc/design/post.php, tools/assets/css/design.css, tools/assets/js/design.js, tools/_/app/frameworkindex.php, tools/_/app/nav.php, tools/composer.json |
|
|
@@ -14,4 +14,5 @@
|
|
|
14
14
|
| [Tools SAML SSO Consumer & Persona-Gated Auth (App_Auth)](features/saml-sso-auth.md) | `App_Auth` is the Tools app's authentication layer: it consumes the SAML gateway `?saml=` handoff (see the 2.0 SAML downstream integration contract), establishe | tools/_/app/auth.php, tools/mvc/sso/initiate/get.php, tools/mvc/sso/get.php, tools/mvc/login/get.php, tools/mvc/login/post.php, tools/mvc/logout/get.php, tools/mvc/get.php, tools/config.production.ini, tools/config.local.ini |
|
|
15
15
|
| [Talos Knowledge Base Admin UI (KB Documents + Vocabulary)](features/talos-kb-documents-admin.md) | > **PER-AI-MODEL, DATA-DRIVEN SCOPING (2026-07-29).** The KB-documents and Vocabulary admin > UIs were refactored from a single hard-coded **"development-team"* | tools/mvc/talos/kb-documents/get.php, tools/mvc/talos/kb-documents/post.php, tools/mvc/talos/knowledge-bases/get.php, tools/mvc/talos/knowledge-bases/post.php, tools/mvc/talos/vocabulary/get.php, tools/mvc/talos/vocabulary/post.php, tools/_/app/talos/s3.php, tools/_/app/talos/bedrock.php, tools/_/app/pg.php, tools/_/app/model/true/aimodels.php, tools/_/app/model/true/vectorindexes.php, tools/_/app/model/true/aimodels_vectorindexes.php, tools/_/app/worker.php, tools/_/app/nav.php, tools/config.production.ini, tools/config.alpha.ini, tools/.platform/httpd/conf.d/timeouts.conf, tools/.platform/hooks/prebuild/01-install-php-pgsql.sh, tools/.platform/hooks/postdeploy/01-restart-php.sh |
|
|
16
16
|
| [Talos Pricing UI (Contracts, Pricing Dashboard, Usage, Settings + Estimator)](features/talos-pricing-ui.md) | The 1.0 (tools app) face of the **Talos Pricing Platform** — a "Talos Pricing" nav folder with four pages plus the estimate engine. | tools/_/app/nav.php, tools/_/app/talos/estimator.php, tools/_/app/talos/usage.php, tools/mvc/talos/contracts/get.php, tools/mvc/talos/contracts/post.php, tools/mvc/talos/pricing/get.php, tools/mvc/talos/usage/get.php, tools/mvc/talos/settings/get.php, tools/mvc/talos/settings/post.php, tools/assets/css/style.css |
|
|
17
|
+
| [App-Wide Colour Theme (light / dark / auto)](features/theme-light-dark.md) | A **light / dark / auto** colour theme for the *entire* Tools app, built as a single semantic-token layer (`assets/css/theme.css`) rather than per-page edits. | tools/assets/css/theme.css, tools/assets/js/theme.js, tools/_/app/frameworkindex.php, tools/_/app/nav.php, tools/common/header.php, tools/mvc/login/get.php, tools/assets/css/style.css, tools/assets/css/design.css, tools/assets/css/login.css, tools/assets/css/cloudfront-clients.css, tools/assets/css/gchart.css, tools/mvc/talos/usage/get.php, tools/mvc/talos/pricing/get.php, tools/mvc/talos/contracts/get.php, tools/mvc/clickup/dashboard/get.php, tools/mvc/email-migration/notify/post.php |
|
|
17
18
|
| [Deploying Tools to Elastic Beanstalk (PHP 8.5 / Amazon Linux 2023)](workflows/deploy-to-elastic-beanstalk-al2023.md) | How the **Tools** 1.0 app boots on Elastic Beanstalk running `PHP 8.5 on 64bit Amazon Linux 2023/4.13.1 (aarch64)`. | tools/.ebextensions/004_http_to_https.config, tools/.ebextensions/006_mount-s3fs.config, tools/.ebextensions/007_setup_export_cache_folders.config, tools/.ebextensions/008_setup_ldap.config, tools/.ebextensions/009_setup_phpini.config, tools/.ebextensions/020_setup_git_libraries.config, tools/.ebextensions/050_register_instance_to_shared_application_load_balancer.config, tools/ebs/git.json, tools/ebs/setup_export_cache_folders.php, tools/_/app/frameworkindex.php |
|
|
@@ -6,7 +6,7 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-04
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/index.php
|
|
@@ -23,7 +23,10 @@ files:
|
|
|
23
23
|
- tools/mvc/get.php
|
|
24
24
|
- tools/mvc/_TEMPLATE/get.php
|
|
25
25
|
- tools/docs/ADDING_A_TOOL.md
|
|
26
|
+
- tools/assets/css/theme.css
|
|
27
|
+
- tools/assets/js/theme.js
|
|
26
28
|
related:
|
|
29
|
+
- ./features/theme-light-dark.md
|
|
27
30
|
- ./features/saml-sso-auth.md
|
|
28
31
|
- ./features/persona-gated-navigation.md
|
|
29
32
|
- ./features/developer-tools.md
|
|
@@ -47,7 +50,13 @@ double-gated (`[internal] dev_mode` AND `App_Registry::inDevMode()`). `config.*.
|
|
|
47
50
|
(incl. `config.prod.ini`) is committed with plaintext production secrets that were **publicly
|
|
48
51
|
web-readable** until the front-controller deny blocks added 2026-07-24 (see Known issues);
|
|
49
52
|
rotation of every exposed secret is still owed. Never add more secrets, and keep config files
|
|
50
|
-
denied at the front controller / out of the web root.
|
|
53
|
+
denied at the front controller / out of the web root. New CSS must use `--c-*` tokens from
|
|
54
|
+
`assets/css/theme.css`, never raw colour literals. **`assets/css/style.scss` is DEAD — edit
|
|
55
|
+
`style.css` directly** (the `.css` has drifted ~450 lines ahead; there is no sass binary or
|
|
56
|
+
`package.json`, so recompiling the SCSS would silently revert shipped work).
|
|
57
|
+
`App_FrameworkIndex::setHeaderCode()` is the **only** hook for markup in `<head>`, and it
|
|
58
|
+
**SETS rather than appends** — the theme bootstrap is its sole caller, so a second call
|
|
59
|
+
silently clobbers it.
|
|
51
60
|
|
|
52
61
|
## Boot & structure
|
|
53
62
|
|
|
@@ -72,6 +81,14 @@ sibling apps. A fixed left two-column shell: dark left sidebar (`.app-nav`) with
|
|
|
72
81
|
persona-filtered nav in the middle, pinned footer with the user's name + a ghost "Log out"
|
|
73
82
|
button; content area on the right. **No top header bar** (see Critical rules).
|
|
74
83
|
|
|
84
|
+
**All colour comes from a semantic token layer, `assets/css/theme.css`**, registered **first**
|
|
85
|
+
in `App_FrameworkIndex_Tools::__construct()` so every other sheet resolves through it. The app
|
|
86
|
+
supports light / dark / auto; the resolved theme is stamped on `<html>` as `data-theme` by an
|
|
87
|
+
inline pre-paint script and the user's choice lives in `localStorage` (`toga-tools-theme`) —
|
|
88
|
+
**not** the DB. The left sidebar and public header stay dark in **both** themes, which is why
|
|
89
|
+
`toga-brandmark-white.png` still works everywhere. Login shows `toga-horizontal-colored.png` in
|
|
90
|
+
light and `toga-horizontal-white.png` in dark. See `features/theme-light-dark.md`.
|
|
91
|
+
|
|
75
92
|
## Home route
|
|
76
93
|
|
|
77
94
|
`/` (`mvc/get.php`) renders a persona-filtered dashboard of available tools (tiles grouped by
|
|
@@ -106,6 +123,11 @@ Create `mvc/<folder>/<tool>/get.php` (copy `mvc/_TEMPLATE/get.php`), add one ent
|
|
|
106
123
|
composer package must `require_once` the autoloader itself.
|
|
107
124
|
|
|
108
125
|
## Change history
|
|
126
|
+
- 2026-08-04 — Added the app-wide light/dark/auto theme: `assets/css/theme.css` token layer
|
|
127
|
+
registered first in `App_FrameworkIndex_Tools`, pre-paint `data-theme` stamping via the
|
|
128
|
+
one-and-only `setHeaderCode()` hook, `localStorage` preference (no DB column), nav/public
|
|
129
|
+
header dark in both themes. Recorded that `style.scss` is dead and `setHeaderCode()`
|
|
130
|
+
sets-not-appends. Not committed to `tools` yet (jcardinal)
|
|
109
131
|
- 2026-07-24 — Fixed the config-file web-root exposure: `config.*.ini` was publicly readable via
|
|
110
132
|
the `!-f`-only front controller (`GET /config.production.ini` served it verbatim); added
|
|
111
133
|
`<FilesMatch>` deny blocks referencing the new 1.0 back-end security standard. Softened the
|
|
@@ -6,8 +6,8 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: [kyalamarthi]
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: [kyalamarthi, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/_/app/clickup/sprint.php
|
|
13
13
|
- tools/v2/sprints/tile/index.php
|
|
@@ -24,6 +24,7 @@ related:
|
|
|
24
24
|
- ./persona-gated-navigation.md
|
|
25
25
|
- ./saml-sso-auth.md
|
|
26
26
|
- ./mvc-data-access-patterns.md
|
|
27
|
+
- ./theme-light-dark.md
|
|
27
28
|
- ../architecture.md
|
|
28
29
|
- ../../../2.0/apps/worker2/features/team-sprint-management.md
|
|
29
30
|
---
|
|
@@ -138,5 +139,15 @@ the React dashboard supersedes it.
|
|
|
138
139
|
passed by reference", which the E_ALL error handler promotes to a fatal 500. Assign first:
|
|
139
140
|
`$res = App_Database::query(...); buildArrayOfRows($res, false);`.
|
|
140
141
|
|
|
142
|
+
- **The old Power BI embed page (`mvc/clickup/dashboard/get.php`) keeps ~6 fixed colour
|
|
143
|
+
literals on purpose — do NOT theme them.** Its card/frame colour `#0b1020` is matched to the
|
|
144
|
+
embedded Power BI report canvas so nothing flashes white while the iframe loads. Themed
|
|
145
|
+
surfaces there would reintroduce the flash in one theme or the other. Everything else on the
|
|
146
|
+
page was tokenised for the app-wide theme — see
|
|
147
|
+
[App-Wide Colour Theme](./theme-light-dark.md).
|
|
148
|
+
|
|
141
149
|
## Change history
|
|
150
|
+
- 2026-08-04 — Tokenised the `/clickup/dashboard` page-local styles for the app-wide light/dark
|
|
151
|
+
theme, keeping ~6 deliberate fixed literals (incl. the `#0b1020` frame that matches the
|
|
152
|
+
embedded Power BI canvas so the iframe load does not flash white). Not committed. (jcardinal)
|
|
142
153
|
- 2026-07-29 — Built `/clickup/react` as a native tools feature (data engine, `/v2/sprints/*` routes, CDN-React TV UI, iframe embed, "Dashboard/Clickup" nav); removed the old `/clickup/dashboard` Power BI nav entry; documented two reusable 1.0 gotchas (queryLogging preflight on Defaults-less DBs, buildArrayOfRows by-ref). (kyalamarthi)
|
|
@@ -208,5 +208,4 @@ None — internal/shared design-team tool.
|
|
|
208
208
|
- [Tools Architecture](../architecture.md)
|
|
209
209
|
- [Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md)
|
|
210
210
|
- [Deploying Tools to Elastic Beanstalk (AL2023)](../workflows/deploy-to-elastic-beanstalk-al2023.md)
|
|
211
|
-
|
|
212
|
-
</invoke>
|
|
211
|
+
|
|
@@ -6,14 +6,15 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
10
|
-
owners: [bala]
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: [bala, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/mvc/email-migration/notify/get.php
|
|
13
13
|
- tools/mvc/email-migration/notify/post.php
|
|
14
14
|
- tools/_/app/nav.php
|
|
15
15
|
related:
|
|
16
16
|
- ./mvc-data-access-patterns.md
|
|
17
|
+
- ./theme-light-dark.md
|
|
17
18
|
- ../workflows/deploy-to-elastic-beanstalk-al2023.md
|
|
18
19
|
- ../architecture.md
|
|
19
20
|
---
|
|
@@ -104,7 +105,19 @@ endpoint that swallows non-fatal diagnostics (returns `true`) for the duration o
|
|
|
104
105
|
fatals and thrown exceptions still reach the `catch`. The framework mechanism is documented in
|
|
105
106
|
[mvc-data-access-patterns](./mvc-data-access-patterns.md).
|
|
106
107
|
|
|
108
|
+
## Gotcha — the colour literals in `post.php` are EMAIL HTML; never tokenise them
|
|
109
|
+
|
|
110
|
+
`post.php` builds the digest **email** body. Email clients cannot resolve CSS custom
|
|
111
|
+
properties, so its colour literals must stay hard-coded — a `var(--c-…)` there renders as no
|
|
112
|
+
colour at all. This is easy to get wrong because the on-screen `get.php` in the same folder
|
|
113
|
+
*was* tokenised for the app-wide theme (see
|
|
114
|
+
[App-Wide Colour Theme](./theme-light-dark.md)). Rule: styles that leave the browser are not
|
|
115
|
+
themed.
|
|
116
|
+
|
|
107
117
|
## Change history
|
|
118
|
+
- 2026-08-04 — Tokenised the on-screen `get.php` page styles for the app-wide light/dark theme
|
|
119
|
+
and recorded that `post.php`'s email-HTML colour literals must stay hard-coded (email clients
|
|
120
|
+
cannot resolve CSS custom properties). Not committed. (jcardinal)
|
|
108
121
|
- 2026-08-03 — Built the Legacy Email Notifier: multi-file xlsx merge via vendored PhpSpreadsheet,
|
|
109
122
|
legacy-domain filter, `Client_True.Users` local-part resolution with always-domain-swap
|
|
110
123
|
(never-skip), one digest per person per legacy domain, and chunked (25/batch) browser-driven
|
|
@@ -20,6 +20,7 @@ files:
|
|
|
20
20
|
- tools/mvc/talos/settings/post.php
|
|
21
21
|
- tools/assets/css/style.css
|
|
22
22
|
related:
|
|
23
|
+
- ./theme-light-dark.md
|
|
23
24
|
- ./persona-gated-navigation.md
|
|
24
25
|
- ./mvc-data-access-patterns.md
|
|
25
26
|
- ../architecture.md
|
|
@@ -97,6 +98,20 @@ about cost; cost is driven by tool-call **intensity** via the profile
|
|
|
97
98
|
- **Voice multipliers (0.25 / 0.50) are UNVALIDATED** — supplied second-hand, never measured,
|
|
98
99
|
and possibly inverted. Flagged in the UI. **Do not quote voice from them.**
|
|
99
100
|
- **`db_team`** is read/write on Contracts and Settings, read-only on the dashboards.
|
|
101
|
+
- **Chart.js colours must come from theme tokens, and a live theme switch needs
|
|
102
|
+
`chart.update()`.** These pages now read series/axis/tick/gridline colours via
|
|
103
|
+
`TogaTheme.color('--c-…')` inside a `paint()` registered with `TogaTheme.onChange(paint)`,
|
|
104
|
+
then call `chart.update()` — `update()` clears Chart.js's resolver cache, so without it a
|
|
105
|
+
`Chart.defaults` change does not reach charts that already exist. Chart.js otherwise defaults
|
|
106
|
+
axis/tick text to a dark colour that is unreadable in dark mode. See
|
|
107
|
+
[App-Wide Colour Theme](./theme-light-dark.md).
|
|
108
|
+
- **These pages keep an explicit `--c-bs-*` token group** because they were authored against
|
|
109
|
+
Bootstrap's palette (`#0d6efd`, `#6c757d`, `#495057`, `#dc3545`, `#198754`…), which is close
|
|
110
|
+
to but not the TOGA brand palette — folding them into `--c-primary` visibly restyles them.
|
|
111
|
+
- **Open (dark mode):** the error toast measures ~2.4:1 (white on `--c-danger`) and likely
|
|
112
|
+
wants a fixed dark fill; the Pricing Dashboard target-band dataset never had a
|
|
113
|
+
`backgroundColor` (pre-existing authoring slip) so the band shades rather than tints; the
|
|
114
|
+
three chart pages cannot be statically proven light-identical and still need a browser pass.
|
|
100
115
|
|
|
101
116
|
## Security
|
|
102
117
|
- `config.*.ini` carries committed plaintext secrets (team-accepted, see architecture Known
|
|
@@ -104,5 +119,10 @@ about cost; cost is driven by tool-call **intensity** via the profile
|
|
|
104
119
|
(`[api] talos_backend_url` / `talos_backend_key`); no values recorded. Do not add secrets.
|
|
105
120
|
|
|
106
121
|
## Change history
|
|
122
|
+
- 2026-08-04 — Tokenised these pages for the new app-wide light/dark theme: page-local styles
|
|
123
|
+
now use `--c-*` tokens (Talos pages keep their own `--c-bs-*` Bootstrap ramp), and the three
|
|
124
|
+
Chart.js pages read colours via `TogaTheme.color()` + repaint on `TogaTheme.onChange()` with
|
|
125
|
+
`chart.update()`. Recorded the dark-mode open items (error-toast contrast, missing
|
|
126
|
+
target-band `backgroundColor`). Not committed. (jcardinal)
|
|
107
127
|
- 2026-08-04 — Restructured to **Contracts / Pricing Dashboard / Usage / Pricing Settings**. New Contracts pages replace Onboarding with a real PROSPECT/ACTIVE/CHURNED lifecycle (signed contracts churn, never hard-delete — `TalosPricingBands` cascades; post-signature edits append to `TalosContractAmendments`), plus actual-vs-estimate variance, month history chart and one-click apply-recommended-org-fee. Settings replaces the Cost Factors editor with measured rows read-only (`UPDATE ... AND isDerived = 0`). Benchmarks page deleted. Pricing Dashboard rebuilt for sales leadership. Fixed `usage.php clientOptions()` reading an always-empty `TalosClients` (selector rendered empty) → `GET /api/usage/clients` with Team-DB fallback; added cost-intensity panel. Estimator moved to token unit price + workload profiles. Wrote the missing `.app-content:has(.tool--wide)` rule and consolidated Talos CSS into `style.css`. (jcardinal)
|
|
108
128
|
- 2026-06-29 — Built the Talos Pricing UI: nav folder + 4 pages (Onboarding w/ live JS estimate and contract-signing that locks the band schedule into `TalosClients`+`TalosPricingBands` in one txn; read-only Pricing Dashboard + Usage Benchmarks; technical-only Cost Factors editor) and `App_Talos_Estimator` (reads `TalosCostFactors`, derives conversations/user so sales need not enter it). Reads Team DB via new `db_team` connection. Offerings CHAT/VOICE_TO_VOICE/NATURAL_VOICE = 1.0/0.25/0.50 (voice ambiguous, deferred). (jcardinal)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: App-Wide Colour Theme (light / dark / auto)
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
repo: tools
|
|
5
|
+
project: Tools
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-08-04
|
|
10
|
+
owners: [jcardinal]
|
|
11
|
+
files:
|
|
12
|
+
- tools/assets/css/theme.css
|
|
13
|
+
- tools/assets/js/theme.js
|
|
14
|
+
- tools/_/app/frameworkindex.php
|
|
15
|
+
- tools/_/app/nav.php
|
|
16
|
+
- tools/common/header.php
|
|
17
|
+
- tools/mvc/login/get.php
|
|
18
|
+
- tools/assets/css/style.css
|
|
19
|
+
- tools/assets/css/design.css
|
|
20
|
+
- tools/assets/css/login.css
|
|
21
|
+
- tools/assets/css/cloudfront-clients.css
|
|
22
|
+
- tools/assets/css/gchart.css
|
|
23
|
+
- tools/mvc/talos/usage/get.php
|
|
24
|
+
- tools/mvc/talos/pricing/get.php
|
|
25
|
+
- tools/mvc/talos/contracts/get.php
|
|
26
|
+
- tools/mvc/clickup/dashboard/get.php
|
|
27
|
+
- tools/mvc/email-migration/notify/post.php
|
|
28
|
+
related:
|
|
29
|
+
- ../architecture.md
|
|
30
|
+
- ./persona-gated-navigation.md
|
|
31
|
+
- ./talos-pricing-ui.md
|
|
32
|
+
- ./clickup-sprint-dashboard.md
|
|
33
|
+
- ./legacy-email-notifier.md
|
|
34
|
+
- ./design-demo-admin.md
|
|
35
|
+
- ../../../standards/frontend.md
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Summary
|
|
39
|
+
|
|
40
|
+
A **light / dark / auto** colour theme for the *entire* Tools app, built as a single
|
|
41
|
+
semantic-token layer (`assets/css/theme.css`) rather than per-page edits. The user's choice
|
|
42
|
+
is a three-way switcher rendered next to their name in the left-nav footer, persisted in
|
|
43
|
+
**`localStorage` (`toga-tools-theme`) — deliberately not a DB column**, and applied
|
|
44
|
+
**before first paint** by an inline bootstrap script so there is no flash of the wrong theme.
|
|
45
|
+
|
|
46
|
+
**Light mode is a hard constraint: pixel-identical to before the change**, verified
|
|
47
|
+
mechanically (see [Verification](#verification-how-to-prove-light-mode-did-not-change)).
|
|
48
|
+
Any new page or stylesheet in this app should consume `--c-*` tokens and will then theme for
|
|
49
|
+
free.
|
|
50
|
+
|
|
51
|
+
> **Status (2026-08-04): NOT COMMITTED.** All of this lives in the working tree of the local
|
|
52
|
+
> `tools` checkout, which is on `_production` — it needs a feature branch before it lands.
|
|
53
|
+
> Verified **statically only**: resolver diffs, contrast maths, `php -l` on the 14 changed PHP
|
|
54
|
+
> files, `node --check` on `theme.js`. **No browser check and no specialist-reviewer pass has
|
|
55
|
+
> been run.** See [Open items](#open-items).
|
|
56
|
+
|
|
57
|
+
## How it works
|
|
58
|
+
|
|
59
|
+
### 1. The token layer — `assets/css/theme.css`
|
|
60
|
+
|
|
61
|
+
~142 semantic CSS custom properties are declared on `:root` **with the original light
|
|
62
|
+
values**, and overridden in a `:root[data-theme="dark"]` block. It is registered **first** in
|
|
63
|
+
`App_FrameworkIndex_Tools::__construct()` so every other stylesheet resolves through it.
|
|
64
|
+
|
|
65
|
+
Token groups and their meaning:
|
|
66
|
+
|
|
67
|
+
| Group | Meaning |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `--c-surface*` | fills |
|
|
70
|
+
| `--c-text*` | foregrounds |
|
|
71
|
+
| `--c-border*` | borders |
|
|
72
|
+
| `--c-primary*` | brand (`-tNN` = alpha tint) |
|
|
73
|
+
| `--c-nav-*` | left nav / public header — **dark in BOTH themes** |
|
|
74
|
+
| `--c-on-accent`, `--c-on-dark-tNN` | white in BOTH themes; content sitting on a permanently dark/coloured fill |
|
|
75
|
+
| `--c-slate-*`, `--c-indigo-*`, `--c-green-*`, `--c-red-*` | secondary ramp used by `design.css` + `cloudfront-clients.css` |
|
|
76
|
+
| `--c-bs-*` | legacy Bootstrap ramp, Talos pages only (see gotchas) |
|
|
77
|
+
| `--c-code-bg`, `--c-code-fg` | fixed dark code slab (not themed) |
|
|
78
|
+
|
|
79
|
+
`color-scheme` is itself tokenised as `--c-color-scheme` so native scrollbars and form
|
|
80
|
+
controls follow the theme. Note `style.css` previously hard-coded `color-scheme: light` on
|
|
81
|
+
`html, body`, which beat the `:root` token — that had to be removed.
|
|
82
|
+
|
|
83
|
+
### 2. Pre-paint application (no flash)
|
|
84
|
+
|
|
85
|
+
`App_FrameworkIndex_Tools::themeBootstrapScript()` returns a tiny inline `<script>` injected
|
|
86
|
+
with `App_FrameworkIndex::setHeaderCode()`; `library` echoes it inside `<head>` **after** the
|
|
87
|
+
CSS/JS includes. It reads `localStorage` and stamps `data-theme` + `data-theme-pref` onto
|
|
88
|
+
`<html>` before the first paint. **An external JS file cannot do this** — it runs too late and
|
|
89
|
+
you get a flash of the wrong theme.
|
|
90
|
+
|
|
91
|
+
### 3. Runtime API — `assets/js/theme.js` (`window.TogaTheme`)
|
|
92
|
+
|
|
93
|
+
- `get()` → `'light' | 'dark'` (resolved theme)
|
|
94
|
+
- `getPreference()` → `'light' | 'dark' | 'system'`
|
|
95
|
+
- `set(pref)` — persists + applies
|
|
96
|
+
- `onChange(fn)` — **fires immediately with the current theme**, then on every change, so a
|
|
97
|
+
caller has one code path for both "colour yourself now" and "recolour"
|
|
98
|
+
- `color('--token')` — read a themed colour out of CSS for JS-driven visuals
|
|
99
|
+
|
|
100
|
+
Persistence: `localStorage` key **`toga-tools-theme`**, values `light|dark|system`. Every
|
|
101
|
+
access is `try`/`catch`ed — Safari private mode *throws* rather than returning `null`.
|
|
102
|
+
`system` follows the OS live via a `matchMedia('(prefers-color-scheme: dark)')` change
|
|
103
|
+
listener (with the `addListener` fallback for Safari <14), and a `storage` event listener keeps
|
|
104
|
+
multiple open tabs in agreement.
|
|
105
|
+
|
|
106
|
+
### 4. The switcher (server-rendered)
|
|
107
|
+
|
|
108
|
+
Rendered in `common/header.php` inside `.app-nav-footer` — next to the signed-in user's name,
|
|
109
|
+
above **Log out** — as a `role="radiogroup"` of three buttons carrying
|
|
110
|
+
`data-theme-choice="light|dark|system"` (labelled **Light / Dark / Auto**). `theme.js` manages
|
|
111
|
+
`aria-checked` + a roving `tabindex` and supports arrow keys. Icons come from a new
|
|
112
|
+
`App_Nav::themeIcon($theme)` that returns **inline SVG** from a fixed 3-key allowlist — inline
|
|
113
|
+
so the glyph inherits `currentColor`.
|
|
114
|
+
|
|
115
|
+
### 5. Login-page wordmark
|
|
116
|
+
|
|
117
|
+
Two `<img>` wordmarks are in the markup and CSS shows one per theme
|
|
118
|
+
(`.login-logo--light` / `.login-logo--dark`, keyed off `:root[data-theme="dark"]`):
|
|
119
|
+
`toga-horizontal-colored.png` in light, `toga-horizontal-white.png` in dark.
|
|
120
|
+
|
|
121
|
+
Chosen over swapping `src` in JS or using `<picture>`: CSS honours an **explicit** Light/Dark
|
|
122
|
+
choice, whereas a `<picture>` media query only sees `prefers-color-scheme` and would ignore a
|
|
123
|
+
forced override — and CSS cannot flash the wrong logo. Both PNGs are 3610×700 so there is no
|
|
124
|
+
reflow. Tradeoff: the browser downloads both (~49 KB extra).
|
|
125
|
+
|
|
126
|
+
### 6. Chart.js pages
|
|
127
|
+
|
|
128
|
+
`talos/usage`, `talos/pricing` and `talos/contracts` read series/axis/tick/gridline colours
|
|
129
|
+
from tokens via `TogaTheme.color()` inside a `paint()` function registered with
|
|
130
|
+
`TogaTheme.onChange(paint)`, followed by `chart.update()`. **`chart.update()` is required** —
|
|
131
|
+
it clears Chart.js's resolver cache so `Chart.defaults` changes take effect on charts that
|
|
132
|
+
already exist. Without this, Chart.js defaults axis/tick text to a dark colour that is
|
|
133
|
+
unreadable on a dark background.
|
|
134
|
+
|
|
135
|
+
## Decisions
|
|
136
|
+
|
|
137
|
+
- **Left nav and public header stay dark in BOTH themes** (deepened for dark). Preserves the
|
|
138
|
+
app's identity, guarantees zero light-mode regression there, and keeps the existing white
|
|
139
|
+
brandmark PNG usable in both themes.
|
|
140
|
+
- **Preference is browser-local (`localStorage`), not a DB column** — explicit developer
|
|
141
|
+
requirement.
|
|
142
|
+
- **Light mode must be pixel-identical**, proven mechanically rather than by eye.
|
|
143
|
+
- **`assets/css/style.scss` left untouched** — neither revived nor deleted (out of scope; that
|
|
144
|
+
is already the status quo — see gotcha below).
|
|
145
|
+
|
|
146
|
+
## Verification (how to prove light mode did not change)
|
|
147
|
+
|
|
148
|
+
1. **Resolver diff.** Resolve every `var(--c-*)` back through the `:root` (light) block and
|
|
149
|
+
diff the result against the pre-change file. Identical output = light mode unchanged.
|
|
150
|
+
2. **Dark-coverage assert.** Every `:root` token must have a `[data-theme="dark"]` override
|
|
151
|
+
unless it is intentionally fixed.
|
|
152
|
+
3. **Per-file colour multiset vs. `git HEAD`.** Compare the *multiset* of resolved colour
|
|
153
|
+
values per file, not a line-by-line diff — an index/line-based diff is useless once lines
|
|
154
|
+
are inserted, whereas a multiset comparison is immune to line-number shifts.
|
|
155
|
+
|
|
156
|
+
## Gotchas
|
|
157
|
+
|
|
158
|
+
- **`assets/css/style.scss` is DEAD in this repo — edit `style.css` directly.** Many recent
|
|
159
|
+
commits touch the `.css` while the `.scss` was last touched in an old ticket; the `.css` has
|
|
160
|
+
drifted ~450 lines ahead. There is no sass binary and no `package.json`. "Helpfully"
|
|
161
|
+
recompiling the SCSS would **silently revert a large amount of shipped work**.
|
|
162
|
+
- **`App_FrameworkIndex::setHeaderCode()` is the only hook for injecting markup into `<head>`
|
|
163
|
+
in 1.0** (library echoes `$this->headerCode` inside `<head>` after the CSS/JS includes). It
|
|
164
|
+
**SETS rather than appends**, and before this feature it was unused anywhere in tools — so a
|
|
165
|
+
**second caller silently clobbers the first**. If another feature needs head markup, refactor
|
|
166
|
+
to compose both strings; do not add a second `setHeaderCode()` call.
|
|
167
|
+
- **A colour literal used as both a foreground and a background must be split into two
|
|
168
|
+
tokens**, or dark mode inverts it the wrong way. Real cases in tools: `#10113A` (body text
|
|
169
|
+
*and* nav/public-header background), `#20242c` (text-strong *and* a dark chip fill), `#999`
|
|
170
|
+
(text grey *and* a badge fill), `#eef0f4` (border *and* badge fill), `#0f172a` (design.css
|
|
171
|
+
`--d-ink` text *and* the code-block background). Detect these **mechanically** — collect, per
|
|
172
|
+
candidate token, the set of CSS properties it appears with (background vs. colour/border).
|
|
173
|
+
Do not eyeball it.
|
|
174
|
+
- **A brand colour needs TWO tokens in a dark theme.** `--c-primary` (text/borders on a panel)
|
|
175
|
+
is **lightened** in dark for legibility; `--c-primary-fill` (a fill behind white text) is
|
|
176
|
+
kept **dark enough** in dark mode to preserve contrast. The roles pull in opposite
|
|
177
|
+
directions — one token makes white-on-brand buttons fail contrast.
|
|
178
|
+
- **The Talos admin pages keep their own `--c-bs-*` group.** They were authored against
|
|
179
|
+
Bootstrap's palette (`#0d6efd`, `#6c757d`, `#495057`, `#dc3545`, `#198754`…), which is close
|
|
180
|
+
to but **not** the TOGA brand palette; folding them into `--c-primary` visibly restyles those
|
|
181
|
+
pages. Only map a literal onto a token whose light value is **identical**.
|
|
182
|
+
- **`mvc/email-migration/notify/post.php` generates EMAIL HTML — its colour literals must stay
|
|
183
|
+
hard-coded.** Email clients cannot resolve CSS custom properties. It is the same file family
|
|
184
|
+
as the on-screen page, so it is easy to tokenise by mistake.
|
|
185
|
+
- **`mvc/clickup/dashboard/get.php` keeps ~6 fixed literals on purpose.** Its card/frame colour
|
|
186
|
+
`#0b1020` matches the embedded Power BI report canvas so nothing flashes white while the
|
|
187
|
+
iframe loads. Do not theme those.
|
|
188
|
+
|
|
189
|
+
## Open items
|
|
190
|
+
|
|
191
|
+
- The three Chart.js pages **cannot** be statically proven light-identical — their colours are
|
|
192
|
+
now generated at runtime. Needs a browser pass.
|
|
193
|
+
- The Talos error toast measures ~2.4:1 in dark (white text on `--c-danger`) and probably wants
|
|
194
|
+
a fixed dark fill.
|
|
195
|
+
- `talos/pricing`'s target-band dataset never had a `backgroundColor` (a pre-existing authoring
|
|
196
|
+
slip), so its band renders as a faint darkening rather than a green tint.
|
|
197
|
+
- A few Talos overlay shadows shifted from black-based to slate-based (imperceptible but real),
|
|
198
|
+
and 7 imperceptible colour substitutions were accepted on Talos pages (largest:
|
|
199
|
+
`#f8f9fa` → `#f8f9fb`).
|
|
200
|
+
|
|
201
|
+
## Change history
|
|
202
|
+
- 2026-08-04 — Built the app-wide light/dark/auto theme: `assets/css/theme.css` (~142 `--c-*`
|
|
203
|
+
tokens on `:root` + a `[data-theme="dark"]` override, registered first in
|
|
204
|
+
`App_FrameworkIndex_Tools`), `assets/js/theme.js` (`window.TogaTheme`), a pre-paint inline
|
|
205
|
+
bootstrap via `setHeaderCode()`, a Light/Dark/Auto radiogroup in the nav footer
|
|
206
|
+
(`App_Nav::themeIcon()`), per-theme login wordmarks, and token-driven Chart.js repaint on the
|
|
207
|
+
three Talos chart pages. Preference in `localStorage` (`toga-tools-theme`), no DB column; nav
|
|
208
|
+
+ public header stay dark in both themes; light mode proven unchanged by resolver diff.
|
|
209
|
+
**Not committed — working tree on `_production`, no browser verification yet.** (jcardinal)
|
|
@@ -5,12 +5,13 @@ project: Library
|
|
|
5
5
|
client: shared
|
|
6
6
|
type: standard
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
8
|
+
updated: 2026-08-04
|
|
9
9
|
owners: [jcardinal]
|
|
10
10
|
files: []
|
|
11
11
|
related:
|
|
12
12
|
- ./backend-php.md
|
|
13
13
|
- ./framework-rules.md
|
|
14
|
+
- ../apps/tools/features/theme-light-dark.md
|
|
14
15
|
- ../apps/tools/features/design-demo-admin.md
|
|
15
16
|
- ../apps/tools/features/talos-kb-documents-admin.md
|
|
16
17
|
---
|
|
@@ -66,10 +67,48 @@ Each round trip adds a level. Equality comparisons then fail too, because a fres
|
|
|
66
67
|
- `tools/_/app/design/github.php` — `App_Design_Github::decodeEntities()` applied to tab
|
|
67
68
|
names and project titles on both the storage-read and request-read sides.
|
|
68
69
|
|
|
70
|
+
## Tokenising an existing stylesheet for a second theme
|
|
71
|
+
|
|
72
|
+
When retrofitting light/dark onto a legacy sheet, four rules turn a guessing game into a
|
|
73
|
+
mechanical exercise.
|
|
74
|
+
|
|
75
|
+
**1. Split any literal used as both a foreground and a background into two tokens.** A single
|
|
76
|
+
token inverts the wrong way in the second theme. Detect these **mechanically** — collect, per
|
|
77
|
+
candidate token, the set of CSS properties it appears with (`background*` vs.
|
|
78
|
+
`color`/`border*`) — do not eyeball it. Real cases in `tools`: `#10113A` (body text *and* nav
|
|
79
|
+
background), `#20242c`, `#999`, `#eef0f4`, `#0f172a`.
|
|
80
|
+
|
|
81
|
+
**2. A brand colour needs TWO tokens.** `--c-primary` (text/borders on a panel) must be
|
|
82
|
+
**lightened** in dark for legibility; `--c-primary-fill` (a fill behind white text) must stay
|
|
83
|
+
**dark enough** in dark to preserve contrast. The roles pull in opposite directions; one token
|
|
84
|
+
makes white-on-brand buttons fail contrast.
|
|
85
|
+
|
|
86
|
+
**3. Only map a literal onto a token whose light value is IDENTICAL.** Measure perceptual
|
|
87
|
+
distance before accepting a "close enough" substitution — a near-match silently restyles the
|
|
88
|
+
page. Legacy pages authored against a *different* palette (the Talos admin pages use
|
|
89
|
+
Bootstrap's `#0d6efd`/`#6c757d`/`#dc3545`/`#198754`) keep their own token group with their
|
|
90
|
+
exact original values rather than being folded into the brand tokens.
|
|
91
|
+
|
|
92
|
+
**4. Prove the original theme is unchanged, mechanically.** (a) Resolve every `var(--…)` back
|
|
93
|
+
through the `:root` block and diff against the pre-change file; (b) assert every `:root` token
|
|
94
|
+
has a second-theme override unless intentionally fixed; (c) compare the per-file **multiset**
|
|
95
|
+
of resolved colour values against `git HEAD` — a line-index diff is useless once lines are
|
|
96
|
+
inserted, a multiset comparison is immune to line shifts.
|
|
97
|
+
|
|
98
|
+
Two things are **never** tokenised: HTML destined for an **email** (email clients cannot
|
|
99
|
+
resolve custom properties) and colours deliberately matched to third-party embedded content
|
|
100
|
+
(e.g. an iframe's canvas colour, to prevent a white flash on load).
|
|
101
|
+
|
|
102
|
+
Reference implementation:
|
|
103
|
+
[tools App-Wide Colour Theme](../apps/tools/features/theme-light-dark.md).
|
|
104
|
+
|
|
69
105
|
## Change history
|
|
70
106
|
|
|
107
|
+
- 2026-08-04 — Added "Tokenising an existing stylesheet for a second theme": split
|
|
108
|
+
dual-role colour literals, two tokens per brand colour, identical-value-only substitutions,
|
|
109
|
+
and the mechanical proof that the original theme is unchanged; plus the never-tokenise cases
|
|
110
|
+
(email HTML, colours matched to third-party embeds). Drawn from the `tools` light/dark
|
|
111
|
+
build. (jcardinal)
|
|
71
112
|
- 2026-07-30 — Created. Establishes the escape-once/plain-text-storage invariant after a
|
|
72
113
|
second independent occurrence in `tools` (Design Demo Admin, following the Talos
|
|
73
114
|
vocabulary tool). (jcardinal)
|
|
74
|
-
</content>
|
|
75
|
-
</invoke>
|
package/knowledge/INDEX.md
CHANGED
|
@@ -14,7 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
14
14
|
- **walmarttechservices** (Walmart Tech Services) — 1 doc(s) → [1.0/apps/walmarttechservices/INDEX.md](1.0/apps/walmarttechservices/INDEX.md)
|
|
15
15
|
- **test** (Test) — 14 doc(s) → [1.0/apps/test/INDEX.md](1.0/apps/test/INDEX.md)
|
|
16
16
|
- **toga** (TOGa) — 2 doc(s) → [1.0/apps/toga/INDEX.md](1.0/apps/toga/INDEX.md)
|
|
17
|
-
- **tools** (Tools) —
|
|
17
|
+
- **tools** (Tools) — 14 doc(s) → [1.0/apps/tools/INDEX.md](1.0/apps/tools/INDEX.md)
|
|
18
18
|
|
|
19
19
|
## 2.0 framework
|
|
20
20
|
|
package/package.json
CHANGED