@sienklogic/plan-build-run 2.15.0 → 2.16.0
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/CHANGELOG.md +37 -0
- package/dashboard/package.json +3 -1
- package/dashboard/public/css/layout.css +237 -82
- package/dashboard/public/css/tokens.css +59 -0
- package/dashboard/public/js/sidebar-toggle.js +21 -7
- package/dashboard/public/js/sse-client.js +99 -0
- package/dashboard/public/js/theme-toggle.js +46 -0
- package/dashboard/src/app.js +4 -0
- package/dashboard/src/middleware/current-phase.js +24 -0
- package/dashboard/src/routes/events.routes.js +5 -0
- package/dashboard/src/routes/index.routes.js +2 -1
- package/dashboard/src/routes/pages.routes.js +94 -6
- package/dashboard/src/services/analytics.service.js +143 -0
- package/dashboard/src/services/milestone.service.js +50 -4
- package/dashboard/src/services/roadmap.service.js +73 -0
- package/dashboard/src/services/todo.service.js +11 -2
- package/dashboard/src/services/watcher.service.js +1 -1
- package/dashboard/src/utils/cache.js +55 -0
- package/dashboard/src/views/analytics.ejs +5 -0
- package/dashboard/src/views/dependencies.ejs +5 -0
- package/dashboard/src/views/error.ejs +16 -9
- package/dashboard/src/views/partials/analytics-content.ejs +71 -0
- package/dashboard/src/views/partials/breadcrumbs.ejs +14 -0
- package/dashboard/src/views/partials/dashboard-content.ejs +1 -0
- package/dashboard/src/views/partials/dependencies-content.ejs +16 -0
- package/dashboard/src/views/partials/empty-state.ejs +7 -0
- package/dashboard/src/views/partials/head.ejs +4 -1
- package/dashboard/src/views/partials/header.ejs +9 -0
- package/dashboard/src/views/partials/layout-bottom.ejs +1 -10
- package/dashboard/src/views/partials/layout-top.ejs +7 -0
- package/dashboard/src/views/partials/milestone-detail-content.ejs +1 -0
- package/dashboard/src/views/partials/milestones-content.ejs +55 -19
- package/dashboard/src/views/partials/phase-content.ejs +1 -0
- package/dashboard/src/views/partials/phase-doc-content.ejs +1 -1
- package/dashboard/src/views/partials/phases-content.ejs +1 -0
- package/dashboard/src/views/partials/roadmap-content.ejs +1 -0
- package/dashboard/src/views/partials/sidebar.ejs +88 -43
- package/dashboard/src/views/partials/todo-create-content.ejs +1 -0
- package/dashboard/src/views/partials/todo-detail-content.ejs +5 -1
- package/dashboard/src/views/partials/todos-content.ejs +44 -3
- package/package.json +1 -1
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@ All notable changes to Plan-Build-Run will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.16.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.15.0...plan-build-run-v2.16.0) (2026-02-22)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **11-01:** create tokens.css with dual-mode design tokens ([53afcc1](https://github.com/SienkLogic/plan-build-run/commit/53afcc1cc84e56d3d98b66a5758591f79af5c725))
|
|
14
|
+
* **11-01:** refactor layout.css to use semantic design tokens ([f0fa53c](https://github.com/SienkLogic/plan-build-run/commit/f0fa53c88ac4345b135033e14dc37b937a65d5dd))
|
|
15
|
+
* **11-02:** add theme toggle button with localStorage persistence ([f84576e](https://github.com/SienkLogic/plan-build-run/commit/f84576ec4596dedc9643b7cb76e55d14865ebffb))
|
|
16
|
+
* **11-02:** pin Pico.css CDN to v2.0.6 ([1e46ef6](https://github.com/SienkLogic/plan-build-run/commit/1e46ef6bd56e12222d735590d9dea3b3c7f1f5b6))
|
|
17
|
+
* **12-01:** add current-phase middleware for sidebar context ([d7d0e16](https://github.com/SienkLogic/plan-build-run/commit/d7d0e16419f692aa9883d9262b119dc514f7e9b0))
|
|
18
|
+
* **12-01:** implement mobile overlay sidebar with backdrop ([3cc59ac](https://github.com/SienkLogic/plan-build-run/commit/3cc59acf55ab90568d4f11c11df43c7a07ca43f4))
|
|
19
|
+
* **12-01:** redesign sidebar with collapsible sections and current phase card ([0b6aa07](https://github.com/SienkLogic/plan-build-run/commit/0b6aa076bfea6fdac3c7c855a6ad522f6d8f50ce))
|
|
20
|
+
* **12-02:** add breadcrumb data to routes and include partial in content templates ([a90684e](https://github.com/SienkLogic/plan-build-run/commit/a90684e7752fcf232bcaee223e392e1dd4a9d52e))
|
|
21
|
+
* **12-02:** create breadcrumbs partial and CSS styles ([82ba448](https://github.com/SienkLogic/plan-build-run/commit/82ba44833b09398d5abec88bf6e6a60032694412))
|
|
22
|
+
* **13-01:** add milestone history expandable table with stats and deliverables ([ccc82d5](https://github.com/SienkLogic/plan-build-run/commit/ccc82d59f28e67b8ab5cb0db803d228e35af8339))
|
|
23
|
+
* **13-01:** add todo filtering by priority, status, and search with bulk complete ([0abbf4c](https://github.com/SienkLogic/plan-build-run/commit/0abbf4c173395bfeda4b5c73f312a3820e86ad67))
|
|
24
|
+
* **13-02:** add dependency graph route, views, and sidebar link ([d7224c2](https://github.com/SienkLogic/plan-build-run/commit/d7224c297dcbd07ac3659e95c4047448e5c6292b))
|
|
25
|
+
* **13-02:** add Mermaid dependency graph generation to roadmap service ([662056b](https://github.com/SienkLogic/plan-build-run/commit/662056b703f4ef4d7910d7afb1f79444be6e11d1))
|
|
26
|
+
* **13-03:** add analytics route, views, and sidebar link ([750562b](https://github.com/SienkLogic/plan-build-run/commit/750562b94287f151d89d7160ab124e2dc3279abc))
|
|
27
|
+
* **13-03:** create analytics service with git-based metrics and TTL cache ([41eb38a](https://github.com/SienkLogic/plan-build-run/commit/41eb38aebf930ec085edaee416913c2319a6d216))
|
|
28
|
+
* **14-01:** add Last-Event-ID state recovery to SSE endpoint ([b41c8dd](https://github.com/SienkLogic/plan-build-run/commit/b41c8ddc0638cd1bd103110ed6aa74a1512ca137))
|
|
29
|
+
* **14-01:** create custom SSE client with exponential backoff ([8d75876](https://github.com/SienkLogic/plan-build-run/commit/8d758765f0465820560a72fdad745481e091f04b))
|
|
30
|
+
* **14-01:** reduce chokidar stability threshold to 500ms ([1ef4dc4](https://github.com/SienkLogic/plan-build-run/commit/1ef4dc49604d98f5b6d9847f6ce835ee909b519f))
|
|
31
|
+
* **14-02:** add hx-indicator spinners to todo complete actions ([a166d94](https://github.com/SienkLogic/plan-build-run/commit/a166d946323255696f46f0550892878eda622ebd))
|
|
32
|
+
* **14-02:** add TTL cache utility and integrate into analytics and milestone services ([123c2d2](https://github.com/SienkLogic/plan-build-run/commit/123c2d25ea7050515e685c05231401de76d4cd7c))
|
|
33
|
+
* **15-01:** add error-card styling, loading indicator, and favicon ([6f3c550](https://github.com/SienkLogic/plan-build-run/commit/6f3c550f79885010af59377a95c7dfd2e53038c3))
|
|
34
|
+
* **15-01:** add skip-to-content link, focus-visible styles, and ARIA labels ([47c3c9b](https://github.com/SienkLogic/plan-build-run/commit/47c3c9bcaf8f2a5e143fe1b5a8ed18e5da9b20ba))
|
|
35
|
+
* **15-01:** create reusable empty-state partial and integrate into views ([48c6807](https://github.com/SienkLogic/plan-build-run/commit/48c6807b9f19fcd4abc8582820155c5ccc73a244))
|
|
36
|
+
* **15-02:** GREEN - analytics, cache, SSE tests pass against existing code ([1f6e3c2](https://github.com/SienkLogic/plan-build-run/commit/1f6e3c2feef7ba601afe66c68e41401aa44568be))
|
|
37
|
+
* **15-02:** GREEN - dependencies and breadcrumbs tests pass ([8fd48fc](https://github.com/SienkLogic/plan-build-run/commit/8fd48fc63e5414cf93bec4d7afadf86b463a6f32))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Bug Fixes
|
|
41
|
+
|
|
42
|
+
* **14-01:** add missing #sse-status element to header ([b831104](https://github.com/SienkLogic/plan-build-run/commit/b831104feaee62a5f6768ec8060aa2387e82322c))
|
|
43
|
+
* **14-02:** clear milestone cache between tests to prevent stale data ([192b53c](https://github.com/SienkLogic/plan-build-run/commit/192b53cbb4777350950ed8bd0d4993d3229f1630))
|
|
44
|
+
|
|
8
45
|
## [2.15.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.14.0...plan-build-run-v2.15.0) (2026-02-22)
|
|
9
46
|
|
|
10
47
|
|
package/dashboard/package.json
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/cli.js",
|
|
11
11
|
"dev": "node --watch bin/cli.js",
|
|
12
|
-
"test": "vitest run"
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:coverage": "vitest run --coverage"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
15
16
|
"plan-build-run",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"marked": "^17.0.1"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
32
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
31
33
|
"memfs": "^4.56.10",
|
|
32
34
|
"supertest": "^7.2.2",
|
|
33
35
|
"vitest": "^3.1.0"
|
|
@@ -4,23 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/* --- Custom Properties --- */
|
|
6
6
|
:root {
|
|
7
|
-
--sidebar-width: 220px;
|
|
8
|
-
--header-height: 3.5rem;
|
|
9
7
|
--content-max-width: 960px;
|
|
10
|
-
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
|
|
11
|
-
--font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Fira Code', monospace;
|
|
12
|
-
--space-xs: 0.25rem;
|
|
13
|
-
--space-sm: 0.5rem;
|
|
14
|
-
--space-md: 1rem;
|
|
15
|
-
--space-lg: 1.5rem;
|
|
16
|
-
--space-xl: 2rem;
|
|
17
|
-
--space-2xl: 3rem;
|
|
18
|
-
--radius-sm: 0.375rem;
|
|
19
|
-
--radius-md: 0.5rem;
|
|
20
|
-
--border-subtle: rgba(255, 255, 255, 0.08);
|
|
21
|
-
--bg-surface: rgba(255, 255, 255, 0.03);
|
|
22
|
-
--bg-surface-hover: rgba(255, 255, 255, 0.06);
|
|
23
|
-
--text-dim: rgba(255, 255, 255, 0.5);
|
|
24
8
|
}
|
|
25
9
|
|
|
26
10
|
/* --- Typography Override --- */
|
|
@@ -41,14 +25,14 @@ h3 { font-size: 1.1rem; font-weight: 600; }
|
|
|
41
25
|
|
|
42
26
|
p { line-height: 1.65; }
|
|
43
27
|
|
|
44
|
-
small { font-size: 0.8125rem; color: var(--text-dim); }
|
|
28
|
+
small { font-size: 0.8125rem; color: var(--color-text-dim); }
|
|
45
29
|
|
|
46
30
|
/* --- Page Grid Layout --- */
|
|
47
31
|
.page-wrapper {
|
|
48
32
|
display: grid;
|
|
49
33
|
gap: 0;
|
|
50
|
-
grid-template-columns: var(--sidebar
|
|
51
|
-
grid-template-rows: var(--header
|
|
34
|
+
grid-template-columns: var(--size-sidebar) 1fr;
|
|
35
|
+
grid-template-rows: var(--size-header) 1fr auto;
|
|
52
36
|
grid-template-areas:
|
|
53
37
|
"header header"
|
|
54
38
|
"sidebar content"
|
|
@@ -62,8 +46,8 @@ small { font-size: 0.8125rem; color: var(--text-dim); }
|
|
|
62
46
|
display: flex;
|
|
63
47
|
align-items: center;
|
|
64
48
|
padding: 0 var(--space-xl);
|
|
65
|
-
border-bottom: 1px solid var(--border
|
|
66
|
-
background: var(--
|
|
49
|
+
border-bottom: 1px solid var(--color-border);
|
|
50
|
+
background: var(--color-surface-raised);
|
|
67
51
|
backdrop-filter: blur(8px);
|
|
68
52
|
position: sticky;
|
|
69
53
|
top: 0;
|
|
@@ -96,11 +80,11 @@ small { font-size: 0.8125rem; color: var(--text-dim); }
|
|
|
96
80
|
.page-wrapper > aside.sidebar {
|
|
97
81
|
grid-area: sidebar;
|
|
98
82
|
padding: var(--space-lg) 0;
|
|
99
|
-
border-right: 1px solid var(--border
|
|
100
|
-
background: var(--
|
|
83
|
+
border-right: 1px solid var(--color-border);
|
|
84
|
+
background: var(--color-surface-raised);
|
|
101
85
|
position: sticky;
|
|
102
|
-
top: var(--header
|
|
103
|
-
height: calc(100vh - var(--header
|
|
86
|
+
top: var(--size-header);
|
|
87
|
+
height: calc(100vh - var(--size-header));
|
|
104
88
|
overflow-y: auto;
|
|
105
89
|
}
|
|
106
90
|
|
|
@@ -123,21 +107,86 @@ aside.sidebar nav a {
|
|
|
123
107
|
border-left: 3px solid transparent;
|
|
124
108
|
font-size: 0.9rem;
|
|
125
109
|
font-weight: 500;
|
|
126
|
-
color: var(--text-dim);
|
|
110
|
+
color: var(--color-text-dim);
|
|
127
111
|
transition: all 0.15s ease;
|
|
128
112
|
}
|
|
129
113
|
|
|
130
114
|
aside.sidebar nav a:hover {
|
|
131
115
|
color: var(--pico-color);
|
|
132
|
-
background: var(--
|
|
133
|
-
border-left-color:
|
|
116
|
+
background: var(--color-surface-hover);
|
|
117
|
+
border-left-color: var(--color-border);
|
|
134
118
|
}
|
|
135
119
|
|
|
136
120
|
aside.sidebar nav a[aria-current="page"] {
|
|
137
121
|
font-weight: 600;
|
|
138
|
-
color: var(--
|
|
139
|
-
border-left-color: var(--
|
|
140
|
-
background: var(--
|
|
122
|
+
color: var(--color-accent);
|
|
123
|
+
border-left-color: var(--color-accent);
|
|
124
|
+
background: var(--color-surface-hover);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* --- Sidebar: Current Phase Card --- */
|
|
128
|
+
.sidebar-current-phase {
|
|
129
|
+
padding: var(--space-md) var(--space-lg);
|
|
130
|
+
border-bottom: 1px solid var(--color-border);
|
|
131
|
+
margin-bottom: var(--space-sm);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.sidebar-current-phase small {
|
|
135
|
+
text-transform: uppercase;
|
|
136
|
+
letter-spacing: 0.04em;
|
|
137
|
+
font-size: 0.6875rem;
|
|
138
|
+
color: var(--color-text-dim);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.sidebar-current-phase a {
|
|
142
|
+
display: block;
|
|
143
|
+
text-decoration: none;
|
|
144
|
+
color: inherit;
|
|
145
|
+
margin-top: var(--space-xs);
|
|
146
|
+
line-height: 1.4;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.sidebar-current-phase a strong {
|
|
150
|
+
font-size: 0.875rem;
|
|
151
|
+
display: block;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.sidebar-current-phase a span {
|
|
155
|
+
display: block;
|
|
156
|
+
font-size: 0.8125rem;
|
|
157
|
+
color: var(--color-text-dim);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.sidebar-current-phase .phase-status {
|
|
161
|
+
font-size: 0.75rem;
|
|
162
|
+
text-transform: capitalize;
|
|
163
|
+
color: var(--color-accent);
|
|
164
|
+
margin-top: 2px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* --- Sidebar: Section Details --- */
|
|
168
|
+
aside.sidebar details {
|
|
169
|
+
border: none;
|
|
170
|
+
margin: 0;
|
|
171
|
+
padding: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
aside.sidebar details summary {
|
|
175
|
+
text-transform: uppercase;
|
|
176
|
+
font-size: 0.75rem;
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
letter-spacing: 0.04em;
|
|
179
|
+
color: var(--color-text-dim);
|
|
180
|
+
padding: var(--space-sm) var(--space-lg);
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
aside.sidebar details[open] summary {
|
|
185
|
+
color: var(--color-accent);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
aside.sidebar details ul {
|
|
189
|
+
margin: 0;
|
|
141
190
|
}
|
|
142
191
|
|
|
143
192
|
/* --- Main Content --- */
|
|
@@ -152,27 +201,27 @@ aside.sidebar nav a[aria-current="page"] {
|
|
|
152
201
|
.page-wrapper > footer {
|
|
153
202
|
grid-area: footer;
|
|
154
203
|
padding: var(--space-md) var(--space-xl);
|
|
155
|
-
border-top: 1px solid var(--border
|
|
204
|
+
border-top: 1px solid var(--color-border);
|
|
156
205
|
text-align: center;
|
|
157
206
|
}
|
|
158
207
|
|
|
159
208
|
.page-wrapper > footer small {
|
|
160
209
|
font-size: 0.75rem;
|
|
161
|
-
color: var(--text-dim);
|
|
210
|
+
color: var(--color-text-dim);
|
|
162
211
|
}
|
|
163
212
|
|
|
164
213
|
/* --- Article Cards --- */
|
|
165
214
|
article {
|
|
166
|
-
border: 1px solid var(--border
|
|
215
|
+
border: 1px solid var(--color-border);
|
|
167
216
|
border-radius: var(--radius-md);
|
|
168
|
-
background: var(--
|
|
217
|
+
background: var(--color-surface-raised);
|
|
169
218
|
margin-bottom: var(--space-lg);
|
|
170
219
|
}
|
|
171
220
|
|
|
172
221
|
article > header {
|
|
173
|
-
border-bottom: 1px solid var(--border
|
|
222
|
+
border-bottom: 1px solid var(--color-border);
|
|
174
223
|
padding: var(--space-md) var(--space-lg);
|
|
175
|
-
background:
|
|
224
|
+
background: var(--color-surface-raised);
|
|
176
225
|
border-radius: var(--radius-md) var(--radius-md) 0 0;
|
|
177
226
|
}
|
|
178
227
|
|
|
@@ -200,7 +249,7 @@ th {
|
|
|
200
249
|
font-size: 0.8125rem;
|
|
201
250
|
text-transform: uppercase;
|
|
202
251
|
letter-spacing: 0.04em;
|
|
203
|
-
color: var(--text-dim);
|
|
252
|
+
color: var(--color-text-dim);
|
|
204
253
|
white-space: nowrap;
|
|
205
254
|
}
|
|
206
255
|
|
|
@@ -216,18 +265,18 @@ progress {
|
|
|
216
265
|
}
|
|
217
266
|
|
|
218
267
|
progress::-webkit-progress-bar {
|
|
219
|
-
background:
|
|
268
|
+
background: var(--color-border);
|
|
220
269
|
border-radius: 999px;
|
|
221
270
|
}
|
|
222
271
|
|
|
223
272
|
progress::-webkit-progress-value {
|
|
224
|
-
background: var(--
|
|
273
|
+
background: var(--color-accent);
|
|
225
274
|
border-radius: 999px;
|
|
226
275
|
transition: width 0.4s ease;
|
|
227
276
|
}
|
|
228
277
|
|
|
229
278
|
progress::-moz-progress-bar {
|
|
230
|
-
background: var(--
|
|
279
|
+
background: var(--color-accent);
|
|
231
280
|
border-radius: 999px;
|
|
232
281
|
}
|
|
233
282
|
|
|
@@ -242,7 +291,7 @@ details summary {
|
|
|
242
291
|
font-weight: 500;
|
|
243
292
|
cursor: pointer;
|
|
244
293
|
padding: var(--space-xs) 0;
|
|
245
|
-
color: var(--
|
|
294
|
+
color: var(--color-accent);
|
|
246
295
|
}
|
|
247
296
|
|
|
248
297
|
details summary:hover {
|
|
@@ -264,7 +313,7 @@ details li {
|
|
|
264
313
|
font-size: 0.8125em;
|
|
265
314
|
padding: 0.15em 0.4em;
|
|
266
315
|
border-radius: var(--radius-sm);
|
|
267
|
-
background:
|
|
316
|
+
background: var(--color-surface-hover);
|
|
268
317
|
}
|
|
269
318
|
|
|
270
319
|
/* --- Markdown Body (rendered markdown content) --- */
|
|
@@ -281,13 +330,13 @@ details li {
|
|
|
281
330
|
|
|
282
331
|
.markdown-body th,
|
|
283
332
|
.markdown-body td {
|
|
284
|
-
border: 1px solid var(--border
|
|
333
|
+
border: 1px solid var(--color-border);
|
|
285
334
|
padding: var(--space-xs) var(--space-sm);
|
|
286
335
|
text-align: left;
|
|
287
336
|
}
|
|
288
337
|
|
|
289
338
|
.markdown-body th {
|
|
290
|
-
background:
|
|
339
|
+
background: var(--color-surface-hover);
|
|
291
340
|
}
|
|
292
341
|
|
|
293
342
|
.markdown-body pre {
|
|
@@ -305,8 +354,8 @@ details li {
|
|
|
305
354
|
}
|
|
306
355
|
|
|
307
356
|
.markdown-body blockquote {
|
|
308
|
-
border-left: 3px solid var(--
|
|
309
|
-
background:
|
|
357
|
+
border-left: 3px solid var(--color-accent);
|
|
358
|
+
background: var(--color-surface-raised);
|
|
310
359
|
margin: var(--space-md) 0;
|
|
311
360
|
padding: var(--space-sm) var(--space-md);
|
|
312
361
|
}
|
|
@@ -329,19 +378,49 @@ details li {
|
|
|
329
378
|
|
|
330
379
|
.markdown-body hr {
|
|
331
380
|
border: none;
|
|
332
|
-
border-top: 1px solid var(--border
|
|
381
|
+
border-top: 1px solid var(--color-border);
|
|
333
382
|
margin: var(--space-lg) 0;
|
|
334
383
|
}
|
|
335
384
|
|
|
385
|
+
/* --- Breadcrumbs --- */
|
|
386
|
+
.breadcrumbs {
|
|
387
|
+
display: flex;
|
|
388
|
+
list-style: none;
|
|
389
|
+
padding: 0;
|
|
390
|
+
margin: 0 0 var(--space-md) 0;
|
|
391
|
+
font-size: 0.85rem;
|
|
392
|
+
gap: var(--space-xs);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.breadcrumbs li + li::before {
|
|
396
|
+
content: ">";
|
|
397
|
+
margin-right: var(--space-xs);
|
|
398
|
+
color: var(--color-text-dim);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.breadcrumbs a {
|
|
402
|
+
color: var(--color-accent);
|
|
403
|
+
text-decoration: none;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.breadcrumbs a:hover {
|
|
407
|
+
text-decoration: underline;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.breadcrumbs [aria-current="page"] {
|
|
411
|
+
color: var(--color-text-dim);
|
|
412
|
+
font-weight: 500;
|
|
413
|
+
}
|
|
414
|
+
|
|
336
415
|
/* --- Back Link --- */
|
|
337
416
|
main > p:first-of-type > a[href="/"] {
|
|
338
417
|
font-size: 0.875rem;
|
|
339
|
-
color: var(--text-dim);
|
|
418
|
+
color: var(--color-text-dim);
|
|
340
419
|
text-decoration: none;
|
|
341
420
|
}
|
|
342
421
|
|
|
343
422
|
main > p:first-of-type > a[href="/"]:hover {
|
|
344
|
-
color: var(--
|
|
423
|
+
color: var(--color-accent);
|
|
345
424
|
}
|
|
346
425
|
|
|
347
426
|
/* --- SSE Connection Status --- */
|
|
@@ -363,11 +442,91 @@ main > p:first-of-type > a[href="/"]:hover {
|
|
|
363
442
|
background-color: var(--status-not-started);
|
|
364
443
|
}
|
|
365
444
|
|
|
445
|
+
/* --- Skip Link (Accessibility) --- */
|
|
446
|
+
.skip-link {
|
|
447
|
+
position: absolute;
|
|
448
|
+
left: -9999px;
|
|
449
|
+
top: 0;
|
|
450
|
+
z-index: 1000;
|
|
451
|
+
padding: var(--space-sm) var(--space-md);
|
|
452
|
+
background: var(--pico-primary);
|
|
453
|
+
color: var(--pico-primary-inverse);
|
|
454
|
+
text-decoration: none;
|
|
455
|
+
font-weight: 600;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.skip-link:focus {
|
|
459
|
+
position: static;
|
|
460
|
+
display: block;
|
|
461
|
+
text-align: center;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* --- Focus Visible --- */
|
|
465
|
+
:focus-visible {
|
|
466
|
+
outline: 2px solid var(--pico-primary);
|
|
467
|
+
outline-offset: 2px;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/* --- Empty State --- */
|
|
471
|
+
.empty-state {
|
|
472
|
+
text-align: center;
|
|
473
|
+
padding: var(--space-2xl) var(--space-lg);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.empty-state h3 {
|
|
477
|
+
color: var(--color-text-dim);
|
|
478
|
+
font-weight: 500;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/* --- Error Card --- */
|
|
482
|
+
.error-card {
|
|
483
|
+
border-left: 4px solid var(--pico-del-color);
|
|
484
|
+
padding: var(--space-lg);
|
|
485
|
+
margin: var(--space-lg) 0;
|
|
486
|
+
background: var(--color-surface-raised);
|
|
487
|
+
border-radius: var(--radius-md);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.error-card__icon {
|
|
491
|
+
font-size: 1.5rem;
|
|
492
|
+
margin-bottom: var(--space-sm);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.error-card__message {
|
|
496
|
+
margin-bottom: var(--space-md);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.error-card__action a {
|
|
500
|
+
font-size: 0.875rem;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* --- Loading Bar --- */
|
|
504
|
+
.loading-bar {
|
|
505
|
+
position: fixed;
|
|
506
|
+
top: 0;
|
|
507
|
+
left: 0;
|
|
508
|
+
width: 100%;
|
|
509
|
+
height: 3px;
|
|
510
|
+
z-index: 100;
|
|
511
|
+
display: none;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.htmx-request .loading-bar {
|
|
515
|
+
display: block;
|
|
516
|
+
background: var(--pico-primary);
|
|
517
|
+
animation: loading-shimmer 1.2s ease-in-out infinite;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
@keyframes loading-shimmer {
|
|
521
|
+
0% { transform: translateX(-100%); }
|
|
522
|
+
100% { transform: translateX(100%); }
|
|
523
|
+
}
|
|
524
|
+
|
|
366
525
|
/* --- Mobile hamburger toggle --- */
|
|
367
526
|
.sidebar-toggle {
|
|
368
527
|
display: none;
|
|
369
528
|
background: none;
|
|
370
|
-
border: 1px solid var(--border
|
|
529
|
+
border: 1px solid var(--color-border);
|
|
371
530
|
border-radius: var(--radius-sm);
|
|
372
531
|
color: var(--pico-color);
|
|
373
532
|
font-size: 1.25rem;
|
|
@@ -383,7 +542,7 @@ main > p:first-of-type > a[href="/"]:hover {
|
|
|
383
542
|
/* Tablet: narrower sidebar */
|
|
384
543
|
@media (max-width: 1024px) {
|
|
385
544
|
:root {
|
|
386
|
-
--sidebar
|
|
545
|
+
--size-sidebar: 180px;
|
|
387
546
|
}
|
|
388
547
|
|
|
389
548
|
.page-wrapper > main {
|
|
@@ -391,14 +550,13 @@ main > p:first-of-type > a[href="/"]:hover {
|
|
|
391
550
|
}
|
|
392
551
|
}
|
|
393
552
|
|
|
394
|
-
/* Mobile: sidebar
|
|
553
|
+
/* Mobile: sidebar overlays content */
|
|
395
554
|
@media (max-width: 768px) {
|
|
396
555
|
.page-wrapper {
|
|
397
556
|
grid-template-columns: 1fr;
|
|
398
|
-
grid-template-rows: var(--header
|
|
557
|
+
grid-template-rows: var(--size-header) 1fr auto;
|
|
399
558
|
grid-template-areas:
|
|
400
559
|
"header"
|
|
401
|
-
"sidebar"
|
|
402
560
|
"content"
|
|
403
561
|
"footer";
|
|
404
562
|
}
|
|
@@ -408,40 +566,37 @@ main > p:first-of-type > a[href="/"]:hover {
|
|
|
408
566
|
}
|
|
409
567
|
|
|
410
568
|
.page-wrapper > aside.sidebar {
|
|
411
|
-
position:
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
.page-wrapper > aside.sidebar.open {
|
|
569
|
+
position: fixed;
|
|
570
|
+
top: var(--size-header);
|
|
571
|
+
left: 0;
|
|
572
|
+
width: 260px;
|
|
573
|
+
height: calc(100vh - var(--size-header));
|
|
574
|
+
z-index: 20;
|
|
575
|
+
transform: translateX(-100%);
|
|
576
|
+
transition: transform 0.25s ease;
|
|
421
577
|
display: block;
|
|
578
|
+
border-right: 1px solid var(--color-border);
|
|
579
|
+
border-bottom: none;
|
|
580
|
+
padding: var(--space-lg) 0;
|
|
581
|
+
overflow-y: auto;
|
|
582
|
+
background: var(--color-surface-raised);
|
|
422
583
|
}
|
|
423
584
|
|
|
424
|
-
aside.sidebar
|
|
425
|
-
|
|
426
|
-
flex-wrap: wrap;
|
|
427
|
-
gap: 0;
|
|
428
|
-
padding: var(--space-sm) var(--space-md);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
aside.sidebar nav a {
|
|
432
|
-
border-left: none;
|
|
433
|
-
border-bottom: 2px solid transparent;
|
|
434
|
-
padding: var(--space-sm) var(--space-md);
|
|
435
|
-
font-size: 0.85rem;
|
|
585
|
+
.page-wrapper > aside.sidebar.open {
|
|
586
|
+
transform: translateX(0);
|
|
436
587
|
}
|
|
437
588
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
589
|
+
.sidebar-backdrop {
|
|
590
|
+
display: none;
|
|
591
|
+
position: fixed;
|
|
592
|
+
inset: 0;
|
|
593
|
+
top: var(--size-header);
|
|
594
|
+
background: rgba(0, 0, 0, 0.4);
|
|
595
|
+
z-index: 19;
|
|
441
596
|
}
|
|
442
597
|
|
|
443
|
-
|
|
444
|
-
|
|
598
|
+
.sidebar-backdrop.open {
|
|
599
|
+
display: block;
|
|
445
600
|
}
|
|
446
601
|
|
|
447
602
|
.page-wrapper > main {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Towline Dashboard — Design Tokens
|
|
3
|
+
Single source of truth for colors, spacing,
|
|
4
|
+
typography, and sizing across all CSS files.
|
|
5
|
+
============================================ */
|
|
6
|
+
|
|
7
|
+
/* --- Light Mode (Default) --- */
|
|
8
|
+
:root {
|
|
9
|
+
/* Colors */
|
|
10
|
+
--color-surface: #ffffff;
|
|
11
|
+
--color-surface-raised: #f8f9fa;
|
|
12
|
+
--color-surface-hover: rgba(0, 0, 0, 0.04);
|
|
13
|
+
--color-border: rgba(0, 0, 0, 0.1);
|
|
14
|
+
--color-text-dim: rgba(0, 0, 0, 0.5);
|
|
15
|
+
--color-text: #1a1a2e;
|
|
16
|
+
--color-accent: var(--pico-primary);
|
|
17
|
+
|
|
18
|
+
/* Spacing */
|
|
19
|
+
--space-xs: 0.25rem;
|
|
20
|
+
--space-sm: 0.5rem;
|
|
21
|
+
--space-md: 1rem;
|
|
22
|
+
--space-lg: 1.5rem;
|
|
23
|
+
--space-xl: 2rem;
|
|
24
|
+
--space-2xl: 3rem;
|
|
25
|
+
|
|
26
|
+
/* Radii */
|
|
27
|
+
--radius-sm: 0.375rem;
|
|
28
|
+
--radius-md: 0.5rem;
|
|
29
|
+
|
|
30
|
+
/* Typography */
|
|
31
|
+
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
|
|
32
|
+
--font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Fira Code', monospace;
|
|
33
|
+
|
|
34
|
+
/* Sizing */
|
|
35
|
+
--size-sidebar: 220px;
|
|
36
|
+
--size-header: 3.5rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* --- Dark Mode (explicit attribute) --- */
|
|
40
|
+
[data-theme="dark"] {
|
|
41
|
+
--color-surface: #13131a;
|
|
42
|
+
--color-surface-raised: rgba(255, 255, 255, 0.03);
|
|
43
|
+
--color-surface-hover: rgba(255, 255, 255, 0.06);
|
|
44
|
+
--color-border: rgba(255, 255, 255, 0.08);
|
|
45
|
+
--color-text-dim: rgba(255, 255, 255, 0.5);
|
|
46
|
+
--color-text: #e8e8f0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* --- Dark Mode (system preference, unless light forced) --- */
|
|
50
|
+
@media (prefers-color-scheme: dark) {
|
|
51
|
+
:root:not([data-theme="light"]) {
|
|
52
|
+
--color-surface: #13131a;
|
|
53
|
+
--color-surface-raised: rgba(255, 255, 255, 0.03);
|
|
54
|
+
--color-surface-hover: rgba(255, 255, 255, 0.06);
|
|
55
|
+
--color-border: rgba(255, 255, 255, 0.08);
|
|
56
|
+
--color-text-dim: rgba(255, 255, 255, 0.5);
|
|
57
|
+
--color-text: #e8e8f0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
// Mobile sidebar toggle
|
|
1
|
+
// Mobile sidebar toggle with backdrop overlay
|
|
2
2
|
document.addEventListener('DOMContentLoaded', function() {
|
|
3
3
|
var toggle = document.querySelector('.sidebar-toggle');
|
|
4
4
|
var sidebar = document.querySelector('.sidebar');
|
|
5
|
-
if (toggle
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
if (!toggle || !sidebar) return;
|
|
6
|
+
|
|
7
|
+
// Create backdrop element
|
|
8
|
+
var backdrop = document.createElement('div');
|
|
9
|
+
backdrop.className = 'sidebar-backdrop';
|
|
10
|
+
document.body.appendChild(backdrop);
|
|
11
|
+
|
|
12
|
+
function closeSidebar() {
|
|
13
|
+
sidebar.classList.remove('open');
|
|
14
|
+
backdrop.classList.remove('open');
|
|
9
15
|
}
|
|
10
16
|
|
|
17
|
+
function toggleSidebar() {
|
|
18
|
+
sidebar.classList.toggle('open');
|
|
19
|
+
backdrop.classList.toggle('open');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
toggle.addEventListener('click', toggleSidebar);
|
|
23
|
+
backdrop.addEventListener('click', closeSidebar);
|
|
24
|
+
|
|
11
25
|
// Close sidebar when a nav link is clicked (mobile)
|
|
12
|
-
var navLinks = sidebar
|
|
26
|
+
var navLinks = sidebar.querySelectorAll('a');
|
|
13
27
|
navLinks.forEach(function(link) {
|
|
14
28
|
link.addEventListener('click', function() {
|
|
15
29
|
if (window.innerWidth <= 768) {
|
|
16
|
-
|
|
30
|
+
closeSidebar();
|
|
17
31
|
}
|
|
18
32
|
});
|
|
19
33
|
});
|