@profpowell/vb-project-planning 0.1.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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/vb-project-planning.css +1603 -0
  4. package/dist/vb-project-planning.js +11138 -0
  5. package/dist/vb-project-planning.min.css +1 -0
  6. package/dist/vb-project-planning.min.js +3312 -0
  7. package/package.json +80 -0
  8. package/src/index.css +14 -0
  9. package/src/index.js +26 -0
  10. package/src/lib/bundle-registry.js +156 -0
  11. package/src/lib/diff-by-key.js +86 -0
  12. package/src/lib/portrait-url.js +87 -0
  13. package/src/lib/vb-collection.js +122 -0
  14. package/src/lib/vb-element.js +96 -0
  15. package/src/lib/vb-store.js +200 -0
  16. package/src/lib/vb-view-transition.js +44 -0
  17. package/src/utils/copy-init.js +287 -0
  18. package/src/web-components/_ux-base.js +106 -0
  19. package/src/web-components/adr-wc/README.md +77 -0
  20. package/src/web-components/adr-wc/api.json +26 -0
  21. package/src/web-components/adr-wc/logic.js +376 -0
  22. package/src/web-components/adr-wc/static.html +17 -0
  23. package/src/web-components/adr-wc/styles.js +338 -0
  24. package/src/web-components/burndown-chart/api.json +74 -0
  25. package/src/web-components/burndown-chart/logic.js +198 -0
  26. package/src/web-components/burndown-chart/static.html +31 -0
  27. package/src/web-components/burndown-chart/styles.css +59 -0
  28. package/src/web-components/capacity-plan/_capacity-utils.js +56 -0
  29. package/src/web-components/capacity-plan/api.json +20 -0
  30. package/src/web-components/capacity-plan/logic.js +222 -0
  31. package/src/web-components/capacity-plan/static.html +24 -0
  32. package/src/web-components/capacity-plan/styles.css +84 -0
  33. package/src/web-components/empathy-map/api.json +31 -0
  34. package/src/web-components/empathy-map/logic.js +502 -0
  35. package/src/web-components/empathy-map/static.html +32 -0
  36. package/src/web-components/empathy-map/styles.js +534 -0
  37. package/src/web-components/gantt-chart/README.md +91 -0
  38. package/src/web-components/gantt-chart/api.json +20 -0
  39. package/src/web-components/gantt-chart/logic.js +812 -0
  40. package/src/web-components/gantt-chart/static.html +60 -0
  41. package/src/web-components/gantt-chart/styles.css +391 -0
  42. package/src/web-components/impact-effort/api.json +21 -0
  43. package/src/web-components/impact-effort/logic.js +392 -0
  44. package/src/web-components/impact-effort/static.html +23 -0
  45. package/src/web-components/impact-effort/styles.css +193 -0
  46. package/src/web-components/iron-triangle/_capacity.js +83 -0
  47. package/src/web-components/iron-triangle/_triangle-geometry.js +322 -0
  48. package/src/web-components/iron-triangle/api.json +26 -0
  49. package/src/web-components/iron-triangle/logic.js +474 -0
  50. package/src/web-components/iron-triangle/static.html +28 -0
  51. package/src/web-components/iron-triangle/styles.css +158 -0
  52. package/src/web-components/kanban-board/api.json +24 -0
  53. package/src/web-components/kanban-board/logic.js +698 -0
  54. package/src/web-components/kanban-board/static.html +20 -0
  55. package/src/web-components/kanban-board/styles.css +242 -0
  56. package/src/web-components/product-roadmap/api.json +118 -0
  57. package/src/web-components/product-roadmap/logic.js +460 -0
  58. package/src/web-components/product-roadmap/static.html +44 -0
  59. package/src/web-components/product-roadmap/styles.css +158 -0
  60. package/src/web-components/quadrant-grid/api.json +125 -0
  61. package/src/web-components/quadrant-grid/logic.js +397 -0
  62. package/src/web-components/quadrant-grid/static.html +49 -0
  63. package/src/web-components/quadrant-grid/styles.css +184 -0
  64. package/src/web-components/quality-target/_quality-utils.js +460 -0
  65. package/src/web-components/quality-target/api.json +30 -0
  66. package/src/web-components/quality-target/logic.js +516 -0
  67. package/src/web-components/quality-target/static.html +22 -0
  68. package/src/web-components/quality-target/styles.css +203 -0
  69. package/src/web-components/requirement-card/api.json +25 -0
  70. package/src/web-components/requirement-card/logic.js +166 -0
  71. package/src/web-components/requirement-card/static.html +36 -0
  72. package/src/web-components/requirement-card/styles.js +114 -0
  73. package/src/web-components/review-surface/README.md +178 -0
  74. package/src/web-components/review-surface/api.json +30 -0
  75. package/src/web-components/review-surface/logic.js +807 -0
  76. package/src/web-components/review-surface/static.html +33 -0
  77. package/src/web-components/review-surface/styles.js +459 -0
  78. package/src/web-components/risk-register/api.json +47 -0
  79. package/src/web-components/risk-register/logic.js +180 -0
  80. package/src/web-components/risk-register/static.html +46 -0
  81. package/src/web-components/risk-register/styles.css +37 -0
  82. package/src/web-components/story-map/api.json +22 -0
  83. package/src/web-components/story-map/logic.js +445 -0
  84. package/src/web-components/story-map/static.html +35 -0
  85. package/src/web-components/story-map/styles.css +169 -0
  86. package/src/web-components/traceability-matrix/api.json +84 -0
  87. package/src/web-components/traceability-matrix/logic.js +243 -0
  88. package/src/web-components/traceability-matrix/static.html +41 -0
  89. package/src/web-components/traceability-matrix/styles.css +94 -0
  90. package/src/web-components/user-journey/api.json +84 -0
  91. package/src/web-components/user-journey/logic.js +368 -0
  92. package/src/web-components/user-journey/static.html +14 -0
  93. package/src/web-components/user-journey/styles.js +317 -0
  94. package/src/web-components/user-persona/api.json +35 -0
  95. package/src/web-components/user-persona/logic.js +502 -0
  96. package/src/web-components/user-persona/static.html +20 -0
  97. package/src/web-components/user-persona/styles.js +299 -0
  98. package/src/web-components/user-story/api.json +35 -0
  99. package/src/web-components/user-story/logic.js +567 -0
  100. package/src/web-components/user-story/static.html +27 -0
  101. package/src/web-components/user-story/styles.js +332 -0
  102. package/src/web-components/work-item/README.md +85 -0
  103. package/src/web-components/work-item/api.json +30 -0
  104. package/src/web-components/work-item/logic.js +414 -0
  105. package/src/web-components/work-item/static.html +24 -0
  106. package/src/web-components/work-item/styles.js +376 -0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Shadow DOM styles for <empathy-map>
3
+ *
4
+ * Theme integration:
5
+ * --empathy-map-* (per-instance author override)
6
+ * --color-* (VB theme token — adapts to data-theme and color mode)
7
+ * #fallback (last-resort hex for component used outside VB)
8
+ *
9
+ * VB tokens already swap on color-scheme change and across themes, so we
10
+ * deliberately DO NOT define our own @media (prefers-color-scheme: dark)
11
+ * overrides. Quadrant accent colors map to VB semantic tokens:
12
+ * says → --color-info
13
+ * thinks → --color-accent
14
+ * does → --color-warning
15
+ * feels → --color-error
16
+ * Quadrant icon backgrounds derive via color-mix from the same accents
17
+ * so they shift automatically with the active theme.
18
+ */
19
+ const styles = `
20
+ :host {
21
+ display: block;
22
+ font-family: var(--_font-sans);
23
+ line-height: 1.6;
24
+ color: var(--_text);
25
+ container-type: inline-size;
26
+
27
+ --_bg: var(--empathy-map-bg, var(--color-surface, #ffffff));
28
+ --_card: var(--empathy-map-card, var(--color-surface-raised, #f8f9fa));
29
+ --_border: var(--empathy-map-border, var(--color-border, #e0e0e0));
30
+ --_muted: var(--empathy-map-muted, var(--color-text-muted, #666666));
31
+ --_text: var(--empathy-map-text, var(--color-text, #1a1a1a));
32
+ --_radius: var(--empathy-map-radius, var(--radius-xl, 1rem));
33
+ --_accent: var(--empathy-map-accent, var(--color-interactive, var(--color-primary, #0066cc)));
34
+ --_link: var(--empathy-map-link, var(--color-interactive, var(--color-primary, #6366f1)));
35
+ --_primary: var(--empathy-map-primary, var(--color-primary, #6366f1));
36
+ --_accent-bar: var(--empathy-map-accent-bar, var(--color-accent, #8b5cf6));
37
+ --_says: var(--empathy-map-says, var(--color-info, #3b82f6));
38
+ --_thinks: var(--empathy-map-thinks, var(--color-accent, #8b5cf6));
39
+ --_does: var(--empathy-map-does, var(--color-warning, #f59e0b));
40
+ --_feels: var(--empathy-map-feels, var(--color-error, #ef4444));
41
+ --_says-bg: color-mix(in oklch, var(--_says) 16%, var(--_card));
42
+ --_thinks-bg: color-mix(in oklch, var(--_thinks) 16%, var(--_card));
43
+ --_does-bg: color-mix(in oklch, var(--_does) 16%, var(--_card));
44
+ --_feels-bg: color-mix(in oklch, var(--_feels) 16%, var(--_card));
45
+ --_chip-type-bg: color-mix(in oklch, var(--_accent-bar) 18%, var(--_card));
46
+ --_chip-type-fg: var(--_accent-bar);
47
+ --_font-sans: var(--empathy-map-font, var(--font-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif));
48
+ --_font-xs: var(--empathy-map-font-xs, var(--font-size-xs, 0.75rem));
49
+ --_font-sm: var(--empathy-map-font-sm, var(--font-size-sm, 0.875rem));
50
+ --_font-md: var(--empathy-map-font-md, var(--font-size-md, 1rem));
51
+ --_font-xl: var(--empathy-map-font-xl, var(--font-size-xl, 1.25rem));
52
+ --_space-2xs: var(--empathy-map-space-2xs, var(--size-2xs, 0.25rem));
53
+ --_space-xs: var(--empathy-map-space-xs, var(--size-xs, 0.5rem));
54
+ --_space-s: var(--empathy-map-space-s, var(--size-s, 0.75rem));
55
+ --_space-m: var(--empathy-map-space-m, var(--size-m, 1rem));
56
+ --_space-l: var(--empathy-map-space-l, var(--size-l, 1.5rem));
57
+ --_radius-m: var(--empathy-map-radius-m, var(--radius-m, 0.5rem));
58
+ --_radius-full: var(--empathy-map-radius-full, var(--radius-full, 9999px));
59
+ --_duration: var(--empathy-map-duration, var(--duration-normal, 200ms));
60
+ --_duration-fast: var(--empathy-map-duration-fast, var(--duration-fast, 100ms));
61
+ --_ease: var(--empathy-map-ease, var(--ease-default, ease));
62
+ }
63
+
64
+ *, *::before, *::after { box-sizing: border-box; margin: 0; }
65
+
66
+ /* ── Card ─────────────────────────────────────────── */
67
+ .empathy-map {
68
+ background: var(--_card);
69
+ border: 1px solid var(--_border);
70
+ border-radius: var(--_radius);
71
+ overflow: hidden;
72
+ }
73
+
74
+ /* ── Header ───────────────────────────────────────── */
75
+ .empathy-map__header {
76
+ padding: 20px 24px 16px 28px;
77
+ border-block-end: 1px solid var(--_border);
78
+ position: relative;
79
+ }
80
+
81
+ /* Left accent bar */
82
+ .empathy-map__header::before {
83
+ content: '';
84
+ position: absolute;
85
+ inset-block: 0;
86
+ inset-inline-start: 0;
87
+ width: 4px;
88
+ background: linear-gradient(135deg, var(--_primary), var(--_accent-bar));
89
+ }
90
+
91
+ .empathy-map__header-top {
92
+ display: flex;
93
+ align-items: flex-start;
94
+ justify-content: space-between;
95
+ gap: var(--_space-s);
96
+ flex-wrap: wrap;
97
+ margin-block-end: var(--_space-xs);
98
+ }
99
+
100
+ .empathy-map__chips {
101
+ display: flex;
102
+ flex-wrap: wrap;
103
+ align-items: center;
104
+ gap: 6px;
105
+ }
106
+
107
+ /* ── Chips ─────────────────────────────────────────── */
108
+ .chip {
109
+ display: inline-block;
110
+ font-size: 11px;
111
+ font-weight: 700;
112
+ letter-spacing: 0.05em;
113
+ padding: 2px 9px;
114
+ border-radius: var(--_radius-full);
115
+ text-decoration: none;
116
+ line-height: 1.6;
117
+ }
118
+
119
+ .chip--type {
120
+ color: var(--_chip-type-fg);
121
+ background: var(--_chip-type-bg);
122
+ }
123
+
124
+ /* ── Persona ref ──────────────────────────────────── */
125
+ .persona-ref {
126
+ display: inline-flex;
127
+ align-items: center;
128
+ gap: 4px;
129
+ font-size: 13px;
130
+ font-weight: 600;
131
+ color: var(--_muted);
132
+ text-decoration: none;
133
+ white-space: nowrap;
134
+ }
135
+
136
+ .persona-ref svg {
137
+ width: 14px;
138
+ height: 14px;
139
+ flex-shrink: 0;
140
+ }
141
+
142
+ a.persona-ref:hover { color: var(--_link); text-decoration: underline; }
143
+
144
+ /* ── Title & summary (slotted content) ────────────── */
145
+ .empathy-map__title-wrap {
146
+ margin-block-end: var(--_space-2xs);
147
+ }
148
+
149
+ .empathy-map__title {
150
+ font-size: var(--_font-xl);
151
+ font-weight: 700;
152
+ color: var(--_text);
153
+ margin: 0;
154
+ }
155
+
156
+ ::slotted([slot="title"]) {
157
+ font-size: var(--_font-xl) !important;
158
+ font-weight: 700 !important;
159
+ color: var(--_text) !important;
160
+ margin: 0 !important;
161
+ }
162
+
163
+ .empathy-map__summary-wrap {
164
+ max-width: 72ch;
165
+ }
166
+
167
+ ::slotted([slot="summary"]) {
168
+ font-size: var(--_font-sm) !important;
169
+ color: var(--_muted) !important;
170
+ margin: 0 !important;
171
+ }
172
+
173
+ /* ── Grid ──────────────────────────────────────────── */
174
+ .empathy-map__grid {
175
+ display: grid;
176
+ grid-template-columns: 1fr 1fr;
177
+ gap: 1px;
178
+ background: var(--_border);
179
+ }
180
+
181
+ /* ── Quadrant ─────────────────────────────────────── */
182
+ .quadrant {
183
+ background: var(--_card);
184
+ padding: 0;
185
+ position: relative;
186
+ display: grid;
187
+ }
188
+
189
+ .quadrant::before {
190
+ content: '';
191
+ position: absolute;
192
+ inset-block-start: 0;
193
+ inset-inline: 0;
194
+ height: 3px;
195
+ }
196
+
197
+ .quadrant--says::before { background: var(--_says); }
198
+ .quadrant--thinks::before { background: var(--_thinks); }
199
+ .quadrant--does::before { background: var(--_does); }
200
+ .quadrant--feels::before { background: var(--_feels); }
201
+
202
+ .quadrant__inner {
203
+ display: grid;
204
+ padding: 16px 20px;
205
+ padding-block-start: 19px; /* 16 + 3px top border */
206
+ }
207
+
208
+ /* ── Quadrant header ──────────────────────────────── */
209
+ .quadrant__header {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: 10px;
213
+ margin-block-end: var(--_space-s);
214
+ }
215
+
216
+ .quadrant__icon {
217
+ display: flex;
218
+ align-items: center;
219
+ justify-content: center;
220
+ width: 32px;
221
+ height: 32px;
222
+ border-radius: var(--_radius-m);
223
+ flex-shrink: 0;
224
+ }
225
+
226
+ .quadrant__icon svg {
227
+ width: 16px;
228
+ height: 16px;
229
+ }
230
+
231
+ .quadrant--says .quadrant__icon { background: var(--_says-bg); color: var(--_says); }
232
+ .quadrant--thinks .quadrant__icon { background: var(--_thinks-bg); color: var(--_thinks); }
233
+ .quadrant--does .quadrant__icon { background: var(--_does-bg); color: var(--_does); }
234
+ .quadrant--feels .quadrant__icon { background: var(--_feels-bg); color: var(--_feels); }
235
+
236
+ .quadrant__label {
237
+ font-size: 13px;
238
+ font-weight: 700;
239
+ text-transform: uppercase;
240
+ letter-spacing: 0.06em;
241
+ color: var(--_muted);
242
+ flex: 1;
243
+ }
244
+
245
+ /* ── Quadrant content / faces ─────────────────────── */
246
+ .quadrant__face {
247
+ grid-area: 1 / 1;
248
+ transition: opacity var(--_duration) var(--_ease), transform var(--_duration) var(--_ease);
249
+ }
250
+
251
+ .quadrant__face--front {
252
+ opacity: 1;
253
+ transform: scale(1);
254
+ }
255
+
256
+ .quadrant__face--back {
257
+ opacity: 0;
258
+ transform: scale(0.95);
259
+ pointer-events: none;
260
+ }
261
+
262
+ [data-editing] .quadrant__face--front {
263
+ opacity: 0;
264
+ transform: scale(0.95);
265
+ pointer-events: none;
266
+ }
267
+
268
+ [data-editing] .quadrant__face--back {
269
+ opacity: 1;
270
+ transform: scale(1);
271
+ pointer-events: auto;
272
+ }
273
+
274
+ .quadrant__content {
275
+ font-size: 14px;
276
+ line-height: 1.55;
277
+ color: var(--_text);
278
+ }
279
+
280
+ .quadrant__content p {
281
+ margin-block: 0 6px;
282
+ }
283
+
284
+ .quadrant__content p:last-child {
285
+ margin-block-end: 0;
286
+ }
287
+
288
+ /* ── Slot fallback / placeholder ──────────────────── */
289
+ .placeholder {
290
+ font-size: 13px;
291
+ color: var(--_muted);
292
+ font-style: italic;
293
+ }
294
+
295
+ ::slotted(ul),
296
+ ::slotted(ol) {
297
+ margin: 0;
298
+ padding-inline-start: 1.4em;
299
+ font-size: 14px;
300
+ line-height: 1.55;
301
+ }
302
+
303
+ /* ── Emotion tags ─────────────────────────────────── */
304
+ .emotion-tag {
305
+ display: inline-block;
306
+ font-size: 12px;
307
+ font-weight: 600;
308
+ padding: 2px 10px;
309
+ border-radius: var(--_radius-full);
310
+ margin-block-end: 6px;
311
+ margin-inline-end: 4px;
312
+ background: color-mix(in srgb, var(--ec, #94a3b8) 15%, transparent);
313
+ color: var(--ec, #94a3b8);
314
+ border: 1px solid color-mix(in srgb, var(--ec, #94a3b8) 30%, transparent);
315
+ }
316
+
317
+ /* ── Edit / Done buttons ──────────────────────────── */
318
+ .quadrant__edit-btn,
319
+ .quadrant__done-btn {
320
+ display: inline-flex;
321
+ align-items: center;
322
+ justify-content: center;
323
+ width: 28px;
324
+ height: 28px;
325
+ border: 1px solid var(--_border);
326
+ border-radius: 6px;
327
+ background: var(--_bg);
328
+ color: var(--_muted);
329
+ cursor: pointer;
330
+ line-height: 1;
331
+ padding: 0;
332
+ flex-shrink: 0;
333
+ transition: background var(--_duration-fast) var(--_ease), color var(--_duration-fast) var(--_ease);
334
+ }
335
+
336
+ .quadrant__edit-btn svg,
337
+ .quadrant__done-btn svg {
338
+ width: 14px;
339
+ height: 14px;
340
+ }
341
+
342
+ .quadrant__edit-btn:hover,
343
+ .quadrant__done-btn:hover {
344
+ background: var(--_border);
345
+ color: var(--_text);
346
+ }
347
+
348
+ .quadrant__done-btn {
349
+ margin-block-start: 8px;
350
+ width: auto;
351
+ padding: 0 12px;
352
+ font-size: 12px;
353
+ font-weight: 600;
354
+ gap: 4px;
355
+ }
356
+
357
+ /* ── Editor textarea ──────────────────────────────── */
358
+ .quadrant__editor {
359
+ width: 100%;
360
+ min-height: 100px;
361
+ padding: 10px 12px;
362
+ border: 1px solid var(--_border);
363
+ border-radius: var(--_radius-m);
364
+ font-family: inherit;
365
+ font-size: 13px;
366
+ line-height: 1.55;
367
+ color: var(--_text);
368
+ background: var(--_bg);
369
+ resize: vertical;
370
+ }
371
+
372
+ .quadrant__editor:focus {
373
+ outline: 2px solid var(--_accent);
374
+ outline-offset: -1px;
375
+ border-color: var(--_accent);
376
+ }
377
+
378
+ /* ── Footer ───────────────────────────────────────── */
379
+ .empathy-map__footer {
380
+ display: flex;
381
+ gap: 1px;
382
+ background: var(--_border);
383
+ border-block-start: 1px solid var(--_border);
384
+ }
385
+
386
+ .summary-row {
387
+ flex: 1;
388
+ background: var(--_card);
389
+ padding: 16px 20px;
390
+ display: flex;
391
+ align-items: flex-start;
392
+ gap: 10px;
393
+ }
394
+
395
+ .summary-row__icon {
396
+ line-height: 1;
397
+ flex-shrink: 0;
398
+ margin-block-start: 2px;
399
+ color: var(--_muted);
400
+ }
401
+
402
+ .summary-row__icon svg {
403
+ width: 18px;
404
+ height: 18px;
405
+ }
406
+
407
+ .summary-row__body {
408
+ flex: 1;
409
+ min-width: 0;
410
+ }
411
+
412
+ .summary-row__label {
413
+ font-size: 11px;
414
+ font-weight: 700;
415
+ text-transform: uppercase;
416
+ letter-spacing: 0.06em;
417
+ color: var(--_muted);
418
+ margin-block-end: 4px;
419
+ }
420
+
421
+ .summary-row__content {
422
+ font-size: 14px;
423
+ line-height: 1.55;
424
+ color: var(--_text);
425
+ }
426
+
427
+ .summary-row__content p {
428
+ margin-block: 0 4px;
429
+ }
430
+
431
+ .summary-row__content p:last-child {
432
+ margin-block-end: 0;
433
+ }
434
+
435
+ /* ── Compact variant ──────────────────────────────── */
436
+ :host([compact]) .empathy-map__header {
437
+ padding: 14px 18px 12px 22px;
438
+ }
439
+
440
+ :host([compact]) .empathy-map__title {
441
+ font-size: 16px;
442
+ }
443
+
444
+ :host([compact]) .empathy-map__summary {
445
+ font-size: 12px;
446
+ }
447
+
448
+ :host([compact]) .quadrant__inner {
449
+ padding: 12px 14px;
450
+ padding-block-start: 15px;
451
+ }
452
+
453
+ :host([compact]) .quadrant__icon {
454
+ width: 26px;
455
+ height: 26px;
456
+ font-size: 13px;
457
+ border-radius: 6px;
458
+ }
459
+
460
+ :host([compact]) .quadrant__label {
461
+ font-size: 11px;
462
+ }
463
+
464
+ :host([compact]) .quadrant__content {
465
+ font-size: 12px;
466
+ }
467
+
468
+ :host([compact]) .summary-row {
469
+ padding: 12px 14px;
470
+ }
471
+
472
+ :host([compact]) .summary-row__content {
473
+ font-size: 12px;
474
+ }
475
+
476
+ /* ── Responsive ───────────────────────────────────── */
477
+ @container (max-width: 500px) {
478
+ .empathy-map__grid {
479
+ grid-template-columns: 1fr;
480
+ }
481
+
482
+ .empathy-map__header {
483
+ padding: 14px 16px 12px 20px;
484
+ }
485
+
486
+ .empathy-map__title {
487
+ font-size: 17px;
488
+ }
489
+
490
+ .empathy-map__footer {
491
+ flex-direction: column;
492
+ }
493
+ }
494
+
495
+ /* ── Motion ───────────────────────────────────────── */
496
+ @media (prefers-reduced-motion: reduce) {
497
+ .quadrant__face {
498
+ transition: none;
499
+ }
500
+
501
+ .quadrant__edit-btn,
502
+ .quadrant__done-btn {
503
+ transition: none;
504
+ }
505
+ }
506
+
507
+ /* ── Print ────────────────────────────────────────── */
508
+ @media print {
509
+ .empathy-map {
510
+ break-inside: avoid;
511
+ border-color: var(--_border);
512
+ }
513
+
514
+ .quadrant__edit-btn,
515
+ .quadrant__done-btn {
516
+ display: none;
517
+ }
518
+
519
+ .quadrant__face--back {
520
+ display: none;
521
+ }
522
+
523
+ .empathy-map__footer {
524
+ flex-direction: column;
525
+ }
526
+ }
527
+
528
+ /* ── Utility ──────────────────────────────────────── */
529
+ .state-msg { padding: var(--_space-l); font-size: var(--_font-sm); color: var(--_muted); font-style: italic; }
530
+ .state-msg--error { color: var(--color-error-text, var(--color-error, #dc2626)); }
531
+ code { font-family: var(--font-mono, Monaco, Menlo, monospace); font-size: 0.88em; }
532
+ `;
533
+
534
+ export { styles };
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: "Gantt Chart Specification"
3
+ component: gantt-chart
4
+ version: 1.0.0
5
+ status: stable
6
+ ---
7
+
8
+ # Gantt Chart
9
+
10
+ HTML-first project schedule visualization using native table, progress, and time elements.
11
+
12
+ ## Purpose
13
+
14
+ Progressively enhances a semantic `<table>` into an interactive Gantt timeline. Without JavaScript, users see a fully readable data table with progress bars. With JavaScript, the component renders a visual timeline with positioned bars, milestones, groups, and a today marker.
15
+
16
+ Part of the **UX Planning Pack**.
17
+
18
+ ## Static HTML Form
19
+
20
+ ```html
21
+ <gantt-chart title="Q2 Roadmap" show-today show-progress>
22
+ <table>
23
+ <thead>
24
+ <tr><th>Task</th><th>Start</th><th>End</th><th>Progress</th></tr>
25
+ </thead>
26
+ <tbody>
27
+ <tr data-task-id="design" data-group="Design" data-status="in-progress">
28
+ <td>Design system</td>
29
+ <td><time datetime="2026-04-01">Apr 1</time></td>
30
+ <td><time datetime="2026-04-15">Apr 15</time></td>
31
+ <td><progress value="75" max="100">75%</progress></td>
32
+ </tr>
33
+ </tbody>
34
+ </table>
35
+ </gantt-chart>
36
+ ```
37
+
38
+ ## Attributes
39
+
40
+ | Attribute | Type | Default | Description |
41
+ |-----------|------|---------|-------------|
42
+ | `title` | string | — | Chart heading |
43
+ | `src` | string | — | JSON data URL |
44
+ | `view` | string | `"auto"` | `auto`, `day`, `week`, `month`, `quarter` |
45
+ | `show-today` | boolean | — | Show today marker |
46
+ | `show-progress` | boolean | — | Show progress fill |
47
+ | `show-dependencies` | boolean | — | Draw dependency arrows |
48
+ | `compact` | boolean | — | Reduced spacing |
49
+
50
+ ### Child Attributes (on `<tr>`)
51
+
52
+ | Attribute | Type | Description |
53
+ |-----------|------|-------------|
54
+ | `data-task-id` | string | Unique task ID |
55
+ | `data-group` | string | Swim lane group |
56
+ | `data-depends` | string | Comma-separated dependency IDs |
57
+ | `data-status` | string | `not-started`, `in-progress`, `done`, `blocked` |
58
+ | `data-assignee` | string | Assignee name |
59
+ | `data-milestone` | boolean | Render as diamond marker |
60
+ | `data-color` | string | Custom bar color |
61
+
62
+ ## Events
63
+
64
+ | Event | Detail | Description |
65
+ |-------|--------|-------------|
66
+ | `gantt-chart:ready` | `{ taskCount, dateRange }` | After render |
67
+ | `gantt-chart:task-click` | `{ task }` | Bar clicked |
68
+
69
+ ## Failure Modes
70
+
71
+ - **No JavaScript:** Table renders normally with progress bars and time elements.
72
+ - **No CSS:** Table renders as default HTML table.
73
+ - **Keyboard Only:** Tab navigates bars; Enter triggers click event.
74
+ - **Screen Reader:** Original table is preserved (visually hidden) for full table semantics.
75
+
76
+ ## Accessibility
77
+
78
+ - Original table stays in DOM (sr-only) for screen reader access
79
+ - Bars have `role="img"` with descriptive `aria-label`
80
+ - Bars are focusable with `tabindex="0"`
81
+ - `aria-live="polite"` region announces task selection
82
+ - `prefers-reduced-motion: reduce` disables transitions
83
+ - Print shows the original table, hides the visual chart
84
+
85
+ ## CSS Custom Properties
86
+
87
+ | Token | Description |
88
+ |-------|-------------|
89
+ | `--gc-task-width` | Left panel width (default: 180px) |
90
+ | `--gc-row-height` | Row height (default: 36px) |
91
+ | `--gc-bar-color` | Per-bar color override |
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "../../../schemas/api.schema.json",
3
+ "element": "gantt-chart",
4
+ "type": "web-component",
5
+ "description": "HTML-first Gantt chart using native table, progress, and time elements with progressive enhancement to an interactive timeline",
6
+ "htmlvalidate": { "flow": true, "permittedContent": ["@flow"] },
7
+ "attributes": [
8
+ { "name": "title", "kind": "host-api", "purpose": "config", "type": "string", "description": "Chart heading" },
9
+ { "name": "src", "kind": "host-api", "purpose": "config", "type": "string", "description": "URL to JSON task data" },
10
+ { "name": "view", "kind": "host-api", "purpose": "config", "type": "string", "description": "Timeline granularity: auto | day | week | month | quarter" },
11
+ { "name": "show-today", "kind": "host-api", "purpose": "config", "type": "boolean", "description": "Show vertical today marker line" },
12
+ { "name": "show-progress", "kind": "host-api", "purpose": "config", "type": "boolean", "description": "Show progress fill inside bars" },
13
+ { "name": "show-dependencies", "kind": "host-api", "purpose": "config", "type": "boolean", "description": "Draw dependency arrows between tasks" },
14
+ { "name": "compact", "kind": "host-api", "purpose": "config", "type": "boolean", "description": "Reduced spacing variant" }
15
+ ],
16
+ "events": [
17
+ { "name": "gantt-chart:ready", "detail": "{ taskCount, dateRange }", "description": "Fired after initial render" },
18
+ { "name": "gantt-chart:task-click", "detail": "{ task }", "description": "Fired when a task bar is clicked" }
19
+ ]
20
+ }