@vegastack/design 0.1.1 → 0.3.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/css/base.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /* Re-export of @vegastack/design-tokens/base.css — see theme.css for rationale. */
2
- @import '@vegastack/design-tokens/base.css';
2
+ @import "@vegastack/design-tokens/base.css";
package/css/theme.css CHANGED
@@ -1,4 +1,4 @@
1
1
  /* Re-export: token values live in @vegastack/design-tokens (a direct dependency of this package),
2
2
  * so pnpm-strict consumers can import "@vegastack/design/theme.css" without declaring the
3
3
  * transitive tokens package themselves. Tokens-only consumers install @vegastack/design-tokens. */
4
- @import '@vegastack/design-tokens/theme.css';
4
+ @import "@vegastack/design-tokens/theme.css";
package/css/utilities.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /* Re-export of @vegastack/design-tokens/utilities.css — see theme.css for rationale. */
2
- @import '@vegastack/design-tokens/utilities.css';
2
+ @import "@vegastack/design-tokens/utilities.css";
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/icons/index.tsx
21
+ var icons_exports = {};
22
+ __export(icons_exports, {
23
+ AnimatedIcon: () => AnimatedIcon,
24
+ BrandIcon: () => BrandIcon,
25
+ Icon: () => Icon
26
+ });
27
+ module.exports = __toCommonJS(icons_exports);
28
+
29
+ // src/icons/icon.tsx
30
+ var React = require("react");
31
+ var import_jsx_runtime = require("react/jsx-runtime");
32
+ var SIZES = { xs: 14, sm: 16, md: 20, lg: 24 };
33
+ function Icon({
34
+ as: Cmp,
35
+ size = "md",
36
+ "aria-label": label,
37
+ ...props
38
+ }) {
39
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
40
+ Cmp,
41
+ {
42
+ width: SIZES[size],
43
+ height: SIZES[size],
44
+ strokeWidth: 1.75,
45
+ "aria-hidden": label ? void 0 : true,
46
+ "aria-label": label,
47
+ ...props
48
+ }
49
+ );
50
+ }
51
+
52
+ // src/icons/brand-icon.tsx
53
+ var React2 = require("react");
54
+
55
+ // src/index.ts
56
+ var import_clsx = require("clsx");
57
+ var import_tailwind_merge = require("tailwind-merge");
58
+ var twMerge = (0, import_tailwind_merge.extendTailwindMerge)({
59
+ extend: {
60
+ classGroups: {
61
+ "font-size": [
62
+ {
63
+ text: [
64
+ "h1",
65
+ "h2",
66
+ "h3",
67
+ "h4",
68
+ "label",
69
+ "label-sm",
70
+ "code",
71
+ "code-sm",
72
+ "mono-label",
73
+ "display-sm",
74
+ "display-md",
75
+ "display-lg",
76
+ "display-xl"
77
+ ]
78
+ }
79
+ ]
80
+ }
81
+ }
82
+ });
83
+ function cn(...inputs) {
84
+ return twMerge((0, import_clsx.clsx)(inputs));
85
+ }
86
+
87
+ // src/icons/brand-icon.tsx
88
+ var import_jsx_runtime2 = require("react/jsx-runtime");
89
+ var SIZE_CLASS = {
90
+ xs: "size-3.5",
91
+ sm: "size-4",
92
+ md: "size-5",
93
+ lg: "size-6"
94
+ };
95
+ function fitToBox(svg) {
96
+ return svg.replace(/<svg\b[^>]*>/i, (openTag) => {
97
+ const cleaned = openTag.replace(/\s(?:width|height)\s*=\s*"[^"]*"/gi, "").replace(/\s(?:width|height)\s*=\s*'[^']*'/gi, "").replace(/\spreserveAspectRatio\s*=\s*"[^"]*"/gi, "");
98
+ return cleaned.replace(
99
+ /\s*\/?>$/,
100
+ (end) => ` preserveAspectRatio="xMidYMid meet"${end.trimStart()}`
101
+ );
102
+ });
103
+ }
104
+ function BrandIcon({
105
+ icon,
106
+ variant = "color",
107
+ size = "md",
108
+ "aria-label": label,
109
+ className,
110
+ ...props
111
+ }) {
112
+ const accessibleLabel = label === void 0 ? icon.title : label;
113
+ const boxClassName = cn(
114
+ "inline-flex shrink-0 [&_svg]:block [&_svg]:size-full",
115
+ SIZE_CLASS[size],
116
+ className
117
+ );
118
+ const pick = (key) => icon.variants?.[key] ?? icon.variants?.default ?? icon.svg;
119
+ if (variant === "auto") {
120
+ const lightSvg = pick("light");
121
+ const darkSvg = pick("dark");
122
+ if (lightSvg !== darkSvg) {
123
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
124
+ "span",
125
+ {
126
+ role: accessibleLabel ? "img" : void 0,
127
+ "aria-label": accessibleLabel || void 0,
128
+ "aria-hidden": accessibleLabel ? void 0 : true,
129
+ className: boxClassName,
130
+ ...props,
131
+ children: [
132
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
133
+ "span",
134
+ {
135
+ "data-brand-artwork": "light",
136
+ dangerouslySetInnerHTML: { __html: fitToBox(lightSvg) }
137
+ }
138
+ ),
139
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
140
+ "span",
141
+ {
142
+ "data-brand-artwork": "dark",
143
+ dangerouslySetInnerHTML: { __html: fitToBox(darkSvg) }
144
+ }
145
+ )
146
+ ]
147
+ }
148
+ );
149
+ }
150
+ }
151
+ const variantKey = variant === "color" || variant === "auto" ? "default" : variant;
152
+ const svg = icon.variants?.[variantKey] ?? icon.svg;
153
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
154
+ "span",
155
+ {
156
+ role: accessibleLabel ? "img" : void 0,
157
+ "aria-label": accessibleLabel || void 0,
158
+ "aria-hidden": accessibleLabel ? void 0 : true,
159
+ className: boxClassName,
160
+ dangerouslySetInnerHTML: { __html: fitToBox(svg) },
161
+ ...props
162
+ }
163
+ );
164
+ }
165
+
166
+ // src/icons/animated-icon.tsx
167
+ var React3 = require("react");
168
+ var import_jsx_runtime3 = require("react/jsx-runtime");
169
+ var SIZES2 = {
170
+ xs: "var(--icon-inline)",
171
+ sm: "var(--icon-default)",
172
+ md: "var(--icon-action)",
173
+ lg: "var(--icon-feature)"
174
+ };
175
+ function AnimatedIcon({
176
+ as: Cmp,
177
+ size = "md",
178
+ ref,
179
+ "aria-label": label,
180
+ ...props
181
+ }) {
182
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
183
+ Cmp,
184
+ {
185
+ ref,
186
+ size: SIZES2[size],
187
+ role: label ? "img" : void 0,
188
+ "aria-hidden": label ? void 0 : true,
189
+ "aria-label": label,
190
+ ...props
191
+ }
192
+ );
193
+ }
194
+ // Annotate the CommonJS export names for ESM import in node:
195
+ 0 && (module.exports = {
196
+ AnimatedIcon,
197
+ BrandIcon,
198
+ Icon
199
+ });
@@ -0,0 +1,164 @@
1
+ import * as React from 'react';
2
+ import { LucideIcon } from 'lucide-react';
3
+
4
+ /** Functional icon size scale (px). */
5
+ declare const SIZES$1: {
6
+ readonly xs: 14;
7
+ readonly sm: 16;
8
+ readonly md: 20;
9
+ readonly lg: 24;
10
+ };
11
+ type IconSize = keyof typeof SIZES$1;
12
+ interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, "ref"> {
13
+ /** The lucide icon component to render. */
14
+ as: LucideIcon;
15
+ /**
16
+ * Size token — maps to 14/16/20/24px.
17
+ * @default 'md'
18
+ */
19
+ size?: IconSize;
20
+ /**
21
+ * Accessible label. When provided the icon is exposed to assistive tech;
22
+ * otherwise it is `aria-hidden`.
23
+ */
24
+ "aria-label"?: string;
25
+ }
26
+ /**
27
+ * `Icon` — the one sanctioned wrapper for functional UI icons (lucide-react).
28
+ *
29
+ * Color inherits `currentColor` (themes automatically); stroke width is fixed
30
+ * to the design-system standard. `aria-hidden` unless an `aria-label` is given.
31
+ *
32
+ * @example
33
+ * import { Check } from 'lucide-react';
34
+ * <Icon as={Check} size="sm" aria-label="Done" />
35
+ */
36
+ declare function Icon({ as: Cmp, size, "aria-label": label, ...props }: IconProps): React.JSX.Element;
37
+
38
+ /** Structural shape of a `thesvg` icon module (its default export). */
39
+ interface BrandIconModule {
40
+ slug: string;
41
+ title: string;
42
+ hex: string;
43
+ svg: string;
44
+ variants: Record<string, string>;
45
+ }
46
+ /** Brand-icon size scale → Tailwind size utilities (14/16/20/24px). */
47
+ declare const SIZE_CLASS: {
48
+ readonly xs: "size-3.5";
49
+ readonly sm: "size-4";
50
+ readonly md: "size-5";
51
+ readonly lg: "size-6";
52
+ };
53
+ type BrandIconSize = keyof typeof SIZE_CLASS;
54
+ interface BrandIconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
55
+ /** A `thesvg` icon module (default export), e.g. `import github from 'thesvg/github'`. */
56
+ icon: BrandIconModule;
57
+ /**
58
+ * Which `thesvg` variant to render:
59
+ * - `auto` — theme-aware brand colors: the brand's `light` artwork on light
60
+ * surfaces and its `dark` artwork on dark ones, swapped in pure CSS (no JS,
61
+ * no hydration flash). **Use this on any surface that flips theme** —
62
+ * `color` renders the literal brand palette, and single-colour brands like
63
+ * GitHub (`#181717`) disappear against a dark background.
64
+ * - `color` — the brand's official colors (the module `default` variant).
65
+ * - `mono` — single-color, inherits `currentColor` (always theme-safe).
66
+ * - `light` / `dark` — pin one surface-tuned variant explicitly.
67
+ * - `wordmark` — the brand's logotype/wordmark lockup.
68
+ *
69
+ * Falls back to the icon's base `svg` when a brand doesn't ship the variant.
70
+ * @default 'color'
71
+ */
72
+ variant?: "auto" | "color" | "mono" | "light" | "dark" | "wordmark";
73
+ /**
74
+ * Size token — maps to 14/16/20/24px.
75
+ * @default 'md'
76
+ */
77
+ size?: BrandIconSize;
78
+ /**
79
+ * Accessible label. Defaults to the brand title; pass `''` to hide from
80
+ * assistive tech (decorative).
81
+ */
82
+ "aria-label"?: string;
83
+ }
84
+ /**
85
+ * `BrandIcon` — the one sanctioned wrapper for brand/logo icons (`thesvg`).
86
+ *
87
+ * @example
88
+ * import github from 'thesvg/github';
89
+ * <BrandIcon icon={github} size="md" /> // literal brand colors
90
+ * <BrandIcon icon={github} variant="auto" /> // theme-aware (light/dark artwork)
91
+ * <BrandIcon icon={github} variant="mono" /> // inherits currentColor
92
+ */
93
+ declare function BrandIcon({ icon, variant, size, "aria-label": label, className, ...props }: BrandIconProps): React.JSX.Element;
94
+
95
+ /** Runtime icon-role tokens — resolves in the consumer's active theme. */
96
+ declare const SIZES: {
97
+ readonly xs: "var(--icon-inline)";
98
+ readonly sm: "var(--icon-default)";
99
+ readonly md: "var(--icon-action)";
100
+ readonly lg: "var(--icon-feature)";
101
+ };
102
+ type AnimatedIconSize = keyof typeof SIZES;
103
+ /**
104
+ * The imperative handle every `lucide-animated` icon exposes on its ref —
105
+ * drive the animation programmatically instead of (or in addition to) hover.
106
+ */
107
+ interface AnimatedIconHandle {
108
+ startAnimation: () => void;
109
+ stopAnimation: () => void;
110
+ }
111
+ /**
112
+ * Shape of a mirrored `lucide-animated` icon component: a React 19 function
113
+ * component that accepts a runtime CSS length, spreads `div` props, and exposes an
114
+ * {@link AnimatedIconHandle}. This is what `shadcn add @vegastack/<icon>`
115
+ * copies in (e.g. `ActivityIcon`).
116
+ */
117
+ type AnimatedIconComponent = React.ComponentType<{
118
+ size?: number | string;
119
+ ref?: React.Ref<AnimatedIconHandle>;
120
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "ref">>;
121
+ interface AnimatedIconProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "ref"> {
122
+ /** A mirrored lucide-animated icon component, e.g. `import { ActivityIcon } from '@/components/ui/activity'`. */
123
+ as: AnimatedIconComponent;
124
+ /**
125
+ * Size role — resolves through `--icon-inline/default/action/feature` at runtime.
126
+ * @default 'md'
127
+ */
128
+ size?: AnimatedIconSize;
129
+ /** Imperative animation handle forwarded to the mirrored icon. */
130
+ ref?: React.Ref<AnimatedIconHandle>;
131
+ /**
132
+ * Accessible label. When provided the icon is exposed to assistive tech as an
133
+ * image; otherwise it is `aria-hidden` (decorative).
134
+ */
135
+ "aria-label"?: string;
136
+ }
137
+ /**
138
+ * `AnimatedIcon` — the one sanctioned wrapper for motion icons (`lucide-animated`,
139
+ * mirrored into the VegaStack registry). It standardizes the size scale + a11y and
140
+ * forwards the icon's {@link AnimatedIconHandle} ref, while the icon itself owns the
141
+ * animation (pointer/focus/touch auto-trigger where provided upstream; or call
142
+ * `ref.current.startAnimation()`).
143
+ *
144
+ * Deliberately imports no `motion` — the mirrored icon component carries that
145
+ * dependency, so the base package stays lightweight. Every generated mirror reads
146
+ * Motion's `useReducedMotion()` preference intrinsically and settles immediately at
147
+ * its static resting state when the user requests reduced motion.
148
+ *
149
+ * @example
150
+ * 'use client';
151
+ * import { AnimatedIcon } from '@vegastack/design/icons';
152
+ * import { ActivityIcon } from '@/components/ui/activity';
153
+ *
154
+ * <AnimatedIcon as={ActivityIcon} size="sm" aria-label="Activity" />
155
+ *
156
+ * @example
157
+ * // Programmatic control via the forwarded handle
158
+ * const ref = React.useRef<AnimatedIconHandle>(null);
159
+ * <AnimatedIcon as={ActivityIcon} ref={ref} />
160
+ * <button onClick={() => ref.current?.startAnimation()}>Play</button>
161
+ */
162
+ declare function AnimatedIcon({ as: Cmp, size, ref, "aria-label": label, ...props }: AnimatedIconProps): React.JSX.Element;
163
+
164
+ export { AnimatedIcon, type AnimatedIconComponent, type AnimatedIconHandle, type AnimatedIconProps, type AnimatedIconSize, BrandIcon, type BrandIconModule, type BrandIconProps, type BrandIconSize, Icon, type IconProps, type IconSize };
@@ -9,7 +9,7 @@ declare const SIZES$1: {
9
9
  readonly lg: 24;
10
10
  };
11
11
  type IconSize = keyof typeof SIZES$1;
12
- interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, 'ref'> {
12
+ interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, "ref"> {
13
13
  /** The lucide icon component to render. */
14
14
  as: LucideIcon;
15
15
  /**
@@ -21,7 +21,7 @@ interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, 'ref'> {
21
21
  * Accessible label. When provided the icon is exposed to assistive tech;
22
22
  * otherwise it is `aria-hidden`.
23
23
  */
24
- 'aria-label'?: string;
24
+ "aria-label"?: string;
25
25
  }
26
26
  /**
27
27
  * `Icon` — the one sanctioned wrapper for functional UI icons (lucide-react).
@@ -33,7 +33,7 @@ interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, 'ref'> {
33
33
  * import { Check } from 'lucide-react';
34
34
  * <Icon as={Check} size="sm" aria-label="Done" />
35
35
  */
36
- declare function Icon({ as: Cmp, size, 'aria-label': label, ...props }: IconProps): React.JSX.Element;
36
+ declare function Icon({ as: Cmp, size, "aria-label": label, ...props }: IconProps): React.JSX.Element;
37
37
 
38
38
  /** Structural shape of a `thesvg` icon module (its default export). */
39
39
  interface BrandIconModule {
@@ -51,20 +51,25 @@ declare const SIZE_CLASS: {
51
51
  readonly lg: "size-6";
52
52
  };
53
53
  type BrandIconSize = keyof typeof SIZE_CLASS;
54
- interface BrandIconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
54
+ interface BrandIconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
55
55
  /** A `thesvg` icon module (default export), e.g. `import github from 'thesvg/github'`. */
56
56
  icon: BrandIconModule;
57
57
  /**
58
58
  * Which `thesvg` variant to render:
59
+ * - `auto` — theme-aware brand colors: the brand's `light` artwork on light
60
+ * surfaces and its `dark` artwork on dark ones, swapped in pure CSS (no JS,
61
+ * no hydration flash). **Use this on any surface that flips theme** —
62
+ * `color` renders the literal brand palette, and single-colour brands like
63
+ * GitHub (`#181717`) disappear against a dark background.
59
64
  * - `color` — the brand's official colors (the module `default` variant).
60
- * - `mono` — single-color, inherits `currentColor`.
61
- * - `light` / `dark` — variants tuned for light/dark surfaces.
65
+ * - `mono` — single-color, inherits `currentColor` (always theme-safe).
66
+ * - `light` / `dark` — pin one surface-tuned variant explicitly.
62
67
  * - `wordmark` — the brand's logotype/wordmark lockup.
63
68
  *
64
69
  * Falls back to the icon's base `svg` when a brand doesn't ship the variant.
65
70
  * @default 'color'
66
71
  */
67
- variant?: 'color' | 'mono' | 'light' | 'dark' | 'wordmark';
72
+ variant?: "auto" | "color" | "mono" | "light" | "dark" | "wordmark";
68
73
  /**
69
74
  * Size token — maps to 14/16/20/24px.
70
75
  * @default 'md'
@@ -74,24 +79,25 @@ interface BrandIconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'ch
74
79
  * Accessible label. Defaults to the brand title; pass `''` to hide from
75
80
  * assistive tech (decorative).
76
81
  */
77
- 'aria-label'?: string;
82
+ "aria-label"?: string;
78
83
  }
79
84
  /**
80
85
  * `BrandIcon` — the one sanctioned wrapper for brand/logo icons (`thesvg`).
81
86
  *
82
87
  * @example
83
88
  * import github from 'thesvg/github';
84
- * <BrandIcon icon={github} size="md" /> // brand colors
85
- * <BrandIcon icon={github} variant="mono" /> // inherits currentColor
89
+ * <BrandIcon icon={github} size="md" /> // literal brand colors
90
+ * <BrandIcon icon={github} variant="auto" /> // theme-aware (light/dark artwork)
91
+ * <BrandIcon icon={github} variant="mono" /> // inherits currentColor
86
92
  */
87
- declare function BrandIcon({ icon, variant, size, 'aria-label': label, className, ...props }: BrandIconProps): React.JSX.Element;
93
+ declare function BrandIcon({ icon, variant, size, "aria-label": label, className, ...props }: BrandIconProps): React.JSX.Element;
88
94
 
89
- /** Functional icon size scale (px) — matches `Icon`/`BrandIcon`. */
95
+ /** Runtime icon-role tokens — resolves in the consumer's active theme. */
90
96
  declare const SIZES: {
91
- readonly xs: 14;
92
- readonly sm: 16;
93
- readonly md: 20;
94
- readonly lg: 24;
97
+ readonly xs: "var(--icon-inline)";
98
+ readonly sm: "var(--icon-default)";
99
+ readonly md: "var(--icon-action)";
100
+ readonly lg: "var(--icon-feature)";
95
101
  };
96
102
  type AnimatedIconSize = keyof typeof SIZES;
97
103
  /**
@@ -103,38 +109,42 @@ interface AnimatedIconHandle {
103
109
  stopAnimation: () => void;
104
110
  }
105
111
  /**
106
- * Shape of a mirrored `lucide-animated` icon component: a `forwardRef` that
107
- * accepts a numeric `size`, spreads `div` props, and exposes an
112
+ * Shape of a mirrored `lucide-animated` icon component: a React 19 function
113
+ * component that accepts a runtime CSS length, spreads `div` props, and exposes an
108
114
  * {@link AnimatedIconHandle}. This is what `shadcn add @vegastack/<icon>`
109
115
  * copies in (e.g. `ActivityIcon`).
110
116
  */
111
- type AnimatedIconComponent = React.ForwardRefExoticComponent<{
112
- size?: number;
113
- } & Omit<React.HTMLAttributes<HTMLDivElement>, 'ref'> & React.RefAttributes<AnimatedIconHandle>>;
114
- interface AnimatedIconProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'ref'> {
117
+ type AnimatedIconComponent = React.ComponentType<{
118
+ size?: number | string;
119
+ ref?: React.Ref<AnimatedIconHandle>;
120
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "ref">>;
121
+ interface AnimatedIconProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "ref"> {
115
122
  /** A mirrored lucide-animated icon component, e.g. `import { ActivityIcon } from '@/components/ui/activity'`. */
116
123
  as: AnimatedIconComponent;
117
124
  /**
118
- * Size token — maps to 14/16/20/24px (overrides the icon's default 28px).
125
+ * Size role — resolves through `--icon-inline/default/action/feature` at runtime.
119
126
  * @default 'md'
120
127
  */
121
128
  size?: AnimatedIconSize;
129
+ /** Imperative animation handle forwarded to the mirrored icon. */
130
+ ref?: React.Ref<AnimatedIconHandle>;
122
131
  /**
123
132
  * Accessible label. When provided the icon is exposed to assistive tech as an
124
133
  * image; otherwise it is `aria-hidden` (decorative).
125
134
  */
126
- 'aria-label'?: string;
135
+ "aria-label"?: string;
127
136
  }
128
137
  /**
129
138
  * `AnimatedIcon` — the one sanctioned wrapper for motion icons (`lucide-animated`,
130
139
  * mirrored into the VegaStack registry). It standardizes the size scale + a11y and
131
140
  * forwards the icon's {@link AnimatedIconHandle} ref, while the icon itself owns the
132
- * animation (animates on hover; or call `ref.current.startAnimation()`).
141
+ * animation (pointer/focus/touch auto-trigger where provided upstream; or call
142
+ * `ref.current.startAnimation()`).
133
143
  *
134
144
  * Deliberately imports no `motion` — the mirrored icon component carries that
135
- * dependency, so the base package stays lightweight. For app-wide
136
- * reduced-motion, wrap your tree once in Motion's
137
- * `<MotionConfig reducedMotion="user">`.
145
+ * dependency, so the base package stays lightweight. Every generated mirror reads
146
+ * Motion's `useReducedMotion()` preference intrinsically and settles immediately at
147
+ * its static resting state when the user requests reduced motion.
138
148
  *
139
149
  * @example
140
150
  * 'use client';
@@ -149,6 +159,6 @@ interface AnimatedIconProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
149
159
  * <AnimatedIcon as={ActivityIcon} ref={ref} />
150
160
  * <button onClick={() => ref.current?.startAnimation()}>Play</button>
151
161
  */
152
- declare const AnimatedIcon: React.ForwardRefExoticComponent<AnimatedIconProps & React.RefAttributes<AnimatedIconHandle>>;
162
+ declare function AnimatedIcon({ as: Cmp, size, ref, "aria-label": label, ...props }: AnimatedIconProps): React.JSX.Element;
153
163
 
154
164
  export { AnimatedIcon, type AnimatedIconComponent, type AnimatedIconHandle, type AnimatedIconProps, type AnimatedIconSize, BrandIcon, type BrandIconModule, type BrandIconProps, type BrandIconSize, Icon, type IconProps, type IconSize };
@@ -6,7 +6,12 @@ import {
6
6
  import "react";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  var SIZES = { xs: 14, sm: 16, md: 20, lg: 24 };
9
- function Icon({ as: Cmp, size = "md", "aria-label": label, ...props }) {
9
+ function Icon({
10
+ as: Cmp,
11
+ size = "md",
12
+ "aria-label": label,
13
+ ...props
14
+ }) {
10
15
  return /* @__PURE__ */ jsx(
11
16
  Cmp,
12
17
  {
@@ -22,8 +27,22 @@ function Icon({ as: Cmp, size = "md", "aria-label": label, ...props }) {
22
27
 
23
28
  // src/icons/brand-icon.tsx
24
29
  import "react";
25
- import { jsx as jsx2 } from "react/jsx-runtime";
26
- var SIZE_CLASS = { xs: "size-3.5", sm: "size-4", md: "size-5", lg: "size-6" };
30
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
31
+ var SIZE_CLASS = {
32
+ xs: "size-3.5",
33
+ sm: "size-4",
34
+ md: "size-5",
35
+ lg: "size-6"
36
+ };
37
+ function fitToBox(svg) {
38
+ return svg.replace(/<svg\b[^>]*>/i, (openTag) => {
39
+ const cleaned = openTag.replace(/\s(?:width|height)\s*=\s*"[^"]*"/gi, "").replace(/\s(?:width|height)\s*=\s*'[^']*'/gi, "").replace(/\spreserveAspectRatio\s*=\s*"[^"]*"/gi, "");
40
+ return cleaned.replace(
41
+ /\s*\/?>$/,
42
+ (end) => ` preserveAspectRatio="xMidYMid meet"${end.trimStart()}`
43
+ );
44
+ });
45
+ }
27
46
  function BrandIcon({
28
47
  icon,
29
48
  variant = "color",
@@ -32,28 +51,77 @@ function BrandIcon({
32
51
  className,
33
52
  ...props
34
53
  }) {
35
- const variantKey = variant === "color" ? "default" : variant;
36
- const svg = icon.variants?.[variantKey] ?? icon.svg;
37
54
  const accessibleLabel = label === void 0 ? icon.title : label;
55
+ const boxClassName = cn(
56
+ "inline-flex shrink-0 [&_svg]:block [&_svg]:size-full",
57
+ SIZE_CLASS[size],
58
+ className
59
+ );
60
+ const pick = (key) => icon.variants?.[key] ?? icon.variants?.default ?? icon.svg;
61
+ if (variant === "auto") {
62
+ const lightSvg = pick("light");
63
+ const darkSvg = pick("dark");
64
+ if (lightSvg !== darkSvg) {
65
+ return /* @__PURE__ */ jsxs(
66
+ "span",
67
+ {
68
+ role: accessibleLabel ? "img" : void 0,
69
+ "aria-label": accessibleLabel || void 0,
70
+ "aria-hidden": accessibleLabel ? void 0 : true,
71
+ className: boxClassName,
72
+ ...props,
73
+ children: [
74
+ /* @__PURE__ */ jsx2(
75
+ "span",
76
+ {
77
+ "data-brand-artwork": "light",
78
+ dangerouslySetInnerHTML: { __html: fitToBox(lightSvg) }
79
+ }
80
+ ),
81
+ /* @__PURE__ */ jsx2(
82
+ "span",
83
+ {
84
+ "data-brand-artwork": "dark",
85
+ dangerouslySetInnerHTML: { __html: fitToBox(darkSvg) }
86
+ }
87
+ )
88
+ ]
89
+ }
90
+ );
91
+ }
92
+ }
93
+ const variantKey = variant === "color" || variant === "auto" ? "default" : variant;
94
+ const svg = icon.variants?.[variantKey] ?? icon.svg;
38
95
  return /* @__PURE__ */ jsx2(
39
96
  "span",
40
97
  {
41
98
  role: accessibleLabel ? "img" : void 0,
42
99
  "aria-label": accessibleLabel || void 0,
43
100
  "aria-hidden": accessibleLabel ? void 0 : true,
44
- className: cn("inline-flex shrink-0 [&>svg]:block [&>svg]:size-full", SIZE_CLASS[size], className),
45
- dangerouslySetInnerHTML: { __html: svg },
101
+ className: boxClassName,
102
+ dangerouslySetInnerHTML: { __html: fitToBox(svg) },
46
103
  ...props
47
104
  }
48
105
  );
49
106
  }
50
107
 
51
108
  // src/icons/animated-icon.tsx
52
- import * as React3 from "react";
109
+ import "react";
53
110
  import { jsx as jsx3 } from "react/jsx-runtime";
54
- var SIZES2 = { xs: 14, sm: 16, md: 20, lg: 24 };
55
- var AnimatedIcon = React3.forwardRef(
56
- ({ as: Cmp, size = "md", "aria-label": label, ...props }, ref) => /* @__PURE__ */ jsx3(
111
+ var SIZES2 = {
112
+ xs: "var(--icon-inline)",
113
+ sm: "var(--icon-default)",
114
+ md: "var(--icon-action)",
115
+ lg: "var(--icon-feature)"
116
+ };
117
+ function AnimatedIcon({
118
+ as: Cmp,
119
+ size = "md",
120
+ ref,
121
+ "aria-label": label,
122
+ ...props
123
+ }) {
124
+ return /* @__PURE__ */ jsx3(
57
125
  Cmp,
58
126
  {
59
127
  ref,
@@ -63,9 +131,8 @@ var AnimatedIcon = React3.forwardRef(
63
131
  "aria-label": label,
64
132
  ...props
65
133
  }
66
- )
67
- );
68
- AnimatedIcon.displayName = "AnimatedIcon";
134
+ );
135
+ }
69
136
  export {
70
137
  AnimatedIcon,
71
138
  BrandIcon,