@ps1ui/core 0.0.1 → 0.1.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.
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)
@@ -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"}
@@ -2,7 +2,9 @@ import { ComponentPropsWithoutRef, ElementType } from "react";
2
2
  //#region src/components/Button/Button.d.ts
3
3
  type ButtonVariant = "primary" | "secondary";
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. */
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\";\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. */\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"}
@@ -7,10 +7,24 @@ type StackGap = SpaceScale;
7
7
  type StackAlign = "start" | "center" | "end" | "stretch" | "baseline";
8
8
  type StackJustify = "start" | "center" | "end" | "between" | "around" | "evenly";
9
9
  type StackProps = ComponentProps<"div"> & {
10
+ /**
11
+ * Main-axis direction.
12
+ * @default "column"
13
+ */
10
14
  direction?: Responsive<StackDirection>;
15
+ /**
16
+ * Gap between items on the space scale.
17
+ * @default "md"
18
+ */
11
19
  gap?: Responsive<StackGap>;
20
+ /** Cross-axis alignment (align-items). */
12
21
  align?: Responsive<StackAlign>;
22
+ /** Main-axis distribution (justify-content). */
13
23
  justify?: Responsive<StackJustify>;
24
+ /**
25
+ * Wrap items onto multiple lines instead of overflowing.
26
+ * @default false
27
+ */
14
28
  wrap?: Responsive<boolean>;
15
29
  };
16
30
  declare function Stack({ direction, gap, align, justify, wrap, className, style, ...rest }: StackProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"Stack.mjs","names":[],"sources":["../../../src/components/Stack/Stack.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 StackDirection = \"row\" | \"column\";\nexport type StackGap = SpaceScale;\nexport type StackAlign = \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\";\nexport type StackJustify = \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\";\n\nexport type StackProps = ComponentProps<\"div\"> & {\n direction?: Responsive<StackDirection>;\n gap?: Responsive<StackGap>;\n align?: Responsive<StackAlign>;\n justify?: Responsive<StackJustify>;\n wrap?: Responsive<boolean>;\n};\n\n// direction values pass straight through — CSS flex-direction accepts\n// `row` / `column` (and `row-reverse` / `column-reverse`, which the union\n// deliberately excludes; add those to the union if the API ever grows).\nconst directionToValue = (v: StackDirection): string => v;\n\n// Flexbox uses `flex-start` / `flex-end` in the box-alignment spec. The\n// StackAlign union exposes the shorter `start` / `end` alias for terseness;\n// the transform remaps them so the CSS variable holds a valid `align-items`\n// value directly.\nconst alignToValue = (v: StackAlign): string => {\n switch (v) {\n case \"start\":\n return \"flex-start\";\n case \"end\":\n return \"flex-end\";\n default:\n return v;\n }\n};\n\n// Same treatment as `alignToValue`, plus expansions for the `space-*`\n// justify-content variants.\nconst justifyToValue = (v: StackJustify): string => {\n switch (v) {\n case \"start\":\n return \"flex-start\";\n case \"end\":\n return \"flex-end\";\n case \"between\":\n return \"space-between\";\n case \"around\":\n return \"space-around\";\n case \"evenly\":\n return \"space-evenly\";\n default:\n return v;\n }\n};\n\n// boolean → CSS flex-wrap keyword.\nconst wrapToValue = (v: boolean): string => (v ? \"wrap\" : \"nowrap\");\n\nexport function Stack({\n direction,\n gap,\n align,\n justify,\n wrap,\n className,\n style,\n ...rest\n}: StackProps) {\n const directionVars = resolveResponsive(direction, \"--_stack-direction\", directionToValue);\n const gapVars = resolveResponsive(gap, \"--_stack-gap\", spaceToVar);\n const alignVars = resolveResponsive(align, \"--_stack-align\", alignToValue);\n const justifyVars = resolveResponsive(justify, \"--_stack-justify\", justifyToValue);\n const wrapVars = resolveResponsive(wrap, \"--_stack-wrap\", wrapToValue);\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 ...directionVars,\n ...gapVars,\n ...alignVars,\n ...justifyVars,\n ...wrapVars,\n } as CSSProperties;\n\n return <div {...rest} className={cx(\"ps1ui-stack\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;AAqBA,MAAM,oBAAoB,MAA8B;AAMxD,MAAM,gBAAgB,MAA0B;CAC9C,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAIA,MAAM,kBAAkB,MAA4B;CAClD,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAGA,MAAM,eAAe,MAAwB,IAAI,SAAS;AAE1D,SAAgB,MAAM,EACpB,WACA,KACA,OACA,SACA,MACA,WACA,OACA,GAAG,QACU;CACb,MAAM,gBAAgB,kBAAkB,WAAW,sBAAsB,gBAAgB;CACzF,MAAM,UAAU,kBAAkB,KAAK,gBAAgB,UAAU;CACjE,MAAM,YAAY,kBAAkB,OAAO,kBAAkB,YAAY;CACzE,MAAM,cAAc,kBAAkB,SAAS,oBAAoB,cAAc;CACjF,MAAM,WAAW,kBAAkB,MAAM,iBAAiB,WAAW;CAIrE,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACL;CAEA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,eAAe,SAAS;EAAG,OAAO;CAAc,CAAA;AACtF"}
1
+ {"version":3,"file":"Stack.mjs","names":[],"sources":["../../../src/components/Stack/Stack.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 StackDirection = \"row\" | \"column\";\nexport type StackGap = SpaceScale;\nexport type StackAlign = \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\";\nexport type StackJustify = \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\";\n\nexport type StackProps = ComponentProps<\"div\"> & {\n /**\n * Main-axis direction.\n * @default \"column\"\n */\n direction?: Responsive<StackDirection>;\n /**\n * Gap between items on the space scale.\n * @default \"md\"\n */\n gap?: Responsive<StackGap>;\n /** Cross-axis alignment (align-items). */\n align?: Responsive<StackAlign>;\n /** Main-axis distribution (justify-content). */\n justify?: Responsive<StackJustify>;\n /**\n * Wrap items onto multiple lines instead of overflowing.\n * @default false\n */\n wrap?: Responsive<boolean>;\n};\n\n// direction values pass straight through — CSS flex-direction accepts\n// `row` / `column` (and `row-reverse` / `column-reverse`, which the union\n// deliberately excludes; add those to the union if the API ever grows).\nconst directionToValue = (v: StackDirection): string => v;\n\n// Flexbox uses `flex-start` / `flex-end` in the box-alignment spec. The\n// StackAlign union exposes the shorter `start` / `end` alias for terseness;\n// the transform remaps them so the CSS variable holds a valid `align-items`\n// value directly.\nconst alignToValue = (v: StackAlign): string => {\n switch (v) {\n case \"start\":\n return \"flex-start\";\n case \"end\":\n return \"flex-end\";\n default:\n return v;\n }\n};\n\n// Same treatment as `alignToValue`, plus expansions for the `space-*`\n// justify-content variants.\nconst justifyToValue = (v: StackJustify): string => {\n switch (v) {\n case \"start\":\n return \"flex-start\";\n case \"end\":\n return \"flex-end\";\n case \"between\":\n return \"space-between\";\n case \"around\":\n return \"space-around\";\n case \"evenly\":\n return \"space-evenly\";\n default:\n return v;\n }\n};\n\n// boolean → CSS flex-wrap keyword.\nconst wrapToValue = (v: boolean): string => (v ? \"wrap\" : \"nowrap\");\n\nexport function Stack({\n direction,\n gap,\n align,\n justify,\n wrap,\n className,\n style,\n ...rest\n}: StackProps) {\n const directionVars = resolveResponsive(direction, \"--_stack-direction\", directionToValue);\n const gapVars = resolveResponsive(gap, \"--_stack-gap\", spaceToVar);\n const alignVars = resolveResponsive(align, \"--_stack-align\", alignToValue);\n const justifyVars = resolveResponsive(justify, \"--_stack-justify\", justifyToValue);\n const wrapVars = resolveResponsive(wrap, \"--_stack-wrap\", wrapToValue);\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 ...directionVars,\n ...gapVars,\n ...alignVars,\n ...justifyVars,\n ...wrapVars,\n } as CSSProperties;\n\n return <div {...rest} className={cx(\"ps1ui-stack\", className)} style={mergedStyle} />;\n}\n"],"mappings":";;;;;AAmCA,MAAM,oBAAoB,MAA8B;AAMxD,MAAM,gBAAgB,MAA0B;CAC9C,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAIA,MAAM,kBAAkB,MAA4B;CAClD,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAGA,MAAM,eAAe,MAAwB,IAAI,SAAS;AAE1D,SAAgB,MAAM,EACpB,WACA,KACA,OACA,SACA,MACA,WACA,OACA,GAAG,QACU;CACb,MAAM,gBAAgB,kBAAkB,WAAW,sBAAsB,gBAAgB;CACzF,MAAM,UAAU,kBAAkB,KAAK,gBAAgB,UAAU;CACjE,MAAM,YAAY,kBAAkB,OAAO,kBAAkB,YAAY;CACzE,MAAM,cAAc,kBAAkB,SAAS,oBAAoB,cAAc;CACjF,MAAM,WAAW,kBAAkB,MAAM,iBAAiB,WAAW;CAIrE,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACL;CAEA,OAAO,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,eAAe,SAAS;EAAG,OAAO;CAAc,CAAA;AACtF"}
@@ -7,10 +7,18 @@ type TextVariant = "body" | "muted" | "subtle" | "primary" | "accent";
7
7
  type TextSize = "xs" | "sm" | "md" | "lg" | "xl";
8
8
  type TextWeight = FontWeight;
9
9
  type TextOwnProps<E extends TextElement> = {
10
+ /** Element to render. */
10
11
  as?: E;
12
+ /** Color variant. */
11
13
  variant?: TextVariant;
14
+ /**
15
+ * Font size on the type scale.
16
+ * @default "sm"
17
+ */
12
18
  size?: Responsive<TextSize>;
19
+ /** Font weight. */
13
20
  weight?: Responsive<TextWeight>;
21
+ /** Truncate overflowing text with an ellipsis instead of wrapping. */
14
22
  truncate?: boolean;
15
23
  };
16
24
  type TextProps<E extends TextElement = "p"> = TextOwnProps<E> & Omit<ComponentPropsWithoutRef<E>, keyof TextOwnProps<E>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Text.mjs","names":[],"sources":["../../../src/components/Text/Text.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\nimport { fontSizeToVar, weightToValue, type FontWeight } from \"../../utils/typography\";\n\nexport type TextElement = \"p\" | \"span\" | \"div\" | \"label\" | \"strong\" | \"em\" | \"small\";\n\nexport type TextVariant = \"body\" | \"muted\" | \"subtle\" | \"primary\" | \"accent\";\nexport type TextSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n// TextWeight 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 TextWeight = FontWeight;\n\ntype TextOwnProps<E extends TextElement> = {\n as?: E;\n variant?: TextVariant;\n size?: Responsive<TextSize>;\n weight?: Responsive<TextWeight>;\n truncate?: boolean;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow.\n// With the polymorphic `as` prop, TypeScript cannot narrow the `ref` prop's type against the\n// resolved element E — allowing `ref` here would silently accept mismatched ref types\n// (e.g. a ref typed for HTMLParagraphElement on `<Text as=\"span\" ref={…} />`). Keeping `ref`\n// out of the prop type entirely is safer than a misleading loose type; don't switch this\n// to ComponentProps for consistency with the other components.\nexport type TextProps<E extends TextElement = \"p\"> = TextOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof TextOwnProps<E>>;\n\n// Exhaustive over TextElement so adding a tag without classifying it fails to typecheck,\n// instead of silently leaving truncate's inline-block fix un-applied for that tag.\nconst INLINE_TEXT_ELEMENTS: Record<TextElement, boolean> = {\n p: false,\n div: false,\n span: true,\n label: true,\n strong: true,\n em: true,\n small: true,\n};\n\nexport function Text<E extends TextElement = \"p\">({\n as,\n variant = \"body\",\n size,\n weight,\n truncate = false,\n className,\n style,\n ...rest\n}: TextProps<E>) {\n const tag = as ?? \"p\";\n const sizeVars = resolveResponsive(size, \"--_text-size\", fontSizeToVar);\n const weightVars = resolveResponsive(weight, \"--_text-weight\", weightToValue);\n\n const classes = cx(\n \"ps1ui-text\",\n `ps1ui-text--${variant}`,\n truncate && \"ps1ui-text--truncate\",\n truncate && INLINE_TEXT_ELEMENTS[tag] && \"ps1ui-text--truncate-inline\",\n className,\n );\n\n // Caller style first, resolved vars last: `--_*` is the reserved internal\n // prefix and wins on purpose — responsive values are prop-controlled. Cast\n // because 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, { ...rest, className: classes, style: mergedStyle });\n}\n"],"mappings":";;;;;AAkCA,MAAM,uBAAqD;CACzD,GAAG;CACH,KAAK;CACL,MAAM;CACN,OAAO;CACP,QAAQ;CACR,IAAI;CACJ,OAAO;AACT;AAEA,SAAgB,KAAkC,EAChD,IACA,UAAU,QACV,MACA,QACA,WAAW,OACX,WACA,OACA,GAAG,QACY;CACf,MAAM,MAAM,MAAM;CAClB,MAAM,WAAW,kBAAkB,MAAM,gBAAgB,aAAa;CACtE,MAAM,aAAa,kBAAkB,QAAQ,kBAAkB,aAAa;CAE5E,MAAM,UAAU,GACd,cACA,eAAe,WACf,YAAY,wBACZ,YAAY,qBAAqB,QAAQ,+BACzC,SACF;CAKA,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CAEA,OAAO,cAAc,KAAK;EAAE,GAAG;EAAM,WAAW;EAAS,OAAO;CAAY,CAAC;AAC/E"}
1
+ {"version":3,"file":"Text.mjs","names":[],"sources":["../../../src/components/Text/Text.tsx"],"sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { cx } from \"../../utils/cx\";\nimport { resolveResponsive, type Responsive } from \"../../utils/responsive\";\nimport { fontSizeToVar, weightToValue, type FontWeight } from \"../../utils/typography\";\n\nexport type TextElement = \"p\" | \"span\" | \"div\" | \"label\" | \"strong\" | \"em\" | \"small\";\n\nexport type TextVariant = \"body\" | \"muted\" | \"subtle\" | \"primary\" | \"accent\";\nexport type TextSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n// TextWeight 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 TextWeight = FontWeight;\n\ntype TextOwnProps<E extends TextElement> = {\n /** Element to render. */\n as?: E;\n /** Color variant. */\n variant?: TextVariant;\n /**\n * Font size on the type scale.\n * @default \"sm\"\n */\n size?: Responsive<TextSize>;\n /** Font weight. */\n weight?: Responsive<TextWeight>;\n /** Truncate overflowing text with an ellipsis instead of wrapping. */\n truncate?: boolean;\n};\n\n// Deliberate exception to the general \"use ComponentProps<'tag'>\" rule other components follow.\n// With the polymorphic `as` prop, TypeScript cannot narrow the `ref` prop's type against the\n// resolved element E — allowing `ref` here would silently accept mismatched ref types\n// (e.g. a ref typed for HTMLParagraphElement on `<Text as=\"span\" ref={…} />`). Keeping `ref`\n// out of the prop type entirely is safer than a misleading loose type; don't switch this\n// to ComponentProps for consistency with the other components.\nexport type TextProps<E extends TextElement = \"p\"> = TextOwnProps<E> &\n Omit<ComponentPropsWithoutRef<E>, keyof TextOwnProps<E>>;\n\n// Exhaustive over TextElement so adding a tag without classifying it fails to typecheck,\n// instead of silently leaving truncate's inline-block fix un-applied for that tag.\nconst INLINE_TEXT_ELEMENTS: Record<TextElement, boolean> = {\n p: false,\n div: false,\n span: true,\n label: true,\n strong: true,\n em: true,\n small: true,\n};\n\nexport function Text<E extends TextElement = \"p\">({\n as,\n variant = \"body\",\n size,\n weight,\n truncate = false,\n className,\n style,\n ...rest\n}: TextProps<E>) {\n const tag = as ?? \"p\";\n const sizeVars = resolveResponsive(size, \"--_text-size\", fontSizeToVar);\n const weightVars = resolveResponsive(weight, \"--_text-weight\", weightToValue);\n\n const classes = cx(\n \"ps1ui-text\",\n `ps1ui-text--${variant}`,\n truncate && \"ps1ui-text--truncate\",\n truncate && INLINE_TEXT_ELEMENTS[tag] && \"ps1ui-text--truncate-inline\",\n className,\n );\n\n // Caller style first, resolved vars last: `--_*` is the reserved internal\n // prefix and wins on purpose — responsive values are prop-controlled. Cast\n // because 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, { ...rest, className: classes, style: mergedStyle });\n}\n"],"mappings":";;;;;AA0CA,MAAM,uBAAqD;CACzD,GAAG;CACH,KAAK;CACL,MAAM;CACN,OAAO;CACP,QAAQ;CACR,IAAI;CACJ,OAAO;AACT;AAEA,SAAgB,KAAkC,EAChD,IACA,UAAU,QACV,MACA,QACA,WAAW,OACX,WACA,OACA,GAAG,QACY;CACf,MAAM,MAAM,MAAM;CAClB,MAAM,WAAW,kBAAkB,MAAM,gBAAgB,aAAa;CACtE,MAAM,aAAa,kBAAkB,QAAQ,kBAAkB,aAAa;CAE5E,MAAM,UAAU,GACd,cACA,eAAe,WACf,YAAY,wBACZ,YAAY,qBAAqB,QAAQ,+BACzC,SACF;CAKA,MAAM,cAA6B;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL;CAEA,OAAO,cAAc,KAAK;EAAE,GAAG;EAAM,WAAW;EAAS,OAAO;CAAY,CAAC;AAC/E"}
@@ -782,13 +782,13 @@
782
782
  color: var(--ps1ui-color-fg);
783
783
  }
784
784
 
785
- /* List — semantic <ul>/<ol> wrapper with terminal-style markers.
785
+ /* List — semantic <ul>/<ol> wrapper with plain-text markers.
786
786
 
787
787
  The global reset in base.css sets `list-style: none` on every <ul>/<ol>,
788
788
  which strips the UA bullet/number glyph. List re-introduces a marker via
789
789
  `::before` on each direct <li> — `-` for unordered, `NN.` for ordered —
790
790
  so the "reset removes bullets" side effect turns into an on-brand
791
- terminal glyph rather than a hole.
791
+ plain-text glyph rather than a hole.
792
792
 
793
793
  The ordered marker uses `counter(list-item)` — the browser-maintained
794
794
  list-item counter that already honours `<ol start>`, `<ol reversed>`,
@@ -1104,7 +1104,7 @@
1104
1104
  display: none;
1105
1105
  }
1106
1106
 
1107
- /* Terminal-style disclosure marker: `▸` when closed, `▾` when open. Fixed
1107
+ /* Glyph disclosure marker: `▸` when closed, `▾` when open. Fixed
1108
1108
  inline-block width so the summary text baseline stays put across states —
1109
1109
  the two glyphs don't measure identically in the JetBrains Mono fallback
1110
1110
  monospace stack. Colored with fg-muted so the arrow reads as chrome, not
@@ -1402,7 +1402,7 @@
1402
1402
  }
1403
1403
  }
1404
1404
 
1405
- /* Table — semantic <table> with terminal-style grid borders, wrapped in a
1405
+ /* Table — semantic <table> with box-drawing-style grid borders, wrapped in a
1406
1406
  horizontal-scroll container.
1407
1407
 
1408
1408
  Scroll wrapper: `display: table` boxes don't honor overflow, so Table.tsx
@@ -1414,8 +1414,8 @@
1414
1414
  high-contrast mode; the grouped rule restores a real outline).
1415
1415
 
1416
1416
  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
1417
+ --ps1ui-color-border draw the closed single-stroke grid a box-drawing
1418
+ table (┌─┬─┐) renders — collapsing is what merges adjacent
1419
1419
  cell edges into one shared rule. The header row separates on two axes:
1420
1420
  a surface background and a border-strong bottom rule. That rule is
1421
1421
  declared on the <th> (cell origin) deliberately — in border-collapse
package/dist/styles.css CHANGED
@@ -975,13 +975,13 @@ html {
975
975
  line-height: 1.6;
976
976
  color: var(--ps1ui-color-fg);
977
977
  }
978
- /* List — semantic <ul>/<ol> wrapper with terminal-style markers.
978
+ /* List — semantic <ul>/<ol> wrapper with plain-text markers.
979
979
 
980
980
  The global reset in base.css sets `list-style: none` on every <ul>/<ol>,
981
981
  which strips the UA bullet/number glyph. List re-introduces a marker via
982
982
  `::before` on each direct <li> — `-` for unordered, `NN.` for ordered —
983
983
  so the "reset removes bullets" side effect turns into an on-brand
984
- terminal glyph rather than a hole.
984
+ plain-text glyph rather than a hole.
985
985
 
986
986
  The ordered marker uses `counter(list-item)` — the browser-maintained
987
987
  list-item counter that already honours `<ol start>`, `<ol reversed>`,
@@ -1261,7 +1261,7 @@ html {
1261
1261
  .ps1ui-details__summary::-webkit-details-marker {
1262
1262
  display: none;
1263
1263
  }
1264
- /* Terminal-style disclosure marker: `▸` when closed, `▾` when open. Fixed
1264
+ /* Glyph disclosure marker: `▸` when closed, `▾` when open. Fixed
1265
1265
  inline-block width so the summary text baseline stays put across states —
1266
1266
  the two glyphs don't measure identically in the JetBrains Mono fallback
1267
1267
  monospace stack. Colored with fg-muted so the arrow reads as chrome, not
@@ -1519,7 +1519,7 @@ html {
1519
1519
  --_wrap: var(--_wrap-xl);
1520
1520
  }
1521
1521
  }
1522
- /* Table — semantic <table> with terminal-style grid borders, wrapped in a
1522
+ /* Table — semantic <table> with box-drawing-style grid borders, wrapped in a
1523
1523
  horizontal-scroll container.
1524
1524
 
1525
1525
  Scroll wrapper: `display: table` boxes don't honor overflow, so Table.tsx
@@ -1531,8 +1531,8 @@ html {
1531
1531
  high-contrast mode; the grouped rule restores a real outline).
1532
1532
 
1533
1533
  Grid borders: `border-collapse: collapse` + a uniform 1px cell border in
1534
- --ps1ui-color-border draw the closed single-stroke grid a terminal
1535
- box-drawing table (┌─┬─┐) renders — collapsing is what merges adjacent
1534
+ --ps1ui-color-border draw the closed single-stroke grid a box-drawing
1535
+ table (┌─┬─┐) renders — collapsing is what merges adjacent
1536
1536
  cell edges into one shared rule. The header row separates on two axes:
1537
1537
  a surface background and a border-strong bottom rule. That rule is
1538
1538
  declared on the <th> (cell origin) deliberately — in border-collapse
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ps1ui/core",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "homepage": "https://github.com/koki-develop/ps1ui",
5
5
  "license": "MIT",
6
6
  "author": "koki-develop <kou.pg.0131@gmail.com>",
@@ -53,7 +53,7 @@
53
53
  "build-storybook": "storybook build"
54
54
  },
55
55
  "dependencies": {
56
- "@fontsource-variable/jetbrains-mono": "^5.2.8",
56
+ "@fontsource-variable/jetbrains-mono": "5.2.8",
57
57
  "hast-util-to-jsx-runtime": "2.3.6",
58
58
  "refractor": "5.0.0"
59
59
  },