@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
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ <h1 align="center">
2
+ @ps1ui/core
3
+ </h1>
4
+
5
+ <p align="center">
6
+ <i>For engineers who think in monospace.</i>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://koki-develop.github.io/ps1ui/">Documentation</a> | <a href="https://koki-develop.github.io/ps1ui/getting-started/">Getting Started</a> | <a href="https://koki-develop.github.io/ps1ui/components/">Components</a> | <a href="https://koki-develop.github.io/ps1ui/storybook/">Storybook</a>
11
+ </p>
12
+
13
+ ## Install
14
+
15
+ React 19 is a peer dependency — install it alongside the package.
16
+
17
+ ```bash
18
+ pnpm add @ps1ui/core react react-dom
19
+ ```
20
+
21
+ ## Getting Started
22
+
23
+ Import the stylesheet once at your app's entry point. The JS entry ships no styles — the CSS brings the design tokens, the dark canvas, and every component's styles.
24
+
25
+ ```tsx
26
+ import "@ps1ui/core/styles.css";
27
+ ```
28
+
29
+ Wrap your tree in `PS1Root`. It establishes the container-query context that responsive props resolve against — without it, responsive props silently fall back to their `base` values.
30
+
31
+ ```tsx
32
+ import "@ps1ui/core/styles.css";
33
+ import { PS1Root, Button, Heading } from "@ps1ui/core";
34
+
35
+ export function App() {
36
+ return (
37
+ <PS1Root>
38
+ <Heading level={1}>Hello, PS1 UI</Heading>
39
+ <Button variant="primary">Click me</Button>
40
+ </PS1Root>
41
+ );
42
+ }
43
+ ```
44
+
45
+ Browse the full component catalog at [koki-develop.github.io/ps1ui/components](https://koki-develop.github.io/ps1ui/components/).
46
+
47
+ ## License
48
+
49
+ [MIT](./LICENSE)
package/dist/base.css CHANGED
@@ -19,6 +19,17 @@
19
19
  --ps1ui-color-primary-active: #3fb950;
20
20
  --ps1ui-color-primary-fg: #0b0f14;
21
21
  --ps1ui-color-accent: #ffa657;
22
+ /* Destructive / error signalling. Hue sits in the rose-coral band the code
23
+ palette already inhabits (--ps1ui-code-keyword / -tag / -deleted) so the
24
+ surface reads as one system; luminance is tuned so the base color hits
25
+ ≥ 4.5:1 vs BOTH --ps1ui-color-bg and --ps1ui-color-surface (verified by
26
+ Text.contrast.test.tsx) and danger-fg on each danger-* fill hits the
27
+ same threshold (verified by Button.contrast.test.tsx). Hover/active
28
+ progressively darken while staying above contrast. */
29
+ --ps1ui-color-danger: #ff7d8d;
30
+ --ps1ui-color-danger-hover: #f0637a;
31
+ --ps1ui-color-danger-active: #e04a60;
32
+ --ps1ui-color-danger-fg: #0b0f14;
22
33
  --ps1ui-color-focus-ring-soft: color-mix(in srgb, var(--ps1ui-color-primary) 14%, transparent);
23
34
  --ps1ui-shadow-focus-ring: 0 0 0 3px var(--ps1ui-color-focus-ring-soft);
24
35
 
@@ -88,6 +99,7 @@
88
99
 
89
100
  --ps1ui-transition-fast: 120ms ease;
90
101
  }
102
+
91
103
  /* ==========================================================================
92
104
  * Reset
93
105
  * --------------------------------------------------------------------------
@@ -107,6 +119,7 @@
107
119
  * layouts unless you set display:inline explicitly), <ul>/<ol> lose their
108
120
  * bullets. Locked in by src/styles/reset.test.tsx.
109
121
  * ========================================================================== */
122
+
110
123
  /* Box model + border baseline. Applied to `*` only — NOT to ::before/::after
111
124
  /::backdrop/::file-selector-button. Pseudo-elements with border-drawn
112
125
  glyphs (the classic rotated-L checkmark, tooltip carets, radio-dot rings)
@@ -120,6 +133,7 @@
120
133
  box-sizing: border-box;
121
134
  border: 0 solid;
122
135
  }
136
+
123
137
  body,
124
138
  blockquote,
125
139
  dl,
@@ -140,6 +154,7 @@ pre,
140
154
  ul {
141
155
  margin: 0;
142
156
  }
157
+
143
158
  fieldset,
144
159
  legend,
145
160
  menu,
@@ -147,6 +162,7 @@ ol,
147
162
  ul {
148
163
  padding: 0;
149
164
  }
165
+
150
166
  html {
151
167
  color-scheme: dark;
152
168
  line-height: 1.5;
@@ -157,11 +173,13 @@ html {
157
173
  tab-size: 4;
158
174
  text-rendering: optimizeLegibility;
159
175
  }
176
+
160
177
  body {
161
178
  min-height: 100dvh;
162
179
  -webkit-font-smoothing: antialiased;
163
180
  -moz-osx-font-smoothing: grayscale;
164
181
  }
182
+
165
183
  /* Heading responsibility belongs to <Heading>; raw h1..h6 inherit so a bare
166
184
  <h1> outside <Heading> doesn't blow up to the UA default. */
167
185
  h1,
@@ -175,15 +193,18 @@ h6 {
175
193
  text-wrap: balance;
176
194
  overflow-wrap: break-word;
177
195
  }
196
+
178
197
  p {
179
198
  text-wrap: pretty;
180
199
  overflow-wrap: break-word;
181
200
  }
201
+
182
202
  ol,
183
203
  ul,
184
204
  menu {
185
205
  list-style: none;
186
206
  }
207
+
187
208
  img,
188
209
  svg,
189
210
  video,
@@ -195,17 +216,20 @@ object {
195
216
  display: block;
196
217
  vertical-align: middle;
197
218
  }
219
+
198
220
  img,
199
221
  video {
200
222
  max-width: 100%;
201
223
  height: auto;
202
224
  }
225
+
203
226
  /* Anchor responsibility belongs to <Anchor>; raw <a> inherits color/decoration
204
227
  so it doesn't paint as UA blue underline against the dark canvas. */
205
228
  a {
206
229
  color: inherit;
207
230
  text-decoration: inherit;
208
231
  }
232
+
209
233
  /* UA still refuses to inherit font/color into form controls in 2026.
210
234
  Inherit font-feature-settings / font-variation-settings too so JetBrains
211
235
  Mono Variable's axes stay live inside <input> / <textarea>. */
@@ -224,13 +248,16 @@ textarea,
224
248
  border-radius: 0;
225
249
  opacity: 1;
226
250
  }
251
+
227
252
  :where(select:is([multiple], [size])) optgroup {
228
253
  font-weight: bolder;
229
254
  }
255
+
230
256
  button,
231
257
  select {
232
258
  text-transform: none;
233
259
  }
260
+
234
261
  button,
235
262
  input:where([type="button"], [type="reset"], [type="submit"]),
236
263
  ::file-selector-button {
@@ -238,12 +265,15 @@ input:where([type="button"], [type="reset"], [type="submit"]),
238
265
  appearance: button;
239
266
  -webkit-appearance: button;
240
267
  }
268
+
241
269
  button {
242
270
  cursor: pointer;
243
271
  }
272
+
244
273
  textarea {
245
274
  resize: vertical;
246
275
  }
276
+
247
277
  /* `in srgb` for consistency with the library's other color-mix calls. The
248
278
  space is irrelevant when mixing with `transparent` (premultiplied-alpha
249
279
  interpolation, CSS Color 4), so diverging from Tailwind Preflight's oklab
@@ -256,23 +286,28 @@ textarea {
256
286
  opacity: 1;
257
287
  color: color-mix(in srgb, currentcolor 55%, transparent);
258
288
  }
289
+
259
290
  ::-webkit-date-and-time-value {
260
291
  min-height: 1lh;
261
292
  text-align: inherit;
262
293
  }
294
+
263
295
  ::-webkit-datetime-edit {
264
296
  display: inline-flex;
265
297
  padding: 0;
266
298
  }
299
+
267
300
  ::-webkit-datetime-edit-fields-wrapper {
268
301
  padding: 0;
269
302
  }
303
+
270
304
  ::-webkit-inner-spin-button,
271
305
  ::-webkit-outer-spin-button {
272
306
  -webkit-appearance: none;
273
307
  margin: 0;
274
308
  height: auto;
275
309
  }
310
+
276
311
  /* number spinner: Chromium is already handled by ::-webkit-inner/outer-spin-button
277
312
  { appearance: none } above. Firefox needs its own vendor prefix — the unprefixed
278
313
  `appearance: textfield` is deprecated per CSS UI Level 4 (moved to compatibility
@@ -280,27 +315,34 @@ textarea {
280
315
  input[type="number"] {
281
316
  -moz-appearance: textfield;
282
317
  }
318
+
283
319
  [type="search"] {
284
320
  -webkit-appearance: textfield;
285
321
  outline-offset: -2px;
286
322
  }
323
+
287
324
  ::-webkit-search-decoration,
288
325
  ::-webkit-search-cancel-button {
289
326
  -webkit-appearance: none;
290
327
  }
328
+
291
329
  :-moz-ui-invalid {
292
330
  box-shadow: none;
293
331
  }
332
+
294
333
  :-moz-focusring:where(:not(iframe)) {
295
334
  outline: auto;
296
335
  }
336
+
297
337
  b,
298
338
  strong {
299
339
  font-weight: bolder;
300
340
  }
341
+
301
342
  small {
302
343
  font-size: 80%;
303
344
  }
345
+
304
346
  code,
305
347
  kbd,
306
348
  samp,
@@ -308,14 +350,17 @@ pre {
308
350
  font-family: var(--ps1ui-font-mono);
309
351
  font-size: 1em;
310
352
  }
353
+
311
354
  abbr:where([title]) {
312
355
  -webkit-text-decoration: underline dotted;
313
356
  text-decoration: underline dotted;
314
357
  }
358
+
315
359
  q::before,
316
360
  q::after {
317
361
  content: "";
318
362
  }
363
+
319
364
  /* Keeps sub/sup from expanding the parent line-height. */
320
365
  sub,
321
366
  sup {
@@ -324,34 +369,42 @@ sup {
324
369
  position: relative;
325
370
  vertical-align: baseline;
326
371
  }
372
+
327
373
  sub {
328
374
  bottom: -0.25em;
329
375
  }
376
+
330
377
  sup {
331
378
  top: -0.5em;
332
379
  }
380
+
333
381
  table {
334
382
  text-indent: 0;
335
383
  border-color: inherit;
336
384
  border-collapse: collapse;
337
385
  }
386
+
338
387
  hr {
339
388
  height: 0;
340
389
  color: inherit;
341
390
  border-top: 1px solid;
342
391
  }
392
+
343
393
  summary {
344
394
  display: list-item;
345
395
  }
396
+
346
397
  /* [hidden] would lose to `display: flex` etc. without this. */
347
398
  [hidden]:where(:not([hidden="until-found"])) {
348
399
  display: none !important;
349
400
  }
401
+
350
402
  @media (prefers-reduced-motion: no-preference) {
351
403
  html {
352
404
  interpolate-size: allow-keywords;
353
405
  }
354
406
  }
407
+
355
408
  /* ==========================================================================
356
409
  * Ambient environment
357
410
  * --------------------------------------------------------------------------
@@ -363,15 +416,18 @@ summary {
363
416
  * area would reveal the light UA default and flash white on scroll. `color`
364
417
  * and `font-family` cascade down to `body` and descendants naturally.
365
418
  * ========================================================================== */
419
+
366
420
  html {
367
421
  background: var(--ps1ui-color-bg);
368
422
  color: var(--ps1ui-color-fg);
369
423
  font-family: var(--ps1ui-font-mono);
370
424
  }
425
+
371
426
  ::-moz-selection {
372
427
  background: var(--ps1ui-color-primary);
373
428
  color: var(--ps1ui-color-primary-fg);
374
429
  }
430
+
375
431
  ::selection {
376
432
  background: var(--ps1ui-color-primary);
377
433
  color: var(--ps1ui-color-primary-fg);
@@ -2,7 +2,9 @@ import { ComponentPropsWithoutRef, ElementType } from "react";
2
2
  //#region src/components/Anchor/Anchor.d.ts
3
3
  type AnchorVariant = "primary" | "subtle";
4
4
  type AnchorOwnProps<E extends ElementType> = {
5
+ /** Element or component to render instead of the native <a> — e.g. a router Link. */
5
6
  as?: E;
7
+ /** Visual style: "primary" for standalone links, "subtle" for links inside running text. */
6
8
  variant?: AnchorVariant;
7
9
  };
8
10
  type AnchorProps<E extends ElementType = "a"> = AnchorOwnProps<E> & Omit<ComponentPropsWithoutRef<E>, keyof AnchorOwnProps<E>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Anchor.mjs","names":[],"sources":["../../../src/components/Anchor/Anchor.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementType } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\nexport type AnchorVariant = \"primary\" | \"subtle\";\n\ntype AnchorOwnProps<E extends ElementType> = {\n as?: E;\n variant?: AnchorVariant;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow —\n// same reasoning as Text: with a polymorphic `as` prop TypeScript cannot correctly narrow the\n// `ref` prop's type. Anchor accepts both string tags and React component types (`as={NextLink}`),\n// so a loose ref type would silently accept mismatched refs; dropping ref from the prop type is\n// safer than a misleading loose type.\nexport type AnchorProps<E extends ElementType = \"a\"> = AnchorOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof AnchorOwnProps<E>>;\n\nexport function Anchor<E extends ElementType = \"a\">({\n as,\n variant = \"primary\",\n className,\n ...rest\n}: AnchorProps<E>) {\n const tag = as ?? \"a\";\n const classes = cx(\"ps1ui-anchor\", `ps1ui-anchor--${variant}`, className);\n return createElement(tag, { ...rest, className: classes });\n}\n"],"mappings":";;;AAmBA,SAAgB,OAAoC,EAClD,IACA,UAAU,WACV,WACA,GAAG,QACc;CACjB,MAAM,MAAM,MAAM;CAClB,MAAM,UAAU,GAAG,gBAAgB,iBAAiB,WAAW,SAAS;CACxE,OAAO,cAAc,KAAK;EAAE,GAAG;EAAM,WAAW;CAAQ,CAAC;AAC3D"}
1
+ {"version":3,"file":"Anchor.mjs","names":[],"sources":["../../../src/components/Anchor/Anchor.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementType } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\nexport type AnchorVariant = \"primary\" | \"subtle\";\n\ntype AnchorOwnProps<E extends ElementType> = {\n /** Element or component to render instead of the native <a> — e.g. a router Link. */\n as?: E;\n /** Visual style: \"primary\" for standalone links, \"subtle\" for links inside running text. */\n variant?: AnchorVariant;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow —\n// same reasoning as Text: with a polymorphic `as` prop TypeScript cannot correctly narrow the\n// `ref` prop's type. Anchor accepts both string tags and React component types (`as={NextLink}`),\n// so a loose ref type would silently accept mismatched refs; dropping ref from the prop type is\n// safer than a misleading loose type.\nexport type AnchorProps<E extends ElementType = \"a\"> = AnchorOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof AnchorOwnProps<E>>;\n\nexport function Anchor<E extends ElementType = \"a\">({\n as,\n variant = \"primary\",\n className,\n ...rest\n}: AnchorProps<E>) {\n const tag = as ?? \"a\";\n const classes = cx(\"ps1ui-anchor\", `ps1ui-anchor--${variant}`, className);\n return createElement(tag, { ...rest, className: classes });\n}\n"],"mappings":";;;AAqBA,SAAgB,OAAoC,EAClD,IACA,UAAU,WACV,WACA,GAAG,QACc;CACjB,MAAM,MAAM,MAAM;CAClB,MAAM,UAAU,GAAG,gBAAgB,iBAAiB,WAAW,SAAS;CACxE,OAAO,cAAc,KAAK;EAAE,GAAG;EAAM,WAAW;CAAQ,CAAC;AAC3D"}
@@ -1,8 +1,10 @@
1
1
  import { ComponentPropsWithoutRef, ElementType } from "react";
2
2
  //#region src/components/Button/Button.d.ts
3
- type ButtonVariant = "primary" | "secondary";
3
+ type ButtonVariant = "primary" | "secondary" | "danger";
4
4
  type ButtonOwnProps<E extends ElementType> = {
5
+ /** Element or component to render instead of the native <button> — e.g. "a" or a router Link. */
5
6
  as?: E;
7
+ /** Visual style: "primary" for the main action, "secondary" for supporting actions, "danger" for destructive actions. */
6
8
  variant?: ButtonVariant;
7
9
  };
8
10
  /**
@@ -16,6 +18,10 @@ type ButtonOwnProps<E extends ElementType> = {
16
18
  * unavailable, render a native `<Button disabled>` (or no link at all) instead of a
17
19
  * link. Untyped JS callers and `as={Component}` escapes that pass `disabled` anyway
18
20
  * get a leaked no-op attribute — no visual or behavioral disabling happens.
21
+ *
22
+ * `type` on the native tag: when rendering a native <button>, `type` defaults to
23
+ * "button" (not the browser's implicit "submit"), so a Button inside a <form>
24
+ * never submits implicitly — pass type="submit" explicitly to submit.
19
25
  */
20
26
  type ButtonProps<E extends ElementType = "button"> = ButtonOwnProps<E> & Omit<ComponentPropsWithoutRef<E>, keyof ButtonOwnProps<E>>;
21
27
  declare function Button<E extends ElementType = "button">({ as, variant, className, ...rest }: ButtonProps<E>): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Button.mjs","names":[],"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementType } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\";\n\ntype ButtonOwnProps<E extends ElementType> = {\n as?: E;\n variant?: ButtonVariant;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow —\n// same reasoning as Anchor / Text: with a polymorphic `as` prop TypeScript cannot correctly\n// narrow the `ref` prop's type against the resolved element E. Button accepts both string tags\n// (`as=\"a\"`) and React component types (`as={NextLink}`) so consumers can render a link that\n// looks like a button; a loose ref type would silently accept mismatched refs, so dropping ref\n// from the prop type is safer than a misleading loose type.\n/**\n * Props for {@link Button}. Derived from the props of the rendered element `E`,\n * so what's accepted follows the `as` target.\n *\n * `disabled` and polymorphism: `disabled` only exists on form elements, so\n * `<Button as=\"a\" href=\"…\" disabled>` is a type error (`ComponentPropsWithoutRef<\"a\">`\n * has no `disabled` — locked in by `Button.test-d.tsx`). This is intentional and\n * matches the ARIA Authoring Practices: links are not disabled. When the action is\n * unavailable, render a native `<Button disabled>` (or no link at all) instead of a\n * link. Untyped JS callers and `as={Component}` escapes that pass `disabled` anyway\n * get a leaked no-op attribute — no visual or behavioral disabling happens.\n */\nexport type ButtonProps<E extends ElementType = \"button\"> = ButtonOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof ButtonOwnProps<E>>;\n\nexport function Button<E extends ElementType = \"button\">({\n as,\n variant = \"primary\",\n className,\n ...rest\n}: ButtonProps<E>) {\n const tag: ElementType = as ?? \"button\";\n const classes = cx(\"ps1ui-button\", `ps1ui-button--${variant}`, className);\n const props: Record<string, unknown> = { ...rest, className: classes };\n // `type=\"button\"` is a defensive default so a native <button> inside a <form> never\n // submits implicitly. It only makes sense on the native tag — <a>, <NextLink>, and\n // other host/component targets have no such attribute, and stamping one on them\n // would leak an invalid attribute through to the DOM. Explicit callers still win.\n if (tag === \"button\" && props.type === undefined) {\n props.type = \"button\";\n }\n return createElement(tag, props);\n}\n"],"mappings":";;;AAgCA,SAAgB,OAAyC,EACvD,IACA,UAAU,WACV,WACA,GAAG,QACc;CACjB,MAAM,MAAmB,MAAM;CAC/B,MAAM,UAAU,GAAG,gBAAgB,iBAAiB,WAAW,SAAS;CACxE,MAAM,QAAiC;EAAE,GAAG;EAAM,WAAW;CAAQ;CAKrE,IAAI,QAAQ,YAAY,MAAM,SAAS,KAAA,GACrC,MAAM,OAAO;CAEf,OAAO,cAAc,KAAK,KAAK;AACjC"}
1
+ {"version":3,"file":"Button.mjs","names":[],"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementType } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\";\n\ntype ButtonOwnProps<E extends ElementType> = {\n /** Element or component to render instead of the native <button> — e.g. \"a\" or a router Link. */\n as?: E;\n /** Visual style: \"primary\" for the main action, \"secondary\" for supporting actions, \"danger\" for destructive actions. */\n variant?: ButtonVariant;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow —\n// same reasoning as Anchor / Text: with a polymorphic `as` prop TypeScript cannot correctly\n// narrow the `ref` prop's type against the resolved element E. Button accepts both string tags\n// (`as=\"a\"`) and React component types (`as={NextLink}`) so consumers can render a link that\n// looks like a button; a loose ref type would silently accept mismatched refs, so dropping ref\n// from the prop type is safer than a misleading loose type.\n/**\n * Props for {@link Button}. Derived from the props of the rendered element `E`,\n * so what's accepted follows the `as` target.\n *\n * `disabled` and polymorphism: `disabled` only exists on form elements, so\n * `<Button as=\"a\" href=\"…\" disabled>` is a type error (`ComponentPropsWithoutRef<\"a\">`\n * has no `disabled` — locked in by `Button.test-d.tsx`). This is intentional and\n * matches the ARIA Authoring Practices: links are not disabled. When the action is\n * unavailable, render a native `<Button disabled>` (or no link at all) instead of a\n * link. Untyped JS callers and `as={Component}` escapes that pass `disabled` anyway\n * get a leaked no-op attribute — no visual or behavioral disabling happens.\n *\n * `type` on the native tag: when rendering a native <button>, `type` defaults to\n * \"button\" (not the browser's implicit \"submit\"), so a Button inside a <form>\n * never submits implicitly — pass type=\"submit\" explicitly to submit.\n */\nexport type ButtonProps<E extends ElementType = \"button\"> = ButtonOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof ButtonOwnProps<E>>;\n\nexport function Button<E extends ElementType = \"button\">({\n as,\n variant = \"primary\",\n className,\n ...rest\n}: ButtonProps<E>) {\n const tag: ElementType = as ?? \"button\";\n const classes = cx(\"ps1ui-button\", `ps1ui-button--${variant}`, className);\n const props: Record<string, unknown> = { ...rest, className: classes };\n // `type=\"button\"` is a defensive default so a native <button> inside a <form> never\n // submits implicitly. It only makes sense on the native tag — <a>, <NextLink>, and\n // other host/component targets have no such attribute, and stamping one on them\n // would leak an invalid attribute through to the DOM. Explicit callers still win.\n if (tag === \"button\" && props.type === undefined) {\n props.type = \"button\";\n }\n return createElement(tag, props);\n}\n"],"mappings":";;;AAsCA,SAAgB,OAAyC,EACvD,IACA,UAAU,WACV,WACA,GAAG,QACc;CACjB,MAAM,MAAmB,MAAM;CAC/B,MAAM,UAAU,GAAG,gBAAgB,iBAAiB,WAAW,SAAS;CACxE,MAAM,QAAiC;EAAE,GAAG;EAAM,WAAW;CAAQ;CAKrE,IAAI,QAAQ,YAAY,MAAM,SAAS,KAAA,GACrC,MAAM,OAAO;CAEf,OAAO,cAAc,KAAK,KAAK;AACjC"}
@@ -1,6 +1,7 @@
1
1
  import { ComponentProps } from "react";
2
2
  //#region src/components/Checkbox/Checkbox.d.ts
3
3
  type CheckboxProps = Omit<ComponentProps<"input">, "type"> & {
4
+ /** Render the indeterminate ("mixed") visual state. Purely visual — checked is unaffected. */
4
5
  indeterminate?: boolean;
5
6
  };
6
7
  declare function Checkbox({ className, indeterminate, ref: forwardedRef, ...rest }: CheckboxProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.mjs","names":[],"sources":["../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { useLayoutEffect, useRef, type ComponentProps } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { useMergedRef } from \"../../utils/useMergedRef\";\n\nexport type CheckboxProps = Omit<ComponentProps<\"input\">, \"type\"> & {\n indeterminate?: boolean;\n};\n\nexport function Checkbox({\n className,\n indeterminate = false,\n ref: forwardedRef,\n ...rest\n}: CheckboxProps) {\n const localRef = useRef<HTMLInputElement>(null);\n\n // useLayoutEffect syncs .indeterminate before paint, avoiding a ✓-glyph flash on mount.\n useLayoutEffect(() => {\n localRef.current!.indeterminate = indeterminate;\n }, [indeterminate]);\n\n // Both the indeterminate sync above and the caller get the <input> node.\n const mergedRef = useMergedRef(localRef, forwardedRef);\n\n return (\n <input {...rest} ref={mergedRef} type=\"checkbox\" className={cx(\"ps1ui-checkbox\", className)} />\n );\n}\n"],"mappings":";;;;;;AAUA,SAAgB,SAAS,EACvB,WACA,gBAAgB,OAChB,KAAK,cACL,GAAG,QACa;CAChB,MAAM,WAAW,OAAyB,IAAI;CAG9C,sBAAsB;EACpB,SAAS,QAAS,gBAAgB;CACpC,GAAG,CAAC,aAAa,CAAC;CAGlB,MAAM,YAAY,aAAa,UAAU,YAAY;CAErD,OACE,oBAAC,SAAD;EAAO,GAAI;EAAM,KAAK;EAAW,MAAK;EAAW,WAAW,GAAG,kBAAkB,SAAS;CAAI,CAAA;AAElG"}
1
+ {"version":3,"file":"Checkbox.mjs","names":[],"sources":["../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { useLayoutEffect, useRef, type ComponentProps } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { useMergedRef } from \"../../utils/useMergedRef\";\n\nexport type CheckboxProps = Omit<ComponentProps<\"input\">, \"type\"> & {\n /** Render the indeterminate (\"mixed\") visual state. Purely visual — checked is unaffected. */\n indeterminate?: boolean;\n};\n\nexport function Checkbox({\n className,\n indeterminate = false,\n ref: forwardedRef,\n ...rest\n}: CheckboxProps) {\n const localRef = useRef<HTMLInputElement>(null);\n\n // useLayoutEffect syncs .indeterminate before paint, avoiding a ✓-glyph flash on mount.\n useLayoutEffect(() => {\n localRef.current!.indeterminate = indeterminate;\n }, [indeterminate]);\n\n // Both the indeterminate sync above and the caller get the <input> node.\n const mergedRef = useMergedRef(localRef, forwardedRef);\n\n return (\n <input {...rest} ref={mergedRef} type=\"checkbox\" className={cx(\"ps1ui-checkbox\", className)} />\n );\n}\n"],"mappings":";;;;;;AAWA,SAAgB,SAAS,EACvB,WACA,gBAAgB,OAChB,KAAK,cACL,GAAG,QACa;CAChB,MAAM,WAAW,OAAyB,IAAI;CAG9C,sBAAsB;EACpB,SAAS,QAAS,gBAAgB;CACpC,GAAG,CAAC,aAAa,CAAC;CAGlB,MAAM,YAAY,aAAa,UAAU,YAAY;CAErD,OACE,oBAAC,SAAD;EAAO,GAAI;EAAM,KAAK;EAAW,MAAK;EAAW,WAAW,GAAG,kBAAkB,SAAS;CAAI,CAAA;AAElG"}
@@ -2,8 +2,11 @@ import { CodeBlockLanguage } from "./refractor.mjs";
2
2
  import { ComponentProps } from "react";
3
3
  //#region src/components/CodeBlock/CodeBlock.d.ts
4
4
  type CodeBlockProps = Omit<ComponentProps<"pre">, "children"> & {
5
+ /** Code to render. Takes precedence over children; use it where JSX children get rewrapped (e.g. Astro islands). */
5
6
  code?: string;
7
+ /** Code to render, as the element's only child. */
6
8
  children?: string;
9
+ /** Prism language name or alias. Unknown or omitted values render as plain text. */
7
10
  language?: CodeBlockLanguage | (string & {});
8
11
  };
9
12
  declare function CodeBlock({ code, children, language, className, onBlur, ref: forwardedRef, ...rest }: CodeBlockProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock.mjs","names":[],"sources":["../../../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["\"use client\";\n\nimport { Fragment, useLayoutEffect, useMemo, type ComponentProps } from \"react\";\nimport { jsx, jsxs } from \"react/jsx-runtime\";\nimport { toJsxRuntime } from \"hast-util-to-jsx-runtime\";\nimport { cx } from \"../../utils/cx\";\nimport { useMergedRef } from \"../../utils/useMergedRef\";\nimport { useScrollableFocus } from \"../../utils/useScrollableFocus\";\nimport { refractor, type CodeBlockLanguage } from \"./refractor\";\n\nexport type { CodeBlockLanguage };\n\n// `language` is a new prop (not the native HTML `lang` attribute). HTML `lang`\n// is a BCP47 natural-language tag (\"en\", \"ja\") — a different concept that\n// remains part of the base <pre> attrs and passes through untouched. See\n// CodeBlock.test.tsx for the passthrough assertion.\n//\n// `code` is an alternative to `children` for environments that wrap JSX\n// children on the way to a React tree — e.g. Astro's `.astro` → React island\n// boundary rewraps `children` as an AstroComponentInstance, breaking the\n// `children: string` contract. Named props are passed through as-is, so\n// `<CodeBlock code={snippet}>` survives that boundary. `code` wins when both\n// are supplied; the same string appears in the rendered DOM either way.\nexport type CodeBlockProps = Omit<ComponentProps<\"pre\">, \"children\"> & {\n code?: string;\n children?: string;\n // `CodeBlockLanguage | (string & {})` is TypeScript's \"loose union\" pattern:\n // canonical language names surface in autocomplete, but aliases (`ts`, `html`, ...)\n // and dynamically-sourced strings (e.g. from a markdown fenced-code parser) still\n // pass through the type and are resolved at runtime via refractor's alias table.\n language?: CodeBlockLanguage | (string & {});\n};\n\nexport function CodeBlock({\n code,\n children,\n language,\n className,\n onBlur,\n ref: forwardedRef,\n ...rest\n}: CodeBlockProps) {\n // `code` takes precedence — see the type doc for the Astro/RSC-wrapped-children\n // rationale. Empty string is the \"neither provided\" fallback so the component\n // renders a valid empty <pre> instead of throwing on `.highlight(undefined)`.\n const source = code ?? children ?? \"\";\n // `registered` captures both \"language was provided\" and \"refractor knows it\" —\n // used for the highlight path, the language-<x> class, and to keep the class\n // off unhighlighted blocks (so downstream tools and consumer Prism themes\n // don't mistake a raw-text fallback for a highlighted block).\n //\n // useMemo keyed on (children, language) collapses repeated tokenization when a\n // frequently-re-rendering parent forces this component to re-render with the\n // same code — Prism tokenization of a multi-KB snippet + hast → JSX rebuild\n // is not cheap.\n const { highlighted, registered } = useMemo(() => {\n const isRegistered = !!language && refractor.registered(language);\n return {\n registered: isRegistered,\n highlighted: isRegistered\n ? toJsxRuntime(refractor.highlight(source, language), { Fragment, jsx, jsxs })\n : source,\n };\n }, [source, language]);\n\n // Keyboard reachability of the <pre> (its own scroll container) — tabIndex\n // only while the code actually overflows, kept while focused, safe-side\n // under static SSR — is the shared useScrollableFocus contract (see the\n // hook's header for the full account). The overflowing content is the\n // inline <code>, whose box a ResizeObserver can't watch, so content-driven\n // re-measures are keyed on the source string instead.\n const { scrollerRef, tabIndex, measure } = useScrollableFocus<HTMLPreElement>();\n useLayoutEffect(() => measure(), [measure, source]);\n\n // Merge the caller's ref with the hook's so both get the <pre> node.\n const mergedRef = useMergedRef(scrollerRef, forwardedRef);\n\n return (\n <pre\n // oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- axe scrollable-region-focusable requires the <pre> be keyboard-reachable when its content overflows; useScrollableFocus gates this on measured overflow so short snippets stay out of the tab order.\n tabIndex={tabIndex}\n {...rest}\n ref={mergedRef}\n className={cx(\"ps1ui-codeblock\", className)}\n // Chained (not spread) so the caller's handler still runs: the\n // re-measure drops the kept-while-focused tab stop once focus leaves —\n // see useScrollableFocus.\n onBlur={(event) => {\n onBlur?.(event);\n measure();\n }}\n >\n <code className={registered ? `language-${language}` : undefined}>{highlighted}</code>\n </pre>\n );\n}\n"],"mappings":";;;;;;;;;AAiCA,SAAgB,UAAU,EACxB,MACA,UACA,UACA,WACA,QACA,KAAK,cACL,GAAG,QACc;CAIjB,MAAM,SAAS,QAAQ,YAAY;CAUnC,MAAM,EAAE,aAAa,eAAe,cAAc;EAChD,MAAM,eAAe,CAAC,CAAC,YAAY,UAAU,WAAW,QAAQ;EAChE,OAAO;GACL,YAAY;GACZ,aAAa,eACT,aAAa,UAAU,UAAU,QAAQ,QAAQ,GAAG;IAAE;IAAU;IAAK;GAAK,CAAC,IAC3E;EACN;CACF,GAAG,CAAC,QAAQ,QAAQ,CAAC;CAQrB,MAAM,EAAE,aAAa,UAAU,YAAY,mBAAmC;CAC9E,sBAAsB,QAAQ,GAAG,CAAC,SAAS,MAAM,CAAC;CAGlD,MAAM,YAAY,aAAa,aAAa,YAAY;CAExD,OACE,oBAAC,OAAD;EAEY;EACV,GAAI;EACJ,KAAK;EACL,WAAW,GAAG,mBAAmB,SAAS;EAI1C,SAAS,UAAU;GACjB,SAAS,KAAK;GACd,QAAQ;EACV;YAEA,oBAAC,QAAD;GAAM,WAAW,aAAa,YAAY,aAAa,KAAA;aAAY;EAAkB,CAAA;CAClF,CAAA;AAET"}
1
+ {"version":3,"file":"CodeBlock.mjs","names":[],"sources":["../../../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["\"use client\";\n\nimport { Fragment, useLayoutEffect, useMemo, type ComponentProps } from \"react\";\nimport { jsx, jsxs } from \"react/jsx-runtime\";\nimport { toJsxRuntime } from \"hast-util-to-jsx-runtime\";\nimport { cx } from \"../../utils/cx\";\nimport { useMergedRef } from \"../../utils/useMergedRef\";\nimport { useScrollableFocus } from \"../../utils/useScrollableFocus\";\nimport { refractor, type CodeBlockLanguage } from \"./refractor\";\n\nexport type { CodeBlockLanguage };\n\n// `language` is a new prop (not the native HTML `lang` attribute). HTML `lang`\n// is a BCP47 natural-language tag (\"en\", \"ja\") — a different concept that\n// remains part of the base <pre> attrs and passes through untouched. See\n// CodeBlock.test.tsx for the passthrough assertion.\n//\n// `code` is an alternative to `children` for environments that wrap JSX\n// children on the way to a React tree — e.g. Astro's `.astro` → React island\n// boundary rewraps `children` as an AstroComponentInstance, breaking the\n// `children: string` contract. Named props are passed through as-is, so\n// `<CodeBlock code={snippet}>` survives that boundary. `code` wins when both\n// are supplied; the same string appears in the rendered DOM either way.\nexport type CodeBlockProps = Omit<ComponentProps<\"pre\">, \"children\"> & {\n /** Code to render. Takes precedence over children; use it where JSX children get rewrapped (e.g. Astro islands). */\n code?: string;\n /** Code to render, as the element's only child. */\n children?: string;\n // `CodeBlockLanguage | (string & {})` is TypeScript's \"loose union\" pattern:\n // canonical language names surface in autocomplete, but aliases (`ts`, `html`, ...)\n // and dynamically-sourced strings (e.g. from a markdown fenced-code parser) still\n // pass through the type and are resolved at runtime via refractor's alias table.\n /** Prism language name or alias. Unknown or omitted values render as plain text. */\n language?: CodeBlockLanguage | (string & {});\n};\n\nexport function CodeBlock({\n code,\n children,\n language,\n className,\n onBlur,\n ref: forwardedRef,\n ...rest\n}: CodeBlockProps) {\n // `code` takes precedence — see the type doc for the Astro/RSC-wrapped-children\n // rationale. Empty string is the \"neither provided\" fallback so the component\n // renders a valid empty <pre> instead of throwing on `.highlight(undefined)`.\n const source = code ?? children ?? \"\";\n // `registered` captures both \"language was provided\" and \"refractor knows it\" —\n // used for the highlight path, the language-<x> class, and to keep the class\n // off unhighlighted blocks (so downstream tools and consumer Prism themes\n // don't mistake a raw-text fallback for a highlighted block).\n //\n // useMemo keyed on (children, language) collapses repeated tokenization when a\n // frequently-re-rendering parent forces this component to re-render with the\n // same code — Prism tokenization of a multi-KB snippet + hast → JSX rebuild\n // is not cheap.\n const { highlighted, registered } = useMemo(() => {\n const isRegistered = !!language && refractor.registered(language);\n return {\n registered: isRegistered,\n highlighted: isRegistered\n ? toJsxRuntime(refractor.highlight(source, language), { Fragment, jsx, jsxs })\n : source,\n };\n }, [source, language]);\n\n // Keyboard reachability of the <pre> (its own scroll container) — tabIndex\n // only while the code actually overflows, kept while focused, safe-side\n // under static SSR — is the shared useScrollableFocus contract (see the\n // hook's header for the full account). The overflowing content is the\n // inline <code>, whose box a ResizeObserver can't watch, so content-driven\n // re-measures are keyed on the source string instead.\n const { scrollerRef, tabIndex, measure } = useScrollableFocus<HTMLPreElement>();\n useLayoutEffect(() => measure(), [measure, source]);\n\n // Merge the caller's ref with the hook's so both get the <pre> node.\n const mergedRef = useMergedRef(scrollerRef, forwardedRef);\n\n return (\n <pre\n // oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- axe scrollable-region-focusable requires the <pre> be keyboard-reachable when its content overflows; useScrollableFocus gates this on measured overflow so short snippets stay out of the tab order.\n tabIndex={tabIndex}\n {...rest}\n ref={mergedRef}\n className={cx(\"ps1ui-codeblock\", className)}\n // Chained (not spread) so the caller's handler still runs: the\n // re-measure drops the kept-while-focused tab stop once focus leaves —\n // see useScrollableFocus.\n onBlur={(event) => {\n onBlur?.(event);\n measure();\n }}\n >\n <code className={registered ? `language-${language}` : undefined}>{highlighted}</code>\n </pre>\n );\n}\n"],"mappings":";;;;;;;;;AAoCA,SAAgB,UAAU,EACxB,MACA,UACA,UACA,WACA,QACA,KAAK,cACL,GAAG,QACc;CAIjB,MAAM,SAAS,QAAQ,YAAY;CAUnC,MAAM,EAAE,aAAa,eAAe,cAAc;EAChD,MAAM,eAAe,CAAC,CAAC,YAAY,UAAU,WAAW,QAAQ;EAChE,OAAO;GACL,YAAY;GACZ,aAAa,eACT,aAAa,UAAU,UAAU,QAAQ,QAAQ,GAAG;IAAE;IAAU;IAAK;GAAK,CAAC,IAC3E;EACN;CACF,GAAG,CAAC,QAAQ,QAAQ,CAAC;CAQrB,MAAM,EAAE,aAAa,UAAU,YAAY,mBAAmC;CAC9E,sBAAsB,QAAQ,GAAG,CAAC,SAAS,MAAM,CAAC;CAGlD,MAAM,YAAY,aAAa,aAAa,YAAY;CAExD,OACE,oBAAC,OAAD;EAEY;EACV,GAAI;EACJ,KAAK;EACL,WAAW,GAAG,mBAAmB,SAAS;EAI1C,SAAS,UAAU;GACjB,SAAS,KAAK;GACd,QAAQ;EACV;YAEA,oBAAC,QAAD;GAAM,WAAW,aAAa,YAAY,aAAa,KAAA;aAAY;EAAkB,CAAA;CAClF,CAAA;AAET"}
@@ -4,7 +4,15 @@ import { ComponentProps } from "react";
4
4
  //#region src/components/Container/Container.d.ts
5
5
  type ContainerSize = "sm" | "md" | "lg" | "xl" | "full";
6
6
  type ContainerProps = ComponentProps<"div"> & {
7
+ /**
8
+ * Max-width preset; "full" removes the cap.
9
+ * @default "lg"
10
+ */
7
11
  size?: Responsive<ContainerSize>;
12
+ /**
13
+ * Horizontal padding on the space scale.
14
+ * @default "lg"
15
+ */
8
16
  px?: Responsive<SpaceScale>;
9
17
  };
10
18
  declare function Container({ size, px, className, style, ...rest }: ContainerProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"Container.mjs","names":[],"sources":["../../../src/components/Container/Container.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\nimport { spaceToVar, type SpaceScale } from \"../../utils/spacing\";\n\nexport type ContainerSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\nexport type ContainerProps = ComponentProps<\"div\"> & {\n size?: Responsive<ContainerSize>;\n px?: Responsive<SpaceScale>;\n};\n\n// size → max-width. `full` maps to `none` (unbounded), the rest reference\n// the --ps1ui-container-* max-width tokens in tokens.css.\nconst sizeToVar = (v: ContainerSize): string =>\n v === \"full\" ? \"none\" : `var(--ps1ui-container-${v})`;\n\nexport function Container({ size, px, className, style, ...rest }: ContainerProps) {\n const sizeVars = resolveResponsive(size, \"--_container-size\", sizeToVar);\n const pxVars = resolveResponsive(px, \"--_container-px\", spaceToVar);\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...sizeVars,\n ...pxVars,\n } as CSSProperties;\n return <div {...rest} className={cx(\"ps1ui-container\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;AAcA,MAAM,aAAa,MACjB,MAAM,SAAS,SAAS,yBAAyB,EAAE;AAErD,SAAgB,UAAU,EAAE,MAAM,IAAI,WAAW,OAAO,GAAG,QAAwB;CACjF,MAAM,WAAW,kBAAkB,MAAM,qBAAqB,SAAS;CACvE,MAAM,SAAS,kBAAkB,IAAI,mBAAmB,UAAU;CAGlE,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CACA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,mBAAmB,SAAS;EAAG,OAAO;CAAc,CAAA;AAC1F"}
1
+ {"version":3,"file":"Container.mjs","names":[],"sources":["../../../src/components/Container/Container.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\nimport { spaceToVar, type SpaceScale } from \"../../utils/spacing\";\n\nexport type ContainerSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\nexport type ContainerProps = ComponentProps<\"div\"> & {\n /**\n * Max-width preset; \"full\" removes the cap.\n * @default \"lg\"\n */\n size?: Responsive<ContainerSize>;\n /**\n * Horizontal padding on the space scale.\n * @default \"lg\"\n */\n px?: Responsive<SpaceScale>;\n};\n\n// size → max-width. `full` maps to `none` (unbounded), the rest reference\n// the --ps1ui-container-* max-width tokens in tokens.css.\nconst sizeToVar = (v: ContainerSize): string =>\n v === \"full\" ? \"none\" : `var(--ps1ui-container-${v})`;\n\nexport function Container({ size, px, className, style, ...rest }: ContainerProps) {\n const sizeVars = resolveResponsive(size, \"--_container-size\", sizeToVar);\n const pxVars = resolveResponsive(px, \"--_container-px\", spaceToVar);\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...sizeVars,\n ...pxVars,\n } as CSSProperties;\n return <div {...rest} className={cx(\"ps1ui-container\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;AAsBA,MAAM,aAAa,MACjB,MAAM,SAAS,SAAS,yBAAyB,EAAE;AAErD,SAAgB,UAAU,EAAE,MAAM,IAAI,WAAW,OAAO,GAAG,QAAwB;CACjF,MAAM,WAAW,kBAAkB,MAAM,qBAAqB,SAAS;CACvE,MAAM,SAAS,kBAAkB,IAAI,mBAAmB,UAAU;CAGlE,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CACA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,mBAAmB,SAAS;EAAG,OAAO;CAAc,CAAA;AAC1F"}
@@ -1,6 +1,7 @@
1
1
  import { ComponentProps, ReactNode } from "react";
2
2
  //#region src/components/Details/Details.d.ts
3
3
  type DetailsProps = ComponentProps<"details"> & {
4
+ /** Content of the always-visible summary row. */
4
5
  summary: ReactNode;
5
6
  };
6
7
  declare function Details({ summary, children, className, ...rest }: DetailsProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"Details.mjs","names":[],"sources":["../../../src/components/Details/Details.tsx"],"sourcesContent":["import type { ComponentProps, ReactNode } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\nexport type DetailsProps = ComponentProps<\"details\"> & {\n summary: ReactNode;\n};\n\nexport function Details({ summary, children, className, ...rest }: DetailsProps) {\n return (\n <details {...rest} className={cx(\"ps1ui-details\", className)}>\n <summary className=\"ps1ui-details__summary\">{summary}</summary>\n <div className=\"ps1ui-details__body\">{children}</div>\n </details>\n );\n}\n"],"mappings":";;;AAOA,SAAgB,QAAQ,EAAE,SAAS,UAAU,WAAW,GAAG,QAAsB;CAC/E,OACE,qBAAC,WAAD;EAAS,GAAI;EAAM,WAAW,GAAG,iBAAiB,SAAS;YAA3D,CACE,oBAAC,WAAD;GAAS,WAAU;aAA0B;EAAiB,CAAA,GAC9D,oBAAC,OAAD;GAAK,WAAU;GAAuB;EAAc,CAAA,CAC7C;;AAEb"}
1
+ {"version":3,"file":"Details.mjs","names":[],"sources":["../../../src/components/Details/Details.tsx"],"sourcesContent":["import type { ComponentProps, ReactNode } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\nexport type DetailsProps = ComponentProps<\"details\"> & {\n /** Content of the always-visible summary row. */\n summary: ReactNode;\n};\n\nexport function Details({ summary, children, className, ...rest }: DetailsProps) {\n return (\n <details {...rest} className={cx(\"ps1ui-details\", className)}>\n <summary className=\"ps1ui-details__summary\">{summary}</summary>\n <div className=\"ps1ui-details__body\">{children}</div>\n </details>\n );\n}\n"],"mappings":";;;AAQA,SAAgB,QAAQ,EAAE,SAAS,UAAU,WAAW,GAAG,QAAsB;CAC/E,OACE,qBAAC,WAAD;EAAS,GAAI;EAAM,WAAW,GAAG,iBAAiB,SAAS;YAA3D,CACE,oBAAC,WAAD;GAAS,WAAU;aAA0B;EAAiB,CAAA,GAC9D,oBAAC,OAAD;GAAK,WAAU;GAAuB;EAAc,CAAA,CAC7C;;AAEb"}
@@ -4,7 +4,15 @@ import { ComponentProps } from "react";
4
4
  //#region src/components/Grid/Grid.d.ts
5
5
  type GridGap = SpaceScale;
6
6
  type GridProps = ComponentProps<"div"> & {
7
+ /**
8
+ * Number of equal-width columns.
9
+ * @default 1
10
+ */
7
11
  columns?: Responsive<number>;
12
+ /**
13
+ * Gap between cells on the space scale.
14
+ * @default "md"
15
+ */
8
16
  gap?: Responsive<GridGap>;
9
17
  };
10
18
  declare function Grid({ columns, gap, className, style, ...rest }: GridProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"Grid.mjs","names":[],"sources":["../../../src/components/Grid/Grid.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { safePositiveInt } from \"../../utils/numbers\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\nimport { spaceToVar, type SpaceScale } from \"../../utils/spacing\";\n\nexport type GridGap = SpaceScale;\n\nexport type GridProps = ComponentProps<\"div\"> & {\n columns?: Responsive<number>;\n gap?: Responsive<GridGap>;\n};\n\nexport function Grid({ columns, gap, className, style, ...rest }: GridProps) {\n // `repeat(N, ...)` requires N ≥ 1 integer — `safePositiveInt` clamps at\n // the system boundary. See utils/numbers.ts for the full rationale.\n const columnsVars = resolveResponsive(columns, \"--_grid-columns\", safePositiveInt);\n const gapVars = resolveResponsive(gap, \"--_grid-gap\", spaceToVar);\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...columnsVars,\n ...gapVars,\n } as CSSProperties;\n return <div {...rest} className={cx(\"ps1ui-grid\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;;AAaA,SAAgB,KAAK,EAAE,SAAS,KAAK,WAAW,OAAO,GAAG,QAAmB;CAG3E,MAAM,cAAc,kBAAkB,SAAS,mBAAmB,eAAe;CACjF,MAAM,UAAU,kBAAkB,KAAK,eAAe,UAAU;CAGhE,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CACA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,cAAc,SAAS;EAAG,OAAO;CAAc,CAAA;AACrF"}
1
+ {"version":3,"file":"Grid.mjs","names":[],"sources":["../../../src/components/Grid/Grid.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { safePositiveInt } from \"../../utils/numbers\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\nimport { spaceToVar, type SpaceScale } from \"../../utils/spacing\";\n\nexport type GridGap = SpaceScale;\n\nexport type GridProps = ComponentProps<\"div\"> & {\n /**\n * Number of equal-width columns.\n * @default 1\n */\n columns?: Responsive<number>;\n /**\n * Gap between cells on the space scale.\n * @default \"md\"\n */\n gap?: Responsive<GridGap>;\n};\n\nexport function Grid({ columns, gap, className, style, ...rest }: GridProps) {\n // `repeat(N, ...)` requires N ≥ 1 integer — `safePositiveInt` clamps at\n // the system boundary. See utils/numbers.ts for the full rationale.\n const columnsVars = resolveResponsive(columns, \"--_grid-columns\", safePositiveInt);\n const gapVars = resolveResponsive(gap, \"--_grid-gap\", spaceToVar);\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...columnsVars,\n ...gapVars,\n } as CSSProperties;\n return <div {...rest} className={cx(\"ps1ui-grid\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;;AAqBA,SAAgB,KAAK,EAAE,SAAS,KAAK,WAAW,OAAO,GAAG,QAAmB;CAG3E,MAAM,cAAc,kBAAkB,SAAS,mBAAmB,eAAe;CACjF,MAAM,UAAU,kBAAkB,KAAK,eAAe,UAAU;CAGhE,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CACA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,cAAc,SAAS;EAAG,OAAO;CAAc,CAAA;AACrF"}
@@ -2,6 +2,10 @@ import { Responsive } from "../../utils/responsive.mjs";
2
2
  import { ComponentProps } from "react";
3
3
  //#region src/components/GridItem/GridItem.d.ts
4
4
  type GridItemProps = ComponentProps<"div"> & {
5
+ /**
6
+ * Number of grid columns the item spans.
7
+ * @default 1
8
+ */
5
9
  colSpan?: Responsive<number>;
6
10
  };
7
11
  declare function GridItem({ colSpan, className, style, ...rest }: GridItemProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"GridItem.mjs","names":[],"sources":["../../../src/components/GridItem/GridItem.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { safePositiveInt } from \"../../utils/numbers\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\n\nexport type GridItemProps = ComponentProps<\"div\"> & {\n colSpan?: Responsive<number>;\n};\n\nexport function GridItem({ colSpan, className, style, ...rest }: GridItemProps) {\n // `grid-column: span N` requires N ≥ 1 integer — `safePositiveInt` clamps\n // at the system boundary. See utils/numbers.ts for the full rationale.\n const colSpanVars = resolveResponsive(colSpan, \"--_griditem-col-span\", safePositiveInt);\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...colSpanVars,\n } as CSSProperties;\n return <div {...rest} className={cx(\"ps1ui-griditem\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;AASA,SAAgB,SAAS,EAAE,SAAS,WAAW,OAAO,GAAG,QAAuB;CAG9E,MAAM,cAAc,kBAAkB,SAAS,wBAAwB,eAAe;CAGtF,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;CACL;CACA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,kBAAkB,SAAS;EAAG,OAAO;CAAc,CAAA;AACzF"}
1
+ {"version":3,"file":"GridItem.mjs","names":[],"sources":["../../../src/components/GridItem/GridItem.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { safePositiveInt } from \"../../utils/numbers\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\n\nexport type GridItemProps = ComponentProps<\"div\"> & {\n /**\n * Number of grid columns the item spans.\n * @default 1\n */\n colSpan?: Responsive<number>;\n};\n\nexport function GridItem({ colSpan, className, style, ...rest }: GridItemProps) {\n // `grid-column: span N` requires N ≥ 1 integer — `safePositiveInt` clamps\n // at the system boundary. See utils/numbers.ts for the full rationale.\n const colSpanVars = resolveResponsive(colSpan, \"--_griditem-col-span\", safePositiveInt);\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...colSpanVars,\n } as CSSProperties;\n return <div {...rest} className={cx(\"ps1ui-griditem\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;AAaA,SAAgB,SAAS,EAAE,SAAS,WAAW,OAAO,GAAG,QAAuB;CAG9E,MAAM,cAAc,kBAAkB,SAAS,wBAAwB,eAAe;CAGtF,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;CACL;CACA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,kBAAkB,SAAS;EAAG,OAAO;CAAc,CAAA;AACzF"}
@@ -7,9 +7,13 @@ type HeadingElement = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
7
7
  type HeadingSize = "sm" | "md" | "lg" | "xl" | "2xl" | "3xl";
8
8
  type HeadingWeight = FontWeight;
9
9
  type HeadingOwnProps<E extends HeadingElement> = {
10
+ /** Semantic level: renders the matching h1–h6 tag and sets the default size and weight. */
10
11
  level: HeadingLevel;
12
+ /** Heading tag to render when it should differ from level — visual defaults stay driven by level. */
11
13
  as?: E;
14
+ /** Font size. Defaults to the level's size. */
12
15
  size?: Responsive<HeadingSize>;
16
+ /** Font weight. Defaults to the level's weight. */
13
17
  weight?: Responsive<HeadingWeight>;
14
18
  };
15
19
  type HeadingProps<E extends HeadingElement = HeadingElement> = HeadingOwnProps<E> & Omit<ComponentPropsWithoutRef<E>, keyof HeadingOwnProps<E>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Heading.mjs","names":[],"sources":["../../../src/components/Heading/Heading.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { resolveResponsive, withResponsiveBase, type Responsive } from \"../../utils/responsive\";\nimport { fontSizeToVar, weightToValue, type FontWeight } from \"../../utils/typography\";\n\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\nexport type HeadingElement = \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n\nexport type HeadingSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n// HeadingWeight is a re-export of the shared FontWeight scale. Keeping a\n// component-local alias preserves the public type name (backwards compat)\n// while the underlying scale stays single-sourced in utils/typography.ts.\nexport type HeadingWeight = FontWeight;\n\ntype HeadingOwnProps<E extends HeadingElement> = {\n level: HeadingLevel;\n as?: E;\n size?: Responsive<HeadingSize>;\n weight?: Responsive<HeadingWeight>;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow —\n// same reasoning as Text: with a polymorphic `as` prop TypeScript cannot correctly narrow the\n// `ref` type. All h1-h6 do resolve to HTMLHeadingElement so ref would happen to work today, but\n// keeping the polymorphic pattern consistent avoids surprises if the tag set ever widens.\nexport type HeadingProps<E extends HeadingElement = HeadingElement> = HeadingOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof HeadingOwnProps<E>>;\n\n// Exhaustive over HeadingLevel so a new level cannot be added without classifying its defaults.\nconst LEVEL_DEFAULTS: Record<HeadingLevel, { size: HeadingSize; weight: HeadingWeight }> = {\n 1: { size: \"3xl\", weight: \"bold\" },\n 2: { size: \"2xl\", weight: \"semibold\" },\n 3: { size: \"xl\", weight: \"semibold\" },\n 4: { size: \"lg\", weight: \"semibold\" },\n 5: { size: \"md\", weight: \"medium\" },\n 6: { size: \"sm\", weight: \"medium\" },\n};\n\nexport function Heading<E extends HeadingElement = HeadingElement>({\n level,\n as,\n size: sizeProp,\n weight: weightProp,\n className,\n style,\n ...rest\n}: HeadingProps<E>) {\n const tag = as ?? (`h${level}` as HeadingElement);\n const defaults = LEVEL_DEFAULTS[level];\n // `withResponsiveBase` bakes the level's default in at the `base`\n // breakpoint when the caller either omits the prop entirely or passes a\n // responsive object with no `base` entry. Concretely, `<Heading level={1}\n // size={{ md: \"2xl\" }}>` renders as level 1's default `3xl` at base, then\n // `2xl` at the md breakpoint — matching the intuition \"level sets the\n // default; the object overrides at specific widths\".\n const size = withResponsiveBase(sizeProp, defaults.size);\n const weight = withResponsiveBase(weightProp, defaults.weight);\n\n const sizeVars = resolveResponsive(size, \"--_heading-size\", fontSizeToVar);\n const weightVars = resolveResponsive(weight, \"--_heading-weight\", weightToValue);\n\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...sizeVars,\n ...weightVars,\n } as CSSProperties;\n\n return createElement(tag, {\n ...rest,\n className: cx(\"ps1ui-heading\", className),\n style: mergedStyle,\n });\n}\n"],"mappings":";;;;;AA8BA,MAAM,iBAAqF;CACzF,GAAG;EAAE,MAAM;EAAO,QAAQ;CAAO;CACjC,GAAG;EAAE,MAAM;EAAO,QAAQ;CAAW;CACrC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAW;CACpC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAW;CACpC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAS;CAClC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAS;AACpC;AAEA,SAAgB,QAAmD,EACjE,OACA,IACA,MAAM,UACN,QAAQ,YACR,WACA,OACA,GAAG,QACe;CAClB,MAAM,MAAM,MAAO,IAAI;CACvB,MAAM,WAAW,eAAe;CAOhC,MAAM,OAAO,mBAAmB,UAAU,SAAS,IAAI;CACvD,MAAM,SAAS,mBAAmB,YAAY,SAAS,MAAM;CAE7D,MAAM,WAAW,kBAAkB,MAAM,mBAAmB,aAAa;CACzE,MAAM,aAAa,kBAAkB,QAAQ,qBAAqB,aAAa;CAI/E,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CAEA,OAAO,cAAc,KAAK;EACxB,GAAG;EACH,WAAW,GAAG,iBAAiB,SAAS;EACxC,OAAO;CACT,CAAC;AACH"}
1
+ {"version":3,"file":"Heading.mjs","names":[],"sources":["../../../src/components/Heading/Heading.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { resolveResponsive, withResponsiveBase, type Responsive } from \"../../utils/responsive\";\nimport { fontSizeToVar, weightToValue, type FontWeight } from \"../../utils/typography\";\n\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\nexport type HeadingElement = \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n\nexport type HeadingSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n// HeadingWeight is a re-export of the shared FontWeight scale. Keeping a\n// component-local alias preserves the public type name (backwards compat)\n// while the underlying scale stays single-sourced in utils/typography.ts.\nexport type HeadingWeight = FontWeight;\n\ntype HeadingOwnProps<E extends HeadingElement> = {\n /** Semantic level: renders the matching h1–h6 tag and sets the default size and weight. */\n level: HeadingLevel;\n /** Heading tag to render when it should differ from level — visual defaults stay driven by level. */\n as?: E;\n /** Font size. Defaults to the level's size. */\n size?: Responsive<HeadingSize>;\n /** Font weight. Defaults to the level's weight. */\n weight?: Responsive<HeadingWeight>;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow —\n// same reasoning as Text: with a polymorphic `as` prop TypeScript cannot correctly narrow the\n// `ref` type. All h1-h6 do resolve to HTMLHeadingElement so ref would happen to work today, but\n// keeping the polymorphic pattern consistent avoids surprises if the tag set ever widens.\nexport type HeadingProps<E extends HeadingElement = HeadingElement> = HeadingOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof HeadingOwnProps<E>>;\n\n// Exhaustive over HeadingLevel so a new level cannot be added without classifying its defaults.\nconst LEVEL_DEFAULTS: Record<HeadingLevel, { size: HeadingSize; weight: HeadingWeight }> = {\n 1: { size: \"3xl\", weight: \"bold\" },\n 2: { size: \"2xl\", weight: \"semibold\" },\n 3: { size: \"xl\", weight: \"semibold\" },\n 4: { size: \"lg\", weight: \"semibold\" },\n 5: { size: \"md\", weight: \"medium\" },\n 6: { size: \"sm\", weight: \"medium\" },\n};\n\nexport function Heading<E extends HeadingElement = HeadingElement>({\n level,\n as,\n size: sizeProp,\n weight: weightProp,\n className,\n style,\n ...rest\n}: HeadingProps<E>) {\n const tag = as ?? (`h${level}` as HeadingElement);\n const defaults = LEVEL_DEFAULTS[level];\n // `withResponsiveBase` bakes the level's default in at the `base`\n // breakpoint when the caller either omits the prop entirely or passes a\n // responsive object with no `base` entry. Concretely, `<Heading level={1}\n // size={{ md: \"2xl\" }}>` renders as level 1's default `3xl` at base, then\n // `2xl` at the md breakpoint — matching the intuition \"level sets the\n // default; the object overrides at specific widths\".\n const size = withResponsiveBase(sizeProp, defaults.size);\n const weight = withResponsiveBase(weightProp, defaults.weight);\n\n const sizeVars = resolveResponsive(size, \"--_heading-size\", fontSizeToVar);\n const weightVars = resolveResponsive(weight, \"--_heading-weight\", weightToValue);\n\n // Caller style first, internal `--_*` vars win — see Text.tsx. Cast because\n // csstype has no index signature for `--*` keys (React 19 included).\n const mergedStyle: CSSProperties = {\n ...style,\n ...sizeVars,\n ...weightVars,\n } as CSSProperties;\n\n return createElement(tag, {\n ...rest,\n className: cx(\"ps1ui-heading\", className),\n style: mergedStyle,\n });\n}\n"],"mappings":";;;;;AAkCA,MAAM,iBAAqF;CACzF,GAAG;EAAE,MAAM;EAAO,QAAQ;CAAO;CACjC,GAAG;EAAE,MAAM;EAAO,QAAQ;CAAW;CACrC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAW;CACpC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAW;CACpC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAS;CAClC,GAAG;EAAE,MAAM;EAAM,QAAQ;CAAS;AACpC;AAEA,SAAgB,QAAmD,EACjE,OACA,IACA,MAAM,UACN,QAAQ,YACR,WACA,OACA,GAAG,QACe;CAClB,MAAM,MAAM,MAAO,IAAI;CACvB,MAAM,WAAW,eAAe;CAOhC,MAAM,OAAO,mBAAmB,UAAU,SAAS,IAAI;CACvD,MAAM,SAAS,mBAAmB,YAAY,SAAS,MAAM;CAE7D,MAAM,WAAW,kBAAkB,MAAM,mBAAmB,aAAa;CACzE,MAAM,aAAa,kBAAkB,QAAQ,qBAAqB,aAAa;CAI/E,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CAEA,OAAO,cAAc,KAAK;EACxB,GAAG;EACH,WAAW,GAAG,iBAAiB,SAAS;EACxC,OAAO;CACT,CAAC;AACH"}
@@ -1,8 +1,10 @@
1
1
  import { ComponentProps, ReactElement } from "react";
2
2
  //#region src/components/List/List.d.ts
3
3
  type ListProps = ({
4
+ /** Render an <ol> with numbered markers instead of an <ul>. */
4
5
  ordered?: false;
5
6
  } & ComponentProps<"ul">) | ({
7
+ /** Render an <ol> with numbered markers instead of an <ul>. */
6
8
  ordered: true;
7
9
  } & Omit<ComponentProps<"ol">, "type">);
8
10
  declare function List(props: ListProps): ReactElement;
@@ -1 +1 @@
1
- {"version":3,"file":"List.mjs","names":[],"sources":["../../../src/components/List/List.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentProps, ReactElement } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\n// Discriminated union: an unordered <List> shouldn't typecheck with <ol>-only\n// attributes (start / reversed) and shouldn't type its ref as HTMLOListElement.\n// `type` is dropped from the ordered variant because the visible marker is\n// drawn by `::before`, not the browser's `::marker`, so `type=\"a\"` would\n// silently not affect the visible numbering.\nexport type ListProps =\n | ({ ordered?: false } & ComponentProps<\"ul\">)\n | ({ ordered: true } & Omit<ComponentProps<\"ol\">, \"type\">);\n\n// Return type annotated so tsdown emits a small ReactElement in the .d.mts\n// instead of the wide DetailedReactHTMLElement createElement infers for a\n// `\"ol\" | \"ul\"` tag union.\nexport function List(props: ListProps): ReactElement {\n const { ordered = false, className, ...rest } = props;\n const tag = ordered ? \"ol\" : \"ul\";\n return createElement(tag, {\n // Safari's a11y tree drops list semantics (\"list, N items\") when the\n // computed `list-style-type` is `none` — the global reset applies that,\n // so `role=\"list\"` restores VoiceOver's announcement. Placed before\n // `...rest` so a caller can override with a different role.\n role: \"list\",\n ...rest,\n className: cx(\n \"ps1ui-list\",\n ordered ? \"ps1ui-list--ordered\" : \"ps1ui-list--unordered\",\n className,\n ),\n });\n}\n"],"mappings":";;;AAgBA,SAAgB,KAAK,OAAgC;CACnD,MAAM,EAAE,UAAU,OAAO,WAAW,GAAG,SAAS;CAEhD,OAAO,cADK,UAAU,OAAO,MACH;EAKxB,MAAM;EACN,GAAG;EACH,WAAW,GACT,cACA,UAAU,wBAAwB,yBAClC,SACF;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"List.mjs","names":[],"sources":["../../../src/components/List/List.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentProps, ReactElement } from \"react\";\nimport { cx } from \"../../utils/cx\";\n\n// Discriminated union: an unordered <List> shouldn't typecheck with <ol>-only\n// attributes (start / reversed) and shouldn't type its ref as HTMLOListElement.\n// `type` is dropped from the ordered variant because the visible marker is\n// drawn by `::before`, not the browser's `::marker`, so `type=\"a\"` would\n// silently not affect the visible numbering.\nexport type ListProps =\n | ({\n /** Render an <ol> with numbered markers instead of an <ul>. */\n ordered?: false;\n } & ComponentProps<\"ul\">)\n | ({\n /** Render an <ol> with numbered markers instead of an <ul>. */\n ordered: true;\n } & Omit<ComponentProps<\"ol\">, \"type\">);\n\n// Return type annotated so tsdown emits a small ReactElement in the .d.mts\n// instead of the wide DetailedReactHTMLElement createElement infers for a\n// `\"ol\" | \"ul\"` tag union.\nexport function List(props: ListProps): ReactElement {\n const { ordered = false, className, ...rest } = props;\n const tag = ordered ? \"ol\" : \"ul\";\n return createElement(tag, {\n // Safari's a11y tree drops list semantics (\"list, N items\") when the\n // computed `list-style-type` is `none` — the global reset applies that,\n // so `role=\"list\"` restores VoiceOver's announcement. Placed before\n // `...rest` so a caller can override with a different role.\n role: \"list\",\n ...rest,\n className: cx(\n \"ps1ui-list\",\n ordered ? \"ps1ui-list--ordered\" : \"ps1ui-list--unordered\",\n className,\n ),\n });\n}\n"],"mappings":";;;AAsBA,SAAgB,KAAK,OAAgC;CACnD,MAAM,EAAE,UAAU,OAAO,WAAW,GAAG,SAAS;CAEhD,OAAO,cADK,UAAU,OAAO,MACH;EAKxB,MAAM;EACN,GAAG;EACH,WAAW,GACT,cACA,UAAU,wBAAwB,yBAClC,SACF;CACF,CAAC;AACH"}