@civitai/blocks-react 0.11.2 → 0.12.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 (50) hide show
  1. package/README.md +90 -3
  2. package/dist/ui/Alert.d.ts +19 -0
  3. package/dist/ui/Alert.d.ts.map +1 -0
  4. package/dist/ui/Alert.js +12 -0
  5. package/dist/ui/Alert.js.map +1 -0
  6. package/dist/ui/Badge.d.ts +18 -0
  7. package/dist/ui/Badge.d.ts.map +1 -0
  8. package/dist/ui/Badge.js +25 -0
  9. package/dist/ui/Badge.js.map +1 -0
  10. package/dist/ui/Button.d.ts +35 -0
  11. package/dist/ui/Button.d.ts.map +1 -0
  12. package/dist/ui/Button.js +44 -0
  13. package/dist/ui/Button.js.map +1 -0
  14. package/dist/ui/Card.d.ts +14 -0
  15. package/dist/ui/Card.d.ts.map +1 -0
  16. package/dist/ui/Card.js +11 -0
  17. package/dist/ui/Card.js.map +1 -0
  18. package/dist/ui/Group.d.ts +15 -0
  19. package/dist/ui/Group.d.ts.map +1 -0
  20. package/dist/ui/Group.js +22 -0
  21. package/dist/ui/Group.js.map +1 -0
  22. package/dist/ui/Loader.d.ts +18 -0
  23. package/dist/ui/Loader.d.ts.map +1 -0
  24. package/dist/ui/Loader.js +14 -0
  25. package/dist/ui/Loader.js.map +1 -0
  26. package/dist/ui/Modal.d.ts +46 -0
  27. package/dist/ui/Modal.d.ts.map +1 -0
  28. package/dist/ui/Modal.js +67 -0
  29. package/dist/ui/Modal.js.map +1 -0
  30. package/dist/ui/Stack.d.ts +13 -0
  31. package/dist/ui/Stack.d.ts.map +1 -0
  32. package/dist/ui/Stack.js +21 -0
  33. package/dist/ui/Stack.js.map +1 -0
  34. package/dist/ui/TextInput.d.ts +26 -0
  35. package/dist/ui/TextInput.d.ts.map +1 -0
  36. package/dist/ui/TextInput.js +23 -0
  37. package/dist/ui/TextInput.js.map +1 -0
  38. package/dist/ui/Textarea.d.ts +32 -0
  39. package/dist/ui/Textarea.d.ts.map +1 -0
  40. package/dist/ui/Textarea.js +23 -0
  41. package/dist/ui/Textarea.js.map +1 -0
  42. package/dist/ui/index.d.ts +33 -4
  43. package/dist/ui/index.d.ts.map +1 -1
  44. package/dist/ui/index.js +26 -4
  45. package/dist/ui/index.js.map +1 -1
  46. package/dist/ui/styles.d.ts +19 -0
  47. package/dist/ui/styles.d.ts.map +1 -0
  48. package/dist/ui/styles.js +416 -0
  49. package/dist/ui/styles.js.map +1 -0
  50. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../../src/ui/TextInput.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACjE,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,kEAAkE;IAClE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,6GAmErB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId } from 'react';
3
+ import { useBlocksStyles } from './styles.js';
4
+ /**
5
+ * Labeled text input. Wraps a native `<input>` (ref-forwarded). The label,
6
+ * description and error are wired to the control via `htmlFor` / `id` /
7
+ * `aria-describedby` / `aria-invalid` so the field is announced correctly.
8
+ *
9
+ * Auto-themed via `useBlocksStyles()`.
10
+ */
11
+ export const TextInput = forwardRef(function TextInput({ label, description, error, required, className, inputClassName, id, style, ...rest }, ref) {
12
+ useBlocksStyles();
13
+ const reactId = useId();
14
+ const inputId = id ?? `ci-input-${reactId}`;
15
+ const descId = `${inputId}-desc`;
16
+ const errId = `${inputId}-err`;
17
+ const hasError = error != null && error !== false;
18
+ const describedBy = [description != null ? descId : null, hasError ? errId : null]
19
+ .filter(Boolean)
20
+ .join(' ') || undefined;
21
+ return (_jsxs("div", { className: className, "data-civitai-ui": "text-input", "data-invalid": hasError ? 'true' : undefined, style: style, children: [label != null ? (_jsxs("label", { htmlFor: inputId, "data-civitai-ui-label": true, children: [label, required ? (_jsx("span", { "data-civitai-ui-required": true, "aria-hidden": "true", children: "*" })) : null] })) : null, description != null ? (_jsx("span", { id: descId, "data-civitai-ui-description": true, children: description })) : null, _jsx("input", { ref: ref, ...rest, id: inputId, className: inputClassName, "data-civitai-ui-control": true, required: required, "aria-invalid": hasError || undefined, "aria-describedby": describedBy, "aria-required": required || undefined }), hasError ? (_jsx("span", { id: errId, "data-civitai-ui-error": true, role: "alert", children: error })) : null] }));
22
+ });
23
+ //# sourceMappingURL=TextInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextInput.js","sourceRoot":"","sources":["../../src/ui/TextInput.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAqB9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CACjC,SAAS,SAAS,CAChB,EACE,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,SAAS,EACT,cAAc,EACd,EAAE,EACF,KAAK,EACL,GAAG,IAAI,EACR,EACD,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,EAAE,IAAI,YAAY,OAAO,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC;IACjC,MAAM,KAAK,GAAG,GAAG,OAAO,MAAM,CAAC;IAC/B,MAAM,QAAQ,GAAG,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,CAAC;IAClD,MAAM,WAAW,GACf,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;SAC3D,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;IAE5B,OAAO,CACL,eACE,SAAS,EAAE,SAAS,qBACJ,YAAY,kBACd,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC3C,KAAK,EAAE,KAAK,aAEX,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CACf,iBAAO,OAAO,EAAE,OAAO,4CACpB,KAAK,EACL,QAAQ,CAAC,CAAC,CAAC,CACV,gEAA2C,MAAM,kBAE1C,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAC,CAAC,CAAC,IAAI,EACP,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,CACrB,eAAM,EAAE,EAAE,MAAM,iDACb,WAAW,GACP,CACR,CAAC,CAAC,CAAC,IAAI,EACR,gBACE,GAAG,EAAE,GAAG,KACJ,IAAI,EACR,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,cAAc,mCAEzB,QAAQ,EAAE,QAAQ,kBACJ,QAAQ,IAAI,SAAS,sBACjB,WAAW,mBACd,QAAQ,IAAI,SAAS,GACpC,EACD,QAAQ,CAAC,CAAC,CAAC,CACV,eAAM,EAAE,EAAE,KAAK,iCAAwB,IAAI,EAAC,OAAO,YAChD,KAAK,GACD,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC,CACF,CAAC"}
@@ -0,0 +1,32 @@
1
+ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
2
+ /** Visible field label, rendered in a `<label>` linked to the textarea. */
3
+ label?: React.ReactNode;
4
+ /** Helper text under the label, linked via `aria-describedby`. */
5
+ description?: React.ReactNode;
6
+ /**
7
+ * Error message. When set, the control gets `aria-invalid="true"`, the
8
+ * message is announced (`role="alert"`) and linked via `aria-describedby`.
9
+ */
10
+ error?: React.ReactNode;
11
+ /** Mark required: shows an asterisk and sets the native `required`. */
12
+ required?: boolean;
13
+ /**
14
+ * Initial visible rows. Alias of the native `rows`; `minRows` wins if both
15
+ * are passed (kept for parity with Mantine's `minRows`). This is a static
16
+ * minimum — v0 does not auto-grow the textarea.
17
+ */
18
+ minRows?: number;
19
+ /** Class on the wrapping element. */
20
+ className?: string;
21
+ /** Class applied to the native `<textarea>`. */
22
+ textareaClassName?: string;
23
+ }
24
+ /**
25
+ * Labeled multi-line text input. Wraps a native `<textarea>` (ref-forwarded),
26
+ * same label/description/error wiring as `<TextInput>`. Auto-themed.
27
+ *
28
+ * `minRows` is a static minimum-rows hint (maps to the native `rows`); v0 does
29
+ * not auto-grow — note this limitation in the README.
30
+ */
31
+ export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
32
+ //# sourceMappingURL=Textarea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../../src/ui/Textarea.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,aACf,SAAQ,KAAK,CAAC,sBAAsB,CAAC,mBAAmB,CAAC;IACzD,2EAA2E;IAC3E,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,kEAAkE;IAClE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,+GAsEpB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId } from 'react';
3
+ import { useBlocksStyles } from './styles.js';
4
+ /**
5
+ * Labeled multi-line text input. Wraps a native `<textarea>` (ref-forwarded),
6
+ * same label/description/error wiring as `<TextInput>`. Auto-themed.
7
+ *
8
+ * `minRows` is a static minimum-rows hint (maps to the native `rows`); v0 does
9
+ * not auto-grow — note this limitation in the README.
10
+ */
11
+ export const Textarea = forwardRef(function Textarea({ label, description, error, required, minRows, rows, className, textareaClassName, id, style, ...rest }, ref) {
12
+ useBlocksStyles();
13
+ const reactId = useId();
14
+ const inputId = id ?? `ci-textarea-${reactId}`;
15
+ const descId = `${inputId}-desc`;
16
+ const errId = `${inputId}-err`;
17
+ const hasError = error != null && error !== false;
18
+ const describedBy = [description != null ? descId : null, hasError ? errId : null]
19
+ .filter(Boolean)
20
+ .join(' ') || undefined;
21
+ return (_jsxs("div", { className: className, "data-civitai-ui": "textarea", "data-invalid": hasError ? 'true' : undefined, style: style, children: [label != null ? (_jsxs("label", { htmlFor: inputId, "data-civitai-ui-label": true, children: [label, required ? (_jsx("span", { "data-civitai-ui-required": true, "aria-hidden": "true", children: "*" })) : null] })) : null, description != null ? (_jsx("span", { id: descId, "data-civitai-ui-description": true, children: description })) : null, _jsx("textarea", { ref: ref, ...rest, id: inputId, className: textareaClassName, "data-civitai-ui-control": true, rows: minRows ?? rows ?? 3, required: required, "aria-invalid": hasError || undefined, "aria-describedby": describedBy, "aria-required": required || undefined }), hasError ? (_jsx("span", { id: errId, "data-civitai-ui-error": true, role: "alert", children: error })) : null] }));
22
+ });
23
+ //# sourceMappingURL=Textarea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../src/ui/Textarea.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA2B9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAChC,SAAS,QAAQ,CACf,EACE,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,SAAS,EACT,iBAAiB,EACjB,EAAE,EACF,KAAK,EACL,GAAG,IAAI,EACR,EACD,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,EAAE,IAAI,eAAe,OAAO,EAAE,CAAC;IAC/C,MAAM,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC;IACjC,MAAM,KAAK,GAAG,GAAG,OAAO,MAAM,CAAC;IAC/B,MAAM,QAAQ,GAAG,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,CAAC;IAClD,MAAM,WAAW,GACf,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;SAC3D,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;IAE5B,OAAO,CACL,eACE,SAAS,EAAE,SAAS,qBACJ,UAAU,kBACZ,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC3C,KAAK,EAAE,KAAK,aAEX,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CACf,iBAAO,OAAO,EAAE,OAAO,4CACpB,KAAK,EACL,QAAQ,CAAC,CAAC,CAAC,CACV,gEAA2C,MAAM,kBAE1C,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAC,CAAC,CAAC,IAAI,EACP,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,CACrB,eAAM,EAAE,EAAE,MAAM,iDACb,WAAW,GACP,CACR,CAAC,CAAC,CAAC,IAAI,EACR,mBACE,GAAG,EAAE,GAAG,KACJ,IAAI,EACR,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,iBAAiB,mCAE5B,IAAI,EAAE,OAAO,IAAI,IAAI,IAAI,CAAC,EAC1B,QAAQ,EAAE,QAAQ,kBACJ,QAAQ,IAAI,SAAS,sBACjB,WAAW,mBACd,QAAQ,IAAI,SAAS,GACpC,EACD,QAAQ,CAAC,CAAC,CAAC,CACV,eAAM,EAAE,EAAE,KAAK,iCAAwB,IAAI,EAAC,OAAO,YAChD,KAAK,GACD,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC,CACF,CAAC"}
@@ -1,11 +1,40 @@
1
1
  /**
2
2
  * `@civitai/blocks-react/ui` — opinionated UI components for App Block authoring.
3
3
  *
4
- * v0 scope: just the generic, manifest-driven `SettingsForm`. The W6
5
- * component pack (Button, Input, Card, Modal, …) will land here too as
6
- * separate exports; importing from `/ui` (not the root) keeps the
7
- * transport-only consumer's bundle lean.
4
+ * Two surfaces:
5
+ *
6
+ * 1. The headless, manifest-driven `SettingsForm` (W3 host-themed native
7
+ * controls; its contract is intentionally unstyled).
8
+ * 2. The W6 component pack — a small, Civitai-looking, self-styled component
9
+ * set (Button, TextInput, Textarea, Card, Stack, Group, Alert, Loader,
10
+ * Badge, Modal). Zero setup: rendering any of them runs `useBlocksStyles()`
11
+ * which injects the pack's CSS into the block document's `<head>` once.
12
+ * Theme via your block's `data-theme` root (gotcha #60).
13
+ *
14
+ * Importing from `/ui` (not the package root) keeps a transport-only block's
15
+ * bundle lean.
8
16
  */
9
17
  export { SettingsForm, SettingsFormError, isFieldVisible } from './SettingsForm.js';
10
18
  export type { SettingsFormProps } from './SettingsForm.js';
19
+ export { injectBlocksStyles, useBlocksStyles, BLOCKS_UI_STYLES, } from './styles.js';
20
+ export { Button } from './Button.js';
21
+ export type { ButtonProps, ButtonVariant, ButtonSize } from './Button.js';
22
+ export { TextInput } from './TextInput.js';
23
+ export type { TextInputProps } from './TextInput.js';
24
+ export { Textarea } from './Textarea.js';
25
+ export type { TextareaProps } from './Textarea.js';
26
+ export { Card } from './Card.js';
27
+ export type { CardProps, CardPadding } from './Card.js';
28
+ export { Stack } from './Stack.js';
29
+ export type { StackProps } from './Stack.js';
30
+ export { Group } from './Group.js';
31
+ export type { GroupProps } from './Group.js';
32
+ export { Alert } from './Alert.js';
33
+ export type { AlertProps, AlertColor } from './Alert.js';
34
+ export { Loader } from './Loader.js';
35
+ export type { LoaderProps, LoaderSize } from './Loader.js';
36
+ export { Badge } from './Badge.js';
37
+ export type { BadgeProps, BadgeVariant, BadgeSize } from './Badge.js';
38
+ export { Modal } from './Modal.js';
39
+ export type { ModalProps, ModalSize } from './Modal.js';
11
40
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
package/dist/ui/index.js CHANGED
@@ -1,10 +1,32 @@
1
1
  /**
2
2
  * `@civitai/blocks-react/ui` — opinionated UI components for App Block authoring.
3
3
  *
4
- * v0 scope: just the generic, manifest-driven `SettingsForm`. The W6
5
- * component pack (Button, Input, Card, Modal, …) will land here too as
6
- * separate exports; importing from `/ui` (not the root) keeps the
7
- * transport-only consumer's bundle lean.
4
+ * Two surfaces:
5
+ *
6
+ * 1. The headless, manifest-driven `SettingsForm` (W3 host-themed native
7
+ * controls; its contract is intentionally unstyled).
8
+ * 2. The W6 component pack — a small, Civitai-looking, self-styled component
9
+ * set (Button, TextInput, Textarea, Card, Stack, Group, Alert, Loader,
10
+ * Badge, Modal). Zero setup: rendering any of them runs `useBlocksStyles()`
11
+ * which injects the pack's CSS into the block document's `<head>` once.
12
+ * Theme via your block's `data-theme` root (gotcha #60).
13
+ *
14
+ * Importing from `/ui` (not the package root) keeps a transport-only block's
15
+ * bundle lean.
8
16
  */
17
+ // W3 — manifest-driven settings form (host-themed native controls).
9
18
  export { SettingsForm, SettingsFormError, isFieldVisible } from './SettingsForm.js';
19
+ // W6 — runtime style injection.
20
+ export { injectBlocksStyles, useBlocksStyles, BLOCKS_UI_STYLES, } from './styles.js';
21
+ // W6 — component pack.
22
+ export { Button } from './Button.js';
23
+ export { TextInput } from './TextInput.js';
24
+ export { Textarea } from './Textarea.js';
25
+ export { Card } from './Card.js';
26
+ export { Stack } from './Stack.js';
27
+ export { Group } from './Group.js';
28
+ export { Alert } from './Alert.js';
29
+ export { Loader } from './Loader.js';
30
+ export { Badge } from './Badge.js';
31
+ export { Modal } from './Modal.js';
10
32
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,oEAAoE;AACpE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGpF,gCAAgC;AAChC,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** The full stylesheet shipped by the pack. Exported for SSR/manual use. */
2
+ export declare const BLOCKS_UI_STYLES = "\n:root {\n --ci-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --ci-radius: 8px;\n --ci-color-text: #1a1a1a;\n --ci-color-text-dimmed: #6b7280;\n --ci-color-surface: #ffffff;\n --ci-color-surface-2: #f4f4f5;\n --ci-color-border: #e0e0e3;\n --ci-color-primary: #228be6;\n --ci-color-primary-hover: #1c7ed6;\n --ci-color-primary-fg: #ffffff;\n --ci-color-error: #e03131;\n --ci-color-success: #2f9e44;\n --ci-color-warning: #f08c00;\n --ci-color-info: #1971c2;\n}\n\n[data-theme='light'] {\n --ci-color-text: #1a1a1a;\n --ci-color-text-dimmed: #6b7280;\n --ci-color-surface: #ffffff;\n --ci-color-surface-2: #f4f4f5;\n --ci-color-border: #e0e0e3;\n}\n\n[data-theme='dark'] {\n --ci-color-text: #e6e6e6;\n --ci-color-text-dimmed: #909296;\n --ci-color-surface: #1a1b1e;\n --ci-color-surface-2: #25262b;\n --ci-color-border: #2c2e33;\n --ci-color-primary: #228be6;\n --ci-color-primary-hover: #339af0;\n --ci-color-primary-fg: #ffffff;\n --ci-color-error: #ff6b6b;\n --ci-color-success: #51cf66;\n --ci-color-warning: #ffa94d;\n --ci-color-info: #4dabf7;\n}\n\n\n[data-civitai-ui] {\n box-sizing: border-box;\n font-family: var(--ci-font);\n}\n[data-civitai-ui] *,\n[data-civitai-ui] *::before,\n[data-civitai-ui] *::after {\n box-sizing: border-box;\n}\n\n/* ----- Button ----- */\n[data-civitai-ui='button'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid transparent;\n border-radius: var(--ci-radius);\n font-family: var(--ci-font);\n font-weight: 600;\n line-height: 1;\n cursor: pointer;\n user-select: none;\n text-decoration: none;\n transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;\n background: var(--ci-color-primary);\n color: var(--ci-color-primary-fg);\n}\n[data-civitai-ui='button'][data-size='sm'] { height: 30px; padding: 0 14px; font-size: 13px; }\n[data-civitai-ui='button'][data-size='md'] { height: 36px; padding: 0 18px; font-size: 14px; }\n[data-civitai-ui='button'][data-size='lg'] { height: 44px; padding: 0 22px; font-size: 16px; }\n[data-civitai-ui='button'][data-full-width='true'] { width: 100%; }\n[data-civitai-ui='button'][data-variant='filled'] {\n background: var(--ci-color-primary);\n color: var(--ci-color-primary-fg);\n border-color: var(--ci-color-primary);\n}\n[data-civitai-ui='button'][data-variant='filled']:hover:not(:disabled) {\n background: var(--ci-color-primary-hover);\n border-color: var(--ci-color-primary-hover);\n}\n[data-civitai-ui='button'][data-variant='light'] {\n background: color-mix(in srgb, var(--ci-color-primary) 12%, transparent);\n color: var(--ci-color-primary);\n border-color: transparent;\n}\n[data-civitai-ui='button'][data-variant='light']:hover:not(:disabled) {\n background: color-mix(in srgb, var(--ci-color-primary) 22%, transparent);\n}\n[data-civitai-ui='button'][data-variant='outline'] {\n background: transparent;\n color: var(--ci-color-primary);\n border-color: var(--ci-color-primary);\n}\n[data-civitai-ui='button'][data-variant='outline']:hover:not(:disabled) {\n background: color-mix(in srgb, var(--ci-color-primary) 10%, transparent);\n}\n[data-civitai-ui='button'][data-variant='subtle'] {\n background: transparent;\n color: var(--ci-color-primary);\n border-color: transparent;\n}\n[data-civitai-ui='button'][data-variant='subtle']:hover:not(:disabled) {\n background: color-mix(in srgb, var(--ci-color-primary) 10%, transparent);\n}\n[data-civitai-ui='button']:disabled,\n[data-civitai-ui='button'][aria-busy='true'] {\n opacity: 0.6;\n cursor: not-allowed;\n}\n[data-civitai-ui='button'] [data-civitai-ui-section] {\n display: inline-flex;\n align-items: center;\n}\n\n/* ----- TextInput / Textarea ----- */\n[data-civitai-ui='text-input'],\n[data-civitai-ui='textarea'] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n[data-civitai-ui-label] {\n font-size: 14px;\n font-weight: 600;\n color: var(--ci-color-text);\n}\n[data-civitai-ui-required] {\n color: var(--ci-color-error);\n margin-left: 2px;\n}\n[data-civitai-ui-description] {\n font-size: 12px;\n color: var(--ci-color-text-dimmed);\n}\n[data-civitai-ui-error] {\n font-size: 12px;\n color: var(--ci-color-error);\n}\n[data-civitai-ui-control] {\n width: 100%;\n font-family: var(--ci-font);\n font-size: 14px;\n color: var(--ci-color-text);\n background: var(--ci-color-surface);\n border: 1px solid var(--ci-color-border);\n border-radius: var(--ci-radius);\n padding: 8px 12px;\n transition: border-color 120ms ease;\n}\n[data-civitai-ui-control]:focus {\n outline: none;\n border-color: var(--ci-color-primary);\n}\n[data-civitai-ui-control][aria-invalid='true'] {\n border-color: var(--ci-color-error);\n}\n[data-civitai-ui-control]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\ntextarea[data-civitai-ui-control] {\n resize: vertical;\n line-height: 1.5;\n}\n\n/* ----- Card ----- */\n[data-civitai-ui='card'] {\n background: var(--ci-color-surface);\n border-radius: var(--ci-radius);\n color: var(--ci-color-text);\n}\n[data-civitai-ui='card'][data-with-border='true'] {\n border: 1px solid var(--ci-color-border);\n}\n[data-civitai-ui='card'][data-padding='sm'] { padding: 10px; }\n[data-civitai-ui='card'][data-padding='md'] { padding: 16px; }\n[data-civitai-ui='card'][data-padding='lg'] { padding: 24px; }\n\n/* ----- Stack / Group ----- */\n[data-civitai-ui='stack'] {\n display: flex;\n flex-direction: column;\n}\n[data-civitai-ui='group'] {\n display: flex;\n flex-direction: row;\n}\n\n/* ----- Alert ----- */\n[data-civitai-ui='alert'] {\n display: flex;\n gap: 10px;\n align-items: flex-start;\n padding: 12px 14px;\n border-radius: var(--ci-radius);\n border: 1px solid transparent;\n font-size: 14px;\n color: var(--ci-color-text);\n}\n[data-civitai-ui='alert'][data-color='info'] {\n background: color-mix(in srgb, var(--ci-color-info) 12%, transparent);\n border-color: color-mix(in srgb, var(--ci-color-info) 35%, transparent);\n}\n[data-civitai-ui='alert'][data-color='success'] {\n background: color-mix(in srgb, var(--ci-color-success) 12%, transparent);\n border-color: color-mix(in srgb, var(--ci-color-success) 35%, transparent);\n}\n[data-civitai-ui='alert'][data-color='warning'] {\n background: color-mix(in srgb, var(--ci-color-warning) 14%, transparent);\n border-color: color-mix(in srgb, var(--ci-color-warning) 35%, transparent);\n}\n[data-civitai-ui='alert'][data-color='error'] {\n background: color-mix(in srgb, var(--ci-color-error) 12%, transparent);\n border-color: color-mix(in srgb, var(--ci-color-error) 35%, transparent);\n}\n[data-civitai-ui='alert'] [data-civitai-ui-alert-body] {\n flex: 1;\n min-width: 0;\n}\n[data-civitai-ui='alert'] [data-civitai-ui-alert-title] {\n font-weight: 600;\n margin-bottom: 2px;\n}\n[data-civitai-ui='alert'] [data-civitai-ui-alert-close] {\n background: transparent;\n border: none;\n cursor: pointer;\n color: inherit;\n font-size: 16px;\n line-height: 1;\n padding: 0;\n opacity: 0.7;\n}\n[data-civitai-ui='alert'] [data-civitai-ui-alert-close]:hover {\n opacity: 1;\n}\n\n/* ----- Loader ----- */\n[data-civitai-ui='loader'] {\n display: inline-block;\n border-radius: 50%;\n border-style: solid;\n border-color: color-mix(in srgb, currentColor 25%, transparent);\n border-top-color: currentColor;\n color: var(--ci-color-primary);\n animation: civitai-ui-spin 0.7s linear infinite;\n}\n[data-civitai-ui='loader'][data-size='sm'] { width: 16px; height: 16px; border-width: 2px; }\n[data-civitai-ui='loader'][data-size='md'] { width: 22px; height: 22px; border-width: 3px; }\n[data-civitai-ui='loader'][data-size='lg'] { width: 32px; height: 32px; border-width: 4px; }\n@keyframes civitai-ui-spin {\n to { transform: rotate(360deg); }\n}\n[data-civitai-ui='button'] [data-civitai-ui='loader'] {\n color: currentColor;\n}\n\n/* ----- Badge ----- */\n[data-civitai-ui='badge'] {\n display: inline-flex;\n align-items: center;\n border: 1px solid transparent;\n border-radius: 999px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n}\n[data-civitai-ui='badge'][data-size='sm'] { height: 18px; padding: 0 8px; font-size: 10px; }\n[data-civitai-ui='badge'][data-size='md'] { height: 22px; padding: 0 10px; font-size: 11px; }\n[data-civitai-ui='badge'][data-size='lg'] { height: 26px; padding: 0 12px; font-size: 13px; }\n[data-civitai-ui='badge'][data-variant='filled'] {\n background: var(--ci-color-primary);\n color: var(--ci-color-primary-fg);\n border-color: var(--ci-color-primary);\n}\n[data-civitai-ui='badge'][data-variant='light'] {\n background: color-mix(in srgb, var(--ci-color-primary) 14%, transparent);\n color: var(--ci-color-primary);\n}\n[data-civitai-ui='badge'][data-variant='outline'] {\n background: transparent;\n color: var(--ci-color-primary);\n border-color: var(--ci-color-primary);\n}\n\n/* ----- Modal ----- */\n[data-civitai-ui='modal-overlay'] {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.55);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: 32px 16px;\n overflow-y: auto;\n z-index: 1000;\n}\n[data-civitai-ui='modal'] {\n background: var(--ci-color-surface);\n color: var(--ci-color-text);\n border: 1px solid var(--ci-color-border);\n border-radius: var(--ci-radius);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);\n width: 100%;\n max-width: 440px;\n outline: none;\n}\n[data-civitai-ui='modal'][data-size='sm'] { max-width: 340px; }\n[data-civitai-ui='modal'][data-size='md'] { max-width: 440px; }\n[data-civitai-ui='modal'][data-size='lg'] { max-width: 620px; }\n[data-civitai-ui='modal'] [data-civitai-ui-modal-header] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 16px 18px;\n border-bottom: 1px solid var(--ci-color-border);\n}\n[data-civitai-ui='modal'] [data-civitai-ui-modal-title] {\n font-size: 16px;\n font-weight: 700;\n margin: 0;\n}\n[data-civitai-ui='modal'] [data-civitai-ui-modal-close] {\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--ci-color-text-dimmed);\n font-size: 20px;\n line-height: 1;\n padding: 0;\n}\n[data-civitai-ui='modal'] [data-civitai-ui-modal-close]:hover {\n color: var(--ci-color-text);\n}\n[data-civitai-ui='modal'] [data-civitai-ui-modal-body] {\n padding: 18px;\n}\n";
3
+ /**
4
+ * Inject the pack's stylesheet into a document's `<head>` exactly once.
5
+ * Idempotent: subsequent calls (including from other components) detect the
6
+ * marker `<style>` and no-op. Safe to call from any component's effect.
7
+ *
8
+ * @param doc Target document. Defaults to the ambient `document`. No-op when
9
+ * no document is available (e.g. SSR) — callers in the browser get styling,
10
+ * server renders fall back to the unstyled-but-functional markup.
11
+ */
12
+ export declare function injectBlocksStyles(doc?: Document): void;
13
+ /**
14
+ * Hook that injects the pack's styles once on mount. Every `/ui` component
15
+ * calls this so rendering any of them is enough to get the styling — the
16
+ * author never imports CSS or runs a setup step.
17
+ */
18
+ export declare function useBlocksStyles(): void;
19
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/ui/styles.ts"],"names":[],"mappings":"AA4XA,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,uzUAAgC,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,IAAI,CAgBvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAItC"}
@@ -0,0 +1,416 @@
1
+ import { useEffect } from 'react';
2
+ /**
3
+ * W6 component pack — runtime style injection.
4
+ *
5
+ * The package builds with `tsc` only — there is no bundler and no CSS
6
+ * pipeline (verified: `dist/` emits no `.css`). To give block authors a
7
+ * zero-setup, auto-themed pack we ship the CSS as a TS string constant and
8
+ * inject it into the block document's `<head>` once, idempotently. Every
9
+ * component calls `useBlocksStyles()` (a `useEffect` that calls
10
+ * `injectBlocksStyles()`), so simply rendering any `/ui` component is enough
11
+ * to get the styling — no CSS import, no setup step.
12
+ *
13
+ * Theming (gotcha #60): a block sets `data-theme={theme}` on its OWN root
14
+ * (from `BLOCK_INIT.theme`); the host can't reach across the iframe boundary.
15
+ * These rules theme via an ancestor `[data-theme='dark']` selector. The
16
+ * default (no `data-theme`, i.e. light) matches the palette in
17
+ * `starters/examples/hello-world/src/index.css`.
18
+ */
19
+ /** Marker attribute on the injected `<style>` so injection is idempotent. */
20
+ const STYLE_MARKER = 'data-civitai-blocks-ui';
21
+ /**
22
+ * Design tokens. CSS custom properties under `:root` (light defaults),
23
+ * overridden under `[data-theme='dark']`. Palette mirrors Civitai's Mantine
24
+ * design language (8px radius, blue primary `#228be6`, the dark/light
25
+ * surfaces already used by the hello-world example).
26
+ */
27
+ const TOKENS = `
28
+ :root {
29
+ --ci-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
30
+ --ci-radius: 8px;
31
+ --ci-color-text: #1a1a1a;
32
+ --ci-color-text-dimmed: #6b7280;
33
+ --ci-color-surface: #ffffff;
34
+ --ci-color-surface-2: #f4f4f5;
35
+ --ci-color-border: #e0e0e3;
36
+ --ci-color-primary: #228be6;
37
+ --ci-color-primary-hover: #1c7ed6;
38
+ --ci-color-primary-fg: #ffffff;
39
+ --ci-color-error: #e03131;
40
+ --ci-color-success: #2f9e44;
41
+ --ci-color-warning: #f08c00;
42
+ --ci-color-info: #1971c2;
43
+ }
44
+
45
+ [data-theme='light'] {
46
+ --ci-color-text: #1a1a1a;
47
+ --ci-color-text-dimmed: #6b7280;
48
+ --ci-color-surface: #ffffff;
49
+ --ci-color-surface-2: #f4f4f5;
50
+ --ci-color-border: #e0e0e3;
51
+ }
52
+
53
+ [data-theme='dark'] {
54
+ --ci-color-text: #e6e6e6;
55
+ --ci-color-text-dimmed: #909296;
56
+ --ci-color-surface: #1a1b1e;
57
+ --ci-color-surface-2: #25262b;
58
+ --ci-color-border: #2c2e33;
59
+ --ci-color-primary: #228be6;
60
+ --ci-color-primary-hover: #339af0;
61
+ --ci-color-primary-fg: #ffffff;
62
+ --ci-color-error: #ff6b6b;
63
+ --ci-color-success: #51cf66;
64
+ --ci-color-warning: #ffa94d;
65
+ --ci-color-info: #4dabf7;
66
+ }
67
+ `;
68
+ /**
69
+ * Component CSS. All selectors hang off the `data-civitai-ui="<name>"`
70
+ * attribute each component renders, so the pack never leaks styles onto the
71
+ * author's own markup.
72
+ */
73
+ const COMPONENT_CSS = `
74
+ [data-civitai-ui] {
75
+ box-sizing: border-box;
76
+ font-family: var(--ci-font);
77
+ }
78
+ [data-civitai-ui] *,
79
+ [data-civitai-ui] *::before,
80
+ [data-civitai-ui] *::after {
81
+ box-sizing: border-box;
82
+ }
83
+
84
+ /* ----- Button ----- */
85
+ [data-civitai-ui='button'] {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ gap: 8px;
90
+ border: 1px solid transparent;
91
+ border-radius: var(--ci-radius);
92
+ font-family: var(--ci-font);
93
+ font-weight: 600;
94
+ line-height: 1;
95
+ cursor: pointer;
96
+ user-select: none;
97
+ text-decoration: none;
98
+ transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
99
+ background: var(--ci-color-primary);
100
+ color: var(--ci-color-primary-fg);
101
+ }
102
+ [data-civitai-ui='button'][data-size='sm'] { height: 30px; padding: 0 14px; font-size: 13px; }
103
+ [data-civitai-ui='button'][data-size='md'] { height: 36px; padding: 0 18px; font-size: 14px; }
104
+ [data-civitai-ui='button'][data-size='lg'] { height: 44px; padding: 0 22px; font-size: 16px; }
105
+ [data-civitai-ui='button'][data-full-width='true'] { width: 100%; }
106
+ [data-civitai-ui='button'][data-variant='filled'] {
107
+ background: var(--ci-color-primary);
108
+ color: var(--ci-color-primary-fg);
109
+ border-color: var(--ci-color-primary);
110
+ }
111
+ [data-civitai-ui='button'][data-variant='filled']:hover:not(:disabled) {
112
+ background: var(--ci-color-primary-hover);
113
+ border-color: var(--ci-color-primary-hover);
114
+ }
115
+ [data-civitai-ui='button'][data-variant='light'] {
116
+ background: color-mix(in srgb, var(--ci-color-primary) 12%, transparent);
117
+ color: var(--ci-color-primary);
118
+ border-color: transparent;
119
+ }
120
+ [data-civitai-ui='button'][data-variant='light']:hover:not(:disabled) {
121
+ background: color-mix(in srgb, var(--ci-color-primary) 22%, transparent);
122
+ }
123
+ [data-civitai-ui='button'][data-variant='outline'] {
124
+ background: transparent;
125
+ color: var(--ci-color-primary);
126
+ border-color: var(--ci-color-primary);
127
+ }
128
+ [data-civitai-ui='button'][data-variant='outline']:hover:not(:disabled) {
129
+ background: color-mix(in srgb, var(--ci-color-primary) 10%, transparent);
130
+ }
131
+ [data-civitai-ui='button'][data-variant='subtle'] {
132
+ background: transparent;
133
+ color: var(--ci-color-primary);
134
+ border-color: transparent;
135
+ }
136
+ [data-civitai-ui='button'][data-variant='subtle']:hover:not(:disabled) {
137
+ background: color-mix(in srgb, var(--ci-color-primary) 10%, transparent);
138
+ }
139
+ [data-civitai-ui='button']:disabled,
140
+ [data-civitai-ui='button'][aria-busy='true'] {
141
+ opacity: 0.6;
142
+ cursor: not-allowed;
143
+ }
144
+ [data-civitai-ui='button'] [data-civitai-ui-section] {
145
+ display: inline-flex;
146
+ align-items: center;
147
+ }
148
+
149
+ /* ----- TextInput / Textarea ----- */
150
+ [data-civitai-ui='text-input'],
151
+ [data-civitai-ui='textarea'] {
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 4px;
155
+ }
156
+ [data-civitai-ui-label] {
157
+ font-size: 14px;
158
+ font-weight: 600;
159
+ color: var(--ci-color-text);
160
+ }
161
+ [data-civitai-ui-required] {
162
+ color: var(--ci-color-error);
163
+ margin-left: 2px;
164
+ }
165
+ [data-civitai-ui-description] {
166
+ font-size: 12px;
167
+ color: var(--ci-color-text-dimmed);
168
+ }
169
+ [data-civitai-ui-error] {
170
+ font-size: 12px;
171
+ color: var(--ci-color-error);
172
+ }
173
+ [data-civitai-ui-control] {
174
+ width: 100%;
175
+ font-family: var(--ci-font);
176
+ font-size: 14px;
177
+ color: var(--ci-color-text);
178
+ background: var(--ci-color-surface);
179
+ border: 1px solid var(--ci-color-border);
180
+ border-radius: var(--ci-radius);
181
+ padding: 8px 12px;
182
+ transition: border-color 120ms ease;
183
+ }
184
+ [data-civitai-ui-control]:focus {
185
+ outline: none;
186
+ border-color: var(--ci-color-primary);
187
+ }
188
+ [data-civitai-ui-control][aria-invalid='true'] {
189
+ border-color: var(--ci-color-error);
190
+ }
191
+ [data-civitai-ui-control]:disabled {
192
+ opacity: 0.6;
193
+ cursor: not-allowed;
194
+ }
195
+ textarea[data-civitai-ui-control] {
196
+ resize: vertical;
197
+ line-height: 1.5;
198
+ }
199
+
200
+ /* ----- Card ----- */
201
+ [data-civitai-ui='card'] {
202
+ background: var(--ci-color-surface);
203
+ border-radius: var(--ci-radius);
204
+ color: var(--ci-color-text);
205
+ }
206
+ [data-civitai-ui='card'][data-with-border='true'] {
207
+ border: 1px solid var(--ci-color-border);
208
+ }
209
+ [data-civitai-ui='card'][data-padding='sm'] { padding: 10px; }
210
+ [data-civitai-ui='card'][data-padding='md'] { padding: 16px; }
211
+ [data-civitai-ui='card'][data-padding='lg'] { padding: 24px; }
212
+
213
+ /* ----- Stack / Group ----- */
214
+ [data-civitai-ui='stack'] {
215
+ display: flex;
216
+ flex-direction: column;
217
+ }
218
+ [data-civitai-ui='group'] {
219
+ display: flex;
220
+ flex-direction: row;
221
+ }
222
+
223
+ /* ----- Alert ----- */
224
+ [data-civitai-ui='alert'] {
225
+ display: flex;
226
+ gap: 10px;
227
+ align-items: flex-start;
228
+ padding: 12px 14px;
229
+ border-radius: var(--ci-radius);
230
+ border: 1px solid transparent;
231
+ font-size: 14px;
232
+ color: var(--ci-color-text);
233
+ }
234
+ [data-civitai-ui='alert'][data-color='info'] {
235
+ background: color-mix(in srgb, var(--ci-color-info) 12%, transparent);
236
+ border-color: color-mix(in srgb, var(--ci-color-info) 35%, transparent);
237
+ }
238
+ [data-civitai-ui='alert'][data-color='success'] {
239
+ background: color-mix(in srgb, var(--ci-color-success) 12%, transparent);
240
+ border-color: color-mix(in srgb, var(--ci-color-success) 35%, transparent);
241
+ }
242
+ [data-civitai-ui='alert'][data-color='warning'] {
243
+ background: color-mix(in srgb, var(--ci-color-warning) 14%, transparent);
244
+ border-color: color-mix(in srgb, var(--ci-color-warning) 35%, transparent);
245
+ }
246
+ [data-civitai-ui='alert'][data-color='error'] {
247
+ background: color-mix(in srgb, var(--ci-color-error) 12%, transparent);
248
+ border-color: color-mix(in srgb, var(--ci-color-error) 35%, transparent);
249
+ }
250
+ [data-civitai-ui='alert'] [data-civitai-ui-alert-body] {
251
+ flex: 1;
252
+ min-width: 0;
253
+ }
254
+ [data-civitai-ui='alert'] [data-civitai-ui-alert-title] {
255
+ font-weight: 600;
256
+ margin-bottom: 2px;
257
+ }
258
+ [data-civitai-ui='alert'] [data-civitai-ui-alert-close] {
259
+ background: transparent;
260
+ border: none;
261
+ cursor: pointer;
262
+ color: inherit;
263
+ font-size: 16px;
264
+ line-height: 1;
265
+ padding: 0;
266
+ opacity: 0.7;
267
+ }
268
+ [data-civitai-ui='alert'] [data-civitai-ui-alert-close]:hover {
269
+ opacity: 1;
270
+ }
271
+
272
+ /* ----- Loader ----- */
273
+ [data-civitai-ui='loader'] {
274
+ display: inline-block;
275
+ border-radius: 50%;
276
+ border-style: solid;
277
+ border-color: color-mix(in srgb, currentColor 25%, transparent);
278
+ border-top-color: currentColor;
279
+ color: var(--ci-color-primary);
280
+ animation: civitai-ui-spin 0.7s linear infinite;
281
+ }
282
+ [data-civitai-ui='loader'][data-size='sm'] { width: 16px; height: 16px; border-width: 2px; }
283
+ [data-civitai-ui='loader'][data-size='md'] { width: 22px; height: 22px; border-width: 3px; }
284
+ [data-civitai-ui='loader'][data-size='lg'] { width: 32px; height: 32px; border-width: 4px; }
285
+ @keyframes civitai-ui-spin {
286
+ to { transform: rotate(360deg); }
287
+ }
288
+ [data-civitai-ui='button'] [data-civitai-ui='loader'] {
289
+ color: currentColor;
290
+ }
291
+
292
+ /* ----- Badge ----- */
293
+ [data-civitai-ui='badge'] {
294
+ display: inline-flex;
295
+ align-items: center;
296
+ border: 1px solid transparent;
297
+ border-radius: 999px;
298
+ font-weight: 600;
299
+ line-height: 1;
300
+ white-space: nowrap;
301
+ text-transform: uppercase;
302
+ letter-spacing: 0.02em;
303
+ }
304
+ [data-civitai-ui='badge'][data-size='sm'] { height: 18px; padding: 0 8px; font-size: 10px; }
305
+ [data-civitai-ui='badge'][data-size='md'] { height: 22px; padding: 0 10px; font-size: 11px; }
306
+ [data-civitai-ui='badge'][data-size='lg'] { height: 26px; padding: 0 12px; font-size: 13px; }
307
+ [data-civitai-ui='badge'][data-variant='filled'] {
308
+ background: var(--ci-color-primary);
309
+ color: var(--ci-color-primary-fg);
310
+ border-color: var(--ci-color-primary);
311
+ }
312
+ [data-civitai-ui='badge'][data-variant='light'] {
313
+ background: color-mix(in srgb, var(--ci-color-primary) 14%, transparent);
314
+ color: var(--ci-color-primary);
315
+ }
316
+ [data-civitai-ui='badge'][data-variant='outline'] {
317
+ background: transparent;
318
+ color: var(--ci-color-primary);
319
+ border-color: var(--ci-color-primary);
320
+ }
321
+
322
+ /* ----- Modal ----- */
323
+ [data-civitai-ui='modal-overlay'] {
324
+ position: fixed;
325
+ inset: 0;
326
+ background: rgba(0, 0, 0, 0.55);
327
+ display: flex;
328
+ align-items: flex-start;
329
+ justify-content: center;
330
+ padding: 32px 16px;
331
+ overflow-y: auto;
332
+ z-index: 1000;
333
+ }
334
+ [data-civitai-ui='modal'] {
335
+ background: var(--ci-color-surface);
336
+ color: var(--ci-color-text);
337
+ border: 1px solid var(--ci-color-border);
338
+ border-radius: var(--ci-radius);
339
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
340
+ width: 100%;
341
+ max-width: 440px;
342
+ outline: none;
343
+ }
344
+ [data-civitai-ui='modal'][data-size='sm'] { max-width: 340px; }
345
+ [data-civitai-ui='modal'][data-size='md'] { max-width: 440px; }
346
+ [data-civitai-ui='modal'][data-size='lg'] { max-width: 620px; }
347
+ [data-civitai-ui='modal'] [data-civitai-ui-modal-header] {
348
+ display: flex;
349
+ align-items: center;
350
+ justify-content: space-between;
351
+ gap: 12px;
352
+ padding: 16px 18px;
353
+ border-bottom: 1px solid var(--ci-color-border);
354
+ }
355
+ [data-civitai-ui='modal'] [data-civitai-ui-modal-title] {
356
+ font-size: 16px;
357
+ font-weight: 700;
358
+ margin: 0;
359
+ }
360
+ [data-civitai-ui='modal'] [data-civitai-ui-modal-close] {
361
+ background: transparent;
362
+ border: none;
363
+ cursor: pointer;
364
+ color: var(--ci-color-text-dimmed);
365
+ font-size: 20px;
366
+ line-height: 1;
367
+ padding: 0;
368
+ }
369
+ [data-civitai-ui='modal'] [data-civitai-ui-modal-close]:hover {
370
+ color: var(--ci-color-text);
371
+ }
372
+ [data-civitai-ui='modal'] [data-civitai-ui-modal-body] {
373
+ padding: 18px;
374
+ }
375
+ `;
376
+ /** The full stylesheet shipped by the pack. Exported for SSR/manual use. */
377
+ export const BLOCKS_UI_STYLES = `${TOKENS}\n${COMPONENT_CSS}`;
378
+ /**
379
+ * Inject the pack's stylesheet into a document's `<head>` exactly once.
380
+ * Idempotent: subsequent calls (including from other components) detect the
381
+ * marker `<style>` and no-op. Safe to call from any component's effect.
382
+ *
383
+ * @param doc Target document. Defaults to the ambient `document`. No-op when
384
+ * no document is available (e.g. SSR) — callers in the browser get styling,
385
+ * server renders fall back to the unstyled-but-functional markup.
386
+ */
387
+ export function injectBlocksStyles(doc) {
388
+ const target = doc ?? (typeof document !== 'undefined' ? document : undefined);
389
+ if (!target)
390
+ return;
391
+ // Already injected → no-op (idempotent).
392
+ if (target.querySelector(`style[${STYLE_MARKER}]`))
393
+ return;
394
+ const style = target.createElement('style');
395
+ style.setAttribute(STYLE_MARKER, 'true');
396
+ style.textContent = BLOCKS_UI_STYLES;
397
+ const head = target.head ?? target.getElementsByTagName('head')[0];
398
+ if (head) {
399
+ head.appendChild(style);
400
+ }
401
+ else {
402
+ // Degenerate document with no <head>; fall back to documentElement.
403
+ target.documentElement.appendChild(style);
404
+ }
405
+ }
406
+ /**
407
+ * Hook that injects the pack's styles once on mount. Every `/ui` component
408
+ * calls this so rendering any of them is enough to get the styling — the
409
+ * author never imports CSS or runs a setup step.
410
+ */
411
+ export function useBlocksStyles() {
412
+ useEffect(() => {
413
+ injectBlocksStyles();
414
+ }, []);
415
+ }
416
+ //# sourceMappingURL=styles.js.map