@ps1ui/core 0.0.1 → 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 (34) hide show
  1. package/README.md +49 -0
  2. package/dist/base.css +56 -0
  3. package/dist/components/Anchor/Anchor.d.mts +2 -0
  4. package/dist/components/Anchor/Anchor.mjs.map +1 -1
  5. package/dist/components/Button/Button.d.mts +7 -1
  6. package/dist/components/Button/Button.mjs.map +1 -1
  7. package/dist/components/Checkbox/Checkbox.d.mts +1 -0
  8. package/dist/components/Checkbox/Checkbox.mjs.map +1 -1
  9. package/dist/components/CodeBlock/CodeBlock.d.mts +3 -0
  10. package/dist/components/CodeBlock/CodeBlock.mjs.map +1 -1
  11. package/dist/components/Container/Container.d.mts +8 -0
  12. package/dist/components/Container/Container.mjs.map +1 -1
  13. package/dist/components/Details/Details.d.mts +1 -0
  14. package/dist/components/Details/Details.mjs.map +1 -1
  15. package/dist/components/Grid/Grid.d.mts +8 -0
  16. package/dist/components/Grid/Grid.mjs.map +1 -1
  17. package/dist/components/GridItem/GridItem.d.mts +4 -0
  18. package/dist/components/GridItem/GridItem.mjs.map +1 -1
  19. package/dist/components/Heading/Heading.d.mts +4 -0
  20. package/dist/components/Heading/Heading.mjs.map +1 -1
  21. package/dist/components/List/List.d.mts +2 -0
  22. package/dist/components/List/List.mjs.map +1 -1
  23. package/dist/components/Stack/Stack.d.mts +14 -0
  24. package/dist/components/Stack/Stack.mjs.map +1 -1
  25. package/dist/components/Text/Text.d.mts +9 -1
  26. package/dist/components/Text/Text.mjs.map +1 -1
  27. package/dist/components/Textarea/Textarea.d.mts +7 -0
  28. package/dist/components/Textarea/Textarea.mjs +14 -0
  29. package/dist/components/Textarea/Textarea.mjs.map +1 -0
  30. package/dist/components.css +85 -159
  31. package/dist/index.d.mts +2 -1
  32. package/dist/index.mjs +2 -1
  33. package/dist/styles.css +206 -7
  34. package/package.json +4 -4
@@ -21,7 +21,6 @@
21
21
  * ========================================================================== */
22
22
 
23
23
  /* Palette hexes are mirrored in .storybook/ps1uiTheme.ts — see note there. */
24
-
25
24
  :root {
26
25
  --ps1ui-font-mono:
27
26
  "JetBrains Mono Variable", ui-monospace, "SF Mono", Menlo, Monaco, "Cascadia Mono", Consolas,
@@ -41,6 +40,17 @@
41
40
  --ps1ui-color-primary-active: #3fb950;
42
41
  --ps1ui-color-primary-fg: #0b0f14;
43
42
  --ps1ui-color-accent: #ffa657;
43
+ /* Destructive / error signalling. Hue sits in the rose-coral band the code
44
+ palette already inhabits (--ps1ui-code-keyword / -tag / -deleted) so the
45
+ surface reads as one system; luminance is tuned so the base color hits
46
+ ≥ 4.5:1 vs BOTH --ps1ui-color-bg and --ps1ui-color-surface (verified by
47
+ Text.contrast.test.tsx) and danger-fg on each danger-* fill hits the
48
+ same threshold (verified by Button.contrast.test.tsx). Hover/active
49
+ progressively darken while staying above contrast. */
50
+ --ps1ui-color-danger: #ff7d8d;
51
+ --ps1ui-color-danger-hover: #f0637a;
52
+ --ps1ui-color-danger-active: #e04a60;
53
+ --ps1ui-color-danger-fg: #0b0f14;
44
54
  --ps1ui-color-focus-ring-soft: color-mix(in srgb, var(--ps1ui-color-primary) 14%, transparent);
45
55
  --ps1ui-shadow-focus-ring: 0 0 0 3px var(--ps1ui-color-focus-ring-soft);
46
56
 
@@ -110,20 +120,16 @@
110
120
 
111
121
  --ps1ui-transition-fast: 120ms ease;
112
122
  }
113
-
114
123
  /* PS1Root establishes the app-level responsive containment context.
115
124
  `container-type: inline-size` turns this element into a query context for
116
125
  `@container` rules used by responsive ps1ui components. Named `ps1ui-root`
117
126
  for future targeted queries; unnamed queries in component CSS match this
118
127
  via nearest-ancestor lookup. */
119
-
120
128
  /* Containment side-effect defense (align-self / justify-self / min-width) lives
121
129
  in components.css's shared selector list — see the block header there. */
122
-
123
130
  .ps1ui-root {
124
131
  container: ps1ui-root / inline-size;
125
132
  }
126
-
127
133
  .ps1ui-anchor {
128
134
  font-family: var(--ps1ui-font-mono);
129
135
  text-decoration: underline;
@@ -138,7 +144,6 @@
138
144
 
139
145
  /* outline (not box-shadow) so a wrapping inline anchor renders as connected fragments
140
146
  per line-box instead of two disjoint rectangles. Modern browsers follow border-radius. */
141
-
142
147
  .ps1ui-anchor:focus-visible {
143
148
  outline: 2px solid var(--ps1ui-color-primary);
144
149
  outline-offset: 2px;
@@ -147,15 +152,12 @@
147
152
  /* text-decoration-color defaults to currentColor, so setting only `color` per state
148
153
  also shifts the underline. subtle's default intentionally decouples the two — the
149
154
  underline is dimmer than the text — hence the explicit override there. */
150
-
151
155
  .ps1ui-anchor--primary {
152
156
  color: var(--ps1ui-color-primary);
153
157
  }
154
-
155
158
  .ps1ui-anchor--primary:hover {
156
159
  color: var(--ps1ui-color-primary-hover);
157
160
  }
158
-
159
161
  .ps1ui-anchor--primary:active {
160
162
  color: var(--ps1ui-color-primary-active);
161
163
  }
@@ -164,21 +166,17 @@
164
166
  color: inherit;
165
167
  text-decoration-color: var(--ps1ui-color-fg-subtle);
166
168
  }
167
-
168
169
  /* Reset text-decoration-color to currentColor so the underline follows the
169
170
  hover/active color shift; without this it would stay pinned to fg-subtle
170
171
  from the base rule. */
171
-
172
172
  .ps1ui-anchor--subtle:hover {
173
173
  color: var(--ps1ui-color-primary);
174
174
  text-decoration-color: currentColor;
175
175
  }
176
-
177
176
  .ps1ui-anchor--subtle:active {
178
177
  color: var(--ps1ui-color-primary-active);
179
178
  text-decoration-color: currentColor;
180
179
  }
181
-
182
180
  .ps1ui-button {
183
181
  display: inline-flex;
184
182
  align-items: center;
@@ -221,17 +219,14 @@
221
219
  color: var(--ps1ui-color-primary-fg);
222
220
  border-color: var(--ps1ui-color-primary);
223
221
  }
224
-
225
222
  .ps1ui-button--primary:hover:not(:disabled) {
226
223
  background: var(--ps1ui-color-primary-hover);
227
224
  border-color: var(--ps1ui-color-primary-hover);
228
225
  }
229
-
230
226
  .ps1ui-button--primary:active:not(:disabled) {
231
227
  background: var(--ps1ui-color-primary-active);
232
228
  border-color: var(--ps1ui-color-primary-active);
233
229
  }
234
-
235
230
  .ps1ui-button--primary:disabled {
236
231
  background: transparent;
237
232
  color: var(--ps1ui-color-fg-subtle);
@@ -247,22 +242,40 @@
247
242
  down to its text glyphs next to a solid primary. */
248
243
  border-color: var(--ps1ui-color-fg-subtle);
249
244
  }
250
-
251
245
  .ps1ui-button--secondary:hover:not(:disabled) {
252
246
  border-color: var(--ps1ui-color-primary);
253
247
  background: color-mix(in srgb, var(--ps1ui-color-primary) 6%, transparent);
254
248
  }
255
-
256
249
  .ps1ui-button--secondary:active:not(:disabled) {
257
250
  border-color: var(--ps1ui-color-primary-active);
258
251
  background: color-mix(in srgb, var(--ps1ui-color-primary-active) 10%, transparent);
259
252
  }
260
-
261
253
  .ps1ui-button--secondary:disabled {
262
254
  color: var(--ps1ui-color-fg-subtle);
263
255
  border-color: var(--ps1ui-color-border);
264
256
  }
265
257
 
258
+ /* Same filled shape as --primary — variant only shifts hue, not typographic
259
+ weight or footprint, so a danger button reads as an equal-weight sibling
260
+ of a primary one and the visual difference is purely semantic colour. */
261
+ .ps1ui-button--danger {
262
+ background: var(--ps1ui-color-danger);
263
+ color: var(--ps1ui-color-danger-fg);
264
+ border-color: var(--ps1ui-color-danger);
265
+ }
266
+ .ps1ui-button--danger:hover:not(:disabled) {
267
+ background: var(--ps1ui-color-danger-hover);
268
+ border-color: var(--ps1ui-color-danger-hover);
269
+ }
270
+ .ps1ui-button--danger:active:not(:disabled) {
271
+ background: var(--ps1ui-color-danger-active);
272
+ border-color: var(--ps1ui-color-danger-active);
273
+ }
274
+ .ps1ui-button--danger:disabled {
275
+ background: transparent;
276
+ color: var(--ps1ui-color-fg-subtle);
277
+ border-color: var(--ps1ui-color-border);
278
+ }
266
279
  .ps1ui-checkbox {
267
280
  -moz-appearance: none;
268
281
  appearance: none;
@@ -325,7 +338,6 @@
325
338
  sit OUTSIDE the 4×8 content — base.css's reset intentionally excludes pseudo
326
339
  elements from its `box-sizing: border-box` block for exactly this pattern.
327
340
  Rendered geometry locked in by Checkbox.checkmark-geometry.test.tsx. */
328
-
329
341
  .ps1ui-checkbox:checked:not(:indeterminate)::after {
330
342
  content: "";
331
343
  position: absolute;
@@ -356,7 +368,6 @@
356
368
  needs nothing). The focus-outline counterpart lives in the grouped
357
369
  forced-colors rule in styles/components.css. Tested in
358
370
  src/styles/forced-colors.test.tsx. */
359
-
360
371
  @media (forced-colors: active) {
361
372
  .ps1ui-checkbox:indeterminate::after {
362
373
  background: none;
@@ -378,7 +389,6 @@
378
389
  background: var(--ps1ui-color-surface);
379
390
  border-color: var(--ps1ui-color-border);
380
391
  }
381
-
382
392
  /* Input custom properties emitted by Container.tsx per set responsive-prop
383
393
  breakpoint (`--_container-<axis>-<bp>`). Registered as non-inheriting for
384
394
  the same reason as Stack — see Stack.css's @property block for the full
@@ -386,52 +396,42 @@
386
396
  `unset`/`revert` won't block; only `initial` or `@property inherits:false`
387
397
  works). Any new responsive axis added to Container.tsx must add matching
388
398
  @property blocks here. */
389
-
390
399
  @property --_container-size-base {
391
400
  syntax: "*";
392
401
  inherits: false;
393
402
  }
394
-
395
403
  @property --_container-size-sm {
396
404
  syntax: "*";
397
405
  inherits: false;
398
406
  }
399
-
400
407
  @property --_container-size-md {
401
408
  syntax: "*";
402
409
  inherits: false;
403
410
  }
404
-
405
411
  @property --_container-size-lg {
406
412
  syntax: "*";
407
413
  inherits: false;
408
414
  }
409
-
410
415
  @property --_container-size-xl {
411
416
  syntax: "*";
412
417
  inherits: false;
413
418
  }
414
-
415
419
  @property --_container-px-base {
416
420
  syntax: "*";
417
421
  inherits: false;
418
422
  }
419
-
420
423
  @property --_container-px-sm {
421
424
  syntax: "*";
422
425
  inherits: false;
423
426
  }
424
-
425
427
  @property --_container-px-md {
426
428
  syntax: "*";
427
429
  inherits: false;
428
430
  }
429
-
430
431
  @property --_container-px-lg {
431
432
  syntax: "*";
432
433
  inherits: false;
433
434
  }
434
-
435
435
  @property --_container-px-xl {
436
436
  syntax: "*";
437
437
  inherits: false;
@@ -453,10 +453,8 @@
453
453
  Breakpoint thresholds mirror --ps1ui-breakpoint-* in tokens.css; kept
454
454
  inline as literal values because `@container` conditions cannot resolve
455
455
  `var()`. Keep them in sync. */
456
-
457
456
  /* Containment side-effect defense (align-self / justify-self / min-width) lives
458
457
  in components.css's shared selector list — see the block header there. */
459
-
460
458
  .ps1ui-container {
461
459
  container: ps1ui-container / inline-size;
462
460
  width: 100%;
@@ -487,79 +485,65 @@
487
485
  --_px: var(--_px-sm);
488
486
  }
489
487
  }
490
-
491
488
  @container (min-width: 48rem) {
492
489
  .ps1ui-container {
493
490
  --_size: var(--_size-md);
494
491
  --_px: var(--_px-md);
495
492
  }
496
493
  }
497
-
498
494
  @container (min-width: 64rem) {
499
495
  .ps1ui-container {
500
496
  --_size: var(--_size-lg);
501
497
  --_px: var(--_px-lg);
502
498
  }
503
499
  }
504
-
505
500
  @container (min-width: 80rem) {
506
501
  .ps1ui-container {
507
502
  --_size: var(--_size-xl);
508
503
  --_px: var(--_px-xl);
509
504
  }
510
505
  }
511
-
512
506
  /* Input custom properties emitted by Grid.tsx per set responsive-prop
513
507
  breakpoint (`--_grid-<axis>-<bp>`). Registered as non-inheriting for the
514
508
  same reason as Stack — see Stack.css's @property block for the full
515
509
  rationale. Any new responsive axis added to Grid.tsx must add matching
516
510
  @property blocks here. */
517
-
518
511
  @property --_grid-columns-base {
519
512
  syntax: "*";
520
513
  inherits: false;
521
514
  }
522
-
523
515
  @property --_grid-columns-sm {
524
516
  syntax: "*";
525
517
  inherits: false;
526
518
  }
527
-
528
519
  @property --_grid-columns-md {
529
520
  syntax: "*";
530
521
  inherits: false;
531
522
  }
532
-
533
523
  @property --_grid-columns-lg {
534
524
  syntax: "*";
535
525
  inherits: false;
536
526
  }
537
-
538
527
  @property --_grid-columns-xl {
539
528
  syntax: "*";
540
529
  inherits: false;
541
530
  }
542
-
543
531
  @property --_grid-gap-base {
544
532
  syntax: "*";
545
533
  inherits: false;
546
534
  }
547
-
548
535
  @property --_grid-gap-sm {
549
536
  syntax: "*";
550
537
  inherits: false;
551
538
  }
552
-
553
539
  @property --_grid-gap-md {
554
540
  syntax: "*";
555
541
  inherits: false;
556
542
  }
557
-
558
543
  @property --_grid-gap-lg {
559
544
  syntax: "*";
560
545
  inherits: false;
561
546
  }
562
-
563
547
  @property --_grid-gap-xl {
564
548
  syntax: "*";
565
549
  inherits: false;
@@ -581,10 +565,8 @@
581
565
  Breakpoint thresholds mirror --ps1ui-breakpoint-* in tokens.css; kept
582
566
  inline as literal values because `@container` conditions cannot resolve
583
567
  `var()`. Keep them in sync. */
584
-
585
568
  /* Containment side-effect defense (align-self / justify-self / min-width) lives
586
569
  in components.css's shared selector list — see the block header there. */
587
-
588
570
  .ps1ui-grid {
589
571
  container: ps1ui-grid / inline-size;
590
572
  display: grid;
@@ -614,54 +596,45 @@
614
596
  --_gap: var(--_gap-sm);
615
597
  }
616
598
  }
617
-
618
599
  @container (min-width: 48rem) {
619
600
  .ps1ui-grid {
620
601
  --_columns: var(--_columns-md);
621
602
  --_gap: var(--_gap-md);
622
603
  }
623
604
  }
624
-
625
605
  @container (min-width: 64rem) {
626
606
  .ps1ui-grid {
627
607
  --_columns: var(--_columns-lg);
628
608
  --_gap: var(--_gap-lg);
629
609
  }
630
610
  }
631
-
632
611
  @container (min-width: 80rem) {
633
612
  .ps1ui-grid {
634
613
  --_columns: var(--_columns-xl);
635
614
  --_gap: var(--_gap-xl);
636
615
  }
637
616
  }
638
-
639
617
  /* Input custom properties emitted by GridItem.tsx per set responsive-prop
640
618
  breakpoint (`--_griditem-col-span-<bp>`). Registered as non-inheriting for
641
619
  the same reason as Stack — see Stack.css's @property block for the full
642
620
  rationale. Any new responsive axis added to GridItem.tsx must add matching
643
621
  @property blocks here. */
644
-
645
622
  @property --_griditem-col-span-base {
646
623
  syntax: "*";
647
624
  inherits: false;
648
625
  }
649
-
650
626
  @property --_griditem-col-span-sm {
651
627
  syntax: "*";
652
628
  inherits: false;
653
629
  }
654
-
655
630
  @property --_griditem-col-span-md {
656
631
  syntax: "*";
657
632
  inherits: false;
658
633
  }
659
-
660
634
  @property --_griditem-col-span-lg {
661
635
  syntax: "*";
662
636
  inherits: false;
663
637
  }
664
-
665
638
  @property --_griditem-col-span-xl {
666
639
  syntax: "*";
667
640
  inherits: false;
@@ -694,7 +667,6 @@
694
667
  the item still renders, the declaration is simply ignored. That matches
695
668
  the "no runtime enforcement of parent type" convention used across the
696
669
  library. */
697
-
698
670
  .ps1ui-griditem {
699
671
  --_col-span-base: var(--_griditem-col-span-base, 1);
700
672
  --_col-span-sm: var(--_griditem-col-span-sm, var(--_col-span-base));
@@ -712,25 +684,21 @@
712
684
  --_col-span: var(--_col-span-sm);
713
685
  }
714
686
  }
715
-
716
687
  @container (min-width: 48rem) {
717
688
  .ps1ui-griditem {
718
689
  --_col-span: var(--_col-span-md);
719
690
  }
720
691
  }
721
-
722
692
  @container (min-width: 64rem) {
723
693
  .ps1ui-griditem {
724
694
  --_col-span: var(--_col-span-lg);
725
695
  }
726
696
  }
727
-
728
697
  @container (min-width: 80rem) {
729
698
  .ps1ui-griditem {
730
699
  --_col-span: var(--_col-span-xl);
731
700
  }
732
701
  }
733
-
734
702
  .ps1ui-input {
735
703
  display: block;
736
704
  width: 100%;
@@ -772,7 +740,6 @@
772
740
  border-color: var(--ps1ui-color-border);
773
741
  background: var(--ps1ui-color-surface);
774
742
  }
775
-
776
743
  .ps1ui-label {
777
744
  display: inline-block;
778
745
  font-family: var(--ps1ui-font-mono);
@@ -781,14 +748,13 @@
781
748
  line-height: 1.6;
782
749
  color: var(--ps1ui-color-fg);
783
750
  }
784
-
785
- /* List — semantic <ul>/<ol> wrapper with terminal-style markers.
751
+ /* List — semantic <ul>/<ol> wrapper with plain-text markers.
786
752
 
787
753
  The global reset in base.css sets `list-style: none` on every <ul>/<ol>,
788
754
  which strips the UA bullet/number glyph. List re-introduces a marker via
789
755
  `::before` on each direct <li> — `-` for unordered, `NN.` for ordered —
790
756
  so the "reset removes bullets" side effect turns into an on-brand
791
- terminal glyph rather than a hole.
757
+ plain-text glyph rather than a hole.
792
758
 
793
759
  The ordered marker uses `counter(list-item)` — the browser-maintained
794
760
  list-item counter that already honours `<ol start>`, `<ol reversed>`,
@@ -875,11 +841,9 @@
875
841
  as `<li><List>…</List></li>`. The outer flex `gap` on `.ps1ui-list`
876
842
  only reaches direct children, so without this rule any nested list
877
843
  loses breathing room. */
878
-
879
844
  .ps1ui-list li .ps1ui-list {
880
845
  margin-top: var(--ps1ui-space-xs);
881
846
  }
882
-
883
847
  /* ListItem — no rules of its own.
884
848
 
885
849
  The `.ps1ui-listitem` class (naming parallels `.ps1ui-griditem`) is a
@@ -888,7 +852,6 @@
888
852
  `<List>` continues to render identically to `<ListItem>`. Keeping this
889
853
  file present (and imported from components.css) matches the
890
854
  per-component file layout the `new-component` skill enforces. */
891
-
892
855
  .ps1ui-card {
893
856
  background: var(--ps1ui-color-surface);
894
857
  border: 1px solid var(--ps1ui-color-border);
@@ -899,7 +862,6 @@
899
862
  font-size: var(--ps1ui-font-size-sm);
900
863
  line-height: 1.6;
901
864
  }
902
-
903
865
  /* Code — inline code chip.
904
866
 
905
867
  Companion to <CodeBlock>, but for inline fragments that live inside body
@@ -936,7 +898,6 @@
936
898
  in-repo callers (Text = 1.6, Heading = 1.3) leave ample slack;
937
899
  consumers who tighten line-height below ~1.2 need to widen it around
938
900
  inline `<Code>` themselves. */
939
-
940
901
  .ps1ui-code {
941
902
  font-family: var(--ps1ui-font-mono);
942
903
  font-size: 0.9em;
@@ -947,7 +908,6 @@
947
908
  color: var(--ps1ui-color-fg);
948
909
  overflow-wrap: anywhere;
949
910
  }
950
-
951
911
  .ps1ui-codeblock {
952
912
  margin: 0;
953
913
  padding: var(--ps1ui-space-md) var(--ps1ui-space-lg);
@@ -994,7 +954,6 @@
994
954
  /* Prism token classes, mapped to ps1ui code palette (see tokens.css).
995
955
  Refractor emits hast <span class="token <type>"> nodes; keep this list
996
956
  in sync with the token types produced by Prism grammars. */
997
-
998
957
  .ps1ui-codeblock .token.comment,
999
958
  .ps1ui-codeblock .token.prolog,
1000
959
  .ps1ui-codeblock .token.doctype,
@@ -1028,11 +987,9 @@
1028
987
 
1029
988
  /* diff: line-level markers get their own hue so "+" reads as added (green) and
1030
989
  "-" reads as removed (rose). Distinct from tag/string groups on purpose. */
1031
-
1032
990
  .ps1ui-codeblock .token.inserted {
1033
991
  color: var(--ps1ui-code-inserted);
1034
992
  }
1035
-
1036
993
  .ps1ui-codeblock .token.deleted {
1037
994
  color: var(--ps1ui-code-deleted);
1038
995
  }
@@ -1069,7 +1026,6 @@
1069
1026
  .ps1ui-codeblock .token.italic {
1070
1027
  font-style: italic;
1071
1028
  }
1072
-
1073
1029
  .ps1ui-details {
1074
1030
  background: var(--ps1ui-color-surface);
1075
1031
  border: 1px solid var(--ps1ui-color-border);
@@ -1099,17 +1055,15 @@
1099
1055
  /* Older WebKit (Safari < 17) renders a native disclosure triangle via its
1100
1056
  private ::-webkit-details-marker pseudo — `list-style: none` alone doesn't
1101
1057
  suppress it there. Explicitly hide it so ::before is the only glyph. */
1102
-
1103
1058
  .ps1ui-details__summary::-webkit-details-marker {
1104
1059
  display: none;
1105
1060
  }
1106
1061
 
1107
- /* Terminal-style disclosure marker: `▸` when closed, `▾` when open. Fixed
1062
+ /* Glyph disclosure marker: `▸` when closed, `▾` when open. Fixed
1108
1063
  inline-block width so the summary text baseline stays put across states —
1109
1064
  the two glyphs don't measure identically in the JetBrains Mono fallback
1110
1065
  monospace stack. Colored with fg-muted so the arrow reads as chrome, not
1111
1066
  content; ::before is not exposed to the a11y tree so this is safe. */
1112
-
1113
1067
  .ps1ui-details__summary::before {
1114
1068
  content: "▸";
1115
1069
  display: inline-block;
@@ -1130,7 +1084,6 @@
1130
1084
  itself — .ps1ui-details__summary:focus-visible is registered in
1131
1085
  styles/components.css's forced-colors grouped selector so the box-shadow
1132
1086
  ring falls back to a real outline in high-contrast mode. */
1133
-
1134
1087
  .ps1ui-details__summary:focus-visible {
1135
1088
  outline: none;
1136
1089
  box-shadow: var(--ps1ui-shadow-focus-ring);
@@ -1140,7 +1093,6 @@
1140
1093
  padding: var(--ps1ui-space-md);
1141
1094
  border-top: 1px solid var(--ps1ui-color-border);
1142
1095
  }
1143
-
1144
1096
  /* Input custom properties emitted by Stack.tsx per set responsive-prop
1145
1097
  breakpoint (`--_stack-<axis>-<bp>`). Registered as non-inheriting so a
1146
1098
  parent Stack's `--_stack-gap-md` (etc.) does NOT cascade into a nested
@@ -1158,127 +1110,102 @@
1158
1110
  every axis so a forgotten one trips CI immediately. `unset` / `revert` /
1159
1111
  `revert-layer` are NOT interchangeable with this approach for custom
1160
1112
  properties — they resolve to the inherited value, defeating the block. */
1161
-
1162
1113
  @property --_stack-direction-base {
1163
1114
  syntax: "*";
1164
1115
  inherits: false;
1165
1116
  }
1166
-
1167
1117
  @property --_stack-direction-sm {
1168
1118
  syntax: "*";
1169
1119
  inherits: false;
1170
1120
  }
1171
-
1172
1121
  @property --_stack-direction-md {
1173
1122
  syntax: "*";
1174
1123
  inherits: false;
1175
1124
  }
1176
-
1177
1125
  @property --_stack-direction-lg {
1178
1126
  syntax: "*";
1179
1127
  inherits: false;
1180
1128
  }
1181
-
1182
1129
  @property --_stack-direction-xl {
1183
1130
  syntax: "*";
1184
1131
  inherits: false;
1185
1132
  }
1186
-
1187
1133
  @property --_stack-gap-base {
1188
1134
  syntax: "*";
1189
1135
  inherits: false;
1190
1136
  }
1191
-
1192
1137
  @property --_stack-gap-sm {
1193
1138
  syntax: "*";
1194
1139
  inherits: false;
1195
1140
  }
1196
-
1197
1141
  @property --_stack-gap-md {
1198
1142
  syntax: "*";
1199
1143
  inherits: false;
1200
1144
  }
1201
-
1202
1145
  @property --_stack-gap-lg {
1203
1146
  syntax: "*";
1204
1147
  inherits: false;
1205
1148
  }
1206
-
1207
1149
  @property --_stack-gap-xl {
1208
1150
  syntax: "*";
1209
1151
  inherits: false;
1210
1152
  }
1211
-
1212
1153
  @property --_stack-align-base {
1213
1154
  syntax: "*";
1214
1155
  inherits: false;
1215
1156
  }
1216
-
1217
1157
  @property --_stack-align-sm {
1218
1158
  syntax: "*";
1219
1159
  inherits: false;
1220
1160
  }
1221
-
1222
1161
  @property --_stack-align-md {
1223
1162
  syntax: "*";
1224
1163
  inherits: false;
1225
1164
  }
1226
-
1227
1165
  @property --_stack-align-lg {
1228
1166
  syntax: "*";
1229
1167
  inherits: false;
1230
1168
  }
1231
-
1232
1169
  @property --_stack-align-xl {
1233
1170
  syntax: "*";
1234
1171
  inherits: false;
1235
1172
  }
1236
-
1237
1173
  @property --_stack-justify-base {
1238
1174
  syntax: "*";
1239
1175
  inherits: false;
1240
1176
  }
1241
-
1242
1177
  @property --_stack-justify-sm {
1243
1178
  syntax: "*";
1244
1179
  inherits: false;
1245
1180
  }
1246
-
1247
1181
  @property --_stack-justify-md {
1248
1182
  syntax: "*";
1249
1183
  inherits: false;
1250
1184
  }
1251
-
1252
1185
  @property --_stack-justify-lg {
1253
1186
  syntax: "*";
1254
1187
  inherits: false;
1255
1188
  }
1256
-
1257
1189
  @property --_stack-justify-xl {
1258
1190
  syntax: "*";
1259
1191
  inherits: false;
1260
1192
  }
1261
-
1262
1193
  @property --_stack-wrap-base {
1263
1194
  syntax: "*";
1264
1195
  inherits: false;
1265
1196
  }
1266
-
1267
1197
  @property --_stack-wrap-sm {
1268
1198
  syntax: "*";
1269
1199
  inherits: false;
1270
1200
  }
1271
-
1272
1201
  @property --_stack-wrap-md {
1273
1202
  syntax: "*";
1274
1203
  inherits: false;
1275
1204
  }
1276
-
1277
1205
  @property --_stack-wrap-lg {
1278
1206
  syntax: "*";
1279
1207
  inherits: false;
1280
1208
  }
1281
-
1282
1209
  @property --_stack-wrap-xl {
1283
1210
  syntax: "*";
1284
1211
  inherits: false;
@@ -1311,10 +1238,8 @@
1311
1238
  Each default matches the property's own initial-value behaviour so
1312
1239
  omitting the corresponding prop is equivalent to not writing the
1313
1240
  declaration at all. */
1314
-
1315
1241
  /* Containment side-effect defense (align-self / justify-self / min-width) lives
1316
1242
  in components.css's shared selector list — see the block header there. */
1317
-
1318
1243
  .ps1ui-stack {
1319
1244
  container: ps1ui-stack / inline-size;
1320
1245
  display: flex;
@@ -1371,7 +1296,6 @@
1371
1296
  --_wrap: var(--_wrap-sm);
1372
1297
  }
1373
1298
  }
1374
-
1375
1299
  @container (min-width: 48rem) {
1376
1300
  .ps1ui-stack {
1377
1301
  --_direction: var(--_direction-md);
@@ -1381,7 +1305,6 @@
1381
1305
  --_wrap: var(--_wrap-md);
1382
1306
  }
1383
1307
  }
1384
-
1385
1308
  @container (min-width: 64rem) {
1386
1309
  .ps1ui-stack {
1387
1310
  --_direction: var(--_direction-lg);
@@ -1391,7 +1314,6 @@
1391
1314
  --_wrap: var(--_wrap-lg);
1392
1315
  }
1393
1316
  }
1394
-
1395
1317
  @container (min-width: 80rem) {
1396
1318
  .ps1ui-stack {
1397
1319
  --_direction: var(--_direction-xl);
@@ -1401,8 +1323,7 @@
1401
1323
  --_wrap: var(--_wrap-xl);
1402
1324
  }
1403
1325
  }
1404
-
1405
- /* Table — semantic <table> with terminal-style grid borders, wrapped in a
1326
+ /* Table — semantic <table> with box-drawing-style grid borders, wrapped in a
1406
1327
  horizontal-scroll container.
1407
1328
 
1408
1329
  Scroll wrapper: `display: table` boxes don't honor overflow, so Table.tsx
@@ -1414,8 +1335,8 @@
1414
1335
  high-contrast mode; the grouped rule restores a real outline).
1415
1336
 
1416
1337
  Grid borders: `border-collapse: collapse` + a uniform 1px cell border in
1417
- --ps1ui-color-border draw the closed single-stroke grid a terminal
1418
- box-drawing table (┌─┬─┐) renders — collapsing is what merges adjacent
1338
+ --ps1ui-color-border draw the closed single-stroke grid a box-drawing
1339
+ table (┌─┬─┐) renders — collapsing is what merges adjacent
1419
1340
  cell edges into one shared rule. The header row separates on two axes:
1420
1341
  a surface background and a border-strong bottom rule. That rule is
1421
1342
  declared on the <th> (cell origin) deliberately — in border-collapse
@@ -1447,7 +1368,6 @@
1447
1368
  /* No width override — the UA's shrink-to-fit keeps the grid hugging its
1448
1369
  content like a box-drawing table. Call sites that want a full-bleed table
1449
1370
  (e.g. a props table filling a docs column) opt in with `width: 100%`. */
1450
-
1451
1371
  .ps1ui-table {
1452
1372
  border-collapse: collapse;
1453
1373
  color: var(--ps1ui-color-fg);
@@ -1478,7 +1398,6 @@
1478
1398
  .ps1ui-table thead th {
1479
1399
  border-bottom-color: var(--ps1ui-color-border-strong);
1480
1400
  }
1481
-
1482
1401
  /* Thead — no rules of its own.
1483
1402
 
1484
1403
  The `.ps1ui-thead` class is a stable API hook for consumers. All header
@@ -1487,7 +1406,6 @@
1487
1406
  continues to render identically to <Thead>. Keeping this file present
1488
1407
  (and imported from components.css) matches the per-component file layout
1489
1408
  the `new-component` skill enforces — same precedent as ListItem.css. */
1490
-
1491
1409
  /* Tbody — no rules of its own.
1492
1410
 
1493
1411
  The `.ps1ui-tbody` class is a stable API hook for consumers. All cell
@@ -1496,7 +1414,6 @@
1496
1414
  file present (and imported from components.css) matches the per-component
1497
1415
  file layout the `new-component` skill enforces — same precedent as
1498
1416
  ListItem.css. */
1499
-
1500
1417
  /* Tr — no rules of its own.
1501
1418
 
1502
1419
  The `.ps1ui-tr` class is a stable API hook for consumers. All cell
@@ -1505,7 +1422,6 @@
1505
1422
  present (and imported from components.css) matches the per-component file
1506
1423
  layout the `new-component` skill enforces — same precedent as
1507
1424
  ListItem.css. */
1508
-
1509
1425
  /* Th — no rules of its own.
1510
1426
 
1511
1427
  The `.ps1ui-th` class is a stable API hook for consumers. All header-cell
@@ -1514,7 +1430,6 @@
1514
1430
  render identically to <Th>. Keeping this file present (and imported from
1515
1431
  components.css) matches the per-component file layout the `new-component`
1516
1432
  skill enforces — same precedent as ListItem.css. */
1517
-
1518
1433
  /* Td — no rules of its own.
1519
1434
 
1520
1435
  The `.ps1ui-td` class is a stable API hook for consumers. All data-cell
@@ -1523,57 +1438,46 @@
1523
1438
  <Td>. Keeping this file present (and imported from components.css) matches
1524
1439
  the per-component file layout the `new-component` skill enforces — same
1525
1440
  precedent as ListItem.css. */
1526
-
1527
1441
  /* Input custom properties emitted by Text.tsx per set responsive-prop
1528
1442
  breakpoint (`--_text-<axis>-<bp>`). Registered as non-inheriting for the
1529
1443
  same reason as Stack — see Stack.css's @property block for the full
1530
1444
  rationale. */
1531
-
1532
1445
  @property --_text-size-base {
1533
1446
  syntax: "*";
1534
1447
  inherits: false;
1535
1448
  }
1536
-
1537
1449
  @property --_text-size-sm {
1538
1450
  syntax: "*";
1539
1451
  inherits: false;
1540
1452
  }
1541
-
1542
1453
  @property --_text-size-md {
1543
1454
  syntax: "*";
1544
1455
  inherits: false;
1545
1456
  }
1546
-
1547
1457
  @property --_text-size-lg {
1548
1458
  syntax: "*";
1549
1459
  inherits: false;
1550
1460
  }
1551
-
1552
1461
  @property --_text-size-xl {
1553
1462
  syntax: "*";
1554
1463
  inherits: false;
1555
1464
  }
1556
-
1557
1465
  @property --_text-weight-base {
1558
1466
  syntax: "*";
1559
1467
  inherits: false;
1560
1468
  }
1561
-
1562
1469
  @property --_text-weight-sm {
1563
1470
  syntax: "*";
1564
1471
  inherits: false;
1565
1472
  }
1566
-
1567
1473
  @property --_text-weight-md {
1568
1474
  syntax: "*";
1569
1475
  inherits: false;
1570
1476
  }
1571
-
1572
1477
  @property --_text-weight-lg {
1573
1478
  syntax: "*";
1574
1479
  inherits: false;
1575
1480
  }
1576
-
1577
1481
  @property --_text-weight-xl {
1578
1482
  syntax: "*";
1579
1483
  inherits: false;
@@ -1600,7 +1504,6 @@
1600
1504
  NOT the UA `<strong>` style). Users who need the UA-strong appearance
1601
1505
  should pair `as="strong"` with `weight="bold"` for consistency across
1602
1506
  engines. */
1603
-
1604
1507
  .ps1ui-text {
1605
1508
  margin: 0;
1606
1509
  font-family: var(--ps1ui-font-mono);
@@ -1631,21 +1534,18 @@
1631
1534
  --_weight: var(--_weight-sm);
1632
1535
  }
1633
1536
  }
1634
-
1635
1537
  @container (min-width: 48rem) {
1636
1538
  .ps1ui-text {
1637
1539
  --_size: var(--_size-md);
1638
1540
  --_weight: var(--_weight-md);
1639
1541
  }
1640
1542
  }
1641
-
1642
1543
  @container (min-width: 64rem) {
1643
1544
  .ps1ui-text {
1644
1545
  --_size: var(--_size-lg);
1645
1546
  --_weight: var(--_weight-lg);
1646
1547
  }
1647
1548
  }
1648
-
1649
1549
  @container (min-width: 80rem) {
1650
1550
  .ps1ui-text {
1651
1551
  --_size: var(--_size-xl);
@@ -1667,73 +1567,104 @@
1667
1567
  .ps1ui-text--body {
1668
1568
  color: var(--ps1ui-color-fg);
1669
1569
  }
1670
-
1671
1570
  .ps1ui-text--muted {
1672
1571
  color: var(--ps1ui-color-fg-muted);
1673
1572
  }
1674
-
1675
1573
  .ps1ui-text--subtle {
1676
1574
  color: var(--ps1ui-color-fg-subtle);
1677
1575
  }
1678
-
1679
1576
  .ps1ui-text--primary {
1680
1577
  color: var(--ps1ui-color-primary);
1681
1578
  }
1682
-
1683
1579
  .ps1ui-text--accent {
1684
1580
  color: var(--ps1ui-color-accent);
1685
1581
  }
1582
+ .ps1ui-text--danger {
1583
+ color: var(--ps1ui-color-danger);
1584
+ }
1585
+ .ps1ui-textarea {
1586
+ display: block;
1587
+ width: 100%;
1588
+ box-sizing: border-box;
1589
+ font-family: var(--ps1ui-font-mono);
1590
+ font-size: var(--ps1ui-font-size-sm);
1591
+ line-height: 1.5;
1592
+ color: var(--ps1ui-color-fg);
1593
+ background: var(--ps1ui-color-bg);
1594
+ border: 1px solid var(--ps1ui-color-border-strong);
1595
+ border-radius: var(--ps1ui-radius-md);
1596
+ padding: var(--ps1ui-control-padding-y) var(--ps1ui-space-md);
1597
+ resize: vertical;
1598
+ transition:
1599
+ border-color var(--ps1ui-transition-fast),
1600
+ box-shadow var(--ps1ui-transition-fast);
1601
+ }
1602
+
1603
+ .ps1ui-textarea::-moz-placeholder {
1604
+ color: var(--ps1ui-color-fg-subtle);
1605
+ }
1606
+
1607
+ .ps1ui-textarea::placeholder {
1608
+ color: var(--ps1ui-color-fg-subtle);
1609
+ }
1610
+
1611
+ .ps1ui-textarea:hover:not(:disabled) {
1612
+ border-color: var(--ps1ui-color-primary);
1613
+ }
1614
+
1615
+ .ps1ui-textarea:focus {
1616
+ outline: none;
1617
+ border-color: var(--ps1ui-color-primary);
1618
+ box-shadow: var(--ps1ui-shadow-focus-ring);
1619
+ }
1686
1620
 
1621
+ .ps1ui-textarea:disabled {
1622
+ cursor: not-allowed;
1623
+ color: var(--ps1ui-color-fg-subtle);
1624
+ border-color: var(--ps1ui-color-border);
1625
+ background: var(--ps1ui-color-surface);
1626
+ resize: none;
1627
+ }
1687
1628
  /* Input custom properties emitted by Heading.tsx per set responsive-prop
1688
1629
  breakpoint (`--_heading-<axis>-<bp>`). Registered as non-inheriting for
1689
1630
  the same reason as Stack — see Stack.css's @property block for the full
1690
1631
  rationale. */
1691
-
1692
1632
  @property --_heading-size-base {
1693
1633
  syntax: "*";
1694
1634
  inherits: false;
1695
1635
  }
1696
-
1697
1636
  @property --_heading-size-sm {
1698
1637
  syntax: "*";
1699
1638
  inherits: false;
1700
1639
  }
1701
-
1702
1640
  @property --_heading-size-md {
1703
1641
  syntax: "*";
1704
1642
  inherits: false;
1705
1643
  }
1706
-
1707
1644
  @property --_heading-size-lg {
1708
1645
  syntax: "*";
1709
1646
  inherits: false;
1710
1647
  }
1711
-
1712
1648
  @property --_heading-size-xl {
1713
1649
  syntax: "*";
1714
1650
  inherits: false;
1715
1651
  }
1716
-
1717
1652
  @property --_heading-weight-base {
1718
1653
  syntax: "*";
1719
1654
  inherits: false;
1720
1655
  }
1721
-
1722
1656
  @property --_heading-weight-sm {
1723
1657
  syntax: "*";
1724
1658
  inherits: false;
1725
1659
  }
1726
-
1727
1660
  @property --_heading-weight-md {
1728
1661
  syntax: "*";
1729
1662
  inherits: false;
1730
1663
  }
1731
-
1732
1664
  @property --_heading-weight-lg {
1733
1665
  syntax: "*";
1734
1666
  inherits: false;
1735
1667
  }
1736
-
1737
1668
  @property --_heading-weight-xl {
1738
1669
  syntax: "*";
1739
1670
  inherits: false;
@@ -1762,7 +1693,6 @@
1762
1693
  base breakpoint, these floors keep the component in a legible default
1763
1694
  (font-size sm, font-weight normal). Text.css uses a live fallback path
1764
1695
  because Text.tsx does not normalize its size/weight props. */
1765
-
1766
1696
  .ps1ui-heading {
1767
1697
  margin: 0;
1768
1698
  font-family: var(--ps1ui-font-mono);
@@ -1797,21 +1727,18 @@
1797
1727
  --_weight: var(--_weight-sm);
1798
1728
  }
1799
1729
  }
1800
-
1801
1730
  @container (min-width: 48rem) {
1802
1731
  .ps1ui-heading {
1803
1732
  --_size: var(--_size-md);
1804
1733
  --_weight: var(--_weight-md);
1805
1734
  }
1806
1735
  }
1807
-
1808
1736
  @container (min-width: 64rem) {
1809
1737
  .ps1ui-heading {
1810
1738
  --_size: var(--_size-lg);
1811
1739
  --_weight: var(--_weight-lg);
1812
1740
  }
1813
1741
  }
1814
-
1815
1742
  @container (min-width: 80rem) {
1816
1743
  .ps1ui-heading {
1817
1744
  --_size: var(--_size-xl);
@@ -1840,7 +1767,6 @@
1840
1767
  tests (Stack.test.tsx / Container.test.tsx / Grid.test.tsx / PS1Root.test.tsx's
1841
1768
  "resists collapse via align-self: stretch in shrink-wrap flex parent") catch
1842
1769
  accidental removal. */
1843
-
1844
1770
  .ps1ui-root,
1845
1771
  .ps1ui-container,
1846
1772
  .ps1ui-grid,
@@ -1856,14 +1782,14 @@
1856
1782
  box-shadow-focus components to this list. Component-specific forced-colors
1857
1783
  adjustments (e.g. Checkbox's indeterminate bar) stay in their own CSS.
1858
1784
  Tested in src/styles/forced-colors.test.tsx. */
1859
-
1860
1785
  @media (forced-colors: active) {
1861
1786
  .ps1ui-button:focus-visible,
1862
1787
  .ps1ui-checkbox:focus-visible,
1863
1788
  .ps1ui-codeblock:focus-visible,
1864
1789
  .ps1ui-details__summary:focus-visible,
1865
1790
  .ps1ui-input:focus,
1866
- .ps1ui-table__scroller:focus-visible {
1791
+ .ps1ui-table__scroller:focus-visible,
1792
+ .ps1ui-textarea:focus {
1867
1793
  outline: 2px solid CanvasText;
1868
1794
  outline-offset: 2px;
1869
1795
  }