@telepath-computer/television 0.1.177 → 0.1.178

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.
@@ -173,14 +173,20 @@ const ICONS = {
173
173
  regular: `<path d="M104,40H56A16,16,0,0,0,40,56v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,104,40Zm0,64H56V56h48v48Zm96-64H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,64H152V56h48v48Zm-96,32H56a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,104,136Zm0,64H56V152h48v48Zm96-64H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,200,136Zm0,64H152V152h48v48Z"/>`,
174
174
  duotone: `<path d="M112,56v48a8,8,0,0,1-8,8H56a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8h48A8,8,0,0,1,112,56Zm88-8H152a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V56A8,8,0,0,0,200,48Zm-96,96H56a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V152A8,8,0,0,0,104,144Zm96,0H152a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V152A8,8,0,0,0,200,144Z" opacity="0.2"/><path d="M200,136H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,200,136Zm0,64H152V152h48v48ZM104,40H56A16,16,0,0,0,40,56v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,104,40Zm0,64H56V56h48v48Zm96-64H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,64H152V56h48v48Zm-96,32H56a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,104,136Zm0,64H56V152h48v48Z"/>`,
175
175
  },
176
+ "calendar-blank": {
177
+ regular: `<path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Z"/>`,
178
+ },
179
+ hash: {
180
+ regular: `<path d="M224,88H175.4l8.47-46.57a8,8,0,0,0-15.74-2.86l-9,49.43H111.4l8.47-46.57a8,8,0,0,0-15.74-2.86L95.14,88H48a8,8,0,0,0,0,16H92.23L83.5,152H32a8,8,0,0,0,0,16H80.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,80,224a8,8,0,0,0,7.86-6.57l9-49.43H144.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,144,224a8,8,0,0,0,7.86-6.57l9-49.43H208a8,8,0,0,0,0-16H163.77l8.73-48H224a8,8,0,0,0,0-16Zm-76.5,64H99.77l8.73-48h47.73Z"/>`,
181
+ },
176
182
  };
177
183
  const STYLES = `
178
184
  :host {
179
185
  display: inline-flex;
180
- width: var(--ui-icon-size, 1em);
181
- height: var(--ui-icon-size, 1em);
186
+ width: var(--tv-icon-size, 1em);
187
+ height: var(--tv-icon-size, 1em);
182
188
  vertical-align: -0.125em;
183
- color: var(--ui-icon-color, currentColor);
189
+ color: var(--tv-icon-color, currentColor);
184
190
  }
185
191
 
186
192
  svg {
@@ -191,7 +197,7 @@ const STYLES = `
191
197
  }
192
198
  `;
193
199
  function isIconName(value) {
194
- return value === "plus" || value === "subtract-square" || value === "squares-four" || value === "television";
200
+ return value === "plus" || value === "subtract-square" || value === "squares-four" || value === "television" || value === "calendar-blank" || value === "hash";
195
201
  }
196
202
  function isIconWeight(value) {
197
203
  return value === "regular" || value === "bold" || value === "duotone";
@@ -219,7 +225,7 @@ export class IconElement extends HTMLElement {
219
225
  <style>${STYLES}</style>
220
226
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" focusable="false">${source}</svg>
221
227
  `;
222
- this.style.setProperty("--ui-icon-size", `${size}px`);
228
+ this.style.setProperty("--tv-icon-size", `${size}px`);
223
229
  }
224
230
  #name() {
225
231
  const value = this.getAttribute("name");
@@ -234,7 +240,7 @@ export class IconElement extends HTMLElement {
234
240
  return value === MEDIUM_ICON_SIZE || value === LARGE_ICON_SIZE ? value : DEFAULT_ICON_SIZE;
235
241
  }
236
242
  }
237
- if (!customElements.get("ui-icon")) {
238
- customElements.define("ui-icon", IconElement);
243
+ if (!customElements.get("tv-icon")) {
244
+ customElements.define("tv-icon", IconElement);
239
245
  }
240
246
 
@@ -327,7 +327,11 @@ body {
327
327
  line-height: var(--leading-base);
328
328
  letter-spacing: 0.015em;
329
329
  color: var(--color-text);
330
- background: var(--color-bg);
330
+ /* The artifact page is the card surface — Television's artifact-view frames
331
+ the whole document as a card, so the default page background is the
332
+ surface color. A muted backdrop (--color-bg) is something an artifact
333
+ opts into, not the default. */
334
+ background: var(--color-surface);
331
335
  -webkit-font-smoothing: antialiased;
332
336
  -moz-osx-font-smoothing: grayscale;
333
337
  }
@@ -378,6 +382,18 @@ table {
378
382
  margin-top: var(--space-8);
379
383
  }
380
384
 
385
+ /* Page-header idiom: a <header> holds a heading and optionally one <p>
386
+ subtitle — a date line, a description. A paragraph inside any header is a
387
+ subtitle: slightly smaller than body prose, at body weight, hugging its
388
+ heading — proximity and the heading above do the differentiating. */
389
+ header p {
390
+ font-size: var(--text-base);
391
+ font-weight: var(--font-weight-body);
392
+ }
393
+ header :is(h1, h2, h3, h4, h5, h6) + p {
394
+ margin-top: var(--space-2);
395
+ }
396
+
381
397
  ul,
382
398
  ol {
383
399
  padding-left: var(--space-24);
package/dist/cli.cjs CHANGED
@@ -52695,8 +52695,8 @@ var Server = class {
52695
52695
  };
52696
52696
  };
52697
52697
  function readServerPackageVersion() {
52698
- if ("0.1.177".length > 0) {
52699
- return telemetryVersion("0.1.177");
52698
+ if ("0.1.178".length > 0) {
52699
+ return telemetryVersion("0.1.178");
52700
52700
  }
52701
52701
  const packageJsonPath = import_node_path11.default.resolve(import_node_path11.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
52702
52702
  if (!(0, import_node_fs9.existsSync)(packageJsonPath)) return telemetryVersion("0.0.0");
@@ -53769,8 +53769,8 @@ function resolveVercelSkillsInstallerBin() {
53769
53769
  return localRequire.resolve("skills/bin/cli.mjs");
53770
53770
  }
53771
53771
  function readCLIVersion() {
53772
- if ("0.1.177".length > 0) {
53773
- return "0.1.177";
53772
+ if ("0.1.178".length > 0) {
53773
+ return "0.1.178";
53774
53774
  }
53775
53775
  const devPackageJsonPath = import_node_path15.default.join(getDevPackageDir(), "package.json");
53776
53776
  if (!(0, import_node_fs12.existsSync)(devPackageJsonPath)) {
@@ -23,7 +23,7 @@
23
23
  font-size: 15px;
24
24
  }
25
25
 
26
- ui-icon {
26
+ tv-icon {
27
27
  position: relative;
28
28
  top: 1px;
29
29
  color: var(--color-text-muted);
@@ -181,9 +181,9 @@
181
181
 
182
182
  <p class="lede">Television is the missing GUI for your personal agent, created by the team at Telepath. We’re glad you’re here.</p>
183
183
 
184
- <p>This card is an <ui-icon name="subtract-square" weight="duotone"></ui-icon> <strong>Artifact</strong>: a live surface your agent can create, edit, and update. Artifacts can be notes, editable documents, dashboards, web pages, reports, or custom web apps.</p>
184
+ <p>This card is an <tv-icon name="subtract-square" weight="duotone"></tv-icon> <strong>Artifact</strong>: a live surface your agent can create, edit, and update. Artifacts can be notes, editable documents, dashboards, web pages, reports, or custom web apps.</p>
185
185
 
186
- <p>Artifacts live on <ui-icon name="squares-four" weight="duotone"></ui-icon> <strong>Screens</strong>. We put you on <strong>TV Guide</strong> to start. Double-click its name to rename it, use <ui-icon name="plus" weight="bold"></ui-icon> to add more screens, and drag artifacts to arrange them. Your open screens appear as tabs up the top. If you close one, you can find it again by clicking <ui-icon name="squares-four" weight="bold"></ui-icon>.</p>
186
+ <p>Artifacts live on <tv-icon name="squares-four" weight="duotone"></tv-icon> <strong>Screens</strong>. We put you on <strong>TV Guide</strong> to start. Double-click its name to rename it, use <tv-icon name="plus" weight="bold"></tv-icon> to add more screens, and drag artifacts to arrange them. Your open screens appear as tabs up the top. If you close one, you can find it again by clicking <tv-icon name="squares-four" weight="bold"></tv-icon>.</p>
187
187
 
188
188
  <p>To create or change an artifact, just describe what you want to see. Try asking your agent…</p>
189
189
 
@@ -494,6 +494,29 @@ blockquotes, rules, images, and tables have readable defaults. Add local CSS for
494
494
  artifact-specific layout, density, hierarchy, or specialized presentations; do
495
495
  not recreate baseline prose styling in every artifact.
496
496
 
497
+ ## Page header
498
+
499
+ A page header is optional — the Television card already names the artifact in
500
+ its title bar, so don't add one just to repeat that. Add one when the document
501
+ benefits from its own masthead: a title that differs from or says more than
502
+ the card name, or a subtitle line carrying context like a date, a person, or
503
+ a status.
504
+
505
+ When you do, use the house header idiom — a `<header>` holding an `<h1>` and
506
+ optionally one `<p>` subtitle:
507
+
508
+ ```html
509
+ <header>
510
+ <h1>Quarterly plan</h1>
511
+ <p>Friday, June 26</p>
512
+ </header>
513
+ ```
514
+
515
+ A `<p>` inside any `<header>` renders as a subtitle — slightly smaller than
516
+ body prose, at body weight, hugging its heading; proximity and the heading
517
+ above do the differentiating. The idiom is house language: use it as-is,
518
+ don't restyle it per artifact.
519
+
497
520
  For prose-like documents, consider giving the content enough room above the bottom title bar. A reasonable starting point is 32px padding on the top and sides, with 64px at the bottom; adjust based on the content and layout:
498
521
 
499
522
  ```css
@@ -569,6 +592,23 @@ checkbox-item::part(box) {
569
592
  }
570
593
  ```
571
594
 
595
+ ### Icons
596
+
597
+ `<tv-icon name="…" size="16|24|32">` renders a glyph from the canonical
598
+ catalog. The catalog is small and closed — these are the only valid names:
599
+
600
+ - `calendar-blank`
601
+ - `hash`
602
+ - `plus`
603
+ - `squares-four`
604
+ - `subtract-square`
605
+ - `television`
606
+
607
+ Do not invent other names (an unknown name silently renders the
608
+ `subtract-square` fallback, which looks like a broken glyph). If no glyph
609
+ fits, use text without an icon; if a glyph is genuinely needed and none
610
+ fits, use an emoji instead of `<tv-icon>`.
611
+
572
612
  ## Viewport
573
613
 
574
614
  Artifacts render inside a resizable Television card, not a full browser page.
@@ -0,0 +1,241 @@
1
+ ---
2
+ name: tv-tasks
3
+ description: Create to-do list HTML artifacts for Television, to visualize tasks, plans, check them off, etc.
4
+ ---
5
+
6
+ # Authoring a task list
7
+
8
+ Know the main `television` skill first (re-read only if it's not in context or
9
+ changed) — it covers the Television workflow, artifact registration, and the
10
+ canonical HTML house style. This skill only adds the task-list vocabulary.
11
+
12
+ A calm, scannable to-do list: sections of task rows, each with a checkbox,
13
+ title, optional note, and a metadata line (due date, project, tags). You author
14
+ plain `tv-task-*` markup; the CSS and two small components do the rest.
15
+
16
+ ## Load these
17
+
18
+ Every artifact is self-contained. Carry `task.css` and `task.js` from this skill
19
+ folder into the output (don't reference `/skills/tv-tasks/…` URLs).
20
+
21
+ For a directory artifact, copy both next to `index.html` and load them with the
22
+ canonical boilerplate:
23
+
24
+ ```html
25
+ <link rel="stylesheet" href="/canonical/v1/styles.css" />
26
+ <script type="module" src="/canonical/v1/components.js"></script>
27
+ <link rel="stylesheet" href="./task.css" />
28
+ <script type="module" src="./task.js"></script>
29
+ ```
30
+
31
+ For a single-file artifact, inline `task.css`/`task.js` into `<style>` and
32
+ `<script type="module">` tags instead. `components.js` gives you `<tv-icon>`;
33
+ `task.js` registers `<tv-task-checkbox>` and `<tv-task-meta-due>`.
34
+
35
+ A task list is not a prose document: don't add `body` padding (the house
36
+ style doc suggests it for prose) — `tv-task-list` and the page header supply
37
+ their own gutters. Leave the body at zero.
38
+
39
+ ## Markup
40
+
41
+ Author this shape — a page header, then a list of sections and rows:
42
+
43
+ ```html
44
+ <header>
45
+ <h1>Television</h1>
46
+ <p>Wednesday, June 25</p>
47
+ </header>
48
+
49
+ <tv-task-list>
50
+ <tv-task-section>
51
+ <header><h2>Today</h2></header>
52
+
53
+ <tv-task>
54
+ <tv-task-checkbox></tv-task-checkbox>
55
+ <tv-task-body>
56
+ <tv-task-title>Review the task-list example</tv-task-title>
57
+ <tv-task-note>Density, grouping, and Things-like hierarchy.</tv-task-note>
58
+ <tv-task-meta>
59
+ <tv-task-meta-due date="2026-06-26"></tv-task-meta-due>
60
+ <tv-task-meta-item><tv-icon name="hash"></tv-icon>TV Skills</tv-task-meta-item>
61
+ <tv-task-meta-tag>design</tv-task-meta-tag>
62
+ </tv-task-meta>
63
+ </tv-task-body>
64
+ </tv-task>
65
+ </tv-task-section>
66
+ </tv-task-list>
67
+ ```
68
+
69
+ ## Components
70
+
71
+ ### `<tv-task-list>`
72
+ The list root. One per view, holding the sections (or bare rows). It draws no
73
+ frame of its own — the page is the surface, and Television supplies the card
74
+ chrome around the artifact.
75
+
76
+ ### Headers — `<header>` + `<h1>`/`<h2>` + optional `<p>`
77
+
78
+ A page title is optional — the Television card already names the artifact, so
79
+ a bare list is fine. Add one when the list benefits from its own masthead: a
80
+ date line ("Today" lists usually carry the date), a person, a context the
81
+ card name doesn't say. When you do, use the house header idiom *above*
82
+ `<tv-task-list>` (not inside it): a `<header>` with an `<h1>` and optionally
83
+ a `<p>` subtitle. House styles own its look; the skill only aligns it with
84
+ the list. Title each section with an `<h2>` inside the section's own
85
+ `<header>`. Keep these as real HTML headings, not `tv-task-*`, so the
86
+ document outline stays navigable.
87
+
88
+ ### `<tv-task-section>`
89
+ A titled group of rows: a `<header>` then the `<tv-task>`s. Group by one
90
+ meaningful axis — a when-bucket (Today / Upcoming / Someday), a project, or an
91
+ area. A few purposeful groups read well; one section per task defeats the point,
92
+ and a flat handful of tasks needs no sections at all. Don't render a section
93
+ with no tasks — omit it entirely unless the user asked to see the empty group.
94
+
95
+ ### `<tv-task-count>`
96
+ An optional task count beside a section heading:
97
+ `<header><h2>Today</h2><tv-task-count>3</tv-task-count></header>`. Renders
98
+ inline right of the heading — muted, unbolded, never floated to the far edge.
99
+ You write the number; nothing counts for you — keep it true to the rows
100
+ actually in the section, and skip it when it adds nothing.
101
+
102
+ ### `<tv-task>`
103
+ One task row — a `<tv-task-checkbox>` followed by a `<tv-task-body>`. Give it a
104
+ plain `id` if you'll wire interactivity. One per task.
105
+
106
+ Add `highlighted` to emphasize a row: a soft wash, amber by default. The hue
107
+ is one knob — `--task-highlight` — settable inline for a single row (`<tv-task highlighted style="--task-highlight: #7C5CFF">`)
108
+ or on an ancestor to retint every highlight in the list. Highlight only with
109
+ a good reason: the user asked for it, or the emphasis clearly benefits them
110
+ (the one row they should see first). At more than one or two per list the
111
+ emphasis stops meaning anything.
112
+
113
+ ### `<tv-task-checkbox>`
114
+ The circular checkbox at the head of each row; every row has exactly one. Add
115
+ `checked` to mark a task done (the row strikes through and mutes) or `disabled`
116
+ to mute a row without completing it. No label — it takes its accessible name from
117
+ the row's `<tv-task-title>`. On user toggle it emits a bubbling `toggle` event
118
+ and exposes a `checked` property (see Interactivity).
119
+
120
+ ### `<tv-task-body>`
121
+ The content column right of the checkbox — wraps the title, note, and meta. One
122
+ per row, always present.
123
+
124
+ ### `<tv-task-title>`
125
+ The task's name, as plain text. Required, one per row; keep it a short imperative
126
+ line.
127
+
128
+ ### `<tv-task-note>`
129
+ A brief, muted one-line description under the title. Optional — add it only when
130
+ the title doesn't say enough, and keep it to a single line. Most tasks are
131
+ clearer without one.
132
+
133
+ ### `<tv-task-meta>`
134
+ The metadata line under the title/note. It holds any mix of the three chips
135
+ below, in any order. Include it only when the task has metadata — otherwise omit
136
+ it.
137
+
138
+ ### `<tv-task-meta-due>`
139
+ A due date: `<tv-task-meta-due date="YYYY-MM-DD">`. Give it only the machine
140
+ date — it computes urgency (today → amber, overdue → red, upcoming → muted),
141
+ writes the human label ("Today", "Tomorrow", "Jun 20"), and draws the calendar
142
+ glyph. Never hand-format the date or set a color. At most one per task.
143
+
144
+ ### `<tv-task-meta-item>`
145
+ The escape hatch for metadata that isn't a date or tag — an `<tv-icon>` plus
146
+ text, like a project or context:
147
+
148
+ ```html
149
+ <tv-task-meta-item><tv-icon name="hash"></tv-icon>TV Skills</tv-task-meta-item>
150
+ ```
151
+
152
+ Useful, but restrained: one or two at most. A row crowded with chips stops being
153
+ scannable.
154
+
155
+ ### `<tv-task-meta-tag>`
156
+
157
+ A small pill for a label or tag: `<tv-task-meta-tag>design</tv-task-meta-tag>`.
158
+ Repeatable, but a few keywords — not a tag soup. Two or three sharpen a row; six
159
+ bury it.
160
+
161
+ ### `<tv-task-placeholder>`
162
+
163
+ A centered, muted empty-state message:
164
+ `<tv-task-placeholder>Nothing here yet</tv-task-placeholder>`. Rarely needed —
165
+ by default an empty section simply isn't rendered (see `<tv-task-section>`).
166
+ Use it only when showing the empty group is the point — the user asked to see
167
+ it, or an interactive list empties itself and the space must not collapse.
168
+
169
+ ## Interactivity
170
+
171
+ - On user toggle a checkbox emits a bubbling `toggle` event; read the new state
172
+ from `e.checked`. Setting `el.checked = true/false` programmatically reflects
173
+ the attribute (completes/uncompletes the row) but emits nothing.
174
+ - Listen once on the list and delegate. Give each `<tv-task>` a plain `id` and
175
+ identify the toggled task with `e.target.closest("tv-task").id`.
176
+ - Nothing is persisted — wire the handler to whatever you want (localStorage, a
177
+ `fetch` to your own API, …). On load, restore state by setting `el.checked`.
178
+
179
+ ```js
180
+ const list = document.querySelector("tv-task-list");
181
+ list.addEventListener("toggle", (e) => {
182
+ const id = e.target.closest("tv-task").id;
183
+ save(id, e.checked); // your persistence; nothing is stored for you
184
+ });
185
+ ```
186
+
187
+ ## Example variations
188
+
189
+ Completed and disabled rows:
190
+
191
+ ```html
192
+ <tv-task>
193
+ <tv-task-checkbox checked></tv-task-checkbox>
194
+ <tv-task-body><tv-task-title>Ship the icon migration</tv-task-title></tv-task-body>
195
+ </tv-task>
196
+ <tv-task>
197
+ <tv-task-checkbox disabled></tv-task-checkbox>
198
+ <tv-task-body><tv-task-title>Blocked on review</tv-task-title></tv-task-body>
199
+ </tv-task>
200
+ ```
201
+
202
+ An overdue date and multiple tags — same markup, the component colors it:
203
+
204
+ ```html
205
+ <tv-task-meta>
206
+ <tv-task-meta-due date="2026-06-10"></tv-task-meta-due>
207
+ <tv-task-meta-tag>urgent</tv-task-meta-tag>
208
+ <tv-task-meta-tag>backend</tv-task-meta-tag>
209
+ </tv-task-meta>
210
+ ```
211
+
212
+ A highlighted row, retinted to match the artifact's accent:
213
+
214
+ ```html
215
+ <tv-task highlighted style="--task-highlight: #7C5CFF">
216
+ <tv-task-checkbox></tv-task-checkbox>
217
+ <tv-task-body><tv-task-title>Sign the lease by Friday</tv-task-title></tv-task-body>
218
+ </tv-task>
219
+ ```
220
+
221
+ Dark mode — set `data-theme="dark"` on `<html>` (the page header and body
222
+ background live outside the list, so scoping dark to the list would leave
223
+ them light):
224
+
225
+ ```html
226
+ <html data-theme="dark">
227
+
228
+ </html>
229
+ ```
230
+
231
+ ## Guidelines
232
+
233
+ - **Icons are always `<tv-icon name="…">`** — never a span or inline SVG. In
234
+ metadata use `calendar-blank` (handled by the due component) and `hash`
235
+ (project); for other `tv-task-meta-item`s pick from the canonical icon
236
+ catalog (listed in the main `television` skill) — names outside it render
237
+ a broken-looking fallback.
238
+ - **Sizing is `size="16|24|32"`** on `<tv-icon>` — no em or inline sizes.
239
+ - **Rows are flat by default** — no backdrop, no dividers; whitespace does
240
+ the separating. If you restyle rows (background, radius, spacing), keep
241
+ that model in mind — deliberate, list-wide changes, not per-row chrome.
@@ -0,0 +1,205 @@
1
+ /*
2
+ * Task styles for the tv-tasks artifact skill.
3
+ * Targets the `tv-task-*` element/attribute vocabulary (no classes); uses
4
+ * canonical design tokens (--color-*, --space-*, --radius-*, --text-*).
5
+ *
6
+ * Behavioral parts live in their own web components' shadow DOM:
7
+ * <tv-task-checkbox> — the circular pop checkbox + focus ring
8
+ * <tv-task-meta-due> — the due-date glyph + label; task.css only colors it.
9
+ */
10
+
11
+ :root {
12
+ /* The amber for a task due today. Not a canonical token, so it gets one
13
+ named source here rather than being sprinkled as a literal. */
14
+ --task-due-today: #EA880B;
15
+ /* Highlight hue — the highlighted row's wash derives from this one knob.
16
+ Set it on any ancestor (or the row itself) to retint. */
17
+ --task-highlight: var(--task-due-today);
18
+ /* Skill-local muted: a shade darker than the canonical --color-text-muted
19
+ (neutral-500, L 0.553) so notes hold their own against the darker meta
20
+ treatment below. Declared at :root, so it recolors ALL muted text on the
21
+ page (canonical components included) — deliberate for a single-skill
22
+ artifact; revisit if skills ever share a page. */
23
+ --color-text-muted: oklch(0.52 0 0);
24
+ /* The meta line recedes a step behind the note: 85% of muted. */
25
+ --task-meta-text: color-mix(in srgb, var(--color-text-muted) 85%, transparent);
26
+ }
27
+
28
+ /* One task row — a checkbox column + the body column. */
29
+ tv-task {
30
+ display: grid;
31
+ grid-template-columns: 18px 1fr;
32
+ column-gap: var(--space-12, 12px);
33
+ padding: var(--space-8, 8px) var(--space-4, 4px);
34
+ }
35
+
36
+ /* The content column of a row (title / note / meta). */
37
+ tv-task-body {
38
+ display: block;
39
+ }
40
+
41
+ tv-task-title {
42
+ display: block;
43
+ font-weight: var(--font-weight-body);
44
+ line-height: 1.35;
45
+ }
46
+ /* Completed — tracks the checkbox's reflected host attribute live, not a
47
+ static class. (The real <input> lives in the component's shadow DOM, where
48
+ :has() can't reach it.) */
49
+ tv-task:has(tv-task-checkbox[checked]) tv-task-title {
50
+ color: var(--color-text-muted);
51
+ text-decoration: line-through;
52
+ }
53
+ /* A completed task's due date no longer matters — always mute it, overriding
54
+ the today/overdue emphasis. */
55
+ tv-task:has(tv-task-checkbox[checked]) tv-task-meta-due {
56
+ color: var(--task-meta-text);
57
+ }
58
+
59
+ tv-task-note {
60
+ display: block;
61
+ color: var(--color-text-muted);
62
+ font-size: calc(var(--text-sm) + 1px);
63
+ line-height: 1.4;
64
+ margin-top: 0;
65
+ }
66
+
67
+ /* Shared metadata line under the title/note — holds the due date, project,
68
+ and label pills. */
69
+ tv-task-meta {
70
+ display: flex;
71
+ flex-wrap: wrap;
72
+ align-items: center;
73
+ gap: var(--space-8, 8px);
74
+ margin-top: var(--space-2, 2px);
75
+ font-size: var(--text-sm);
76
+ color: var(--task-meta-text);
77
+ }
78
+
79
+ /* Generic icon + text meta item (project, area, …). The <tv-task-meta-due>
80
+ component supplies its own inline-flex/gap/icon, so it isn't styled here
81
+ beyond color. */
82
+ tv-task-meta-item {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ gap: var(--space-2, 2px);
86
+ white-space: nowrap;
87
+ }
88
+
89
+ /* Due-date urgency — the <tv-task-meta-due> component reflects its computed
90
+ state; task.css only colors it. Default / upcoming stay muted (inherited
91
+ from tv-task-meta). */
92
+ tv-task-meta-due[state="today"] { color: var(--task-due-today); }
93
+ tv-task-meta-due[state="overdue"] { color: var(--color-danger); }
94
+
95
+ /* Label pill — labels, tags, and contexts are the same thing. */
96
+ tv-task-meta-tag {
97
+ display: inline-flex;
98
+ align-items: center;
99
+ height: 18px;
100
+ padding: 0 5px;
101
+ padding-bottom: 1px;
102
+ border-radius: 6px;
103
+ background: var(--color-bg-muted);
104
+ color: var(--task-meta-text);
105
+ font-size: var(--text-sm);
106
+ line-height: 1.5;
107
+ }
108
+
109
+ /* Disabled — the row reads muted rather than dimmed. */
110
+ tv-task:has(tv-task-checkbox[disabled]) tv-task-title {
111
+ color: var(--color-text-muted);
112
+ }
113
+
114
+ /* ---- List container ----
115
+ No frame of its own: the artifact page is the surface, and Television's
116
+ artifact-view supplies the card chrome around the document. (The frameset
117
+ design frame adds its own preview-only border.) */
118
+
119
+ tv-task-list {
120
+ display: block;
121
+ padding: 0 var(--space-16, 16px) var(--space-16, 16px);
122
+ }
123
+ /* Nothing above the list (no page heading): give it its own top breathing
124
+ room. When a header precedes it, the gap is the subtitle's house margin —
125
+ or the h1 rule below when there is no subtitle. */
126
+ tv-task-list:first-child {
127
+ padding-top: var(--space-16, 16px);
128
+ }
129
+
130
+ /* The artifact's own heading lives on the page, above the list, as the house
131
+ page-header idiom (header > h1 + p) — house typography styles it; the
132
+ skill only aligns it with the list's interior padding. */
133
+ header:has(+ tv-task-list) {
134
+ padding: var(--space-16, 16px) var(--space-16, 16px) 0;
135
+ }
136
+ /* A header whose last child is the h1 (no subtitle) has no house margin
137
+ below it — supply the gap the subtitle's margin would have given. */
138
+ header:has(+ tv-task-list) > h1:last-child {
139
+ margin-bottom: var(--space-12, 12px);
140
+ }
141
+
142
+ /* Section headers lay their title out inline. */
143
+ tv-task-section > header {
144
+ display: flex;
145
+ align-items: baseline;
146
+ gap: var(--space-6, 6px);
147
+ }
148
+
149
+ /* ---- Section ----
150
+ A titled group of tasks. Generic — the title can be a when-bucket (Today /
151
+ Upcoming / Someday), a project, or an area. Rows separate by whitespace;
152
+ sections by more whitespace + their header. */
153
+ tv-task-section {
154
+ display: block;
155
+ margin-top: var(--space-20, 20px);
156
+ }
157
+ tv-task-section:first-of-type { margin-top: 0; }
158
+
159
+ tv-task-section > header { padding-bottom: var(--space-6, 6px); }
160
+ tv-task-section > header h2 {
161
+ margin: 0;
162
+ font-size: var(--text-base);
163
+ font-weight: 600;
164
+ }
165
+
166
+ /* Task count beside a section heading — the author writes the number, the
167
+ skill only styles it. Inline right of the heading (the header's baseline
168
+ flex places it), never floated to the far edge. */
169
+ tv-task-count {
170
+ color: var(--task-meta-text);
171
+ font-size: var(--text-sm);
172
+ font-weight: var(--font-weight-body);
173
+ }
174
+
175
+ /* Empty state — shown when a section or list has no tasks. */
176
+ tv-task-placeholder {
177
+ display: block;
178
+ color: var(--color-text-muted);
179
+ text-align: center;
180
+ padding: var(--space-32, 32px) var(--space-16, 16px);
181
+ font-size: calc(var(--text-sm) + 1px);
182
+ }
183
+
184
+
185
+ /* Highlighted row — sanctioned emphasis: a soft wash derived from
186
+ --task-highlight. No edge lines — rows are borderless throughout. */
187
+ tv-task[highlighted] {
188
+ background: color-mix(in srgb, var(--task-highlight) 10%, transparent);
189
+ }
190
+
191
+ /* ---- Dark mode ----
192
+ The canonical theme is light-only, so the skill carries its own dark token
193
+ set, applied when the document has data-theme="dark". */
194
+ [data-theme="dark"] {
195
+ --color-bg: var(--neutral-950);
196
+ --color-bg-muted: var(--neutral-900);
197
+ --color-surface: var(--neutral-800);
198
+ --color-text: var(--neutral-50);
199
+ --color-text-muted: var(--neutral-400);
200
+ /* Re-derive: custom properties resolve where declared, so without this the
201
+ meta tone would keep the light-mode muted it was mixed from. */
202
+ --task-meta-text: color-mix(in srgb, var(--color-text-muted) 85%, transparent);
203
+ --color-border: rgba(255, 255, 255, 0.14);
204
+ color-scheme: dark;
205
+ }