custom-elements-ts 0.0.17 → 0.2.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 (82) hide show
  1. package/.eslintrc.json +47 -0
  2. package/.github/workflows/ci.yml +49 -0
  3. package/.prettierrc +7 -0
  4. package/LICENSE +20 -0
  5. package/README.md +279 -37
  6. package/assets/readme-header.png +0 -0
  7. package/assets/social-preview.jpg +0 -0
  8. package/demos/counter/counter.element.html +1 -0
  9. package/demos/counter/counter.element.scss +234 -0
  10. package/demos/counter/counter.element.ts +68 -0
  11. package/demos/counter/index.html +205 -0
  12. package/demos/counter/index.ts +1 -0
  13. package/demos/site/code-example/code-example.element.scss +168 -0
  14. package/demos/site/code-example/code-example.element.ts +88 -0
  15. package/demos/site/event-log/event-log.element.scss +179 -0
  16. package/demos/site/event-log/event-log.element.ts +123 -0
  17. package/demos/site/favicon.svg +14 -0
  18. package/demos/site/index.html +368 -0
  19. package/demos/site/index.ts +19 -0
  20. package/demos/site/llms.txt +53 -0
  21. package/demos/site/message/message.element.scss +75 -0
  22. package/demos/site/message/message.element.ts +76 -0
  23. package/demos/site/og-image.png +0 -0
  24. package/demos/site/scroll-restoration.ts +28 -0
  25. package/demos/site/source-toggle/source-toggle.ts +88 -0
  26. package/demos/site/source-viewer/source-viewer.element.scss +292 -0
  27. package/demos/site/source-viewer/source-viewer.element.ts +138 -0
  28. package/demos/site/source-viewer/sources.generated.ts +11 -0
  29. package/demos/site/styles/site.css +1135 -0
  30. package/demos/site/styles/tokens.css +56 -0
  31. package/demos/site/toast/toast.element.scss +110 -0
  32. package/demos/site/toast/toast.element.ts +63 -0
  33. package/demos/todo-dashboard/index.html +141 -0
  34. package/demos/todo-dashboard/index.ts +4 -0
  35. package/demos/todo-dashboard/todo-dashboard.element.scss +1150 -0
  36. package/demos/todo-dashboard/todo-dashboard.element.ts +333 -0
  37. package/demos/todo-dashboard/todo-filters.element.ts +54 -0
  38. package/demos/todo-dashboard/todo-item.element.ts +127 -0
  39. package/demos/todo-dashboard/todo-stats.element.ts +189 -0
  40. package/package.json +73 -24
  41. package/src/custom-element.ts +206 -0
  42. package/src/index.ts +8 -0
  43. package/src/listen.ts +70 -0
  44. package/src/prop.ts +92 -0
  45. package/src/signal.ts +66 -0
  46. package/src/state.ts +141 -0
  47. package/src/template-runtime.ts +783 -0
  48. package/src/toggle.ts +66 -0
  49. package/src/tsconfig.json +24 -0
  50. package/src/util.ts +33 -0
  51. package/src/watch.ts +14 -0
  52. package/tests/basic.spec.ts +70 -0
  53. package/tests/custom-element.spec.ts +77 -0
  54. package/tests/dispatch.spec.ts +52 -0
  55. package/tests/init.spec.ts +94 -0
  56. package/tests/listen.spec.ts +118 -0
  57. package/tests/map-shallow-state.spec.ts +184 -0
  58. package/tests/prop.spec.ts +118 -0
  59. package/tests/signal.spec.ts +117 -0
  60. package/tests/templating-runtime.spec.ts +575 -0
  61. package/tests/toggle.spec.ts +92 -0
  62. package/tests/watch.spec.ts +183 -0
  63. package/tools/build.js +128 -0
  64. package/tools/bundle.js +167 -0
  65. package/tools/generate-sources.js +76 -0
  66. package/tools/rollup-config.js +70 -0
  67. package/tools/start.js +194 -0
  68. package/tsconfig.json +38 -0
  69. package/vite.config.mts +30 -0
  70. package/bundles/custom-elements-ts.umd.js +0 -359
  71. package/bundles/custom-elements-ts.umd.js.map +0 -1
  72. package/esm2015/custom-elements-ts.js +0 -283
  73. package/esm2015/custom-elements-ts.js.map +0 -1
  74. package/esm5/custom-element.d.ts +0 -12
  75. package/esm5/custom-elements-ts.js +0 -344
  76. package/esm5/custom-elements-ts.js.map +0 -1
  77. package/esm5/index.d.ts +0 -5
  78. package/esm5/listen.d.ts +0 -17
  79. package/esm5/prop.d.ts +0 -2
  80. package/esm5/toggle.d.ts +0 -1
  81. package/esm5/util.d.ts +0 -4
  82. package/esm5/watch.d.ts +0 -1
@@ -0,0 +1,1150 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ cts-todo-dashboard
3
+ Dark, technical surface aligned with demos/site tokens.
4
+ Sharp radii (2–4px), Geist typography, single TS-blue accent.
5
+ ───────────────────────────────────────────────────────────── */
6
+
7
+ :host {
8
+ /* Surfaces */
9
+ --bg: #0a0c10;
10
+ --bg-elev-1: #11141a;
11
+ --bg-elev-2: #161a22;
12
+ --bg-elev-3: #1c2230;
13
+
14
+ /* Lines */
15
+ --line: rgba(255, 255, 255, 0.06);
16
+ --line-strong: rgba(255, 255, 255, 0.1);
17
+ --line-accent: rgba(49, 120, 198, 0.32);
18
+
19
+ /* Type */
20
+ --fg: #e6ecf3;
21
+ --fg-muted: #97a1ad;
22
+ --fg-subtle: #5b6370;
23
+
24
+ /* Accent — TypeScript brand blue. One color, used sparingly. */
25
+ --accent: #3178c6;
26
+ --accent-bright: #4189d6;
27
+ --accent-fg: #9cc0eb;
28
+ --accent-soft: rgba(49, 120, 198, 0.14);
29
+ --accent-line: rgba(49, 120, 198, 0.42);
30
+
31
+ /* Status — kept neutral except for destructive remove */
32
+ --ok: #6fcf97;
33
+ --ok-soft: rgba(111, 207, 151, 0.18);
34
+ --danger: #c46a6a;
35
+ --danger-soft: rgba(196, 106, 106, 0.1);
36
+
37
+ /* Radii — sharp, technical, editorial. */
38
+ --radius-xs: 2px;
39
+ --radius-sm: 2px;
40
+ --radius-md: 3px;
41
+ --radius-lg: 4px;
42
+
43
+ /* Motion */
44
+ --ease: cubic-bezier(0.22, 1, 0.36, 1);
45
+ --dur-fast: 140ms;
46
+ --dur: 220ms;
47
+ --dur-slow: 420ms;
48
+
49
+ display: block;
50
+ width: min(720px, 100%);
51
+ color: var(--fg);
52
+ font-family:
53
+ 'Geist',
54
+ 'Inter',
55
+ ui-sans-serif,
56
+ system-ui,
57
+ -apple-system,
58
+ 'Segoe UI',
59
+ Roboto,
60
+ 'Helvetica Neue',
61
+ Arial,
62
+ sans-serif;
63
+ -webkit-font-smoothing: antialiased;
64
+ text-rendering: optimizeLegibility;
65
+ }
66
+
67
+ * {
68
+ box-sizing: border-box;
69
+ }
70
+
71
+ /* ── Shell ───────────────────────────────────────────────── */
72
+
73
+ .shell {
74
+ position: relative;
75
+ overflow: hidden;
76
+ border: 1px solid var(--line-strong);
77
+ border-radius: var(--radius-lg);
78
+ background:
79
+ radial-gradient(120% 120% at 0% 0%, rgba(49, 120, 198, 0.06) 0%, transparent 55%),
80
+ linear-gradient(180deg, var(--bg-elev-1) 0%, #0f131b 100%);
81
+ box-shadow:
82
+ 0 1px 0 rgba(255, 255, 255, 0.04) inset,
83
+ 0 22px 50px -28px rgba(0, 0, 0, 0.65),
84
+ 0 8px 24px -12px rgba(0, 0, 0, 0.45);
85
+ animation: shell-in 520ms var(--ease) both;
86
+ }
87
+
88
+ .shell.compact {
89
+ width: min(540px, 100%);
90
+ }
91
+
92
+ @keyframes shell-in {
93
+ from {
94
+ opacity: 0;
95
+ transform: translateY(8px);
96
+ }
97
+ to {
98
+ opacity: 1;
99
+ transform: translateY(0);
100
+ }
101
+ }
102
+
103
+ /* ── Header ──────────────────────────────────────────────── */
104
+
105
+ .header {
106
+ display: grid;
107
+ grid-template-columns: 1fr auto;
108
+ align-items: flex-start;
109
+ gap: 18px;
110
+ padding: 22px 24px 18px;
111
+ }
112
+
113
+ .title-block {
114
+ min-width: 0;
115
+ }
116
+
117
+ .eyebrow {
118
+ display: inline-flex;
119
+ align-items: center;
120
+ gap: 8px;
121
+ margin: 0 0 10px;
122
+ padding: 0;
123
+ color: var(--fg-muted);
124
+ font-family:
125
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
126
+ monospace;
127
+ font-size: 11px;
128
+ font-weight: 500;
129
+ letter-spacing: 0.14em;
130
+ text-transform: uppercase;
131
+ }
132
+
133
+ .eyebrow-dot {
134
+ width: 6px;
135
+ height: 6px;
136
+ background: var(--accent);
137
+ box-shadow: 0 0 10px var(--accent-line);
138
+ }
139
+
140
+ h1 {
141
+ margin: 0 0 8px;
142
+ color: var(--fg);
143
+ font-size: 26px;
144
+ line-height: 1.1;
145
+ font-weight: 600;
146
+ letter-spacing: -0.02em;
147
+ text-wrap: balance;
148
+ }
149
+
150
+ .meta {
151
+ display: flex;
152
+ flex-wrap: wrap;
153
+ align-items: baseline;
154
+ gap: 8px;
155
+ margin: 0;
156
+ color: var(--fg-muted);
157
+ font-size: 13px;
158
+ font-variant-numeric: tabular-nums;
159
+ }
160
+
161
+ .meta strong {
162
+ color: var(--fg);
163
+ font-weight: 600;
164
+ }
165
+
166
+ .dot-sep {
167
+ color: var(--fg-subtle);
168
+ }
169
+
170
+ /* ── Revision badge ──────────────────────────────────────── */
171
+
172
+ .revision {
173
+ display: inline-flex;
174
+ align-items: center;
175
+ gap: 8px;
176
+ padding: 5px 9px;
177
+ border: 1px solid var(--line-strong);
178
+ border-radius: var(--radius-xs);
179
+ background: rgba(255, 255, 255, 0.02);
180
+ color: var(--fg-muted);
181
+ font-family:
182
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
183
+ monospace;
184
+ font-size: 11px;
185
+ font-weight: 500;
186
+ letter-spacing: 0.04em;
187
+ }
188
+
189
+ .revision-pulse {
190
+ position: relative;
191
+ width: 6px;
192
+ height: 6px;
193
+ background: var(--ok);
194
+ box-shadow: 0 0 0 2px var(--ok-soft);
195
+ }
196
+
197
+ .revision-pulse::after {
198
+ content: '';
199
+ position: absolute;
200
+ inset: -3px;
201
+ border: 1px solid rgba(111, 207, 151, 0.55);
202
+ animation: pulse 2200ms var(--ease) infinite;
203
+ }
204
+
205
+ @keyframes pulse {
206
+ 0% {
207
+ transform: scale(0.6);
208
+ opacity: 0.9;
209
+ }
210
+ 100% {
211
+ transform: scale(2.4);
212
+ opacity: 0;
213
+ }
214
+ }
215
+
216
+ .revision-label {
217
+ color: var(--fg-muted);
218
+ text-transform: uppercase;
219
+ letter-spacing: 0.12em;
220
+ font-size: 10px;
221
+ }
222
+
223
+ .revision-num {
224
+ color: var(--accent-fg);
225
+ font-weight: 600;
226
+ }
227
+
228
+ /* ── Progress bar ────────────────────────────────────────── */
229
+
230
+ .progress {
231
+ position: relative;
232
+ height: 2px;
233
+ margin: 0 24px;
234
+ background: var(--line);
235
+ overflow: hidden;
236
+ }
237
+
238
+ .progress-fill {
239
+ display: block;
240
+ width: 100%;
241
+ height: 100%;
242
+ transform: scaleX(var(--progress, 0));
243
+ transform-origin: left center;
244
+ background: linear-gradient(90deg, var(--accent) 0%, var(--accent-bright) 100%);
245
+ transition: transform 360ms var(--ease);
246
+ will-change: transform;
247
+ }
248
+
249
+ /* ── Child component hosts ───────────────────────────────── */
250
+
251
+ cts-todo-stats,
252
+ cts-todo-filters,
253
+ cts-todo-item {
254
+ display: block;
255
+ }
256
+
257
+ /* ── Stats panel (Progress + Sprint goal) ────────────────── */
258
+
259
+ .stats-panel {
260
+ display: grid;
261
+ grid-template-columns: 1fr 1fr;
262
+ align-items: stretch;
263
+ gap: 10px;
264
+ padding: 18px 24px 0;
265
+ }
266
+
267
+ .stat-card {
268
+ position: relative;
269
+ display: grid;
270
+ grid-template-rows: auto auto auto 1fr;
271
+ gap: 10px;
272
+ min-width: 0;
273
+ padding: 14px 16px 14px;
274
+ border: 1px solid var(--line);
275
+ border-radius: var(--radius-md);
276
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.018) 0%, rgba(255, 255, 255, 0) 100%);
277
+ overflow: hidden;
278
+ }
279
+
280
+ .stat-card::before {
281
+ content: '';
282
+ position: absolute;
283
+ inset: 0;
284
+ pointer-events: none;
285
+ background: radial-gradient(80% 60% at 100% 0%, rgba(49, 120, 198, 0.05) 0%, transparent 60%);
286
+ opacity: 0.8;
287
+ transition: opacity var(--dur) var(--ease);
288
+ }
289
+
290
+ .stat-card.is-complete::before,
291
+ .stat-card.is-met::before {
292
+ background: radial-gradient(80% 60% at 100% 0%, rgba(49, 120, 198, 0.16) 0%, transparent 60%);
293
+ opacity: 1;
294
+ }
295
+
296
+ .stat-head {
297
+ display: flex;
298
+ align-items: center;
299
+ justify-content: space-between;
300
+ gap: 10px;
301
+ min-height: 24px;
302
+ }
303
+
304
+ .stat-label {
305
+ color: var(--fg-muted);
306
+ font-family:
307
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
308
+ monospace;
309
+ font-size: 10px;
310
+ font-weight: 500;
311
+ letter-spacing: 0.14em;
312
+ text-transform: uppercase;
313
+ white-space: nowrap;
314
+ }
315
+
316
+ .stat-tag {
317
+ display: inline-flex;
318
+ align-items: center;
319
+ gap: 6px;
320
+ height: 20px;
321
+ padding: 0 7px;
322
+ border: 1px solid var(--line-strong);
323
+ border-radius: var(--radius-xs);
324
+ background: rgba(255, 255, 255, 0.02);
325
+ color: var(--fg-subtle);
326
+ font-family:
327
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
328
+ monospace;
329
+ font-size: 9.5px;
330
+ font-weight: 500;
331
+ letter-spacing: 0.12em;
332
+ text-transform: uppercase;
333
+ white-space: nowrap;
334
+ }
335
+
336
+ .stat-tag::before {
337
+ content: '';
338
+ width: 5px;
339
+ height: 5px;
340
+ background: var(--fg-subtle);
341
+ }
342
+
343
+ .stat-tag[data-state='live']::before {
344
+ background: var(--accent);
345
+ box-shadow: 0 0 6px var(--accent-line);
346
+ }
347
+
348
+ .stat-tag[data-state='complete']::before {
349
+ background: var(--ok);
350
+ box-shadow: 0 0 6px var(--ok-soft);
351
+ }
352
+
353
+ .stat-tag[data-state='live'] {
354
+ color: var(--accent-fg);
355
+ border-color: var(--accent-line);
356
+ background: var(--accent-soft);
357
+ }
358
+
359
+ .stat-tag[data-state='complete'] {
360
+ color: var(--ok);
361
+ border-color: rgba(111, 207, 151, 0.35);
362
+ background: rgba(111, 207, 151, 0.1);
363
+ }
364
+
365
+ /* Progress display */
366
+
367
+ .progress-display {
368
+ display: inline-flex;
369
+ align-items: baseline;
370
+ gap: 0;
371
+ margin-top: 2px;
372
+ font-family:
373
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
374
+ monospace;
375
+ line-height: 0.95;
376
+ font-variant-numeric: tabular-nums;
377
+ }
378
+
379
+ .progress-num {
380
+ font-size: 48px;
381
+ font-weight: 600;
382
+ letter-spacing: -0.045em;
383
+ background: linear-gradient(180deg, var(--fg) 0%, #c8d3e0 100%);
384
+ -webkit-background-clip: text;
385
+ background-clip: text;
386
+ -webkit-text-fill-color: transparent;
387
+ color: var(--fg);
388
+ }
389
+
390
+ .is-complete .progress-num {
391
+ background: linear-gradient(180deg, #b6e6c8 0%, var(--ok) 100%);
392
+ -webkit-background-clip: text;
393
+ background-clip: text;
394
+ -webkit-text-fill-color: transparent;
395
+ }
396
+
397
+ .progress-num.is-empty {
398
+ color: var(--fg-subtle);
399
+ -webkit-text-fill-color: var(--fg-subtle);
400
+ background: none;
401
+ }
402
+
403
+ .progress-suffix {
404
+ margin-left: 2px;
405
+ color: var(--fg-subtle);
406
+ font-size: 22px;
407
+ font-weight: 500;
408
+ letter-spacing: -0.02em;
409
+ }
410
+
411
+ .is-complete .progress-suffix {
412
+ color: var(--ok);
413
+ }
414
+
415
+ /* Inline mini progress track inside the card */
416
+
417
+ .progress-track {
418
+ position: relative;
419
+ height: 3px;
420
+ border-radius: 1px;
421
+ background: var(--line);
422
+ overflow: hidden;
423
+ }
424
+
425
+ .progress-track-fill {
426
+ display: block;
427
+ width: 100%;
428
+ height: 100%;
429
+ transform: scaleX(var(--p, 0));
430
+ transform-origin: left center;
431
+ background: linear-gradient(90deg, var(--accent) 0%, var(--accent-bright) 100%);
432
+ transition: transform 420ms var(--ease);
433
+ will-change: transform;
434
+ }
435
+
436
+ .is-complete .progress-track-fill {
437
+ background: linear-gradient(90deg, var(--accent-bright) 0%, var(--ok) 100%);
438
+ }
439
+
440
+ /* Goal pips */
441
+
442
+ .goal-pips {
443
+ display: flex;
444
+ align-items: center;
445
+ flex-wrap: wrap;
446
+ gap: 5px;
447
+ margin-top: 2px;
448
+ min-height: 22px;
449
+ }
450
+
451
+ .pip {
452
+ width: 16px;
453
+ height: 16px;
454
+ border: 1px solid var(--line-strong);
455
+ background: rgba(255, 255, 255, 0.02);
456
+ transition:
457
+ background var(--dur-fast) var(--ease),
458
+ border-color var(--dur-fast) var(--ease),
459
+ box-shadow var(--dur-fast) var(--ease);
460
+ }
461
+
462
+ .pip.is-filled {
463
+ border-color: var(--accent);
464
+ background: linear-gradient(180deg, var(--accent-bright) 0%, var(--accent) 100%);
465
+ box-shadow: 0 0 0 1px rgba(49, 120, 198, 0.2);
466
+ }
467
+
468
+ .is-met .pip.is-filled {
469
+ border-color: var(--ok);
470
+ background: linear-gradient(180deg, #88d6a4 0%, var(--ok) 100%);
471
+ box-shadow: 0 0 0 1px rgba(111, 207, 151, 0.25);
472
+ }
473
+
474
+ .pip-more {
475
+ margin-left: 4px;
476
+ color: var(--fg-subtle);
477
+ font-family:
478
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
479
+ monospace;
480
+ font-size: 11px;
481
+ letter-spacing: 0.04em;
482
+ }
483
+
484
+ /* Captions used at the bottom of both cards */
485
+
486
+ .stat-caption {
487
+ display: inline-flex;
488
+ align-items: baseline;
489
+ flex-wrap: wrap;
490
+ gap: 6px;
491
+ margin: 0;
492
+ align-self: end;
493
+ color: var(--fg-muted);
494
+ font-family:
495
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
496
+ monospace;
497
+ font-size: 11.5px;
498
+ font-variant-numeric: tabular-nums;
499
+ letter-spacing: 0.01em;
500
+ }
501
+
502
+ .stat-caption strong {
503
+ color: var(--fg);
504
+ font-weight: 600;
505
+ }
506
+
507
+ .goal-sep {
508
+ color: var(--fg-subtle);
509
+ }
510
+
511
+ .goal-met {
512
+ color: var(--ok);
513
+ font-weight: 600;
514
+ letter-spacing: 0.04em;
515
+ text-transform: uppercase;
516
+ font-size: 11px;
517
+ }
518
+
519
+ .goal-over {
520
+ color: var(--accent-fg);
521
+ font-weight: 600;
522
+ }
523
+
524
+ /* Stepper */
525
+
526
+ .stepper {
527
+ display: inline-flex;
528
+ align-items: center;
529
+ gap: 0;
530
+ border: 1px solid var(--line-strong);
531
+ border-radius: var(--radius-xs);
532
+ background: rgba(255, 255, 255, 0.02);
533
+ overflow: hidden;
534
+ }
535
+
536
+ .stepper-btn {
537
+ width: 26px;
538
+ height: 24px;
539
+ padding: 0;
540
+ border: 0;
541
+ border-radius: 0;
542
+ background: transparent;
543
+ color: var(--fg-muted);
544
+ font-family:
545
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
546
+ monospace;
547
+ font-size: 14px;
548
+ font-weight: 600;
549
+ line-height: 1;
550
+ }
551
+
552
+ .stepper-btn:hover {
553
+ background: rgba(255, 255, 255, 0.06);
554
+ color: var(--fg);
555
+ }
556
+
557
+ .stepper-btn:disabled {
558
+ opacity: 0.35;
559
+ background: transparent;
560
+ }
561
+
562
+ .stepper-value {
563
+ display: inline-flex;
564
+ align-items: center;
565
+ justify-content: center;
566
+ min-width: 22px;
567
+ height: 24px;
568
+ padding: 0 4px;
569
+ border-left: 1px solid var(--line);
570
+ border-right: 1px solid var(--line);
571
+ color: var(--fg);
572
+ font-family:
573
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
574
+ monospace;
575
+ font-size: 11px;
576
+ font-weight: 600;
577
+ font-variant-numeric: tabular-nums;
578
+ }
579
+
580
+ /* ── Composer ────────────────────────────────────────────── */
581
+
582
+ .composer {
583
+ display: grid;
584
+ grid-template-columns: 1fr auto;
585
+ align-items: center;
586
+ gap: 10px;
587
+ padding: 18px 24px 20px;
588
+ border-bottom: 1px solid var(--line);
589
+ }
590
+
591
+ .composer-field {
592
+ position: relative;
593
+ display: grid;
594
+ grid-template-columns: auto 1fr auto;
595
+ align-items: center;
596
+ gap: 10px;
597
+ padding: 0 12px;
598
+ height: 42px;
599
+ border: 1px solid var(--line-strong);
600
+ border-radius: var(--radius-sm);
601
+ background: var(--bg);
602
+ transition:
603
+ border-color var(--dur) var(--ease),
604
+ box-shadow var(--dur) var(--ease);
605
+ }
606
+
607
+ .composer-field:focus-within {
608
+ border-color: var(--accent-line);
609
+ box-shadow: 0 0 0 3px var(--accent-soft);
610
+ }
611
+
612
+ .composer-icon {
613
+ display: inline-grid;
614
+ place-items: center;
615
+ width: 18px;
616
+ height: 18px;
617
+ color: var(--fg-subtle);
618
+ transition: color var(--dur) var(--ease);
619
+ }
620
+
621
+ .composer-field:focus-within .composer-icon {
622
+ color: var(--accent-fg);
623
+ }
624
+
625
+ .composer input {
626
+ width: 100%;
627
+ height: 100%;
628
+ border: 0;
629
+ padding: 0;
630
+ background: transparent;
631
+ color: var(--fg);
632
+ font: inherit;
633
+ font-size: 14px;
634
+ outline: none;
635
+ }
636
+
637
+ .composer input::placeholder {
638
+ color: var(--fg-subtle);
639
+ }
640
+
641
+ kbd {
642
+ display: inline-flex;
643
+ align-items: center;
644
+ height: 20px;
645
+ padding: 0 6px;
646
+ border: 1px solid var(--line-strong);
647
+ border-radius: var(--radius-xs);
648
+ background: rgba(255, 255, 255, 0.02);
649
+ color: var(--fg-muted);
650
+ font-family:
651
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
652
+ monospace;
653
+ font-size: 10px;
654
+ letter-spacing: 0.04em;
655
+ user-select: none;
656
+ }
657
+
658
+ /* ── Buttons ─────────────────────────────────────────────── */
659
+
660
+ button {
661
+ display: inline-flex;
662
+ align-items: center;
663
+ justify-content: center;
664
+ gap: 6px;
665
+ height: 38px;
666
+ padding: 0 14px;
667
+ border: 1px solid var(--line-strong);
668
+ border-radius: var(--radius-sm);
669
+ background: rgba(255, 255, 255, 0.02);
670
+ color: var(--fg);
671
+ font-family: inherit;
672
+ font-size: 13px;
673
+ font-weight: 500;
674
+ letter-spacing: -0.005em;
675
+ cursor: pointer;
676
+ transition:
677
+ border-color var(--dur-fast) var(--ease),
678
+ background var(--dur-fast) var(--ease),
679
+ color var(--dur-fast) var(--ease),
680
+ transform var(--dur-fast) var(--ease);
681
+ }
682
+
683
+ button:hover {
684
+ border-color: rgba(255, 255, 255, 0.18);
685
+ background: rgba(255, 255, 255, 0.04);
686
+ }
687
+
688
+ button:focus-visible {
689
+ outline: none;
690
+ border-color: var(--accent-line);
691
+ box-shadow: 0 0 0 3px var(--accent-soft);
692
+ }
693
+
694
+ button:active {
695
+ transform: translateY(1px);
696
+ }
697
+
698
+ button:disabled {
699
+ cursor: not-allowed;
700
+ opacity: 0.4;
701
+ transform: none;
702
+ }
703
+
704
+ button:disabled:hover {
705
+ border-color: var(--line-strong);
706
+ background: rgba(255, 255, 255, 0.02);
707
+ }
708
+
709
+ .primary {
710
+ border-color: var(--accent);
711
+ background: linear-gradient(180deg, var(--accent-bright) 0%, var(--accent) 100%);
712
+ color: #f4f8fd;
713
+ box-shadow:
714
+ 0 1px 0 rgba(255, 255, 255, 0.18) inset,
715
+ 0 6px 16px -8px rgba(49, 120, 198, 0.6);
716
+ }
717
+
718
+ .primary:hover {
719
+ border-color: var(--accent-bright);
720
+ background: linear-gradient(180deg, #5499e0 0%, var(--accent-bright) 100%);
721
+ }
722
+
723
+ .primary:disabled {
724
+ background: var(--accent-soft);
725
+ border-color: transparent;
726
+ color: var(--accent-fg);
727
+ box-shadow: none;
728
+ }
729
+
730
+ /* ── Filters ─────────────────────────────────────────────── */
731
+
732
+ .filters {
733
+ display: flex;
734
+ gap: 4px;
735
+ padding: 10px 18px;
736
+ border-bottom: 1px solid var(--line);
737
+ background: rgba(255, 255, 255, 0.012);
738
+ }
739
+
740
+ .filter {
741
+ position: relative;
742
+ height: 34px;
743
+ padding: 0 12px;
744
+ border: 0;
745
+ border-radius: var(--radius-xs);
746
+ background: transparent;
747
+ color: var(--fg-muted);
748
+ font-size: 12px;
749
+ font-weight: 500;
750
+ text-transform: capitalize;
751
+ }
752
+
753
+ .filter:hover {
754
+ background: rgba(255, 255, 255, 0.04);
755
+ color: var(--fg);
756
+ border-color: transparent;
757
+ }
758
+
759
+ .filter[aria-pressed='true'] {
760
+ color: var(--fg);
761
+ background: rgba(255, 255, 255, 0.05);
762
+ }
763
+
764
+ .filter[aria-pressed='true']::after {
765
+ content: '';
766
+ position: absolute;
767
+ left: 12px;
768
+ right: 12px;
769
+ bottom: -11px;
770
+ height: 1px;
771
+ background: var(--accent);
772
+ box-shadow: 0 0 8px 0 rgba(49, 120, 198, 0.55);
773
+ }
774
+
775
+ .filter-count {
776
+ display: inline-flex;
777
+ align-items: center;
778
+ height: 18px;
779
+ min-width: 18px;
780
+ padding: 0 5px;
781
+ border-radius: var(--radius-xs);
782
+ background: rgba(255, 255, 255, 0.05);
783
+ color: var(--fg-muted);
784
+ font-family:
785
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
786
+ monospace;
787
+ font-size: 10px;
788
+ font-weight: 500;
789
+ font-variant-numeric: tabular-nums;
790
+ }
791
+
792
+ .filter[aria-pressed='true'] .filter-count {
793
+ background: var(--accent-soft);
794
+ color: var(--accent-fg);
795
+ }
796
+
797
+ /* ── List ────────────────────────────────────────────────── */
798
+
799
+ .list {
800
+ margin: 0;
801
+ padding: 0;
802
+ list-style: none;
803
+ }
804
+
805
+ .row {
806
+ display: grid;
807
+ grid-template-columns: auto 1fr auto;
808
+ align-items: center;
809
+ gap: 14px;
810
+ padding: 14px 24px;
811
+ border-bottom: 1px solid var(--line);
812
+ transition: background var(--dur-fast) var(--ease);
813
+ animation: row-in 360ms var(--ease) both;
814
+ animation-delay: calc(var(--index, 0) * 40ms);
815
+ }
816
+
817
+ .row:last-child {
818
+ border-bottom: 0;
819
+ }
820
+
821
+ cts-todo-item .row:last-child {
822
+ border-bottom: 1px solid var(--line);
823
+ }
824
+
825
+ cts-todo-item:last-child .row:last-child {
826
+ border-bottom: 0;
827
+ }
828
+
829
+ .row:hover {
830
+ background: rgba(255, 255, 255, 0.02);
831
+ }
832
+
833
+ @keyframes row-in {
834
+ from {
835
+ opacity: 0;
836
+ transform: translateY(4px);
837
+ }
838
+ to {
839
+ opacity: 1;
840
+ transform: translateY(0);
841
+ }
842
+ }
843
+
844
+ /* Check button — sharp 2px square, GitHub-issue style */
845
+
846
+ .check {
847
+ width: 20px;
848
+ height: 20px;
849
+ min-width: 20px;
850
+ padding: 0;
851
+ border: 1px solid rgba(255, 255, 255, 0.2);
852
+ border-radius: var(--radius-xs);
853
+ background: rgba(255, 255, 255, 0.02);
854
+ color: transparent;
855
+ transition:
856
+ border-color var(--dur-fast) var(--ease),
857
+ background var(--dur-fast) var(--ease),
858
+ color var(--dur-fast) var(--ease),
859
+ box-shadow var(--dur-fast) var(--ease),
860
+ transform var(--dur-fast) var(--ease);
861
+ }
862
+
863
+ .check:hover {
864
+ border-color: var(--accent-line);
865
+ background: var(--accent-soft);
866
+ }
867
+
868
+ .check[aria-pressed='true'] {
869
+ border-color: var(--accent);
870
+ background: var(--accent);
871
+ color: #f4f8fd;
872
+ box-shadow: 0 0 0 3px var(--accent-soft);
873
+ }
874
+
875
+ .check[aria-pressed='true']:hover {
876
+ background: var(--accent-bright);
877
+ border-color: var(--accent-bright);
878
+ }
879
+
880
+ .check-tick {
881
+ display: grid;
882
+ place-items: center;
883
+ width: 100%;
884
+ height: 100%;
885
+ opacity: 0;
886
+ transform: scale(0.6);
887
+ transition:
888
+ opacity var(--dur-fast) var(--ease),
889
+ transform var(--dur) var(--ease);
890
+ }
891
+
892
+ .check[aria-pressed='true'] .check-tick {
893
+ opacity: 1;
894
+ transform: scale(1);
895
+ }
896
+
897
+ /* Task label */
898
+
899
+ .task-label {
900
+ min-width: 0;
901
+ font-size: 14px;
902
+ color: var(--fg);
903
+ letter-spacing: -0.005em;
904
+ }
905
+
906
+ input.task-label {
907
+ height: 32px;
908
+ width: 100%;
909
+ padding: 0 8px;
910
+ margin: 0 -8px;
911
+ border: 1px solid transparent;
912
+ border-radius: var(--radius-xs);
913
+ background: transparent;
914
+ color: var(--fg);
915
+ font: inherit;
916
+ font-size: 14px;
917
+ outline: none;
918
+ transition:
919
+ border-color var(--dur-fast) var(--ease),
920
+ background var(--dur-fast) var(--ease);
921
+ }
922
+
923
+ input.task-label:hover {
924
+ border-color: var(--line-strong);
925
+ background: rgba(255, 255, 255, 0.02);
926
+ }
927
+
928
+ input.task-label:focus {
929
+ border-color: var(--accent-line);
930
+ background: var(--bg);
931
+ box-shadow: 0 0 0 3px var(--accent-soft);
932
+ }
933
+
934
+ .task-label.is-done {
935
+ color: var(--fg-subtle);
936
+ text-decoration: line-through;
937
+ text-decoration-color: rgba(91, 99, 112, 0.6);
938
+ text-decoration-thickness: 1px;
939
+ overflow: hidden;
940
+ text-overflow: ellipsis;
941
+ white-space: nowrap;
942
+ }
943
+
944
+ /* Remove button */
945
+
946
+ .remove {
947
+ width: 30px;
948
+ height: 30px;
949
+ padding: 0;
950
+ border-color: transparent;
951
+ background: transparent;
952
+ color: var(--fg-subtle);
953
+ opacity: 0;
954
+ transform: translateX(-2px);
955
+ transition:
956
+ opacity var(--dur-fast) var(--ease),
957
+ transform var(--dur-fast) var(--ease),
958
+ color var(--dur-fast) var(--ease),
959
+ background var(--dur-fast) var(--ease),
960
+ border-color var(--dur-fast) var(--ease);
961
+ }
962
+
963
+ .row:hover .remove,
964
+ .row:focus-within .remove,
965
+ .remove:focus-visible {
966
+ opacity: 1;
967
+ transform: translateX(0);
968
+ }
969
+
970
+ .remove:hover {
971
+ color: var(--danger);
972
+ background: var(--danger-soft);
973
+ border-color: rgba(196, 106, 106, 0.25);
974
+ }
975
+
976
+ .remove svg {
977
+ display: block;
978
+ }
979
+
980
+ /* ── Empty state ─────────────────────────────────────────── */
981
+
982
+ .empty {
983
+ display: grid;
984
+ justify-items: center;
985
+ gap: 8px;
986
+ padding: 56px 22px 64px;
987
+ text-align: center;
988
+ }
989
+
990
+ .empty-icon {
991
+ display: inline-grid;
992
+ place-items: center;
993
+ width: 56px;
994
+ height: 56px;
995
+ margin-bottom: 6px;
996
+ border-radius: var(--radius-md);
997
+ background: rgba(255, 255, 255, 0.025);
998
+ border: 1px solid var(--line-strong);
999
+ color: var(--fg-muted);
1000
+ }
1001
+
1002
+ .empty-title {
1003
+ margin: 0;
1004
+ color: var(--fg);
1005
+ font-size: 14px;
1006
+ font-weight: 600;
1007
+ letter-spacing: -0.005em;
1008
+ }
1009
+
1010
+ .empty-sub {
1011
+ margin: 0;
1012
+ max-width: 32ch;
1013
+ color: var(--fg-muted);
1014
+ font-size: 13px;
1015
+ line-height: 1.55;
1016
+ text-wrap: balance;
1017
+ }
1018
+
1019
+ /* ── Footer ──────────────────────────────────────────────── */
1020
+
1021
+ .footer {
1022
+ display: flex;
1023
+ align-items: center;
1024
+ justify-content: space-between;
1025
+ flex-wrap: wrap;
1026
+ gap: 10px 12px;
1027
+ padding: 14px 18px 14px 24px;
1028
+ border-top: 1px solid var(--line);
1029
+ background: rgba(0, 0, 0, 0.18);
1030
+ }
1031
+
1032
+ .footer-stats {
1033
+ display: inline-flex;
1034
+ align-items: baseline;
1035
+ gap: 6px;
1036
+ color: var(--fg-muted);
1037
+ font-family:
1038
+ 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Courier New',
1039
+ monospace;
1040
+ font-size: 12px;
1041
+ font-variant-numeric: tabular-nums;
1042
+ }
1043
+
1044
+ .footer-stats strong {
1045
+ color: var(--fg);
1046
+ font-size: 14px;
1047
+ font-weight: 600;
1048
+ }
1049
+
1050
+ .footer-divider {
1051
+ color: var(--fg-subtle);
1052
+ }
1053
+
1054
+ .footer-label {
1055
+ margin-left: 4px;
1056
+ color: var(--fg-subtle);
1057
+ font-family:
1058
+ 'Geist',
1059
+ 'Inter',
1060
+ ui-sans-serif,
1061
+ system-ui,
1062
+ -apple-system,
1063
+ 'Segoe UI',
1064
+ Roboto,
1065
+ 'Helvetica Neue',
1066
+ Arial,
1067
+ sans-serif;
1068
+ font-size: 12px;
1069
+ letter-spacing: 0.01em;
1070
+ }
1071
+
1072
+ .footer-actions {
1073
+ display: flex;
1074
+ gap: 6px;
1075
+ margin-left: auto;
1076
+ }
1077
+
1078
+ .footer-actions button {
1079
+ height: 32px;
1080
+ padding: 0 11px;
1081
+ font-size: 12px;
1082
+ background: transparent;
1083
+ border-color: transparent;
1084
+ color: var(--fg-muted);
1085
+ white-space: nowrap;
1086
+ }
1087
+
1088
+ .footer-actions button:hover {
1089
+ border-color: var(--line-strong);
1090
+ background: rgba(255, 255, 255, 0.04);
1091
+ color: var(--fg);
1092
+ }
1093
+
1094
+ /* ── Responsive ──────────────────────────────────────────── */
1095
+
1096
+ @media (max-width: 560px) {
1097
+ :host {
1098
+ width: 100%;
1099
+ }
1100
+
1101
+ .header,
1102
+ .stats-panel,
1103
+ .composer,
1104
+ .footer,
1105
+ .row {
1106
+ padding-right: 16px;
1107
+ padding-left: 16px;
1108
+ }
1109
+
1110
+ .header {
1111
+ grid-template-columns: 1fr;
1112
+ gap: 12px;
1113
+ }
1114
+
1115
+ .revision {
1116
+ justify-self: start;
1117
+ }
1118
+
1119
+ .composer {
1120
+ grid-template-columns: 1fr;
1121
+ }
1122
+
1123
+ .stats-panel {
1124
+ grid-template-columns: 1fr;
1125
+ }
1126
+
1127
+ .filters {
1128
+ padding-right: 10px;
1129
+ padding-left: 10px;
1130
+ }
1131
+
1132
+ .progress {
1133
+ margin: 0 16px;
1134
+ }
1135
+
1136
+ kbd {
1137
+ display: none;
1138
+ }
1139
+ }
1140
+
1141
+ @media (prefers-reduced-motion: reduce) {
1142
+ .shell,
1143
+ .row {
1144
+ animation: none;
1145
+ }
1146
+ .revision-pulse::after {
1147
+ animation: none;
1148
+ display: none;
1149
+ }
1150
+ }