@rialo/frost 0.1.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/README.md ADDED
@@ -0,0 +1,778 @@
1
+ # Rialo Frost 🧊
2
+
3
+ **The wallet integration toolkit for Rialo dApps** — Connect wallets, sign transactions, and build Web3 apps in minutes.
4
+
5
+ ```bash
6
+ pnpm add @rialo/frost
7
+ ```
8
+
9
+ ## 30-Second Setup
10
+
11
+ ```tsx
12
+ // 1. Create config (do this once, outside components)
13
+ import { createConfig, getDefaultRialoClientConfig } from "@rialo/frost";
14
+
15
+ export const config = createConfig({
16
+ clientConfig: getDefaultRialoClientConfig("devnet"),
17
+ });
18
+
19
+ // 2. Wrap your app
20
+ import { FrostProvider, ConnectButton } from "@rialo/frost";
21
+
22
+ function App() {
23
+ return (
24
+ <FrostProvider config={config}>
25
+ <ConnectButton />
26
+ </FrostProvider>
27
+ );
28
+ }
29
+ ```
30
+
31
+ That's it. You now have a working wallet connection button.
32
+
33
+ ---
34
+
35
+ ## Table of Contents
36
+
37
+ - [New to Web3?](#new-to-web3)
38
+ - [Installation](#installation)
39
+ - [Step-by-Step Guide](#step-by-step-guide)
40
+ - [Hooks Reference](#hooks-reference)
41
+ - [Components](#components)
42
+ - [Error Handling](#error-handling)
43
+ - [Advanced Usage](#advanced-usage)
44
+ - [Troubleshooting](#troubleshooting)
45
+ - [CSS Custom Properties](#css-custom-properties)
46
+
47
+ ---
48
+
49
+ ## New to Web3?
50
+
51
+ <details>
52
+ <summary><strong>Click here if you're new to blockchain development</strong></summary>
53
+
54
+ ### What You Need to Know
55
+
56
+ **Wallet** = A browser extension (like MetaMask, but for Rialo) that stores the user's private keys and lets them approve transactions.
57
+
58
+ **Account/Address** = A public identifier (like `7xKXtg2CW87d97...`) that represents a user on the blockchain. One wallet can have multiple accounts.
59
+
60
+ **Signing** = When a user cryptographically approves something using their wallet. This proves they own the account without exposing their private key.
61
+
62
+ **Transaction** = An operation that changes state on the blockchain (sending tokens, interacting with smart contracts, etc.).
63
+
64
+ ### How Frost Fits In
65
+
66
+ ```
67
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
68
+ │ Your App │ ───► │ Frost │ ───► │ Wallet │
69
+ │ │ │ │ │ Extension │
70
+ │ - UI │ │ - Discovers │ │ │
71
+ │ - Logic │ │ wallets │ │ - Stores │
72
+ │ - Hooks │ │ - Manages │ │ keys │
73
+ │ │ │ state │ │ - Signs │
74
+ └──────────────┘ └──────────────┘ └──────────────┘
75
+ ```
76
+
77
+ Frost handles all the complex wallet communication so you can focus on building your app.
78
+
79
+ </details>
80
+
81
+ ---
82
+
83
+ ## Installation
84
+
85
+ ### Requirements
86
+
87
+ - Node.js 18+
88
+ - React 18 or 19
89
+ - A Rialo wallet extension (for testing)
90
+
91
+ ### Install
92
+
93
+ ```bash
94
+ # pnpm (recommended)
95
+ pnpm add @rialo/frost
96
+
97
+ # npm
98
+ npm install @rialo/frost
99
+
100
+ # yarn
101
+ yarn add @rialo/frost
102
+ ```
103
+
104
+ ### For Non-React Apps
105
+
106
+ Use the framework-agnostic core package:
107
+
108
+ ```bash
109
+ pnpm add @rialo/frost-core
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Step-by-Step Guide
115
+
116
+ ### Step 1: Create Your Config
117
+
118
+ Create a file called `frost.config.ts` in your `src` folder:
119
+
120
+ ```typescript
121
+ // src/frost.config.ts
122
+ import { createConfig, getDefaultRialoClientConfig } from "@rialo/frost";
123
+
124
+ export const frostConfig = createConfig({
125
+ clientConfig: getDefaultRialoClientConfig("devnet"), // Use "mainnet" for production
126
+ autoConnect: true, // Reconnect automatically on page refresh
127
+ });
128
+ ```
129
+
130
+ > ⚠️ **Important**: Create the config outside of React components. This prevents it from being recreated on every render.
131
+
132
+ ### Step 2: Add the Provider
133
+
134
+ Wrap your app with `FrostProvider`:
135
+
136
+ ```tsx
137
+ // src/App.tsx
138
+ import { FrostProvider } from "@rialo/frost";
139
+ import { frostConfig } from "./frost.config";
140
+
141
+ function App() {
142
+ return (
143
+ <FrostProvider config={frostConfig}>
144
+ <YourApp />
145
+ </FrostProvider>
146
+ );
147
+ }
148
+ ```
149
+
150
+ ### Step 3: Add a Connect Button
151
+
152
+ **Option A: Use the built-in component**
153
+
154
+ ```tsx
155
+ import { ConnectButton } from "@rialo/frost";
156
+
157
+ function Header() {
158
+ return (
159
+ <nav>
160
+ <h1>My dApp</h1>
161
+ <ConnectButton />
162
+ </nav>
163
+ );
164
+ }
165
+ ```
166
+
167
+ **Option B: Build your own**
168
+
169
+ ```tsx
170
+ import {
171
+ useWallets,
172
+ useConnectWallet,
173
+ useDisconnectWallet,
174
+ useIsConnected,
175
+ useActiveAccount,
176
+ } from "@rialo/frost";
177
+
178
+ function CustomConnectButton() {
179
+ const wallets = useWallets();
180
+ const { mutate: connect, isPending } = useConnectWallet();
181
+ const { mutate: disconnect } = useDisconnectWallet();
182
+ const isConnected = useIsConnected();
183
+ const account = useActiveAccount();
184
+
185
+ if (isConnected && account) {
186
+ return (
187
+ <div>
188
+ <span>{account.address.slice(0, 6)}...{account.address.slice(-4)}</span>
189
+ <button onClick={() => disconnect()}>Disconnect</button>
190
+ </div>
191
+ );
192
+ }
193
+
194
+ return (
195
+ <div>
196
+ {wallets.map((wallet) => (
197
+ <button
198
+ key={wallet.name}
199
+ onClick={() => connect({ walletName: wallet.name })}
200
+ disabled={isPending}
201
+ >
202
+ {wallet.icon && <img src={wallet.icon} alt="" width={20} />}
203
+ Connect {wallet.name}
204
+ </button>
205
+ ))}
206
+ </div>
207
+ );
208
+ }
209
+ ```
210
+
211
+ ### Step 4: Display Balance
212
+
213
+ ```tsx
214
+ import { useNativeBalance, useIsConnected } from "@rialo/frost";
215
+
216
+ function Balance() {
217
+ const isConnected = useIsConnected();
218
+ const { formatted, isLoading, refetch } = useNativeBalance();
219
+
220
+ if (!isConnected) return null;
221
+
222
+ return (
223
+ <div>
224
+ {isLoading ? "Loading..." : `${formatted} RIA`}
225
+ <button onClick={() => refetch()}>↻</button>
226
+ </div>
227
+ );
228
+ }
229
+ ```
230
+
231
+ ### Step 5: Sign Messages
232
+
233
+ ```tsx
234
+ import { useSignMessage, useIsConnected } from "@rialo/frost";
235
+
236
+ function SignDemo() {
237
+ const isConnected = useIsConnected();
238
+ const { mutate: signMessage, isPending } = useSignMessage({
239
+ onSuccess: (result) => {
240
+ console.log("Signature:", result.signature);
241
+ },
242
+ });
243
+
244
+ if (!isConnected) return <p>Connect wallet first</p>;
245
+
246
+ return (
247
+ <button
248
+ onClick={() => signMessage({ message: "Hello from my dApp!" })}
249
+ disabled={isPending}
250
+ >
251
+ {isPending ? "Signing..." : "Sign Message"}
252
+ </button>
253
+ );
254
+ }
255
+ ```
256
+
257
+ ### Step 6: Send Transactions
258
+
259
+ ```tsx
260
+ import { useSendTransaction } from "@rialo/frost";
261
+
262
+ function SendTx({ transaction }: { transaction: Uint8Array }) {
263
+ const { mutate: send, isPending, isSuccess, data } = useSendTransaction({
264
+ onSuccess: (result) => {
265
+ console.log("Transaction sent:", result.signature);
266
+ },
267
+ onError: (error) => {
268
+ console.error("Failed:", error.message);
269
+ },
270
+ });
271
+
272
+ return (
273
+ <div>
274
+ <button onClick={() => send({ transaction })} disabled={isPending}>
275
+ {isPending ? "Sending..." : "Send Transaction"}
276
+ </button>
277
+ {isSuccess && <p>✓ Signature: {data.signature}</p>}
278
+ </div>
279
+ );
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Hooks Reference
286
+
287
+ ### Connection
288
+
289
+ | Hook | Returns | Description |
290
+ |------|---------|-------------|
291
+ | `useWallets()` | `WalletEntity[]` | All discovered wallets |
292
+ | `useWalletsReady()` | `boolean` | True when wallet discovery is complete |
293
+ | `useConnectWallet()` | Mutation | Connect to a wallet |
294
+ | `useDisconnectWallet()` | Mutation | Disconnect current wallet |
295
+ | `useConnectionStatus()` | `ConnectionStatus` | `"disconnected"` \| `"connecting"` \| `"connected"` \| `"reconnecting"` |
296
+ | `useIsConnected()` | `boolean` | True if connected |
297
+
298
+ ### Account & Wallet
299
+
300
+ | Hook | Returns | Description |
301
+ |------|---------|-------------|
302
+ | `useActiveWallet()` | `WalletEntity \| null` | Currently connected wallet |
303
+ | `useActiveAccount()` | `AccountEntity \| null` | Currently active account |
304
+ | `useAccounts()` | `AccountEntity[]` | All accounts from connected wallet |
305
+
306
+ ### Chain & Client
307
+
308
+ | Hook | Returns | Description |
309
+ |------|---------|-------------|
310
+ | `useChainId()` | `string` | Current chain ID (e.g., `"rialo:devnet"`) |
311
+ | `useSwitchChain()` | Mutation | Switch to a different network |
312
+ | `useClient()` | `RialoClient` | Direct RPC client access |
313
+
314
+ ### Balance
315
+
316
+ | Hook | Returns | Description |
317
+ |------|---------|-------------|
318
+ | `useNativeBalance()` | `{ formatted, balance, isLoading, refetch }` | Native token balance |
319
+
320
+ ### Signing & Transactions
321
+
322
+ | Hook | Description |
323
+ |------|-------------|
324
+ | `useSignMessage()` | Sign arbitrary messages |
325
+ | `useSignIn()` | Sign-In with Wallet (SIWS authentication) |
326
+ | `useSignTransaction()` | Sign transaction without sending |
327
+ | `useSendTransaction()` | Sign and send via wallet |
328
+ | `useSignAndSendTransaction()` | Sign via wallet, send via Frost's RPC |
329
+
330
+ ---
331
+
332
+ ## Hooks in Detail
333
+
334
+ ### useConnectWallet
335
+
336
+ ```tsx
337
+ const {
338
+ mutate: connect, // Call to trigger connection
339
+ isPending, // True while connecting
340
+ isSuccess, // True after successful connection
341
+ isError, // True if connection failed
342
+ error, // Error object if failed
343
+ data, // ConnectResult if successful
344
+ } = useConnectWallet({
345
+ onSuccess: (result) => {
346
+ console.log("Connected:", result.walletName, result.accountAddress);
347
+ },
348
+ onError: (error) => {
349
+ console.error("Failed:", error.message);
350
+ },
351
+ });
352
+
353
+ // Usage
354
+ connect({ walletName: "Rialo" });
355
+ ```
356
+
357
+ ### useSignMessage
358
+
359
+ ```tsx
360
+ const { mutate: signMessage, isPending } = useSignMessage({
361
+ onSuccess: (result) => {
362
+ // result.signature: Uint8Array
363
+ // result.signedMessage: Uint8Array
364
+ },
365
+ });
366
+
367
+ // Sign a string
368
+ signMessage({ message: "Hello World" });
369
+
370
+ // Sign raw bytes
371
+ signMessage({ message: new Uint8Array([1, 2, 3]) });
372
+ ```
373
+
374
+ ### useSignIn
375
+
376
+ SIWS (Sign-In with Wallet) for authentication:
377
+
378
+ ```tsx
379
+ const { mutate: signIn } = useSignIn({
380
+ onSuccess: async (result) => {
381
+ // Send to your backend for verification
382
+ await fetch("/api/auth", {
383
+ method: "POST",
384
+ body: JSON.stringify({
385
+ signature: Array.from(result.signature),
386
+ message: Array.from(result.signedMessage),
387
+ address: result.account.address,
388
+ }),
389
+ });
390
+ },
391
+ });
392
+
393
+ signIn({
394
+ domain: window.location.host,
395
+ statement: "Sign in to MyApp",
396
+ nonce: crypto.randomUUID(),
397
+ expirationTime: new Date(Date.now() + 3600000).toISOString(),
398
+ });
399
+ ```
400
+
401
+ ### useNativeBalance
402
+
403
+ ```tsx
404
+ const {
405
+ balance, // bigint | undefined - Raw balance in lamports
406
+ formatted, // string | undefined - Human readable (e.g., "1.5")
407
+ isLoading, // boolean
408
+ isFetching, // boolean - True during refetch
409
+ isError, // boolean
410
+ error, // Error | null
411
+ refetch, // () => void
412
+ } = useNativeBalance({
413
+ address: "optional-address", // Defaults to active account
414
+ decimals: 9, // Defaults to 9
415
+ });
416
+ ```
417
+
418
+ ### useSwitchChain
419
+
420
+ ```tsx
421
+ import { getDefaultRialoClientConfig } from "@rialo/frost";
422
+
423
+ const { mutate: switchChain } = useSwitchChain();
424
+
425
+ // Switch to mainnet
426
+ switchChain(getDefaultRialoClientConfig("mainnet"));
427
+
428
+ // Switch to testnet
429
+ switchChain(getDefaultRialoClientConfig("testnet"));
430
+ ```
431
+
432
+ ---
433
+
434
+ ## Components
435
+
436
+ ### ConnectButton
437
+
438
+ A complete, styled connect button with dropdown:
439
+
440
+ ```tsx
441
+ <ConnectButton
442
+ label="Connect Wallet" // Button text when disconnected
443
+ connectingLabel="Connecting..." // Text while connecting
444
+ connectedLabel={(addr, wallet) => // Text when connected
445
+ `${addr.slice(0, 6)}...`
446
+ }
447
+ showDropdown={true} // Show dropdown when connected
448
+ onConnect={(wallet, address) => {}} // Connection callback
449
+ onDisconnect={() => {}} // Disconnect callback
450
+ onError={(error) => {}} // Error callback
451
+ />
452
+ ```
453
+
454
+ ### WalletModal
455
+
456
+ A modal for wallet selection:
457
+
458
+ ```tsx
459
+ const [open, setOpen] = useState(false);
460
+
461
+ <WalletModal
462
+ open={open}
463
+ onClose={() => setOpen(false)}
464
+ onConnect={(walletName, address) => {
465
+ console.log(`Connected to ${walletName}`);
466
+ setOpen(false);
467
+ }}
468
+ onError={(error, walletName) => {
469
+ console.error(`${walletName} failed:`, error);
470
+ }}
471
+ title="Select a Wallet"
472
+ />
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Error Handling
478
+
479
+ Frost provides typed errors for precise handling:
480
+
481
+ ```tsx
482
+ import { isFrostError, type FrostError } from "@rialo/frost";
483
+
484
+ function handleError(error: Error) {
485
+ if (!isFrostError(error)) {
486
+ console.error("Unknown error:", error);
487
+ return;
488
+ }
489
+
490
+ switch (error.code) {
491
+ case "WALLET_NOT_FOUND":
492
+ alert(`Install ${error.walletName} to continue`);
493
+ break;
494
+ case "WALLET_DISCONNECTED":
495
+ alert("Please connect your wallet");
496
+ break;
497
+ case "UNSUPPORTED_CHAIN":
498
+ alert("Switch to a supported network");
499
+ break;
500
+ case "UNSUPPORTED_FEATURE":
501
+ alert("Your wallet doesn't support this feature");
502
+ break;
503
+ case "CONNECTION_FAILED":
504
+ alert("Connection rejected or failed");
505
+ break;
506
+ case "SESSION_EXPIRED":
507
+ alert("Session expired, please reconnect");
508
+ break;
509
+ case "TRANSACTION_FAILED":
510
+ alert(`Transaction failed: ${error.reason}`);
511
+ break;
512
+ case "WALLET_ERROR":
513
+ alert(error.message);
514
+ break;
515
+ }
516
+ }
517
+ ```
518
+
519
+ ### Error Types
520
+
521
+ | Code | Class | When |
522
+ |------|-------|------|
523
+ | `WALLET_NOT_FOUND` | `WalletNotFoundError` | Wallet extension not installed |
524
+ | `WALLET_DISCONNECTED` | `WalletDisconnectedError` | No wallet connected |
525
+ | `UNSUPPORTED_CHAIN` | `UnsupportedChainError` | Wallet doesn't support current chain |
526
+ | `UNSUPPORTED_FEATURE` | `UnsupportedFeatureError` | Missing wallet capability |
527
+ | `CONNECTION_FAILED` | `ConnectionFailedError` | User rejected or wallet error |
528
+ | `SESSION_EXPIRED` | `SessionExpiredError` | Saved session too old |
529
+ | `TRANSACTION_FAILED` | `TransactionFailedError` | Transaction confirmed but execution failed on-chain |
530
+ | `WALLET_ERROR` | `WalletError` | Generic wallet error |
531
+
532
+ ---
533
+
534
+ ## Advanced Usage
535
+
536
+ ### Custom RPC URL
537
+
538
+ ```typescript
539
+ import { createConfig } from "@rialo/frost";
540
+
541
+ const config = createConfig({
542
+ clientConfig: {
543
+ chain: {
544
+ id: "rialo:mainnet",
545
+ name: "Mainnet",
546
+ rpcUrl: "https://my-custom-rpc.example.com",
547
+ },
548
+ transport: { timeout: 30000 },
549
+ },
550
+ });
551
+ ```
552
+
553
+ ### Disable Session Persistence
554
+
555
+ ```typescript
556
+ const config = createConfig({
557
+ clientConfig: getDefaultRialoClientConfig("devnet"),
558
+ storage: null, // Won't remember connected wallet
559
+ });
560
+ ```
561
+
562
+ ### Share TanStack Query Client
563
+
564
+ If your app already uses TanStack Query:
565
+
566
+ ```tsx
567
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
568
+
569
+ const queryClient = new QueryClient();
570
+
571
+ function App() {
572
+ return (
573
+ <QueryClientProvider client={queryClient}>
574
+ <FrostProvider config={frostConfig} queryClient={queryClient}>
575
+ <YourApp />
576
+ </FrostProvider>
577
+ </QueryClientProvider>
578
+ );
579
+ }
580
+ ```
581
+
582
+ ### Direct Config Access
583
+
584
+ ```tsx
585
+ import { useFrostConfig } from "@rialo/frost";
586
+
587
+ function Advanced() {
588
+ const config = useFrostConfig();
589
+
590
+ // Access RPC client
591
+ const client = config.client;
592
+
593
+ // Get chain ID
594
+ const chainId = config.getChainId();
595
+
596
+ // Switch chain imperatively
597
+ config.switchChain(getDefaultRialoClientConfig("mainnet"));
598
+ }
599
+ ```
600
+
601
+ ### Non-React Usage
602
+
603
+ ```typescript
604
+ import {
605
+ createConfig,
606
+ getDefaultRialoClientConfig,
607
+ connect,
608
+ disconnect,
609
+ signTransaction,
610
+ WalletRegistry,
611
+ WalletEventBridge,
612
+ initializeConfig,
613
+ } from "@rialo/frost-core";
614
+
615
+ // Setup
616
+ const config = createConfig({
617
+ clientConfig: getDefaultRialoClientConfig("devnet"),
618
+ });
619
+
620
+ const registry = new WalletRegistry(config);
621
+ const bridge = new WalletEventBridge(config);
622
+ initializeConfig(config, registry, bridge);
623
+
624
+ // Connect
625
+ const { accountAddress } = await connect(config, { walletName: "Rialo" });
626
+
627
+ // Sign
628
+ const { signedTransaction } = await signTransaction(config, { transaction });
629
+
630
+ // Disconnect
631
+ await disconnect(config);
632
+
633
+ // Cleanup
634
+ config.destroy();
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Troubleshooting
640
+
641
+ ### "No wallets found"
642
+
643
+ 1. Install a Rialo wallet extension
644
+ 2. Make sure it's enabled for your site
645
+ 3. Refresh the page
646
+
647
+ ### Connection keeps failing
648
+
649
+ 1. Unlock your wallet
650
+ 2. Check if the wallet supports your network (devnet/mainnet)
651
+ 3. Try disabling and re-enabling the extension
652
+ 4. Clear localStorage and refresh
653
+
654
+ ### Hooks not updating
655
+
656
+ **Wrong:** Creating config inside component
657
+
658
+ ```tsx
659
+ function App() {
660
+ const config = createConfig({ ... }); // ❌ Recreated every render
661
+ return <FrostProvider config={config}>...</FrostProvider>;
662
+ }
663
+ ```
664
+
665
+ **Right:** Creating config outside component
666
+
667
+ ```tsx
668
+ const config = createConfig({ ... }); // ✅ Created once
669
+
670
+ function App() {
671
+ return <FrostProvider config={config}>...</FrostProvider>;
672
+ }
673
+ ```
674
+
675
+ ### "Session expired" on page load
676
+
677
+ This is expected behavior. The default session TTL is 7 days. Users just need to reconnect.
678
+
679
+ ---
680
+
681
+ ## Configuration Options
682
+
683
+ ```typescript
684
+ interface CreateConfigOptions {
685
+ /** RPC client configuration (required) */
686
+ clientConfig: RialoClientConfig;
687
+
688
+ /** Auto-reconnect on page load (default: true) */
689
+ autoConnect?: boolean;
690
+
691
+ /** Session TTL in ms (default: 7 days) */
692
+ sessionTTL?: number;
693
+
694
+ /** Storage key prefix (default: "rialo-frost") */
695
+ storageKey?: string;
696
+
697
+ /** Custom storage (default: localStorage, null to disable) */
698
+ storage?: Storage | null;
699
+ }
700
+ ```
701
+
702
+ ---
703
+
704
+ ## Networks
705
+
706
+ | Network | Chain ID | Use Case |
707
+ |---------|----------|----------|
708
+ | Devnet | `rialo:devnet` | Development (free test tokens) |
709
+ | Testnet | `rialo:testnet` | Pre-production testing |
710
+ | Mainnet | `rialo:mainnet` | Production |
711
+
712
+ ---
713
+
714
+ ## CSS Custom Properties
715
+
716
+ Frost components support theming via CSS custom properties. Set these variables to customize the appearance:
717
+
718
+ ### Colors
719
+
720
+ | Variable | Default | Description |
721
+ |----------|---------|-------------|
722
+ | `--frost-primary` | `#3b82f6` | Primary brand color (buttons, accents) |
723
+ | `--frost-text-color` | `#111827` | Main text color |
724
+ | `--frost-text-secondary` | `#6b7280` | Secondary/muted text |
725
+ | `--frost-border-color` | `#e5e7eb` | Border and divider color |
726
+ | `--frost-danger` | `#ef4444` | Destructive action color (disconnect) |
727
+
728
+ ### Backgrounds
729
+
730
+ | Variable | Default | Description |
731
+ |----------|---------|-------------|
732
+ | `--frost-button-text` | `#ffffff` | Button text color |
733
+ | `--frost-button-connected-bg` | `#f3f4f6` | Connected button background |
734
+ | `--frost-button-connected-text` | `#111827` | Connected button text |
735
+ | `--frost-dropdown-bg` | `#ffffff` | Dropdown menu background |
736
+ | `--frost-hover-bg` | `#f3f4f6` | Hover state background |
737
+ | `--frost-modal-bg` | `#ffffff` | Modal dialog background |
738
+ | `--frost-overlay-bg` | `rgba(0, 0, 0, 0.5)` | Modal overlay background |
739
+ | `--frost-icon-bg` | `#f3f4f6` | Wallet icon background |
740
+
741
+ ### Layout
742
+
743
+ | Variable | Default | Description |
744
+ |----------|---------|-------------|
745
+ | `--frost-border-radius` | `8px` | Default border radius |
746
+ | `--frost-border-radius-sm` | `6px` | Small border radius |
747
+ | `--frost-modal-max-width` | `400px` | Maximum modal width |
748
+ | `--frost-shadow` | `0 4px 16px rgba(0, 0, 0, 0.12)` | Box shadow |
749
+ | `--frost-z-index` | `9999` | Z-index for overlays |
750
+
751
+ ### Example: Dark Theme
752
+
753
+ ```css
754
+ :root {
755
+ --frost-primary: #60a5fa;
756
+ --frost-text-color: #f9fafb;
757
+ --frost-text-secondary: #9ca3af;
758
+ --frost-border-color: #374151;
759
+ --frost-button-text: #ffffff;
760
+ --frost-button-connected-bg: #1f2937;
761
+ --frost-button-connected-text: #f9fafb;
762
+ --frost-dropdown-bg: #1f2937;
763
+ --frost-hover-bg: #374151;
764
+ --frost-modal-bg: #111827;
765
+ --frost-overlay-bg: rgba(0, 0, 0, 0.7);
766
+ --frost-icon-bg: #374151;
767
+ }
768
+ ```
769
+
770
+ ### Example: Custom Brand Colors
771
+
772
+ ```css
773
+ :root {
774
+ --frost-primary: #8b5cf6; /* Purple primary */
775
+ --frost-border-radius: 12px; /* More rounded */
776
+ --frost-shadow: 0 8px 32px rgba(139, 92, 246, 0.2); /* Purple tinted shadow */
777
+ }
778
+ ```