@telepath-computer/television 0.1.116 → 0.1.117
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/dist/canonical/v1/styles.css +5 -624
- package/dist/cli.cjs +5673 -709
- package/dist/skills/television/SKILL.md +15 -60
- package/dist/web/assets/index-B4JThtNB.js +606 -0
- package/dist/web/assets/index-CQF4vfjH.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -2
- package/dist/web/assets/index-CCUH0MOZ.css +0 -1
- package/dist/web/assets/index-DSFrQJVS.js +0 -606
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* Canonical artifact stylesheet — the curated subset of the design system
|
|
3
3
|
* artifacts inherit at /canonical/v1/styles.css.
|
|
4
4
|
*
|
|
5
|
-
* Includes design-system primitives
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Includes only the design-system primitives artifacts should inherit by
|
|
6
|
+
* default: reset, fonts, and tokens. Artifact-specific structure and rhythm
|
|
7
|
+
* should be authored explicitly in the artifact HTML so agents can see and
|
|
8
|
+
* control the result instead of relying on hidden bare-tag defaults.
|
|
9
9
|
*
|
|
10
|
-
* Source of truth for what's in v1. Edits here change what artifacts
|
|
11
|
-
* inherit; gaps that surface during real authoring (lists, blockquote,
|
|
12
|
-
* table, hr) get added to elements.css since they apply to bare tags.
|
|
10
|
+
* Source of truth for what's in v1. Edits here change what artifacts inherit.
|
|
13
11
|
*/
|
|
14
12
|
*,
|
|
15
13
|
*::before,
|
|
@@ -294,623 +292,6 @@ select {
|
|
|
294
292
|
|
|
295
293
|
|
|
296
294
|
|
|
297
|
-
:root {
|
|
298
|
-
--color-bg: var(--neutral-50);
|
|
299
|
-
--color-bg-muted: var(--neutral-100);
|
|
300
|
-
--color-surface: var(--white);
|
|
301
|
-
--color-surface-muted: var(--neutral-100);
|
|
302
|
-
--color-interactive: var(--neutral-200);
|
|
303
|
-
--color-interactive-hover: var(--neutral-300);
|
|
304
|
-
|
|
305
|
-
/* Borders use alpha so the same hairline reads on light, dark, or
|
|
306
|
-
image bgs without needing a tone-aware swap. */
|
|
307
|
-
--color-border: rgba(0, 0, 0, 0.10);
|
|
308
|
-
--color-border-muted: rgba(0, 0, 0, 0.06);
|
|
309
|
-
|
|
310
|
-
--color-text: var(--neutral-900);
|
|
311
|
-
--color-text-muted: var(--neutral-500);
|
|
312
|
-
--color-link: inherit;
|
|
313
|
-
--color-focus-ring: var(--neutral-500);
|
|
314
|
-
--color-overlay-backdrop: rgb(0 0 0 / 0.5);
|
|
315
|
-
--color-highlight: rgb(255 255 255 / 0.5);
|
|
316
|
-
--color-primary: var(--blue-500);
|
|
317
|
-
--color-danger: var(--red-600);
|
|
318
|
-
|
|
319
|
-
color-scheme: light;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
295
|
body {
|
|
326
296
|
font-family: var(--font-sans);
|
|
327
|
-
font-size: var(--text-base);
|
|
328
|
-
font-weight: var(--font-weight-body);
|
|
329
|
-
line-height: var(--leading-base);
|
|
330
|
-
letter-spacing: 0.015em;
|
|
331
|
-
color: var(--color-text);
|
|
332
|
-
background: var(--color-bg);
|
|
333
|
-
-webkit-font-smoothing: antialiased;
|
|
334
|
-
-moz-osx-font-smoothing: grayscale;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
a {
|
|
338
|
-
color: var(--color-link);
|
|
339
|
-
text-underline-offset: 0.15em;
|
|
340
|
-
text-decoration-thickness: 0.08em;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
h1,
|
|
344
|
-
h2,
|
|
345
|
-
h3,
|
|
346
|
-
h4,
|
|
347
|
-
h5,
|
|
348
|
-
h6 {
|
|
349
|
-
font-weight: 600;
|
|
350
|
-
color: inherit;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
code {
|
|
354
|
-
font-family: var(--font-mono);
|
|
355
|
-
font-size: 0.9em;
|
|
356
|
-
background: var(--color-bg-muted);
|
|
357
|
-
padding: var(--space-2) var(--space-4);
|
|
358
|
-
border-radius: var(--radius-4);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
pre {
|
|
362
|
-
font-family: var(--font-mono);
|
|
363
|
-
background: var(--color-bg-muted);
|
|
364
|
-
padding: var(--space-12);
|
|
365
|
-
border-radius: var(--radius-8);
|
|
366
|
-
overflow-x: auto;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
pre code {
|
|
370
|
-
background: none;
|
|
371
|
-
padding: 0;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
blockquote {
|
|
375
|
-
padding-left: var(--space-16);
|
|
376
|
-
border-left: 3px solid var(--color-border);
|
|
377
|
-
color: var(--color-text-muted);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
hr {
|
|
381
|
-
border: none;
|
|
382
|
-
border-top: 1px solid var(--color-border);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
table {
|
|
386
|
-
border-collapse: collapse;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
th,
|
|
390
|
-
td {
|
|
391
|
-
padding: var(--space-8) var(--space-12);
|
|
392
|
-
border: 1px solid var(--color-border);
|
|
393
|
-
text-align: left;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
th {
|
|
397
|
-
background: var(--color-bg-muted);
|
|
398
|
-
font-weight: 600;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/* Recessed text input — same geometry as a button at rest, sunken via a
|
|
402
|
-
subtle tint and no drop shadow. Focus deepens the tint one step (more
|
|
403
|
-
recessed) and adds a primary-blue ring as the focus signal. Text
|
|
404
|
-
inherits its color from the surrounding context so the input reads on
|
|
405
|
-
either light or dark surfaces. */
|
|
406
|
-
input,
|
|
407
|
-
textarea {
|
|
408
|
-
border: 0.5px solid var(--tint-300);
|
|
409
|
-
border-radius: var(--radius-12);
|
|
410
|
-
background: var(--tint-100);
|
|
411
|
-
color: inherit;
|
|
412
|
-
font: inherit;
|
|
413
|
-
box-sizing: border-box;
|
|
414
|
-
transition:
|
|
415
|
-
background 120ms ease,
|
|
416
|
-
border-color 120ms ease,
|
|
417
|
-
box-shadow 120ms ease;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/* Single-line input snaps to the `lg` control height — taller than
|
|
421
|
-
buttons/segmented controls (`md`) so text-entry fields have visual
|
|
422
|
-
presence as the primary affordance in forms. Padding goes
|
|
423
|
-
horizontal-only since the explicit height handles vertical centering
|
|
424
|
-
via the line-box. */
|
|
425
|
-
input {
|
|
426
|
-
height: var(--control-height-lg);
|
|
427
|
-
padding: 0 12px;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/* Multi-line textarea stays intrinsic — height grows with content (or
|
|
431
|
-
max-height on a host) rather than locking to a control unit. */
|
|
432
|
-
textarea {
|
|
433
|
-
padding: 6px 10px;
|
|
434
|
-
resize: none;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
input::placeholder,
|
|
438
|
-
textarea::placeholder {
|
|
439
|
-
color: var(--tint-500);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
input:focus,
|
|
443
|
-
textarea:focus {
|
|
444
|
-
background: color-mix(in srgb, var(--white) var(--alpha-300), transparent);
|
|
445
|
-
border-color: var(--color-primary);
|
|
446
|
-
outline: none;
|
|
447
|
-
box-shadow: 0 0 0 1.5px var(--color-primary);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/* Stacked label + control. Label text is smaller and lighter than body
|
|
451
|
-
copy, sits flush with the control. Use as
|
|
452
|
-
<label class="field"><span>Name</span><input /></label>. */
|
|
453
|
-
label.field {
|
|
454
|
-
display: grid;
|
|
455
|
-
gap: 4px;
|
|
456
|
-
font-size: 13px;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
label.field > span {
|
|
460
|
-
font-size: 11.5px;
|
|
461
|
-
font-weight: 450;
|
|
462
|
-
color: var(--tint-800);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/* Universal squircle corners. `corner-shape: squircle` only affects
|
|
466
|
-
elements that already declare a non-zero `border-radius`, so applying
|
|
467
|
-
to `*` is effectively a no-op everywhere except where corners exist.
|
|
468
|
-
Token values in tokens.css bump ~25% inside the same @supports block
|
|
469
|
-
to compensate for the perceived tightness difference. */
|
|
470
|
-
@supports (corner-shape: squircle) {
|
|
471
|
-
* {
|
|
472
|
-
corner-shape: squircle;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/* Button-only layout — placement of icon + label inside a single
|
|
477
|
-
button. Segmented-button doesn't share these (it's a flex row of
|
|
478
|
-
children, not a center-aligned label-and-icon stack).
|
|
479
|
-
`height: var(--control-height-md)` is explicit so a row of mixed
|
|
480
|
-
chrome (text button + icon-only button + segmented-control) lines up
|
|
481
|
-
without each variant having to tune its intrinsic content + padding
|
|
482
|
-
to match. Padding is purely horizontal — vertical centering is handled
|
|
483
|
-
by `align-items: center`. */
|
|
484
|
-
button {
|
|
485
|
-
display: inline-flex;
|
|
486
|
-
align-items: center;
|
|
487
|
-
justify-content: center;
|
|
488
|
-
gap: var(--space-6);
|
|
489
|
-
box-sizing: border-box;
|
|
490
|
-
height: var(--control-height-md);
|
|
491
|
-
/* 2px extra bottom padding compensates for the visual offset from
|
|
492
|
-
font ascender/descender asymmetry — `align-items: center` centers
|
|
493
|
-
the line-box but the glyph mass sits below center because the
|
|
494
|
-
ascender claims more vertical space than the descender. The
|
|
495
|
-
icon-only override resets padding to 0 since icons are already
|
|
496
|
-
symmetric. */
|
|
497
|
-
padding: 0 14px 2px;
|
|
498
|
-
font: inherit;
|
|
499
|
-
cursor: default;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/* Default chrome — shared between <button> and <segmented-control> so
|
|
503
|
-
the two stay in visual lockstep. Variant overrides (ghost, primary,
|
|
504
|
-
danger) and tone-dark / tone-light / disabled cascades live below.
|
|
505
|
-
The shared-with-segmented bits are intentional: a segmented control
|
|
506
|
-
is a chrome surface that looks like a single button containing two
|
|
507
|
-
click targets, so its host adopts the same fill, hairline, radius,
|
|
508
|
-
and shadow. */
|
|
509
|
-
button,
|
|
510
|
-
segmented-control {
|
|
511
|
-
background: var(--color-surface);
|
|
512
|
-
border: 0.5px solid var(--tint-200);
|
|
513
|
-
border-radius: var(--radius-12);
|
|
514
|
-
box-shadow: var(--shadow-control);
|
|
515
|
-
color: inherit;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
button:not([material]):hover:not(:disabled) {
|
|
519
|
-
background: color-mix(in srgb, var(--color-surface) 96%, black);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
button:not([material]):active:not(:disabled) {
|
|
523
|
-
background: color-mix(in srgb, var(--color-surface) 88%, black);
|
|
524
|
-
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
button:focus-visible {
|
|
528
|
-
outline: 2px solid var(--color-focus-ring);
|
|
529
|
-
outline-offset: 2px;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
button[variant="primary"] {
|
|
533
|
-
background: var(--color-primary);
|
|
534
|
-
border-color: rgb(0 0 0 / 0.18);
|
|
535
|
-
color: var(--white);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
button[variant="primary"]:hover:not(:disabled) {
|
|
539
|
-
background: color-mix(in srgb, var(--color-primary) 92%, black);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
button[variant="primary"]:active:not(:disabled) {
|
|
543
|
-
background: color-mix(in srgb, var(--color-primary) 82%, black);
|
|
544
|
-
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.18);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
button[variant="danger"] {
|
|
548
|
-
background: var(--color-danger);
|
|
549
|
-
border-color: rgb(0 0 0 / 0.18);
|
|
550
|
-
color: var(--white);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
button[variant="danger"]:hover:not(:disabled) {
|
|
554
|
-
background: color-mix(in srgb, var(--color-danger) 92%, black);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
button[variant="danger"]:active:not(:disabled) {
|
|
558
|
-
background: color-mix(in srgb, var(--color-danger) 82%, black);
|
|
559
|
-
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.18);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
button[variant="ghost"] {
|
|
563
|
-
background: transparent;
|
|
564
|
-
border-color: transparent;
|
|
565
|
-
color: inherit;
|
|
566
|
-
box-shadow: none;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/* Ghost is a chromeless overlay — at rest it's invisible and relies on
|
|
570
|
-
the surface behind it. The overlay tokens cascade through `tone="dark"`
|
|
571
|
-
(set on the button itself by the auto-tone watcher in
|
|
572
|
-
`utils/button-tone.ts`, or inherited from a tone-sampled ancestor), so
|
|
573
|
-
one rule covers light + dark backdrops.
|
|
574
|
-
|
|
575
|
-
Hover/active also bump the backdrop saturation — the same "comes alive"
|
|
576
|
-
gesture glass uses. Ghost has no blur or brightness to absorb the
|
|
577
|
-
filter, so a much gentler ladder (1.2 / 1.3 / 1.4) reads as the right
|
|
578
|
-
weight against ghost's ~8–16% tint overlay. Glass's higher numbers
|
|
579
|
-
would punch through too hard here. */
|
|
580
|
-
/* Ghost uses currentColor tints directly (auto-flip via tone) rather than
|
|
581
|
-
the shared --color-overlay-hover / -active tokens. The shared tokens
|
|
582
|
-
read too quietly on ghost's bare surface — bumping ghost a step heavier
|
|
583
|
-
(alpha-150 hover, alpha-300 active) gives the hover real presence
|
|
584
|
-
without affecting other chrome that uses the overlay tokens. */
|
|
585
|
-
button[variant="ghost"]:hover:not(:disabled) {
|
|
586
|
-
background: var(--tint-150);
|
|
587
|
-
backdrop-filter: saturate(1.2);
|
|
588
|
-
-webkit-backdrop-filter: saturate(1.2);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
button[variant="ghost"]:active:not(:disabled) {
|
|
592
|
-
background: var(--tint-300);
|
|
593
|
-
backdrop-filter: saturate(1.4);
|
|
594
|
-
-webkit-backdrop-filter: saturate(1.4);
|
|
595
|
-
box-shadow: none;
|
|
596
297
|
}
|
|
597
|
-
|
|
598
|
-
/* Pinned-active state for ghost triggers — used while a dropdown
|
|
599
|
-
`[aria-expanded="true"]` is open against this button (set automatically
|
|
600
|
-
by `dropdown-menu`), while a consumer manually pins `[data-active]`
|
|
601
|
-
(e.g. anchored popover), or while the button is `[aria-pressed="true"]`
|
|
602
|
-
(toggle/tab in its on state). All three override hover so the button
|
|
603
|
-
stays visibly active even as the cursor moves over it. */
|
|
604
|
-
button[variant="ghost"][data-active]:not(:disabled),
|
|
605
|
-
button[variant="ghost"][data-active]:hover:not(:disabled),
|
|
606
|
-
button[variant="ghost"][aria-pressed="true"]:not(:disabled),
|
|
607
|
-
button[variant="ghost"][aria-pressed="true"]:hover:not(:disabled),
|
|
608
|
-
button[variant="ghost"][aria-expanded="true"]:not(:disabled),
|
|
609
|
-
button[variant="ghost"][aria-expanded="true"]:hover:not(:disabled) {
|
|
610
|
-
background: var(--color-overlay-active);
|
|
611
|
-
backdrop-filter: saturate(1.3);
|
|
612
|
-
-webkit-backdrop-filter: saturate(1.3);
|
|
613
|
-
box-shadow: none;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/* Glass variant — translucent surface with backdrop blur. Shared between
|
|
617
|
-
<button variant="glass"> and <segmented-control variant="glass"> so a
|
|
618
|
-
chrome row of buttons + segmented controls reads as one surface
|
|
619
|
-
family. Self-contained per-variant chrome (bg + border + shadow + blur
|
|
620
|
-
+ hover/active overlays) means no fighting with default-button rules
|
|
621
|
-
or tone-driven default fills.
|
|
622
|
-
|
|
623
|
-
No explicit `color` here — auto-tone (utils/button-tone.ts) writes
|
|
624
|
-
`tone="light"` or `tone="dark"` on the element after sampling its
|
|
625
|
-
actual backdrop. The materials.css `[tone="…"]` color rules then set
|
|
626
|
-
text color on the element directly (specificity 0,1,0), beating the
|
|
627
|
-
shared `button { color: inherit }` (0,0,1). An explicit `color:
|
|
628
|
-
inherit` here would be 0,1,1 and would defeat the tone-aware color. */
|
|
629
|
-
button[variant="glass"],
|
|
630
|
-
segmented-control[variant="glass"] {
|
|
631
|
-
background: transparent;
|
|
632
|
-
border: 0.5px solid color-mix(in srgb, var(--black) var(--alpha-100), transparent);
|
|
633
|
-
box-shadow: none;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/* Glass-variant standalone button: backdrop-filter on the host. The
|
|
637
|
-
segmented-control variant moves the filter onto each segment so per-
|
|
638
|
-
child hover/active saturation works without nested-filter compounding
|
|
639
|
-
issues — see segmented-control.css. */
|
|
640
|
-
button[variant="glass"] {
|
|
641
|
-
backdrop-filter: blur(20px) brightness(0.95) saturate(1.5);
|
|
642
|
-
-webkit-backdrop-filter: blur(20px) brightness(0.95) saturate(1.5);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/* `background:` shorthand (not `background-image`) resets background-color
|
|
646
|
-
back to transparent — necessary because the default-button hover rule
|
|
647
|
-
`button:not([material]):hover` fires on glass too (glass has no
|
|
648
|
-
material) at equal specificity and would otherwise paint a 96% white
|
|
649
|
-
surface underneath this overlay.
|
|
650
|
-
|
|
651
|
-
Hover and active also crank the backdrop saturate filter (1.5 → 1.8 →
|
|
652
|
-
2.2). On a light backdrop, darkening overlays read as visual weight
|
|
653
|
-
rather than feedback; the saturation bump is direction-agnostic — the
|
|
654
|
-
surface "comes alive" the same way regardless of tone. The overlay
|
|
655
|
-
stays as a quieter backstop. */
|
|
656
|
-
button[variant="glass"]:hover:not(:disabled) {
|
|
657
|
-
background: linear-gradient(var(--color-overlay-hover), var(--color-overlay-hover));
|
|
658
|
-
backdrop-filter: blur(20px) brightness(0.95) saturate(1.8);
|
|
659
|
-
-webkit-backdrop-filter: blur(20px) brightness(0.95) saturate(1.8);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
button[variant="glass"]:active:not(:disabled) {
|
|
663
|
-
background: linear-gradient(var(--color-overlay-active), var(--color-overlay-active));
|
|
664
|
-
backdrop-filter: blur(20px) brightness(0.95) saturate(2.2);
|
|
665
|
-
-webkit-backdrop-filter: blur(20px) brightness(0.95) saturate(2.2);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/* Persistent active state — `aria-pressed="true"` (tab/toggle that is
|
|
669
|
-
"currently on") and `aria-expanded="true"` (popover/menu trigger whose
|
|
670
|
-
panel is open). Both stay lit through hover so the active overlay isn't
|
|
671
|
-
visually masked by the hover overlay when the cursor is over them.
|
|
672
|
-
`<ui-popover>` and `<dropdown-menu>` set `aria-expanded` automatically;
|
|
673
|
-
tab-style consumers set `aria-pressed`. */
|
|
674
|
-
button[variant="glass"][aria-pressed="true"]:not(:disabled),
|
|
675
|
-
button[variant="glass"][aria-pressed="true"]:hover:not(:disabled),
|
|
676
|
-
button[variant="glass"][aria-expanded="true"]:not(:disabled),
|
|
677
|
-
button[variant="glass"][aria-expanded="true"]:hover:not(:disabled) {
|
|
678
|
-
background: linear-gradient(var(--color-overlay-active), var(--color-overlay-active));
|
|
679
|
-
backdrop-filter: blur(20px) brightness(0.95) saturate(2);
|
|
680
|
-
-webkit-backdrop-filter: blur(20px) brightness(0.95) saturate(2);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
[tone="dark"] button[variant="glass"],
|
|
684
|
-
[tone="dark"] segmented-control[variant="glass"] {
|
|
685
|
-
border-color: var(--tint-400);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/* Material composition with controls (option B in the design notes —
|
|
689
|
-
contained override rather than refactoring materials.css globally).
|
|
690
|
-
Material is a passive-surface primitive: it locks color, draws a
|
|
691
|
-
fixed-alpha hairline, and a default-variant `:hover` would replace
|
|
692
|
-
its background entirely. These overrides let `<button material="...">`
|
|
693
|
-
and `<segmented-control material="...">` compose:
|
|
694
|
-
|
|
695
|
-
- `color: inherit` so the tone-aware text cascade still applies
|
|
696
|
-
(material sets a fixed `--color-text` which doesn't flip on dark).
|
|
697
|
-
- `box-shadow: none` because both materials want a flat surface; the
|
|
698
|
-
default control shadow fights glass especially.
|
|
699
|
-
- hover/active paint via `background-image` (a flat linear-gradient)
|
|
700
|
-
so the material's `background-color` is preserved underneath.
|
|
701
|
-
- dark-tone bumps the hairline to `--tint-400` so the rim reads on
|
|
702
|
-
dark backdrops (material's locked black-alpha border vanishes
|
|
703
|
-
against tone-dark patches). */
|
|
704
|
-
button[material],
|
|
705
|
-
segmented-control[material] {
|
|
706
|
-
color: inherit;
|
|
707
|
-
box-shadow: none;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
button[material]:hover:not(:disabled) {
|
|
711
|
-
background-image: linear-gradient(var(--color-overlay-hover), var(--color-overlay-hover));
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
button[material]:active:not(:disabled) {
|
|
715
|
-
background-image: linear-gradient(var(--color-overlay-active), var(--color-overlay-active));
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
[tone="dark"] button[material],
|
|
719
|
-
[tone="dark"] segmented-control[material] {
|
|
720
|
-
border-color: var(--tint-400);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
button:disabled {
|
|
724
|
-
background: var(--tint-300);
|
|
725
|
-
border-color: transparent;
|
|
726
|
-
color: var(--tint-700);
|
|
727
|
-
box-shadow: none;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
/* Ghost buttons stay chromeless when disabled — they have no
|
|
731
|
-
resting chrome to "grey out", so the dimmed foreground (inherited
|
|
732
|
-
via `color: var(--tint-700)` above) is the entire signal. The
|
|
733
|
-
default disabled bg would otherwise paint a tint-300 surface
|
|
734
|
-
that fights ghost's transparent baseline. */
|
|
735
|
-
button[variant="ghost"]:disabled {
|
|
736
|
-
background: transparent;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/* Default-variant button on a dark surface — drop the chromed light fill,
|
|
740
|
-
pick up a brighter white-tint than inputs (15% vs 8%) to read as raised
|
|
741
|
-
rather than recessed, and replace the ineffective black drop shadow
|
|
742
|
-
with a top-edge inset highlight (macOS NSButton-on-dark convention).
|
|
743
|
-
`:not([material])` opts the tone-driven default fill OUT when the
|
|
744
|
-
element has explicit `material` — material owns the surface in that
|
|
745
|
-
case, regardless of tone. */
|
|
746
|
-
:where([tone="dark"]) button:not([variant]):not([material]):not(:disabled),
|
|
747
|
-
:where([tone="dark"]) button[variant="default"]:not([material]):not(:disabled),
|
|
748
|
-
:where([tone="dark"]) segmented-control:not([variant]):not([material]),
|
|
749
|
-
:where([tone="dark"]) segmented-control[variant="default"]:not([material]) {
|
|
750
|
-
background: var(--tint-200);
|
|
751
|
-
border-color: var(--tint-300);
|
|
752
|
-
box-shadow: inset 0 0.5px 0 var(--tint-300);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
:where([tone="dark"]) button:not([variant]):hover:not(:disabled),
|
|
756
|
-
:where([tone="dark"]) button[variant="default"]:hover:not(:disabled) {
|
|
757
|
-
background: var(--tint-300);
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
:where([tone="dark"]) button:not([variant]):active:not(:disabled),
|
|
761
|
-
:where([tone="dark"]) button[variant="default"]:active:not(:disabled) {
|
|
762
|
-
background: var(--tint-400);
|
|
763
|
-
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.20);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/* Explicit light-tone button rules — restore the chromed white-card
|
|
767
|
-
styling when a `tone="light"` ancestor sits inside a `tone="dark"`
|
|
768
|
-
one (e.g. a paper popover anchored inside dark chrome). Same
|
|
769
|
-
specificity as the dark-tone rules above; source order makes the
|
|
770
|
-
closer light-tone scope win for slotted descendants. */
|
|
771
|
-
:where([tone="light"]) button:not([variant]):not([material]):not(:disabled),
|
|
772
|
-
:where([tone="light"]) button[variant="default"]:not([material]):not(:disabled),
|
|
773
|
-
:where([tone="light"]) segmented-control:not([variant]):not([material]),
|
|
774
|
-
:where([tone="light"]) segmented-control[variant="default"]:not([material]) {
|
|
775
|
-
background: var(--color-surface);
|
|
776
|
-
border-color: var(--tint-200);
|
|
777
|
-
box-shadow: var(--shadow-control);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
:where([tone="light"]) button:not([variant]):hover:not(:disabled) {
|
|
781
|
-
background: color-mix(in srgb, var(--color-surface) 96%, black);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
:where([tone="light"]) button:not([variant]):active:not(:disabled) {
|
|
785
|
-
background: color-mix(in srgb, var(--color-surface) 88%, black);
|
|
786
|
-
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
/* Size opt-in: `size="sm"` swaps to the smaller control-height token.
|
|
790
|
-
Same applies to `<segmented-control size="sm">` so the host stretches
|
|
791
|
-
children correctly, and to `<button size="sm" icon-only>` so the
|
|
792
|
-
square footprint stays at the smaller control unit. The default
|
|
793
|
-
(md, 28px) is set on the base `<button>` rule above; only sm needs
|
|
794
|
-
an explicit override. */
|
|
795
|
-
button[size="sm"],
|
|
796
|
-
segmented-control[size="sm"] {
|
|
797
|
-
height: var(--control-height-sm);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
button[size="sm"][icon-only] {
|
|
801
|
-
width: var(--control-height-sm);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
button[size="xs"] {
|
|
805
|
-
height: var(--control-height-xs);
|
|
806
|
-
padding: 0 var(--space-6) 1px;
|
|
807
|
-
border-radius: var(--radius-8);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
button[size="xs"][icon-only] {
|
|
811
|
-
width: var(--control-height-xs);
|
|
812
|
-
padding: 0;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
/* Square icon-only buttons. Width = control height so the footprint is
|
|
816
|
-
square at the same vertical size as a text-button sibling. Chrome is
|
|
817
|
-
determined by the variant (default = chromed pill, ghost = chromeless
|
|
818
|
-
overlay). */
|
|
819
|
-
button[icon-only] {
|
|
820
|
-
width: var(--control-height-md);
|
|
821
|
-
padding: 0;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
[data-ui-icon] {
|
|
825
|
-
display: inline-block;
|
|
826
|
-
vertical-align: middle;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/* Attribute conventions — see docs/ui.md § Attribute conventions.
|
|
830
|
-
`[selectable]` opts a subtree back into text selection inside an
|
|
831
|
-
ancestor that disabled it (the app's main area sets
|
|
832
|
-
`user-select: none` to suppress accidental chrome selection).
|
|
833
|
-
`[unselectable]` is the inverse — opts a subtree back OUT of
|
|
834
|
-
selection inside a `[selectable]` ancestor, for chrome labels
|
|
835
|
-
(e.g. "Used 5 tools") that sit alongside copyable content. */
|
|
836
|
-
[selectable],
|
|
837
|
-
[selectable] * {
|
|
838
|
-
user-select: text;
|
|
839
|
-
-webkit-user-select: text;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
[unselectable],
|
|
843
|
-
[unselectable] * {
|
|
844
|
-
user-select: none;
|
|
845
|
-
-webkit-user-select: none;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
/* Prose — opt-in typography for document content */
|
|
849
|
-
|
|
850
|
-
.prose {
|
|
851
|
-
line-height: var(--leading-base);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
.prose h1 {
|
|
855
|
-
font-size: var(--text-xl);
|
|
856
|
-
margin-bottom: var(--space-8);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
.prose h2 {
|
|
860
|
-
font-size: var(--text-lg);
|
|
861
|
-
margin-top: var(--space-24);
|
|
862
|
-
margin-bottom: var(--space-8);
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
.prose h3,
|
|
866
|
-
.prose h4,
|
|
867
|
-
.prose h5,
|
|
868
|
-
.prose h6 {
|
|
869
|
-
font-size: var(--text-base);
|
|
870
|
-
margin-top: var(--space-24);
|
|
871
|
-
margin-bottom: var(--space-8);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
.prose p {
|
|
875
|
-
margin: var(--space-12) 0;
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
.prose ul,
|
|
879
|
-
.prose ol {
|
|
880
|
-
padding-left: var(--space-24);
|
|
881
|
-
margin: var(--space-12) 0;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
.prose blockquote {
|
|
885
|
-
margin: var(--space-12) 0;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
.prose pre {
|
|
889
|
-
margin: var(--space-12) 0;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
.prose hr {
|
|
893
|
-
margin: var(--space-24) 0;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
.prose table {
|
|
897
|
-
width: 100%;
|
|
898
|
-
margin: var(--space-12) 0;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
.prose img {
|
|
902
|
-
max-width: 100%;
|
|
903
|
-
height: auto;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
.prose > :first-child {
|
|
907
|
-
margin-top: 0;
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
.prose > :last-child {
|
|
911
|
-
margin-bottom: 0;
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|