@hfunlabs/hypurr-connect 0.1.15 → 0.1.18

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.
@@ -33,6 +33,7 @@ import {
33
33
  } from "./icons/lucide";
34
34
  import {
35
35
  closeBtnStyle as makeCloseBtnStyle,
36
+ colorWithAlpha,
36
37
  fontFamily,
37
38
  modalBackdropStyle,
38
39
  modalHeaderStyle,
@@ -42,7 +43,6 @@ import {
42
43
  type PrincipalColorOverrides,
43
44
  type PrincipalColors,
44
45
  profileColors,
45
- raisedButtonStyle,
46
46
  resolvePrincipalColors,
47
47
  titleStyle,
48
48
  upperLabelStyle,
@@ -104,7 +104,7 @@ export interface UserProfileModalProps {
104
104
 
105
105
  const backdropStyle = modalBackdropStyle(100);
106
106
  const wrapperStyle = modalWrapperStyle(101);
107
- const panelStyle = modalPanelStyle(false);
107
+ const panelStyle = modalPanelStyle(true);
108
108
  const headerStyle = modalHeaderStyle;
109
109
 
110
110
  const profileSectionStyle: CSSProperties = {
@@ -138,23 +138,18 @@ const tabButtonLayoutStyle: CSSProperties = {
138
138
  };
139
139
 
140
140
  const statBoxStyle: CSSProperties = {
141
- background: "rgba(255,255,255,0.03)",
141
+ background: "rgb(var(--btn-bevel) / 0.03)",
142
142
  borderRadius: 8,
143
143
  padding: 12,
144
144
  border: `1px solid ${profileColors.border}`,
145
145
  };
146
146
 
147
- const slippageBtnBaseStyle: CSSProperties = {
148
- flex: 1,
149
- padding: "4px 0",
150
- borderRadius: 4,
151
- fontSize: 12.5,
152
- lineHeight: "1rem",
153
- fontFamily: fontFamily.mono,
154
- fontWeight: 500,
155
- cursor: "pointer",
156
- transition: "background-color 150ms, color 150ms, border-color 150ms",
157
- };
147
+ const slippageBtnBaseClass =
148
+ "flex-1 rounded border px-2 py-1 font-mono font-medium text-base transition-colors";
149
+ const slippageBtnSelectedClass =
150
+ "bg-surface-btn-active text-content border-surface-bd-active";
151
+ const slippageBtnIdleClass =
152
+ "bg-surface-btn text-content-muted border-surface-bd hover:bg-surface-btn-hover hover:border-surface-bd-hover hover:text-content-tertiary";
158
153
 
159
154
  const walletRowStyle: CSSProperties = {
160
155
  display: "flex",
@@ -179,9 +174,9 @@ function getWalletTypeMeta(wallet: HyperliquidWallet): WalletTypeMeta {
179
174
  if (wallet.isAgent) {
180
175
  return {
181
176
  label: "Agent wallet",
182
- color: "#38bdf8",
183
- background: "rgba(56,189,248,0.16)",
184
- border: "rgba(56,189,248,0.34)",
177
+ color: "rgb(var(--blue-500))",
178
+ background: "rgb(var(--blue-500) / 0.16)",
179
+ border: "rgb(var(--blue-500) / 0.34)",
185
180
  icon: <Bot size={10} />,
186
181
  };
187
182
  }
@@ -189,18 +184,18 @@ function getWalletTypeMeta(wallet: HyperliquidWallet): WalletTypeMeta {
189
184
  if (wallet.isReadOnly) {
190
185
  return {
191
186
  label: "Read-only wallet",
192
- color: "#a78bfa",
193
- background: "rgba(167,139,250,0.16)",
194
- border: "rgba(167,139,250,0.34)",
187
+ color: "rgb(var(--purple-400))",
188
+ background: "rgb(var(--purple-400) / 0.16)",
189
+ border: "rgb(var(--purple-400) / 0.34)",
195
190
  icon: <Eye size={10} />,
196
191
  };
197
192
  }
198
193
 
199
194
  return {
200
195
  label: "Private-key wallet",
201
- color: "#34d399",
202
- background: "rgba(52,211,153,0.16)",
203
- border: "rgba(52,211,153,0.34)",
196
+ color: "rgb(var(--green-400))",
197
+ background: "rgb(var(--green-400) / 0.16)",
198
+ border: "rgb(var(--green-400) / 0.34)",
204
199
  icon: <KeyRound size={10} />,
205
200
  };
206
201
  }
@@ -227,7 +222,9 @@ function ToggleSwitch({
227
222
  alignItems: "center",
228
223
  borderRadius: 9999,
229
224
  border: "none",
230
- background: checked ? accentColor : "rgba(255,255,255,0.08)",
225
+ background: checked
226
+ ? colorWithAlpha(accentColor, 0.85)
227
+ : "rgb(var(--btn-bevel) / 0.08)",
231
228
  cursor: "pointer",
232
229
  padding: 0,
233
230
  transition: "background 150ms",
@@ -261,20 +258,12 @@ function RaisedButton({
261
258
  children: ReactNode;
262
259
  style?: CSSProperties;
263
260
  }) {
264
- const [hovered, setHovered] = useState(false);
265
261
  return (
266
262
  <button
267
263
  type="button"
268
264
  onClick={onClick}
269
- onMouseEnter={() => setHovered(true)}
270
- onMouseLeave={() => setHovered(false)}
271
- style={{
272
- ...tabButtonLayoutStyle,
273
- ...raisedButtonStyle(
274
- selected ? "active" : hovered ? "hover" : "default",
275
- ),
276
- ...style,
277
- }}
265
+ className={selected ? "btn-raised-active" : "btn-raised"}
266
+ style={{ ...tabButtonLayoutStyle, ...style }}
278
267
  >
279
268
  {children}
280
269
  </button>
@@ -290,33 +279,13 @@ function SlippageButton({
290
279
  onClick: () => void;
291
280
  children: ReactNode;
292
281
  }) {
293
- const [hovered, setHovered] = useState(false);
294
282
  return (
295
283
  <button
296
284
  type="button"
297
285
  onClick={onClick}
298
- onMouseEnter={() => setHovered(true)}
299
- onMouseLeave={() => setHovered(false)}
300
- style={{
301
- ...slippageBtnBaseStyle,
302
- background: selected
303
- ? profileColors.surfaceBtnActive
304
- : hovered
305
- ? profileColors.surfaceBtnHover
306
- : profileColors.surfaceBtn,
307
- border: `1px solid ${
308
- selected
309
- ? profileColors.surfaceBdActive
310
- : hovered
311
- ? profileColors.surfaceBdHover
312
- : profileColors.surfaceBd
313
- }`,
314
- color: selected
315
- ? profileColors.text
316
- : hovered
317
- ? "#d1d5db"
318
- : profileColors.muted,
319
- }}
286
+ className={`${slippageBtnBaseClass} ${
287
+ selected ? slippageBtnSelectedClass : slippageBtnIdleClass
288
+ }`}
320
289
  >
321
290
  {children}
322
291
  </button>
@@ -426,7 +395,7 @@ export function UserProfileModal({
426
395
  <div
427
396
  style={{
428
397
  padding: "10px 12px",
429
- background: "rgba(255,255,255,0.03)",
398
+ background: "rgb(var(--btn-bevel) / 0.03)",
430
399
  border: `1px solid ${profileColors.border}`,
431
400
  borderRadius: 8,
432
401
  }}
@@ -561,11 +530,15 @@ export function UserProfileModal({
561
530
  display: "flex",
562
531
  alignItems: "center",
563
532
  gap: 6,
564
- color: "#eab308",
533
+ color: "rgb(var(--amber-400))",
565
534
  marginBottom: 6,
566
535
  }}
567
536
  >
568
- <Star size={13} color="#eab308" fill="#eab308" />
537
+ <Star
538
+ size={13}
539
+ color="rgb(var(--amber-400))"
540
+ fill="rgb(var(--amber-400))"
541
+ />
569
542
  <span style={upperLabelStyle}>Hfun Score</span>
570
543
  </div>
571
544
  <p
@@ -762,7 +735,7 @@ export function UserProfileModal({
762
735
  width: 40,
763
736
  height: 40,
764
737
  borderRadius: "50%",
765
- background: "rgba(255,255,255,0.04)",
738
+ background: "rgb(var(--btn-bevel) / 0.04)",
766
739
  display: "flex",
767
740
  alignItems: "center",
768
741
  justifyContent: "center",
@@ -833,8 +806,8 @@ export function UserProfileModal({
833
806
  <div style={{ padding: "0 24px 24px" }}>
834
807
  <button
835
808
  onClick={onClose}
809
+ className="btn-raised"
836
810
  style={{
837
- ...raisedButtonStyle("default"),
838
811
  width: "100%",
839
812
  padding: "6px 0",
840
813
  fontSize: 12.5,
@@ -890,10 +863,6 @@ function WalletRow({
890
863
  const [hovered, setHovered] = useState(false);
891
864
  const agentExpiryTitle = getAgentExpiryTitle(wallet);
892
865
  const isAgentExpired = !!agentExpiryTitle;
893
- const displayAddress =
894
- wallet.isAgent && wallet.agentEthereumAddress?.value
895
- ? wallet.agentEthereumAddress.value
896
- : wallet.ethereumAddress;
897
866
  const typeMeta = getWalletTypeMeta(wallet);
898
867
  return (
899
868
  <div
@@ -960,7 +929,7 @@ function WalletRow({
960
929
  color: typeMeta.color,
961
930
  background: typeMeta.background,
962
931
  border: `1px solid ${typeMeta.border}`,
963
- boxShadow: "0 1px 4px rgba(0,0,0,0.35)",
932
+ boxShadow: "0 1px 4px rgb(var(--btn-bevel) / 0.18)",
964
933
  }}
965
934
  >
966
935
  {typeMeta.icon}
@@ -1001,8 +970,8 @@ function WalletRow({
1001
970
  fontFamily: fontFamily.mono,
1002
971
  }}
1003
972
  >
1004
- {displayAddress?.slice(0, 6)}...
1005
- {displayAddress?.slice(-4)}
973
+ {wallet.ethereumAddress?.slice(0, 6)}...
974
+ {wallet.ethereumAddress?.slice(-4)}
1006
975
  </p>
1007
976
  </div>
1008
977
  {agentExpiryTitle && (
@@ -14,6 +14,7 @@ import {
14
14
  EXPIRED_AGENT_COLOR,
15
15
  } from "./AgentExpiryWarning";
16
16
  import { useHypurrConnectInternal } from "./HypurrConnectProvider";
17
+ import { useIsMobile } from "./useIsMobile";
17
18
  import { UserProfileModal, type SlippageOption } from "./UserProfileModal";
18
19
  import { getAgentExpiryTitle } from "./agentWallet";
19
20
  import {
@@ -95,7 +96,7 @@ export interface ExtraFooterItem {
95
96
  hoverColor?: string;
96
97
  }
97
98
 
98
- const DEFAULT_BACKGROUND_COLOR = "rgba(20,20,20,0.95)";
99
+ const DEFAULT_BACKGROUND_COLOR = "rgb(var(--surface-dropdown))";
99
100
 
100
101
  interface DropdownWallet {
101
102
  id: number;
@@ -201,11 +202,6 @@ const formatCompactAddress = (addr: string | undefined) => {
201
202
  return `${addr.slice(0, 4)}...${addr.slice(-2)}`;
202
203
  };
203
204
 
204
- const getDisplayAddress = (wallet: DropdownWallet) =>
205
- wallet.isAgent && wallet.agentEthereumAddress?.value
206
- ? wallet.agentEthereumAddress.value
207
- : wallet.ethereumAddress;
208
-
209
205
  type WalletTypeMeta = {
210
206
  label: string;
211
207
  color: string;
@@ -241,23 +237,21 @@ const rootStyle: CSSProperties = {
241
237
  right: 0,
242
238
  top: "calc(100% + 4px)",
243
239
  width: 240,
244
- backdropFilter: "blur(10px)",
245
- WebkitBackdropFilter: "blur(10px)",
246
240
  borderRadius: 9,
247
- boxShadow: "0 8px 40px rgba(0,0,0,0.85)",
248
- border: "1px solid rgba(255,255,255,0.08)",
241
+ boxShadow: "var(--shadow-dropdown)",
242
+ border: "1px solid rgb(var(--line))",
249
243
  zIndex: 50,
250
244
  transformOrigin: "top right",
251
245
  overflow: "hidden",
252
246
  };
253
247
 
254
- const sectionBorder = "1px solid rgba(255,255,255,0.06)";
248
+ const sectionBorder = "1px solid rgb(var(--line))";
255
249
 
256
250
  const sectionHeaderStyle: CSSProperties = {
257
251
  padding: "8px 16px",
258
252
  fontSize: 12,
259
253
  fontWeight: 600,
260
- color: "#9ca3af",
254
+ color: "rgb(var(--content-muted))",
261
255
  textTransform: "uppercase",
262
256
  letterSpacing: "0.05em",
263
257
  };
@@ -273,7 +267,7 @@ const footerBtnStyle: CSSProperties = {
273
267
  display: "flex",
274
268
  alignItems: "center",
275
269
  gap: 8,
276
- color: "#9ca3af",
270
+ color: "rgb(var(--content-muted))",
277
271
  transition: "background 120ms, color 120ms",
278
272
  };
279
273
 
@@ -336,8 +330,7 @@ export function WalletSelectorDropdown({
336
330
  const renderWalletRow = (item: WalletListItem, depth: number): ReactNode => {
337
331
  const { wallet, label } = item;
338
332
  const isSelected = wallet.id === selectedWalletId;
339
- const displayAddress = getDisplayAddress(wallet);
340
- const compactAddress = formatCompactAddress(displayAddress);
333
+ const compactAddress = formatCompactAddress(wallet.ethereumAddress);
341
334
  const agentExpiryTitle = getAgentExpiryTitle({
342
335
  isAgent: !!wallet.isAgent,
343
336
  agentExpiresAt: wallet.agentExpiresAt,
@@ -362,7 +355,7 @@ export function WalletSelectorDropdown({
362
355
  }
363
356
  : undefined
364
357
  }
365
- onCopy={() => handleCopyAddress(displayAddress)}
358
+ onCopy={() => handleCopyAddress(wallet.ethereumAddress)}
366
359
  agentExpiryTitle={agentExpiryTitle}
367
360
  onRenewAgentWallet={
368
361
  wallet.isAgent && onRenewAgentWallet
@@ -400,7 +393,7 @@ export function WalletSelectorDropdown({
400
393
  gap: 12,
401
394
  background: "transparent",
402
395
  border: "none",
403
- borderBottomColor: "rgba(255,255,255,0.06)",
396
+ borderBottomColor: "rgb(var(--btn-bevel) / 0.06)",
404
397
  borderBottomStyle: "solid",
405
398
  borderBottomWidth: 1,
406
399
  cursor: "pointer",
@@ -408,7 +401,8 @@ export function WalletSelectorDropdown({
408
401
  transition: "background 120ms",
409
402
  }}
410
403
  onMouseEnter={(e) =>
411
- (e.currentTarget.style.background = "rgba(255,255,255,0.06)")
404
+ (e.currentTarget.style.background =
405
+ "rgb(var(--btn-bevel) / 0.06)")
412
406
  }
413
407
  onMouseLeave={(e) =>
414
408
  (e.currentTarget.style.background = "transparent")
@@ -509,7 +503,7 @@ export function WalletSelectorDropdown({
509
503
  margin: 0,
510
504
  fontSize: 14,
511
505
  fontWeight: 500,
512
- color: "#fff",
506
+ color: "rgb(var(--content))",
513
507
  overflow: "hidden",
514
508
  textOverflow: "ellipsis",
515
509
  whiteSpace: "nowrap",
@@ -568,7 +562,9 @@ export function WalletSelectorDropdown({
568
562
  fontWeight: 600,
569
563
  textTransform: "uppercase",
570
564
  letterSpacing: "0.05em",
571
- color: isSelectedGroup ? "#e5e7eb" : "#6b7280",
565
+ color: isSelectedGroup
566
+ ? "rgb(var(--content-secondary))"
567
+ : "rgb(var(--content-faint))",
572
568
  }}
573
569
  >
574
570
  <Folder size={12} style={{ flexShrink: 0 }} />
@@ -587,7 +583,7 @@ export function WalletSelectorDropdown({
587
583
  style={{
588
584
  flexShrink: 0,
589
585
  fontVariantNumeric: "tabular-nums",
590
- color: "#4b5563",
586
+ color: "rgb(var(--content-disabled))",
591
587
  }}
592
588
  >
593
589
  {entry.items.length}
@@ -620,7 +616,7 @@ export function WalletSelectorDropdown({
620
616
  }}
621
617
  onMouseEnter={(e) =>
622
618
  (e.currentTarget.style.background =
623
- "rgba(255,255,255,0.06)")
619
+ "rgb(var(--btn-bevel) / 0.06)")
624
620
  }
625
621
  onMouseLeave={(e) =>
626
622
  (e.currentTarget.style.background = "transparent")
@@ -690,7 +686,7 @@ function FooterBtn({
690
686
  onClick,
691
687
  icon,
692
688
  label,
693
- hoverColor = "#fff",
689
+ hoverColor = "rgb(var(--content))",
694
690
  }: {
695
691
  onClick: () => void;
696
692
  icon: ReactNode;
@@ -702,12 +698,12 @@ function FooterBtn({
702
698
  onClick={onClick}
703
699
  style={footerBtnStyle}
704
700
  onMouseEnter={(e) => {
705
- e.currentTarget.style.background = "rgba(255,255,255,0.06)";
701
+ e.currentTarget.style.background = "rgb(var(--btn-bevel) / 0.06)";
706
702
  e.currentTarget.style.color = hoverColor;
707
703
  }}
708
704
  onMouseLeave={(e) => {
709
705
  e.currentTarget.style.background = "transparent";
710
- e.currentTarget.style.color = "#9ca3af";
706
+ e.currentTarget.style.color = "rgb(var(--content-muted))";
711
707
  }}
712
708
  >
713
709
  {icon}
@@ -742,8 +738,14 @@ function WalletRow({
742
738
  colors: PrincipalColors;
743
739
  }) {
744
740
  const isAgentExpired = !!agentExpiryTitle;
745
- const walletTextColor = isAgentExpired ? EXPIRED_AGENT_COLOR : "#d1d5db";
746
- const mutedTextColor = isAgentExpired ? "rgba(245,158,11,0.78)" : "#6b7280";
741
+ const walletTextColor = isAgentExpired
742
+ ? EXPIRED_AGENT_COLOR
743
+ : "rgb(var(--content-tertiary))";
744
+ const mutedTextColor = isAgentExpired
745
+ ? "rgba(245,158,11,0.78)"
746
+ : "rgb(var(--content-faint))";
747
+ // Touch devices have no hover, so keep row actions visible on mobile.
748
+ const isMobile = useIsMobile();
747
749
  return (
748
750
  <div
749
751
  style={{
@@ -756,19 +758,21 @@ function WalletRow({
756
758
  color: walletTextColor,
757
759
  display: "flex",
758
760
  alignItems: "center",
759
- background: isSelected ? "rgba(255,255,255,0.06)" : "transparent",
761
+ background: isSelected ? "rgb(var(--btn-bevel) / 0.06)" : "transparent",
760
762
  transition: "background 120ms",
761
763
  }}
762
764
  onMouseEnter={(e) => {
763
- e.currentTarget.style.background = "rgba(255,255,255,0.06)";
765
+ e.currentTarget.style.background = "rgb(var(--btn-bevel) / 0.06)";
766
+ if (isMobile) return;
764
767
  const actions =
765
768
  e.currentTarget.querySelector<HTMLDivElement>("[data-row-actions]");
766
769
  if (actions) actions.style.opacity = "1";
767
770
  }}
768
771
  onMouseLeave={(e) => {
769
772
  e.currentTarget.style.background = isSelected
770
- ? "rgba(255,255,255,0.06)"
773
+ ? "rgb(var(--btn-bevel) / 0.06)"
771
774
  : "transparent";
775
+ if (isMobile) return;
772
776
  const actions =
773
777
  e.currentTarget.querySelector<HTMLDivElement>("[data-row-actions]");
774
778
  if (actions) actions.style.opacity = "0";
@@ -813,7 +817,7 @@ function WalletRow({
813
817
  color: isAgentExpired
814
818
  ? walletTextColor
815
819
  : isSelected
816
- ? "#fff"
820
+ ? "rgb(var(--content))"
817
821
  : undefined,
818
822
  fontWeight: isSelected ? 500 : undefined,
819
823
  }}
@@ -838,8 +842,8 @@ function WalletRow({
838
842
  color: isAgentExpired
839
843
  ? walletTextColor
840
844
  : isSelected
841
- ? "#fff"
842
- : "#9ca3af",
845
+ ? "rgb(var(--content))"
846
+ : "rgb(var(--content-muted))",
843
847
  fontWeight: isSelected ? 500 : undefined,
844
848
  }}
845
849
  >
@@ -858,7 +862,7 @@ function WalletRow({
858
862
  style={{
859
863
  display: "flex",
860
864
  alignItems: "center",
861
- opacity: 0,
865
+ opacity: isMobile ? 1 : 0,
862
866
  transition: "opacity 120ms",
863
867
  }}
864
868
  >
@@ -921,7 +925,7 @@ function RowIconBtn({
921
925
  marginLeft: 4,
922
926
  background: "transparent",
923
927
  border: "none",
924
- color: "#4b5563",
928
+ color: "rgb(var(--content-disabled))",
925
929
  cursor: "pointer",
926
930
  display: "flex",
927
931
  alignItems: "center",
@@ -929,7 +933,9 @@ function RowIconBtn({
929
933
  transition: "color 120ms",
930
934
  }}
931
935
  onMouseEnter={(e) => (e.currentTarget.style.color = hoverColor)}
932
- onMouseLeave={(e) => (e.currentTarget.style.color = "#4b5563")}
936
+ onMouseLeave={(e) =>
937
+ (e.currentTarget.style.color = "rgb(var(--content-disabled))")
938
+ }
933
939
  >
934
940
  {children}
935
941
  </button>
@@ -1,25 +1,39 @@
1
1
  import type { CSSProperties } from "react";
2
2
 
3
+ /**
4
+ * Theme tokens consumed by inline `style={{}}` props. Values resolve to
5
+ * `rgb(var(--token) / alpha)` strings so the active theme (set by the host
6
+ * via `:root` / `.light` in `src/tailwind.css`) flows through on every paint
7
+ * without any JS-side reactivity.
8
+ */
3
9
  export const profileColors = {
10
+ // Accent stays a literal hex — host may override via the accentColor prop
11
+ // and helpers below expect a parseable color.
4
12
  accent: "#a855f7",
5
- panel: "rgba(14,18,24,0.8)",
6
- panelSolid: "#0e1218",
7
- backdrop: "rgba(0,0,0,0.7)",
8
- border: "#1f2937",
9
- surfaceBtn: "#0D1219",
10
- surfaceBtnHover: "#15171A",
11
- surfaceBtnActive: "#1E2124",
12
- surfaceBd: "#262A30",
13
- surfaceBdHover: "#444548",
14
- surfaceBdActive: "#4B4D50",
15
- text: "#ffffff",
16
- muted: "#aab1c1",
17
- subdued: "#7d8597",
18
- disabled: "#4b5563",
19
- danger: "#f87171",
13
+
14
+ panel: "rgb(var(--surface-modal) / 0.8)",
15
+ panelSolid: "rgb(var(--surface-modal))",
16
+ backdrop: "rgb(var(--overlay) / 0.7)",
17
+ border: "rgb(var(--line))",
18
+
19
+ surfaceBtn: "rgb(var(--surface-btn))",
20
+ surfaceBtnHover: "rgb(var(--surface-btn-hover))",
21
+ surfaceBtnActive: "rgb(var(--surface-btn-active))",
22
+ surfaceBd: "rgb(var(--surface-bd))",
23
+ surfaceBdHover: "rgb(var(--surface-bd-hover))",
24
+ surfaceBdActive: "rgb(var(--surface-bd-active))",
25
+
26
+ text: "rgb(var(--content))",
27
+ muted: "rgb(var(--content-muted))",
28
+ subdued: "rgb(var(--content-subtle))",
29
+ disabled: "rgb(var(--content-disabled))",
30
+
31
+ danger: "rgb(var(--trade-down))",
32
+ // Keep the punchier red literal for hover backgrounds where we want a
33
+ // specific warm tint regardless of theme.
20
34
  dangerStrong: "#ef4444",
21
- purple: "#d8b4fe",
22
- yellow: "#eab308",
35
+ purple: "rgb(var(--purple-400))",
36
+ yellow: "rgb(var(--amber-400))",
23
37
  };
24
38
 
25
39
  export const fontFamily = {
@@ -87,7 +101,7 @@ export const relativeLuminance = (color: string): number | null => {
87
101
  */
88
102
  export const pickContrastColor = (
89
103
  background: string,
90
- lightFg: string = profileColors.text,
104
+ lightFg: string = "#ffffff",
91
105
  darkFg: string = "#0a0a0a",
92
106
  ): string => {
93
107
  const lum = relativeLuminance(background);
@@ -95,7 +109,7 @@ export const pickContrastColor = (
95
109
  return lum > 0.5 ? darkFg : lightFg;
96
110
  };
97
111
 
98
- const colorWithAlpha = (color: string, alpha: number): string => {
112
+ export const colorWithAlpha = (color: string, alpha: number): string => {
99
113
  const hex = color.trim();
100
114
  const shortHex = /^#([0-9a-f]{3})$/i.exec(hex);
101
115
  const longHex = /^#([0-9a-f]{6})$/i.exec(hex);
@@ -166,7 +180,7 @@ export const modalPanelStyle = (solid = false): CSSProperties => ({
166
180
  WebkitBackdropFilter: solid ? undefined : "blur(10px)",
167
181
  border: `1px solid ${profileColors.border}`,
168
182
  borderRadius: 8,
169
- boxShadow: "0 25px 50px -12px rgba(0,0,0,0.6)",
183
+ boxShadow: "var(--shadow-modal)",
170
184
  overflow: "hidden",
171
185
  fontFamily: fontFamily.sans,
172
186
  });
@@ -209,6 +223,11 @@ export const upperLabelStyle: CSSProperties = {
209
223
  letterSpacing: "0.1em",
210
224
  };
211
225
 
226
+ /**
227
+ * Raised button surface, computed inline. Uses `--btn-bevel` for the top
228
+ * sheen so dark theme keeps the white-bevel look while light theme picks up
229
+ * a dark bevel (host sets `--btn-bevel: 15 23 42` under `.light`).
230
+ */
212
231
  export const raisedButtonStyle = (
213
232
  state: "default" | "hover" | "active" | "disabled" = "default",
214
233
  ): CSSProperties => {
@@ -216,9 +235,9 @@ export const raisedButtonStyle = (
216
235
  return {
217
236
  backgroundColor: profileColors.surfaceBtnActive,
218
237
  backgroundImage:
219
- "linear-gradient(to bottom, rgba(255,255,255,0.05), transparent)",
238
+ "linear-gradient(to bottom, rgb(var(--btn-bevel) / 0.05), transparent)",
220
239
  boxShadow:
221
- "inset 0 1px 0 rgba(255,255,255,0.18), inset 0 -1px 0 rgba(0,0,0,0.45), inset 0 0 0 1px #4B4D50, 0 1px 2px rgba(0,0,0,0.5)",
240
+ "inset 0 1px 0 rgb(var(--btn-bevel) / 0.18), inset 0 -1px 0 rgba(0,0,0,0.45), inset 0 0 0 1px rgb(var(--surface-bd-active)), 0 1px 2px rgba(0,0,0,0.5)",
222
241
  color: profileColors.text,
223
242
  border: "none",
224
243
  };
@@ -228,9 +247,9 @@ export const raisedButtonStyle = (
228
247
  return {
229
248
  backgroundColor: profileColors.surfaceBtn,
230
249
  backgroundImage:
231
- "linear-gradient(to bottom, rgba(255,255,255,0.02), transparent)",
250
+ "linear-gradient(to bottom, rgb(var(--btn-bevel) / 0.02), transparent)",
232
251
  boxShadow:
233
- "inset 0 1px 0 rgba(255,255,255,0.05), inset 0 -1px 0 rgba(0,0,0,0.25), inset 0 0 0 1px #1c2026",
252
+ "inset 0 1px 0 rgb(var(--btn-bevel) / 0.05), inset 0 -1px 0 rgba(0,0,0,0.25), inset 0 0 0 1px rgb(var(--surface-bd) / 0.6)",
234
253
  color: profileColors.subdued,
235
254
  border: "none",
236
255
  cursor: "not-allowed",
@@ -241,10 +260,10 @@ export const raisedButtonStyle = (
241
260
  return {
242
261
  backgroundColor: profileColors.surfaceBtnHover,
243
262
  backgroundImage:
244
- "linear-gradient(to bottom, rgba(255,255,255,0.04), transparent)",
263
+ "linear-gradient(to bottom, rgb(var(--btn-bevel) / 0.04), transparent)",
245
264
  boxShadow:
246
- "inset 0 1px 0 rgba(255,255,255,0.14), inset 0 -1px 0 rgba(0,0,0,0.40), inset 0 0 0 1px #444548",
247
- color: "#d1d5db",
265
+ "inset 0 1px 0 rgb(var(--btn-bevel) / 0.14), inset 0 -1px 0 rgba(0,0,0,0.40), inset 0 0 0 1px rgb(var(--surface-bd-hover))",
266
+ color: "rgb(var(--content-tertiary))",
248
267
  border: "none",
249
268
  };
250
269
  }
@@ -252,9 +271,9 @@ export const raisedButtonStyle = (
252
271
  return {
253
272
  backgroundColor: profileColors.surfaceBtn,
254
273
  backgroundImage:
255
- "linear-gradient(to bottom, rgba(255,255,255,0.03), transparent)",
274
+ "linear-gradient(to bottom, rgb(var(--btn-bevel) / 0.03), transparent)",
256
275
  boxShadow:
257
- "inset 0 1px 0 rgba(255,255,255,0.10), inset 0 -1px 0 rgba(0,0,0,0.35), inset 0 0 0 1px #262A30",
276
+ "inset 0 1px 0 rgb(var(--btn-bevel) / 0.10), inset 0 -1px 0 rgba(0,0,0,0.35), inset 0 0 0 1px rgb(var(--surface-bd))",
258
277
  color: profileColors.muted,
259
278
  border: "none",
260
279
  };
@@ -265,7 +284,9 @@ export const dangerOutlineButtonStyle = (
265
284
  hovered = false,
266
285
  ): CSSProperties => ({
267
286
  background: enabled && hovered ? "rgba(248,113,113,0.1)" : "transparent",
268
- border: `1px solid ${enabled ? profileColors.danger : "#374151"}`,
287
+ border: `1px solid ${
288
+ enabled ? profileColors.danger : "rgb(var(--line-strong))"
289
+ }`,
269
290
  color: enabled ? profileColors.danger : profileColors.disabled,
270
291
  cursor: enabled ? "pointer" : "not-allowed",
271
292
  });