argentui 0.3.0 → 0.4.1

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/dist/index.d.cts CHANGED
@@ -49,7 +49,7 @@ declare function mountMetal(canvas: HTMLCanvasElement, params: NativeMetalParams
49
49
  * `@paper-design/shaders-react` is a peer dependency — install it alongside
50
50
  * `argentui`. It is licensed under PolyForm Shield by Paper.
51
51
  */
52
- type MetalTone = "silver" | "gold" | "gunmetal" | "obsidian";
52
+ type MetalTone = "silver" | "gold" | "gunmetal" | "obsidian" | "cobalt" | "crimson" | "amethyst" | "emerald";
53
53
  type Tuned = Pick<LiquidMetalParams, "colorBack" | "colorTint" | "repetition" | "softness" | "shiftRed" | "shiftBlue" | "distortion" | "contour" | "angle">;
54
54
  declare const TONE_PARAMS: Record<MetalTone, Tuned>;
55
55
  /** True once mounted on the client — the WebGL canvas can't render during SSR. */
@@ -191,6 +191,8 @@ interface MetalButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>
191
191
  effect?: MetalEffect;
192
192
  angle?: number;
193
193
  halo?: boolean | number;
194
+ /** An icon (any SVG element) rendered before the label, in the label colour. */
195
+ icon?: React.ReactNode;
194
196
  }
195
197
  /** A liquid-metal button — readable at rest, molten on hover, with a stamped press. */
196
198
  declare const MetalButton: react.ForwardRefExoticComponent<MetalButtonProps & react.RefAttributes<HTMLButtonElement>>;
@@ -232,9 +234,10 @@ interface MetalTextProps extends React.HTMLAttributes<HTMLElement> {
232
234
  shimmer?: boolean;
233
235
  /**
234
236
  * Pour the real liquid-metal shader into the glyphs — flowing bands, liquid
235
- * edges, chromatic fringe. Costs a WebGL canvas; the CSS gradient renders as
236
- * a placeholder until the shader is ready, and stands in wherever WebGL
237
- * isn't. Children must be a plain string in this mode.
237
+ * edges, chromatic fringe. On by default (one WebGL canvas; the CSS chrome
238
+ * gradient renders as a placeholder until the shader is ready, and stands in
239
+ * wherever WebGL isn't). Set `shader={false}` for the pure-CSS gradient.
240
+ * Shader mode requires a plain string child; element children fall back to CSS.
238
241
  */
239
242
  shader?: boolean;
240
243
  /**
@@ -270,11 +273,11 @@ interface MetalTextProps extends React.HTMLAttributes<HTMLElement> {
270
273
  speed?: number;
271
274
  }
272
275
  /**
273
- * Metal type. By default a chrome gradient clipped to the glyphs with a flowing
274
- * shimmer pure CSS, use it freely at any scale. Pass `shader` to pour the
275
- * real liquid-metal shader into the letterforms `variant="fill"` floods the
276
- * glyphs; `variant="outline"` runs the metal around their edges over a dark or
277
- * gradient interior.
276
+ * Metal type. By default the real liquid-metal shader is poured into the glyphs
277
+ * — `variant="fill"` floods them, `variant="outline"` runs the metal around
278
+ * their edges over a dark or gradient interior. The CSS chrome gradient stands
279
+ * in until the shader loads. Set `shader={false}` for the pure-CSS gradient
280
+ * (free at any scale); element children also fall back to it.
278
281
  */
279
282
  declare const MetalText: react.ForwardRefExoticComponent<MetalTextProps & react.RefAttributes<HTMLElement>>;
280
283
 
@@ -296,6 +299,27 @@ interface MetalLogoProps extends React.HTMLAttributes<HTMLDivElement> {
296
299
  */
297
300
  declare function MetalLogo({ src, tone, size, width, height, speed, style, ...rest }: MetalLogoProps): react.JSX.Element;
298
301
 
302
+ /**
303
+ * Liquid metal poured into an icon. Pass any SVG icon — a React element from
304
+ * lucide-react / heroicons, a raw SVG string, or a URL — and it renders filled
305
+ * with the liquid-metal shader (one WebGL canvas, gated to the viewport like
306
+ * every metal surface). React icons are serialized from the DOM to a silhouette.
307
+ */
308
+ interface MetalIconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
309
+ /** A React SVG element, e.g. `<Beaker />` (lucide) or `<BeakerIcon />` (heroicons). */
310
+ icon?: React.ReactNode;
311
+ /** Raw SVG markup (alternative to `icon`). */
312
+ svg?: string;
313
+ /** SVG URL or data URI (alternative to `icon`). */
314
+ src?: string;
315
+ tone?: MetalTone;
316
+ /** Rendered size in px (square). Defaults to `32`. */
317
+ size?: number;
318
+ /** Shader animation speed. */
319
+ speed?: number;
320
+ }
321
+ declare function MetalIcon({ icon, svg, src, tone, size, speed, className, style, ...rest }: MetalIconProps): react.JSX.Element;
322
+
299
323
  /**
300
324
  * Tiny haptics for metal presses — `navigator.vibrate` where available
301
325
  * (Android Chrome; iOS Safari ignores it). On by default, like Glacé.
@@ -303,4 +327,4 @@ declare function MetalLogo({ src, tone, size, width, height, speed, style, ...re
303
327
  /** Globally enable/disable Argent haptics. */
304
328
  declare function setHaptics(on: boolean): void;
305
329
 
306
- export { EFFECTS, FINISHES, Metal, MetalBadge, type MetalBadgeProps, MetalButton, type MetalButtonProps, MetalCard, type MetalCardProps, type MetalEffect, type MetalEngine, MetalFill, type MetalFillProps, type MetalFinish, type MetalFrame, MetalLogo, type MetalLogoProps, type MetalMount, MetalProgress, type MetalProgressProps, type MetalProps, MetalText, type MetalTextProps, type MetalTextVariant, MetalToggle, type MetalToggleProps, type MetalTone, type MetalVariant, NATIVE_TONES, type NativeMetalParams, TONE_PARAMS, mountMetal, setHaptics, useInView, useMounted, useReducedMotion };
330
+ export { EFFECTS, FINISHES, Metal, MetalBadge, type MetalBadgeProps, MetalButton, type MetalButtonProps, MetalCard, type MetalCardProps, type MetalEffect, type MetalEngine, MetalFill, type MetalFillProps, type MetalFinish, type MetalFrame, MetalIcon, type MetalIconProps, MetalLogo, type MetalLogoProps, type MetalMount, MetalProgress, type MetalProgressProps, type MetalProps, MetalText, type MetalTextProps, type MetalTextVariant, MetalToggle, type MetalToggleProps, type MetalTone, type MetalVariant, NATIVE_TONES, type NativeMetalParams, TONE_PARAMS, mountMetal, setHaptics, useInView, useMounted, useReducedMotion };
package/dist/index.d.ts CHANGED
@@ -49,7 +49,7 @@ declare function mountMetal(canvas: HTMLCanvasElement, params: NativeMetalParams
49
49
  * `@paper-design/shaders-react` is a peer dependency — install it alongside
50
50
  * `argentui`. It is licensed under PolyForm Shield by Paper.
51
51
  */
52
- type MetalTone = "silver" | "gold" | "gunmetal" | "obsidian";
52
+ type MetalTone = "silver" | "gold" | "gunmetal" | "obsidian" | "cobalt" | "crimson" | "amethyst" | "emerald";
53
53
  type Tuned = Pick<LiquidMetalParams, "colorBack" | "colorTint" | "repetition" | "softness" | "shiftRed" | "shiftBlue" | "distortion" | "contour" | "angle">;
54
54
  declare const TONE_PARAMS: Record<MetalTone, Tuned>;
55
55
  /** True once mounted on the client — the WebGL canvas can't render during SSR. */
@@ -191,6 +191,8 @@ interface MetalButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>
191
191
  effect?: MetalEffect;
192
192
  angle?: number;
193
193
  halo?: boolean | number;
194
+ /** An icon (any SVG element) rendered before the label, in the label colour. */
195
+ icon?: React.ReactNode;
194
196
  }
195
197
  /** A liquid-metal button — readable at rest, molten on hover, with a stamped press. */
196
198
  declare const MetalButton: react.ForwardRefExoticComponent<MetalButtonProps & react.RefAttributes<HTMLButtonElement>>;
@@ -232,9 +234,10 @@ interface MetalTextProps extends React.HTMLAttributes<HTMLElement> {
232
234
  shimmer?: boolean;
233
235
  /**
234
236
  * Pour the real liquid-metal shader into the glyphs — flowing bands, liquid
235
- * edges, chromatic fringe. Costs a WebGL canvas; the CSS gradient renders as
236
- * a placeholder until the shader is ready, and stands in wherever WebGL
237
- * isn't. Children must be a plain string in this mode.
237
+ * edges, chromatic fringe. On by default (one WebGL canvas; the CSS chrome
238
+ * gradient renders as a placeholder until the shader is ready, and stands in
239
+ * wherever WebGL isn't). Set `shader={false}` for the pure-CSS gradient.
240
+ * Shader mode requires a plain string child; element children fall back to CSS.
238
241
  */
239
242
  shader?: boolean;
240
243
  /**
@@ -270,11 +273,11 @@ interface MetalTextProps extends React.HTMLAttributes<HTMLElement> {
270
273
  speed?: number;
271
274
  }
272
275
  /**
273
- * Metal type. By default a chrome gradient clipped to the glyphs with a flowing
274
- * shimmer pure CSS, use it freely at any scale. Pass `shader` to pour the
275
- * real liquid-metal shader into the letterforms `variant="fill"` floods the
276
- * glyphs; `variant="outline"` runs the metal around their edges over a dark or
277
- * gradient interior.
276
+ * Metal type. By default the real liquid-metal shader is poured into the glyphs
277
+ * — `variant="fill"` floods them, `variant="outline"` runs the metal around
278
+ * their edges over a dark or gradient interior. The CSS chrome gradient stands
279
+ * in until the shader loads. Set `shader={false}` for the pure-CSS gradient
280
+ * (free at any scale); element children also fall back to it.
278
281
  */
279
282
  declare const MetalText: react.ForwardRefExoticComponent<MetalTextProps & react.RefAttributes<HTMLElement>>;
280
283
 
@@ -296,6 +299,27 @@ interface MetalLogoProps extends React.HTMLAttributes<HTMLDivElement> {
296
299
  */
297
300
  declare function MetalLogo({ src, tone, size, width, height, speed, style, ...rest }: MetalLogoProps): react.JSX.Element;
298
301
 
302
+ /**
303
+ * Liquid metal poured into an icon. Pass any SVG icon — a React element from
304
+ * lucide-react / heroicons, a raw SVG string, or a URL — and it renders filled
305
+ * with the liquid-metal shader (one WebGL canvas, gated to the viewport like
306
+ * every metal surface). React icons are serialized from the DOM to a silhouette.
307
+ */
308
+ interface MetalIconProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
309
+ /** A React SVG element, e.g. `<Beaker />` (lucide) or `<BeakerIcon />` (heroicons). */
310
+ icon?: React.ReactNode;
311
+ /** Raw SVG markup (alternative to `icon`). */
312
+ svg?: string;
313
+ /** SVG URL or data URI (alternative to `icon`). */
314
+ src?: string;
315
+ tone?: MetalTone;
316
+ /** Rendered size in px (square). Defaults to `32`. */
317
+ size?: number;
318
+ /** Shader animation speed. */
319
+ speed?: number;
320
+ }
321
+ declare function MetalIcon({ icon, svg, src, tone, size, speed, className, style, ...rest }: MetalIconProps): react.JSX.Element;
322
+
299
323
  /**
300
324
  * Tiny haptics for metal presses — `navigator.vibrate` where available
301
325
  * (Android Chrome; iOS Safari ignores it). On by default, like Glacé.
@@ -303,4 +327,4 @@ declare function MetalLogo({ src, tone, size, width, height, speed, style, ...re
303
327
  /** Globally enable/disable Argent haptics. */
304
328
  declare function setHaptics(on: boolean): void;
305
329
 
306
- export { EFFECTS, FINISHES, Metal, MetalBadge, type MetalBadgeProps, MetalButton, type MetalButtonProps, MetalCard, type MetalCardProps, type MetalEffect, type MetalEngine, MetalFill, type MetalFillProps, type MetalFinish, type MetalFrame, MetalLogo, type MetalLogoProps, type MetalMount, MetalProgress, type MetalProgressProps, type MetalProps, MetalText, type MetalTextProps, type MetalTextVariant, MetalToggle, type MetalToggleProps, type MetalTone, type MetalVariant, NATIVE_TONES, type NativeMetalParams, TONE_PARAMS, mountMetal, setHaptics, useInView, useMounted, useReducedMotion };
330
+ export { EFFECTS, FINISHES, Metal, MetalBadge, type MetalBadgeProps, MetalButton, type MetalButtonProps, MetalCard, type MetalCardProps, type MetalEffect, type MetalEngine, MetalFill, type MetalFillProps, type MetalFinish, type MetalFrame, MetalIcon, type MetalIconProps, MetalLogo, type MetalLogoProps, type MetalMount, MetalProgress, type MetalProgressProps, type MetalProps, MetalText, type MetalTextProps, type MetalTextVariant, MetalToggle, type MetalToggleProps, type MetalTone, type MetalVariant, NATIVE_TONES, type NativeMetalParams, TONE_PARAMS, mountMetal, setHaptics, useInView, useMounted, useReducedMotion };
package/dist/index.js CHANGED
@@ -106,7 +106,11 @@ var NATIVE_TONES = {
106
106
  silver: { light: "#f8fafc", dark: "#23262b", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.03, distortion: 0.38 },
107
107
  gold: { light: "#ffe9a8", dark: "#6e5408", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.028, distortion: 0.36 },
108
108
  gunmetal: { light: "#b6bec9", dark: "#14161a", repetition: 1.6, angle: 80, softness: 0.36, dispersion: 0.025, distortion: 0.34 },
109
- obsidian: { light: "#787c88", dark: "#000000", repetition: 1.4, angle: 92, softness: 0.42, dispersion: 0.02, distortion: 0.28 }
109
+ obsidian: { light: "#787c88", dark: "#000000", repetition: 1.4, angle: 92, softness: 0.42, dispersion: 0.02, distortion: 0.28 },
110
+ cobalt: { light: "#dbe7ff", dark: "#16244e", repetition: 1.8, angle: 70, softness: 0.34, dispersion: 0.03, distortion: 0.36 },
111
+ crimson: { light: "#ffd6da", dark: "#48101a", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.03, distortion: 0.36 },
112
+ amethyst: { light: "#e8d6ff", dark: "#2c1854", repetition: 1.8, angle: 70, softness: 0.34, dispersion: 0.03, distortion: 0.36 },
113
+ emerald: { light: "#caffe0", dark: "#0e3a28", repetition: 1.7, angle: 74, softness: 0.34, dispersion: 0.028, distortion: 0.34 }
110
114
  };
111
115
  function compile(gl, type, src) {
112
116
  const sh = gl.createShader(type);
@@ -232,6 +236,50 @@ var TONE_PARAMS = {
232
236
  distortion: 0.07,
233
237
  contour: 0.32,
234
238
  angle: 92
239
+ },
240
+ cobalt: {
241
+ colorBack: "#27407e",
242
+ colorTint: "#cfe0ff",
243
+ repetition: 3,
244
+ softness: 0.2,
245
+ shiftRed: 0.22,
246
+ shiftBlue: 0.34,
247
+ distortion: 0.13,
248
+ contour: 0.5,
249
+ angle: 70
250
+ },
251
+ crimson: {
252
+ colorBack: "#7a1820",
253
+ colorTint: "#ffd0d4",
254
+ repetition: 3,
255
+ softness: 0.2,
256
+ shiftRed: 0.34,
257
+ shiftBlue: 0.16,
258
+ distortion: 0.13,
259
+ contour: 0.5,
260
+ angle: 68
261
+ },
262
+ amethyst: {
263
+ colorBack: "#4a2a86",
264
+ colorTint: "#e6d0ff",
265
+ repetition: 3,
266
+ softness: 0.2,
267
+ shiftRed: 0.3,
268
+ shiftBlue: 0.34,
269
+ distortion: 0.13,
270
+ contour: 0.5,
271
+ angle: 70
272
+ },
273
+ emerald: {
274
+ colorBack: "#155e42",
275
+ colorTint: "#c8ffdf",
276
+ repetition: 3,
277
+ softness: 0.2,
278
+ shiftRed: 0.24,
279
+ shiftBlue: 0.28,
280
+ distortion: 0.12,
281
+ contour: 0.5,
282
+ angle: 74
235
283
  }
236
284
  };
237
285
  var imageMountQueue = Promise.resolve();
@@ -455,7 +503,7 @@ var MetalCard = forwardRef(function MetalCard2({ className, radius = 18, ...rest
455
503
  return /* @__PURE__ */ jsx2(Metal, { ref, radius, className: cx("argent-card", className), ...rest });
456
504
  });
457
505
  var SIZE_RADIUS = { sm: 11, md: 13, lg: 15 };
458
- var MetalButton = forwardRef(function MetalButton2({ tone = "silver", size = "md", variant = "border", radius, borderWidth = 1.5, revealOnHover = true, haptics = true, speed, engine, finish = "button", effect, angle, halo = false, type = "button", className, children, style, onPointerDown, ...rest }, ref) {
506
+ var MetalButton = forwardRef(function MetalButton2({ tone = "silver", size = "md", variant = "border", radius, borderWidth = 1.5, revealOnHover = true, haptics = true, speed, engine, finish = "button", effect, angle, halo = false, icon, type = "button", className, children, style, onPointerDown, ...rest }, ref) {
459
507
  const border = variant === "border";
460
508
  return /* @__PURE__ */ jsxs(
461
509
  "button",
@@ -474,7 +522,10 @@ var MetalButton = forwardRef(function MetalButton2({ tone = "silver", size = "md
474
522
  /* @__PURE__ */ jsx2("span", { className: "argent-fill", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(MetalFill, { tone, speed, engine, finish, effect, angle }) }),
475
523
  border && /* @__PURE__ */ jsx2("span", { className: "argent-core", "aria-hidden": "true" }),
476
524
  /* @__PURE__ */ jsx2("span", { className: "argent-sheen", "aria-hidden": "true" }),
477
- /* @__PURE__ */ jsx2("span", { className: "argent-content argent-btn-label", children })
525
+ /* @__PURE__ */ jsxs("span", { className: "argent-content argent-btn-label", children: [
526
+ icon && /* @__PURE__ */ jsx2("span", { className: "argent-btn-icon", "aria-hidden": "true", children: icon }),
527
+ children
528
+ ] })
478
529
  ]
479
530
  }
480
531
  );
@@ -695,7 +746,7 @@ var MetalText = forwardRef3(function MetalText2({
695
746
  as,
696
747
  tone = "silver",
697
748
  shimmer = true,
698
- shader = false,
749
+ shader = true,
699
750
  variant = "fill",
700
751
  fill = "#101114",
701
752
  fillGradient,
@@ -709,7 +760,7 @@ var MetalText = forwardRef3(function MetalText2({
709
760
  children,
710
761
  ...rest
711
762
  }, ref) {
712
- if (shader) {
763
+ if (shader && typeof children === "string") {
713
764
  return /* @__PURE__ */ jsx4(
714
765
  ShaderText,
715
766
  {
@@ -771,6 +822,42 @@ function MetalLogo({ src, tone = "silver", size = 160, width, height, speed = 1,
771
822
  src
772
823
  ) });
773
824
  }
825
+
826
+ // src/icon.tsx
827
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
828
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
829
+ function svgToUri(markup) {
830
+ return `data:image/svg+xml,${encodeURIComponent(markup)}`;
831
+ }
832
+ function serialize(svg) {
833
+ const clone = svg.cloneNode(true);
834
+ if (!clone.getAttribute("xmlns")) clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
835
+ return svgToUri(new XMLSerializer().serializeToString(clone));
836
+ }
837
+ function useIconUri({ svg, src }) {
838
+ const ref = useRef4(null);
839
+ const direct = src ?? (svg ? svgToUri(svg) : null);
840
+ const [uri, setUri] = useState4(direct);
841
+ useEffect3(() => {
842
+ if (direct) {
843
+ setUri(direct);
844
+ return;
845
+ }
846
+ const node = ref.current?.querySelector("svg");
847
+ if (!node) return;
848
+ const next = serialize(node);
849
+ setUri((prev) => prev === next ? prev : next);
850
+ });
851
+ return { ref, uri, fromNode: !direct };
852
+ }
853
+ function MetalIcon({ icon, svg, src, tone = "silver", size = 32, speed = 1, className, style, ...rest }) {
854
+ const { ref, uri, fromNode } = useIconUri({ svg, src });
855
+ const cls = ["argent-icon-wrap", className].filter(Boolean).join(" ");
856
+ return /* @__PURE__ */ jsxs3("span", { className: cls, style: { display: "inline-flex", width: size, height: size, ...style }, ...rest, children: [
857
+ fromNode && /* @__PURE__ */ jsx6("span", { ref, "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden", color: "#000" }, children: icon }),
858
+ uri && /* @__PURE__ */ jsx6(MetalLogo, { src: uri, tone, size, speed })
859
+ ] });
860
+ }
774
861
  export {
775
862
  EFFECTS,
776
863
  FINISHES,
@@ -779,6 +866,7 @@ export {
779
866
  MetalButton,
780
867
  MetalCard,
781
868
  MetalFill,
869
+ MetalIcon,
782
870
  MetalLogo,
783
871
  MetalProgress,
784
872
  MetalText,