@farcaster/snap 1.13.0 → 1.14.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 (47) hide show
  1. package/dist/react/components/action-button.js +18 -10
  2. package/dist/react/components/badge.js +8 -6
  3. package/dist/react/components/bar-chart.js +12 -17
  4. package/dist/react/components/cell-grid.js +8 -12
  5. package/dist/react/components/icon.js +4 -10
  6. package/dist/react/components/input.js +12 -6
  7. package/dist/react/components/item-group.js +3 -1
  8. package/dist/react/components/item.d.ts +3 -3
  9. package/dist/react/components/item.js +4 -3
  10. package/dist/react/components/progress.js +3 -3
  11. package/dist/react/components/separator.js +3 -1
  12. package/dist/react/components/slider.js +14 -10
  13. package/dist/react/components/switch.js +10 -12
  14. package/dist/react/components/text.js +5 -7
  15. package/dist/react/components/toggle-group.js +20 -6
  16. package/dist/react/hooks/use-snap-colors.d.ts +38 -0
  17. package/dist/react/hooks/use-snap-colors.js +82 -0
  18. package/dist/react-native/components/snap-action-button.js +8 -18
  19. package/dist/react-native/components/snap-cell-grid.js +1 -1
  20. package/dist/react-native/components/snap-switch.js +1 -1
  21. package/dist/react-native/components/snap-toggle-group.js +8 -10
  22. package/dist/react-native/theme.d.ts +6 -0
  23. package/dist/react-native/theme.js +12 -6
  24. package/package.json +1 -1
  25. package/src/react/components/action-button.tsx +24 -17
  26. package/src/react/components/badge.tsx +14 -17
  27. package/src/react/components/bar-chart.tsx +21 -19
  28. package/src/react/components/cell-grid.tsx +9 -16
  29. package/src/react/components/icon.tsx +5 -18
  30. package/src/react/components/input.tsx +20 -9
  31. package/src/react/components/item-group.tsx +4 -1
  32. package/src/react/components/item.tsx +13 -10
  33. package/src/react/components/progress.tsx +12 -7
  34. package/src/react/components/separator.tsx +8 -1
  35. package/src/react/components/slider.tsx +18 -15
  36. package/src/react/components/switch.tsx +12 -16
  37. package/src/react/components/text.tsx +11 -8
  38. package/src/react/components/toggle-group.tsx +26 -9
  39. package/src/react/hooks/use-snap-colors.ts +129 -0
  40. package/src/react-native/components/snap-action-button.tsx +8 -20
  41. package/src/react-native/components/snap-cell-grid.tsx +1 -1
  42. package/src/react-native/components/snap-switch.tsx +1 -1
  43. package/src/react-native/components/snap-toggle-group.tsx +8 -10
  44. package/src/react-native/theme.tsx +18 -6
  45. package/dist/react/hooks/use-snap-accent.d.ts +0 -13
  46. package/dist/react/hooks/use-snap-accent.js +0 -32
  47. package/src/react/hooks/use-snap-accent.ts +0 -45
@@ -2,10 +2,7 @@
2
2
 
3
3
  import { useStateStore } from "@json-render/react";
4
4
  import { Label } from "@neynar/ui/label";
5
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent";
6
-
7
- // TODO: switch back to @neynar/ui/slider once Base UI fixes the inline
8
- // <script> tag that triggers a React console warning on client render.
5
+ import { useSnapColors } from "../hooks/use-snap-colors";
9
6
 
10
7
  export function SnapSlider({
11
8
  element: { props },
@@ -13,21 +10,24 @@ export function SnapSlider({
13
10
  element: { props: Record<string, unknown> };
14
11
  }) {
15
12
  const { get, set } = useStateStore();
16
- const accentStyle = useSnapAccentScopeStyle();
17
-
13
+ const colors = useSnapColors();
18
14
  const name = String(props.name ?? "slider");
19
- const path = `/inputs/${name}`;
20
- const label = props.label ? String(props.label) : undefined;
21
15
  const min = Number(props.min ?? 0);
22
16
  const max = Number(props.max ?? 100);
23
- const step = props.step != null ? Number(props.step) : 1;
24
- const fallback = props.defaultValue != null ? Number(props.defaultValue) : (min + max) / 2;
17
+ const step = Number(props.step ?? 1);
18
+ const label = props.label ? String(props.label) : undefined;
19
+ const path = `/inputs/${name}`;
25
20
  const raw = get(path);
26
- const value = raw === undefined || raw === null ? fallback : Number(raw);
21
+ const value =
22
+ raw !== undefined
23
+ ? Number(raw)
24
+ : props.defaultValue !== undefined
25
+ ? Number(props.defaultValue)
26
+ : (min + max) / 2;
27
27
 
28
28
  return (
29
- <div className="flex w-full flex-col gap-1.5" style={accentStyle}>
30
- {label && <Label>{label}</Label>}
29
+ <div className="flex w-full flex-col gap-1.5">
30
+ {label && <Label style={{ color: colors.text }}>{label}</Label>}
31
31
  <input
32
32
  type="range"
33
33
  min={min}
@@ -35,8 +35,11 @@ export function SnapSlider({
35
35
  step={step}
36
36
  value={value}
37
37
  onChange={(e) => set(path, Number(e.target.value))}
38
- className="w-full h-2.5 rounded-full appearance-none bg-muted cursor-pointer"
39
- style={{ accentColor: "var(--primary)" }}
38
+ className="w-full h-2.5 rounded-full appearance-none cursor-pointer"
39
+ style={{
40
+ backgroundColor: colors.muted,
41
+ accentColor: colors.accent,
42
+ }}
40
43
  />
41
44
  </div>
42
45
  );
@@ -2,32 +2,29 @@
2
2
 
3
3
  import { useId } from "react";
4
4
  import { useStateStore } from "@json-render/react";
5
- import { Label } from "@neynar/ui/label";
6
5
  import { Switch } from "@neynar/ui/switch";
7
- import { useColorMode } from "@neynar/ui/color-mode";
8
- import { cn } from "@neynar/ui/utils";
9
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent";
6
+ import { Label } from "@neynar/ui/label";
7
+ import { useSnapColors } from "../hooks/use-snap-colors";
10
8
 
11
9
  export function SnapSwitch({
12
10
  element: { props },
13
11
  }: {
14
12
  element: { props: Record<string, unknown> };
15
13
  }) {
16
- const id = useId();
17
14
  const { get, set } = useStateStore();
18
- const { mode } = useColorMode();
19
- const accentStyle = useSnapAccentScopeStyle();
15
+ const colors = useSnapColors();
20
16
  const name = String(props.name ?? "switch");
21
- const path = `/inputs/${name}`;
22
17
  const label = props.label ? String(props.label) : undefined;
23
- const fallback = Boolean(props.defaultChecked ?? false);
18
+ const path = `/inputs/${name}`;
24
19
  const raw = get(path);
25
- const checked = raw === undefined || raw === null ? fallback : Boolean(raw);
20
+ const checked =
21
+ raw !== undefined ? Boolean(raw) : Boolean(props.defaultChecked);
22
+ const id = useId();
26
23
 
27
24
  return (
28
25
  <div className="flex items-center justify-between gap-3">
29
26
  {label && (
30
- <Label htmlFor={id} className="text-foreground font-normal">
27
+ <Label htmlFor={id} className="font-normal" style={{ color: colors.text }}>
31
28
  {label}
32
29
  </Label>
33
30
  )}
@@ -35,11 +32,10 @@ export function SnapSwitch({
35
32
  id={id}
36
33
  checked={checked}
37
34
  onCheckedChange={(v) => set(path, v)}
38
- style={accentStyle}
39
- className={cn(
40
- mode === "light" &&
41
- "data-unchecked:!bg-border data-unchecked:!border-(--input-border)",
42
- )}
35
+ style={{
36
+ backgroundColor: checked ? colors.accent : colors.muted,
37
+ borderColor: checked ? colors.accent : colors.inputBorder,
38
+ }}
43
39
  />
44
40
  </div>
45
41
  );
@@ -1,15 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import { Text } from "@neynar/ui/typography";
4
+ import { useSnapColors } from "../hooks/use-snap-colors";
4
5
 
5
6
  const SIZE_MAP = {
6
- md: { component: "text", textSize: "base" as const },
7
- sm: { component: "text", textSize: "sm" as const },
8
- } as const;
9
-
10
- const WEIGHT_MAP = {
11
- bold: "bold",
12
- normal: "normal",
7
+ md: { textSize: "base" as const },
8
+ sm: { textSize: "sm" as const },
13
9
  } as const;
14
10
 
15
11
  export function SnapText({
@@ -22,9 +18,16 @@ export function SnapText({
22
18
  const weight = props.weight ? String(props.weight) as "bold" | "normal" : undefined;
23
19
  const align = (props.align as "left" | "center" | "right") ?? undefined;
24
20
  const config = SIZE_MAP[size] ?? SIZE_MAP.md;
21
+ const colors = useSnapColors();
25
22
 
26
23
  return (
27
- <Text size={config.textSize} weight={weight} align={align} className="flex-1">
24
+ <Text
25
+ size={config.textSize}
26
+ weight={weight}
27
+ align={align}
28
+ className="flex-1"
29
+ style={{ color: colors.text }}
30
+ >
28
31
  {content}
29
32
  </Text>
30
33
  );
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
 
3
+ import { useState } from "react";
3
4
  import { useStateStore } from "@json-render/react";
4
5
  import { Label } from "@neynar/ui/label";
5
6
  import { cn } from "@neynar/ui/utils";
6
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent";
7
+ import { useSnapColors } from "../hooks/use-snap-colors";
7
8
 
8
9
  export function SnapToggleGroup({
9
10
  element: { props },
@@ -11,7 +12,7 @@ export function SnapToggleGroup({
11
12
  element: { props: Record<string, unknown> };
12
13
  }) {
13
14
  const { get, set } = useStateStore();
14
- const accentStyle = useSnapAccentScopeStyle();
15
+ const colors = useSnapColors();
15
16
  const name = String(props.name ?? "toggle_group");
16
17
  const path = `/inputs/${name}`;
17
18
  const label = props.label ? String(props.label) : undefined;
@@ -50,30 +51,46 @@ export function SnapToggleGroup({
50
51
  };
51
52
 
52
53
  const isVertical = orientation === "vertical";
54
+ const [hoveredIdx, setHoveredIdx] = useState<number | null>(null);
53
55
 
54
56
  return (
55
- <div className="w-full space-y-1.5" style={accentStyle}>
56
- {label && <Label>{label}</Label>}
57
+ <div className="w-full space-y-1.5">
58
+ {label && <Label style={{ color: colors.text }}>{label}</Label>}
57
59
  <div
58
60
  className={cn(
59
- "flex gap-1 rounded-lg bg-border/20 p-1",
61
+ "flex gap-1 rounded-lg p-1",
60
62
  isVertical ? "flex-col" : "flex-row",
61
63
  )}
64
+ style={{ backgroundColor: colors.muted }}
62
65
  >
63
- {options.map((opt) => {
66
+ {options.map((opt, i) => {
64
67
  const isSelected = selected.includes(opt);
68
+ const isHovered = hoveredIdx === i && !isSelected;
65
69
  return (
66
70
  <button
67
71
  key={opt}
68
72
  type="button"
69
73
  onClick={() => toggle(opt)}
74
+ onPointerEnter={() => setHoveredIdx(i)}
75
+ onPointerLeave={() => setHoveredIdx(null)}
70
76
  className={cn(
71
77
  "rounded-md px-3 py-2 text-sm font-medium transition-colors",
72
78
  isVertical ? "w-full" : "flex-1",
73
- isSelected
74
- ? "bg-primary text-primary-foreground"
75
- : "text-foreground hover:bg-border/30",
76
79
  )}
80
+ style={{
81
+ transition: "background-color 0.15s, color 0.15s",
82
+ ...(isSelected
83
+ ? {
84
+ backgroundColor: colors.mode === "dark" ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.08)",
85
+ color: colors.text,
86
+ }
87
+ : {
88
+ color: colors.text,
89
+ backgroundColor: isHovered
90
+ ? (colors.mode === "dark" ? "rgba(255,255,255,0.04)" : "rgba(0,0,0,0.04)")
91
+ : (colors.mode === "dark" ? "rgba(255,255,255,0.02)" : "rgba(0,0,0,0.02)"),
92
+ }),
93
+ }}
77
94
  >
78
95
  {opt}
79
96
  </button>
@@ -0,0 +1,129 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useStateStore } from "@json-render/react";
5
+ import { useColorMode } from "@neynar/ui/color-mode";
6
+ import { resolveSnapPaletteHex } from "../lib/resolve-palette-hex";
7
+ import { useSnapPreviewPageAccent } from "../accent-context";
8
+ import type { PaletteColor } from "@farcaster/snap";
9
+ import { PALETTE_DARK_HEX, PALETTE_LIGHT_HEX } from "@farcaster/snap";
10
+
11
+ /** Readable foreground color (black or white) for a given hex background. */
12
+ export function pickForegroundForBg(hex: string): string {
13
+ const h = hex.replace(/^#/, "");
14
+ if (h.length !== 6) return "#ffffff";
15
+ const r = Number.parseInt(h.slice(0, 2), 16);
16
+ const g = Number.parseInt(h.slice(2, 4), 16);
17
+ const b = Number.parseInt(h.slice(4, 6), 16);
18
+ const yiq = (r * 299 + g * 587 + b * 114) / 1000;
19
+ return yiq >= 180 ? "#0a0a0a" : "#ffffff";
20
+ }
21
+
22
+ const NEUTRAL_LIGHT = {
23
+ text: "#111111",
24
+ textMuted: "#6B7280",
25
+ border: "#E5E7EB",
26
+ muted: "rgba(0,0,0,0.06)",
27
+ surface: "#ffffff",
28
+ inputBorder: "#E5E7EB",
29
+ inputBg: "rgba(0,0,0,0.06)",
30
+ } as const;
31
+
32
+ const NEUTRAL_DARK = {
33
+ text: "#FAFAFA",
34
+ textMuted: "#A1A1AA",
35
+ border: "#2D2D44",
36
+ muted: "rgba(255,255,255,0.03)",
37
+ surface: "#23262f",
38
+ inputBorder: "#3F3F46",
39
+ inputBg: "rgba(255,255,255,0.03)",
40
+ } as const;
41
+
42
+ export type SnapColors = {
43
+ /** Resolved accent hex */
44
+ accent: string;
45
+ /** Readable foreground for accent bg (black or white) */
46
+ accentFg: string;
47
+ /** Primary button hover color */
48
+ accentHover: string;
49
+ /** Secondary/outline button hover color */
50
+ outlineHover: string;
51
+ /** Primary text color */
52
+ text: string;
53
+ /** Muted/secondary text color */
54
+ textMuted: string;
55
+ /** Border color */
56
+ border: string;
57
+ /** Muted background (tracks, containers) */
58
+ muted: string;
59
+ /** Surface/card background */
60
+ surface: string;
61
+ /** Input border */
62
+ inputBorder: string;
63
+ /** Input background */
64
+ inputBg: string;
65
+ /** Current color mode */
66
+ mode: "light" | "dark";
67
+ /** Resolve a palette color name to hex */
68
+ paletteHex: (name: string) => string;
69
+ /** Resolve a palette color name to hex, with accent fallback */
70
+ colorHex: (name: string | undefined) => string;
71
+ };
72
+
73
+ function buildSnapColors(
74
+ accentName: string,
75
+ mode: "light" | "dark",
76
+ ): SnapColors {
77
+ const accent = resolveSnapPaletteHex(accentName, mode);
78
+ const accentFg = pickForegroundForBg(accent);
79
+ const neutrals = mode === "dark" ? NEUTRAL_DARK : NEUTRAL_LIGHT;
80
+ const paletteMap = mode === "dark" ? PALETTE_DARK_HEX : PALETTE_LIGHT_HEX;
81
+
82
+ const accentHover =
83
+ mode === "light"
84
+ ? `color-mix(in srgb, ${accent} 82%, #000000)`
85
+ : `color-mix(in srgb, ${accent} 78%, #ffffff)`;
86
+
87
+ const outlineHover = `color-mix(in srgb, ${accent} 14%, ${neutrals.surface})`;
88
+
89
+ const paletteHex = (name: string) => resolveSnapPaletteHex(name, mode);
90
+
91
+ const colorHex = (name: string | undefined) => {
92
+ if (!name || name === "accent") return accent;
93
+ if (Object.hasOwn(paletteMap, name)) return paletteMap[name as PaletteColor];
94
+ return accent;
95
+ };
96
+
97
+ return {
98
+ accent,
99
+ accentFg,
100
+ accentHover,
101
+ outlineHover,
102
+ ...neutrals,
103
+ mode,
104
+ paletteHex,
105
+ colorHex,
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Returns fully resolved color values for snap components.
111
+ * All colors are concrete hex values (or color-mix expressions for hover states)
112
+ * so they can be used as inline styles, independent of host app CSS.
113
+ */
114
+ export function useSnapColors(): SnapColors {
115
+ const { get } = useStateStore();
116
+ const { mode } = useColorMode();
117
+ const pageAccent = useSnapPreviewPageAccent();
118
+ const fromState = get("/theme/accent");
119
+ const accentRaw =
120
+ (typeof pageAccent === "string" && pageAccent.length > 0
121
+ ? pageAccent
122
+ : fromState) ?? undefined;
123
+ const accentName =
124
+ typeof accentRaw === "string" && accentRaw.length > 0
125
+ ? accentRaw
126
+ : "purple";
127
+
128
+ return useMemo(() => buildSnapColors(accentName, mode), [accentName, mode]);
129
+ }
@@ -6,11 +6,6 @@ import { useSnapPalette } from "../use-snap-palette";
6
6
  import { useSnapTheme } from "../theme";
7
7
  import { ICON_MAP } from "./snap-icon";
8
8
 
9
- const VARIANT_MAP: Record<string, "primary" | "secondary"> = {
10
- primary: "primary",
11
- secondary: "secondary",
12
- };
13
-
14
9
  export function SnapActionButton({
15
10
  element: { props },
16
11
  emit,
@@ -18,28 +13,22 @@ export function SnapActionButton({
18
13
  const { accentHex } = useSnapPalette();
19
14
  const { colors } = useSnapTheme();
20
15
  const label = String(props.label ?? "Action");
21
- const variant = VARIANT_MAP[String(props.variant ?? "secondary")] ?? "secondary";
16
+ const variant = String(props.variant ?? "secondary");
17
+ const isPrimary = variant === "primary";
22
18
  const iconName = props.icon ? String(props.icon) : undefined;
23
19
 
24
- const variantStyle = (() => {
25
- switch (variant) {
26
- case "primary":
27
- return { backgroundColor: accentHex };
28
- case "secondary":
29
- return { backgroundColor: "transparent", borderWidth: 1.5, borderColor: accentHex };
30
- }
31
- })();
32
-
33
- const textColor = variant === "primary" ? "#fff" : accentHex;
34
- const iconColor = variant === "primary" ? "#fff" : accentHex;
20
+ const textColor = isPrimary ? "#fff" : colors.text;
21
+ const iconColor = isPrimary ? "#fff" : colors.text;
35
22
 
36
23
  return (
37
24
  <View style={styles.outer}>
38
25
  <Pressable
39
26
  style={({ pressed }) => [
40
27
  styles.btn,
41
- variant === "primary" ? styles.btnDefault : styles.btnOther,
42
- variantStyle,
28
+ isPrimary ? styles.btnDefault : styles.btnOther,
29
+ isPrimary
30
+ ? { backgroundColor: pressed ? accentHex + "DD" : accentHex }
31
+ : { backgroundColor: pressed ? colors.mutedHover : colors.muted },
43
32
  pressed && styles.pressed,
44
33
  ]}
45
34
  onPress={() => {
@@ -48,7 +37,6 @@ export function SnapActionButton({
48
37
  await emit("press");
49
38
  } catch (err: unknown) {
50
39
  if (typeof __DEV__ !== "undefined" && __DEV__) {
51
- // eslint-disable-next-line no-console
52
40
  console.error("[snap] action failed", err);
53
41
  }
54
42
  }
@@ -118,7 +118,7 @@ export function SnapCellGrid({
118
118
  : null;
119
119
 
120
120
  return (
121
- <View style={[styles.wrap, { gap: gapPx }]}>
121
+ <View style={[styles.wrap, { gap: gapPx, backgroundColor: colors.muted, padding: 4, borderRadius: 8 }]}>
122
122
  {rowEls}
123
123
  {selectionLabel ? (
124
124
  <Text style={[styles.selectionText, { color: colors.textSecondary }]}>
@@ -23,7 +23,7 @@ export function SnapSwitch({
23
23
  <Switch
24
24
  value={checked}
25
25
  onValueChange={(v) => set(path, v)}
26
- trackColor={{ false: colors.border, true: accentHex }}
26
+ trackColor={{ false: colors.muted, true: accentHex }}
27
27
  thumbColor="#fff"
28
28
  />
29
29
  </View>
@@ -1,14 +1,12 @@
1
1
  import type { ComponentRenderProps } from "@json-render/react-native";
2
2
  import { useStateStore } from "@json-render/react-native";
3
3
  import { Pressable, StyleSheet, Text, View } from "react-native";
4
- import { useSnapPalette } from "../use-snap-palette";
5
4
  import { useSnapTheme } from "../theme";
6
5
 
7
6
  export function SnapToggleGroup({
8
7
  element: { props },
9
8
  }: ComponentRenderProps<Record<string, unknown>>) {
10
9
  const { get, set } = useStateStore();
11
- const { accentHex } = useSnapPalette();
12
10
  const { colors } = useSnapTheme();
13
11
  const name = String(props.name ?? "toggle_group");
14
12
  const path = `/inputs/${name}`;
@@ -59,7 +57,7 @@ export function SnapToggleGroup({
59
57
  <View
60
58
  style={[
61
59
  styles.group,
62
- { backgroundColor: colors.border + "33" },
60
+ { backgroundColor: colors.muted },
63
61
  isVertical ? styles.groupVertical : styles.groupHorizontal,
64
62
  ]}
65
63
  >
@@ -70,8 +68,13 @@ export function SnapToggleGroup({
70
68
  key={index}
71
69
  style={({ pressed }) => [
72
70
  styles.option,
73
- isSelected && { backgroundColor: accentHex },
74
- pressed && styles.pressed,
71
+ {
72
+ backgroundColor: isSelected
73
+ ? colors.mutedSelected
74
+ : pressed
75
+ ? colors.mutedHover
76
+ : colors.mutedSubtle,
77
+ },
75
78
  !isVertical && styles.optionHorizontal,
76
79
  ]}
77
80
  onPress={() => handlePress(opt)}
@@ -80,7 +83,6 @@ export function SnapToggleGroup({
80
83
  style={[
81
84
  styles.optionText,
82
85
  { color: colors.text },
83
- isSelected && styles.optionTextSelected,
84
86
  ]}
85
87
  >
86
88
  {opt}
@@ -117,12 +119,8 @@ const styles = StyleSheet.create({
117
119
  optionHorizontal: {
118
120
  flex: 1,
119
121
  },
120
- pressed: { opacity: 0.88 },
121
122
  optionText: {
122
123
  fontSize: 13,
123
124
  fontWeight: "500",
124
125
  },
125
- optionTextSelected: {
126
- color: "#fff",
127
- },
128
126
  });
@@ -10,6 +10,12 @@ export type SnapNativeColors = {
10
10
  border: string;
11
11
  inputBg: string;
12
12
  muted: string;
13
+ /** Subtle tint for toggle button resting state */
14
+ mutedSubtle: string;
15
+ /** Slightly stronger tint for hover/press state */
16
+ mutedHover: string;
17
+ /** Stronger tint for selected state (toggle group) */
18
+ mutedSelected: string;
13
19
  };
14
20
 
15
21
  const DEFAULT_LIGHT: SnapNativeColors = {
@@ -17,9 +23,12 @@ const DEFAULT_LIGHT: SnapNativeColors = {
17
23
  surface: "#ffffff",
18
24
  text: "#111111",
19
25
  textSecondary: "#6b7280",
20
- border: "#d1d5db",
21
- inputBg: "#ffffff",
22
- muted: "#f9fafb",
26
+ border: "#E5E7EB",
27
+ inputBg: "rgba(0,0,0,0.12)",
28
+ muted: "rgba(0,0,0,0.12)",
29
+ mutedSubtle: "rgba(0,0,0,0.06)",
30
+ mutedHover: "rgba(0,0,0,0.10)",
31
+ mutedSelected: "rgba(0,0,0,0.18)",
23
32
  };
24
33
 
25
34
  const DEFAULT_DARK: SnapNativeColors = {
@@ -27,9 +36,12 @@ const DEFAULT_DARK: SnapNativeColors = {
27
36
  surface: "#1a1d24",
28
37
  text: "#fafafa",
29
38
  textSecondary: "#a1a1aa",
30
- border: "#374151",
31
- inputBg: "#1a1d24",
32
- muted: "#27272a",
39
+ border: "#2D2D44",
40
+ inputBg: "rgba(255,255,255,0.03)",
41
+ muted: "rgba(255,255,255,0.03)",
42
+ mutedSubtle: "rgba(255,255,255,0.02)",
43
+ mutedHover: "rgba(255,255,255,0.04)",
44
+ mutedSelected: "rgba(255,255,255,0.10)",
33
45
  };
34
46
 
35
47
  // ─── Context ──────────────────────────────────────────
@@ -1,13 +0,0 @@
1
- import { type CSSProperties } from "react";
2
- import type { PaletteColor } from "@farcaster/snap";
3
- /**
4
- * CSS variables so Neynar controls (`bg-primary`, `data-checked:bg-primary`, etc.)
5
- * use the snap `theme.accent` inside json-render catalog components.
6
- */
7
- export declare function useSnapAccentScopeStyle(): CSSProperties;
8
- /** Active snap palette table for the current docs shell theme. */
9
- export declare function useSnapPalette(): {
10
- hex: (name: string) => string;
11
- map: Record<PaletteColor, string>;
12
- theme: "light" | "dark";
13
- };
@@ -1,32 +0,0 @@
1
- "use client";
2
- import { useMemo } from "react";
3
- import { useStateStore } from "@json-render/react";
4
- import { PALETTE_DARK_HEX, PALETTE_LIGHT_HEX } from "@farcaster/snap";
5
- import { useColorMode } from "@neynar/ui/color-mode";
6
- import { resolveSnapPaletteHex } from "../lib/resolve-palette-hex.js";
7
- import { snapPreviewPrimaryCssProperties } from "../lib/preview-primary-css.js";
8
- import { useSnapPreviewPageAccent } from "../accent-context.js";
9
- /**
10
- * CSS variables so Neynar controls (`bg-primary`, `data-checked:bg-primary`, etc.)
11
- * use the snap `theme.accent` inside json-render catalog components.
12
- */
13
- export function useSnapAccentScopeStyle() {
14
- const { get } = useStateStore();
15
- const { mode } = useColorMode();
16
- const pageAccent = useSnapPreviewPageAccent();
17
- const fromState = get("/theme/accent");
18
- const accentRaw = (typeof pageAccent === "string" && pageAccent.length > 0
19
- ? pageAccent
20
- : fromState) ?? undefined;
21
- const accentName = typeof accentRaw === "string" && accentRaw.length > 0
22
- ? accentRaw
23
- : "purple";
24
- return useMemo(() => snapPreviewPrimaryCssProperties(accentName, mode), [accentName, mode]);
25
- }
26
- /** Active snap palette table for the current docs shell theme. */
27
- export function useSnapPalette() {
28
- const { mode } = useColorMode();
29
- const map = mode === "dark" ? PALETTE_DARK_HEX : PALETTE_LIGHT_HEX;
30
- const hex = (name) => resolveSnapPaletteHex(name, mode);
31
- return { hex, map, theme: mode };
32
- }
@@ -1,45 +0,0 @@
1
- "use client";
2
-
3
- import { useMemo, type CSSProperties } from "react";
4
- import { useStateStore } from "@json-render/react";
5
- import type { PaletteColor } from "@farcaster/snap";
6
- import { PALETTE_DARK_HEX, PALETTE_LIGHT_HEX } from "@farcaster/snap";
7
- import { useColorMode } from "@neynar/ui/color-mode";
8
- import { resolveSnapPaletteHex } from "../lib/resolve-palette-hex";
9
- import { snapPreviewPrimaryCssProperties } from "../lib/preview-primary-css";
10
- import { useSnapPreviewPageAccent } from "../accent-context";
11
-
12
- /**
13
- * CSS variables so Neynar controls (`bg-primary`, `data-checked:bg-primary`, etc.)
14
- * use the snap `theme.accent` inside json-render catalog components.
15
- */
16
- export function useSnapAccentScopeStyle(): CSSProperties {
17
- const { get } = useStateStore();
18
- const { mode } = useColorMode();
19
- const pageAccent = useSnapPreviewPageAccent();
20
- const fromState = get("/theme/accent");
21
- const accentRaw =
22
- (typeof pageAccent === "string" && pageAccent.length > 0
23
- ? pageAccent
24
- : fromState) ?? undefined;
25
- const accentName =
26
- typeof accentRaw === "string" && accentRaw.length > 0
27
- ? accentRaw
28
- : "purple";
29
- return useMemo(
30
- () => snapPreviewPrimaryCssProperties(accentName, mode),
31
- [accentName, mode],
32
- );
33
- }
34
-
35
- /** Active snap palette table for the current docs shell theme. */
36
- export function useSnapPalette(): {
37
- hex: (name: string) => string;
38
- map: Record<PaletteColor, string>;
39
- theme: "light" | "dark";
40
- } {
41
- const { mode } = useColorMode();
42
- const map = mode === "dark" ? PALETTE_DARK_HEX : PALETTE_LIGHT_HEX;
43
- const hex = (name: string) => resolveSnapPaletteHex(name, mode);
44
- return { hex, map, theme: mode };
45
- }