@silentswap/ui-kit 0.0.41

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 (39) hide show
  1. package/dist/components/AssetTile.d.ts +10 -0
  2. package/dist/components/AssetTile.js +18 -0
  3. package/dist/components/Button.d.ts +15 -0
  4. package/dist/components/Button.js +26 -0
  5. package/dist/components/Card.d.ts +14 -0
  6. package/dist/components/Card.js +19 -0
  7. package/dist/components/Input.d.ts +19 -0
  8. package/dist/components/Input.js +23 -0
  9. package/dist/components/PopularTokensArea.d.ts +8 -0
  10. package/dist/components/PopularTokensArea.js +34 -0
  11. package/dist/components/ProgressBar.d.ts +17 -0
  12. package/dist/components/ProgressBar.js +30 -0
  13. package/dist/components/Select.d.ts +12 -0
  14. package/dist/components/Select.js +7 -0
  15. package/dist/components/Warning.d.ts +15 -0
  16. package/dist/components/Warning.js +23 -0
  17. package/dist/constants.d.ts +2 -0
  18. package/dist/constants.js +4 -0
  19. package/dist/data.d.ts +7 -0
  20. package/dist/data.js +14 -0
  21. package/dist/index.d.ts +17 -0
  22. package/dist/index.js +13 -0
  23. package/dist/styles.css +399 -0
  24. package/dist/types.d.ts +15 -0
  25. package/dist/types.js +1 -0
  26. package/package.json +52 -0
  27. package/src/components/AssetTile.tsx +69 -0
  28. package/src/components/Button.tsx +51 -0
  29. package/src/components/Card.tsx +49 -0
  30. package/src/components/Input.tsx +72 -0
  31. package/src/components/PopularTokensArea.tsx +78 -0
  32. package/src/components/ProgressBar.tsx +67 -0
  33. package/src/components/Select.tsx +48 -0
  34. package/src/components/Warning.tsx +54 -0
  35. package/src/constants.ts +5 -0
  36. package/src/data.ts +19 -0
  37. package/src/index.ts +26 -0
  38. package/src/styles.css +399 -0
  39. package/src/types.ts +20 -0
@@ -0,0 +1,399 @@
1
+ @import "tailwindcss";
2
+
3
+ /* Note: @theme and @utility are Tailwind CSS 4 directives.
4
+ CSS linter warnings for these are expected and can be safely ignored. */
5
+
6
+ @theme {
7
+ /* Colors */
8
+ --color-dim-dark: #0F0F0F;
9
+ --color-dim-med: #242424;
10
+ --color-dim-light: #4D4D4D;
11
+ --color-dim-bright: #4D4D4D;
12
+
13
+ --color-orange: #FC6226;
14
+ --color-yellow: #F0B90B;
15
+ --color-yellow-alt: #FFD600;
16
+ --color-green: #5FED9B;
17
+ --color-purple: #9E72FF;
18
+ --color-purple-vibrant: #6A27FF;
19
+
20
+ --color-border-bright: #BABCBD;
21
+ --color-border-dark: #000000;
22
+ --color-text-bright: #FFFFFF;
23
+ --color-text-subtle: rgba(0, 0, 0, 0.4);
24
+ --color-text-dim: #999999;
25
+ --color-gray-dark: #545454;
26
+
27
+ /* Font Families */
28
+ --font-family-orbitron: Orbitron, sans-serif;
29
+ --font-family-chakra: Chakra Petch, sans-serif;
30
+ --font-family-poppins: Poppins, sans-serif;
31
+ --font-family-roboto: Roboto, sans-serif;
32
+ --font-family-fira-sans: Fira Sans, serif;
33
+ --font-family-fira-mono: Fira Mono, serif;
34
+ --font-family-fira-code: Fira Code, serif;
35
+ --font-family-tomorrow: Tomorrow, sans-serif;
36
+ --font-family-thasadith: Thasadith, sans-serif;
37
+ --font-family-poiret-one: Poiret One, sans-serif;
38
+ --font-family-epilogue: Epilogue, sans-serif;
39
+ --font-family-pavanam: Pavanam, sans-serif;
40
+ --font-family-titillium-web: Titillium Web, sans-serif;
41
+ --font-family-sofia-sans-extra-condensed: Sofia Sans Extra Condensed, sans-serif;
42
+ --font-family-red-rose: Red Rose, serif;
43
+ --font-family-finlandica: Finlandica, sans-serif;
44
+ --font-family-ramabhadra: Ramabhadra, sans-serif;
45
+ --font-family-lunasima: Lunasima, sans-serif;
46
+ --font-family-oswald: Oswald, sans-serif;
47
+ --font-family-merriweather-sans: Merriweather Sans, sans-serif;
48
+ --font-family-commissioner: Commissioner, sans-serif;
49
+ --font-family-nunito-sans: Nunito Sans, sans-serif;
50
+ --font-family-noto-sans: Noto Sans, sans-serif;
51
+ --font-family-roboto-flex: Roboto Flex, sans-serif;
52
+ --font-family-kantumruy-pro: Kantumruy Pro, sans-serif;
53
+ --font-family-dhurjati: Dhurjati, sans-serif;
54
+ --font-family-nineteen-ninety-seven: Nineteen Ninety Seven, sans-serif;
55
+ --font-family-a-goblin-appears: A Goblin Appears!, sans-serif;
56
+
57
+ /* Easing Functions */
58
+ --ease-ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
59
+ --ease-ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
60
+ --ease-ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
61
+ --ease-ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
62
+ --ease-ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
63
+ --ease-ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
64
+
65
+ --ease-ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
66
+ --ease-ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
67
+ --ease-ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
68
+ --ease-ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
69
+ --ease-ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
70
+ --ease-ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
71
+ --ease-ease-out-quick: cubic-bezier(0.00, 1.00, 0.0, 1.0);
72
+
73
+ --ease-ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
74
+ --ease-ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
75
+ --ease-ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
76
+ --ease-ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
77
+ --ease-ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
78
+ --ease-ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
79
+
80
+ /* Box Shadows */
81
+ --shadow-shadowed-box: 4px 8px 16px black;
82
+
83
+ /* Animations */
84
+ --animate-subtle-throb: subtle-throb 30s infinite;
85
+ --animate-spin: spin 3s linear infinite;
86
+ --animate-spin-quicker: spin 2s linear infinite;
87
+ --animate-flash: flash 0.18s step-start 3;
88
+ --animate-pulse: pulse 2s linear infinite;
89
+ --animate-trill: trill 0.1s linear infinite;
90
+ }
91
+
92
+ /* SilentSwap UI Kit Custom Styles */
93
+
94
+ /* CSS Variables for component use */
95
+ :root {
96
+ --color-dim-dark: #0F0F0F;
97
+ --color-dim-med: #242424;
98
+ --color-dim-light: #4D4D4D;
99
+ --color-orange: #FC6226;
100
+ --color-yellow: #F0B90B;
101
+ --color-green: #5FED9B;
102
+ --color-purple: #9E72FF;
103
+ --color-purple-vibrant: #6A27FF;
104
+ --color-white: #FFFFFF;
105
+ --color-black: #000000;
106
+ --color-border-bright: #BABCBD;
107
+ --color-border-dark: #000000;
108
+ --color-text-bright: #FFFFFF;
109
+ --color-text-subtle: rgba(0, 0, 0, 0.4);
110
+ --color-text-dim: #999999;
111
+ --color-gray-dark: #545454;
112
+ --color-yellow-bright: #FFD600;
113
+
114
+ /* Easing functions */
115
+ --ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
116
+ --ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
117
+ --ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
118
+ --ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
119
+ --ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
120
+ --ease-out-quick: cubic-bezier(0.00, 1.00, 0.0, 1.0);
121
+ --ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
122
+ }
123
+
124
+ /* Keyframe Animations */
125
+ @keyframes subtle-throb {
126
+ 0% { opacity: 0.2; }
127
+ 50% { opacity: 0.35; }
128
+ 100% { opacity: 0.2; }
129
+ }
130
+
131
+ @keyframes spin {
132
+ 0% { transform: rotate(0deg); }
133
+ 100% { transform: rotate(360deg); }
134
+ }
135
+
136
+ @keyframes flash {
137
+ 0% { background-color: rgba(255, 255, 255, 0.9); }
138
+ 50% { background-color: rgba(0, 0, 0, 0.9); }
139
+ 100% { background-color: rgba(255, 255, 255, 0.9); }
140
+ }
141
+
142
+ @keyframes pulse {
143
+ 0% { opacity: 1; }
144
+ 50% { opacity: 0.4; }
145
+ 100% { opacity: 1; }
146
+ }
147
+
148
+ @keyframes trill {
149
+ 0%, 100% {
150
+ opacity: 0.6;
151
+ }
152
+ 50% {
153
+ opacity: 1;
154
+ }
155
+ }
156
+
157
+ /* Component-specific styles that extend Tailwind */
158
+ @layer components {
159
+ /* Button variants */
160
+ .call-to-action {
161
+ width: 100%;
162
+ padding: 1.25rem 1rem;
163
+ background-color: var(--color-yellow);
164
+ font-family: var(--font-family-orbitron);
165
+ font-weight: 600;
166
+ font-size: 1.125rem;
167
+ color: var(--color-black);
168
+ border-radius: 0.5rem;
169
+ transition: background-color 150ms;
170
+ }
171
+
172
+ .call-to-action:hover {
173
+ background-color: var(--color-yellow-bright);
174
+ }
175
+
176
+ .call-to-action:focus {
177
+ outline: none;
178
+ box-shadow: 0 0 0 2px var(--color-yellow), 0 0 0 4px transparent;
179
+ }
180
+
181
+ .call-to-action:disabled {
182
+ opacity: 0.5;
183
+ cursor: not-allowed;
184
+ }
185
+
186
+ .button-link {
187
+ cursor: pointer;
188
+ text-decoration: none;
189
+ transition: opacity 200ms linear;
190
+ }
191
+
192
+ .button-link:hover {
193
+ opacity: 0.8;
194
+ }
195
+
196
+ /* Input variants */
197
+ .digital-input {
198
+ background-color: var(--color-black);
199
+ border: 0;
200
+ border-radius: 0.5rem;
201
+ font-size: 1.5rem;
202
+ font-weight: 700;
203
+ font-family: var(--font-family-chakra);
204
+ color: var(--color-white);
205
+ padding: 0.25rem 0.75rem;
206
+ width: 100%;
207
+ box-sizing: border-box;
208
+ transition: all 150ms;
209
+ }
210
+
211
+ .digital-input:focus {
212
+ outline: none;
213
+ box-shadow: 0 0 0 2px var(--color-yellow), 0 0 0 4px transparent;
214
+ }
215
+
216
+ .digital-input:disabled {
217
+ opacity: 0.5;
218
+ cursor: not-allowed;
219
+ }
220
+
221
+ .amount-input {
222
+ background: var(--color-black);
223
+ border-radius: 8px;
224
+ width: 100%;
225
+ padding: 0;
226
+ }
227
+
228
+ .amount-input input {
229
+ font-size: 28px;
230
+ font-weight: 700;
231
+ color: var(--color-text-bright);
232
+ padding: 4px 12px 0px 12px;
233
+ width: 100%;
234
+ box-sizing: border-box;
235
+ border: none;
236
+ outline: none;
237
+ background: transparent;
238
+ border-radius: 8px 8px 0 0;
239
+ }
240
+
241
+ .amount-input input:disabled {
242
+ opacity: 0.6;
243
+ }
244
+
245
+ .amount-input .info {
246
+ font-size: 11px;
247
+ padding: 0px 12px 9px 14px;
248
+ display: flex;
249
+ flex-direction: row;
250
+ justify-content: space-between;
251
+ width: 100%;
252
+ font-weight: 400;
253
+ align-items: center;
254
+ }
255
+
256
+ /* Card/Section variants */
257
+ .card-section {
258
+ display: flex;
259
+ flex-direction: column;
260
+ gap: 0px;
261
+ background: var(--color-dim-med);
262
+ padding: 12px;
263
+ width: 90vw;
264
+ box-sizing: border-box;
265
+ margin-left: auto;
266
+ margin-right: auto;
267
+ max-width: 600px;
268
+ z-index: 6;
269
+ }
270
+
271
+ .plate-row {
272
+ display: flex;
273
+ flex-direction: row;
274
+ justify-content: space-between;
275
+ align-items: center;
276
+ gap: 12px;
277
+ height: 68px;
278
+ transition: height 500ms linear;
279
+ padding: 13px 12px;
280
+ }
281
+
282
+ /* Warning/Notice */
283
+ .input-notice {
284
+ position: absolute;
285
+ top: 0;
286
+ right: 0;
287
+ font-size: 10px;
288
+ background: rgba(0,0,0,0.8);
289
+ border-radius: 4px;
290
+ display: flex;
291
+ align-items: center;
292
+ gap: 4px;
293
+ padding: 0 6px;
294
+ margin-top: 8px;
295
+ margin-right: 8px;
296
+ }
297
+
298
+ .input-notice.warning {
299
+ color: rgba(254, 215, 3);
300
+ }
301
+
302
+ .input-notice .dot {
303
+ display: inline-block;
304
+ width: 5px;
305
+ height: 6px;
306
+ background-color: rgba(254, 215, 3);
307
+ border-radius: 6px;
308
+ }
309
+
310
+ /* Progress bar */
311
+ .progress-bar {
312
+ height: 6px;
313
+ width: 100%;
314
+ background-color: var(--color-gray-dark);
315
+ transition: height 500ms linear;
316
+ }
317
+
318
+ .progress-bar .fill {
319
+ transition: width 490ms linear;
320
+ background-color: var(--color-green);
321
+ height: 100%;
322
+ }
323
+
324
+ /* Token items */
325
+ .token-item {
326
+ display: flex;
327
+ flex-direction: column;
328
+ align-items: center;
329
+ gap: 0.5rem;
330
+ background-color: var(--color-dim-med);
331
+ border-radius: 0.5rem;
332
+ padding: 0.5rem;
333
+ font-size: 0.75rem;
334
+ cursor: pointer;
335
+ transition: background-color 150ms;
336
+ }
337
+
338
+ .token-item:hover {
339
+ background-color: var(--color-dim-light);
340
+ }
341
+
342
+ .token-label {
343
+ color: var(--color-text-dim);
344
+ text-align: center;
345
+ }
346
+
347
+ .popular-tokens-title {
348
+ font-family: var(--font-family-orbitron);
349
+ font-weight: 700;
350
+ font-size: 1.125rem;
351
+ color: var(--color-text-bright);
352
+ margin-bottom: 0.75rem;
353
+ }
354
+ }
355
+
356
+ /* Utility classes */
357
+ /* stylelint-disable-next-line at-rule-no-unknown */
358
+ @utility text-shadow {
359
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
360
+ }
361
+
362
+ /* stylelint-disable-next-line at-rule-no-unknown */
363
+ @utility shadowed-box {
364
+ box-shadow: 4px 8px 16px black;
365
+ }
366
+
367
+ /* stylelint-disable-next-line at-rule-no-unknown */
368
+ @utility pixel-corners {
369
+ clip-path: polygon(
370
+ 0px calc(100% - 12px),
371
+ 3px calc(100% - 12px),
372
+ 3px calc(100% - 6px),
373
+ 6px calc(100% - 6px),
374
+ 6px calc(100% - 3px),
375
+ 12px calc(100% - 3px),
376
+ 12px 100%,
377
+ calc(100% - 12px) 100%,
378
+ calc(100% - 12px) calc(100% - 3px),
379
+ calc(100% - 6px) calc(100% - 3px),
380
+ calc(100% - 6px) calc(100% - 6px),
381
+ calc(100% - 3px) calc(100% - 6px),
382
+ calc(100% - 3px) calc(100% - 12px),
383
+ 100% calc(100% - 12px),
384
+ 100% 12px,
385
+ calc(100% - 3px) 12px,
386
+ calc(100% - 3px) 6px,
387
+ calc(100% - 6px) 6px,
388
+ calc(100% - 6px) 3px,
389
+ calc(100% - 12px) 3px,
390
+ calc(100% - 12px) 0px,
391
+ 12px 0px,
392
+ 12px 3px,
393
+ 6px 3px,
394
+ 6px 6px,
395
+ 3px 6px,
396
+ 3px 12px,
397
+ 0px 12px
398
+ );
399
+ }
@@ -0,0 +1,15 @@
1
+ export type Asset = {
2
+ caip19: string;
3
+ caip2?: string;
4
+ symbol?: string;
5
+ name?: string;
6
+ coingeckoId?: string;
7
+ ext?: string;
8
+ gradient?: string[];
9
+ style?: string;
10
+ precision?: number;
11
+ };
12
+ export type ContactId = string;
13
+ export type WeakCaip19 = string;
14
+ export type WeakCaip2 = string;
15
+ export type Decimal = string | number;
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@silentswap/ui-kit",
3
+ "type": "module",
4
+ "version": "0.0.41",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "src"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc && cp src/styles.css dist/styles.css",
14
+ "build:css": "cp src/styles.css dist/styles.css",
15
+ "clean": "rm -rf dist",
16
+ "dev": "tsc --watch",
17
+ "dev:css": "cp src/styles.css dist/styles.css",
18
+ "lint": "eslint src --ext .ts,.tsx",
19
+ "lint:fix": "eslint src --ext .ts,.tsx --fix"
20
+ },
21
+ "peerDependencies": {
22
+ "react": ">=16.8.0",
23
+ "react-dom": ">=16.8.0"
24
+ },
25
+ "dependencies": {
26
+ "@silentswap/sdk": "workspace:*",
27
+ "bignumber.js": "9.3.1"
28
+ },
29
+ "devDependencies": {
30
+ "@eslint/js": "9.38.0",
31
+ "@stylistic/eslint-plugin": "5.5.0",
32
+ "@tailwindcss/postcss": "^4.1.17",
33
+ "@tsconfig/node24": "24.0.1",
34
+ "@types/node": "24.9.1",
35
+ "@types/react": "18.3.12",
36
+ "@types/react-dom": "19.2.3",
37
+ "@types/web": "0.0.283",
38
+ "@typescript-eslint/parser": "8.46.2",
39
+ "autoprefixer": "^10.4.20",
40
+ "eslint": "9.38.0",
41
+ "eslint-import-resolver-typescript": "4.4.4",
42
+ "eslint-plugin-import-x": "4.16.1",
43
+ "eslint-plugin-react": "7.37.2",
44
+ "eslint-plugin-react-hooks": "5.0.0",
45
+ "postcss": "^8.5.0",
46
+ "postcss-cli": "^11.0.0",
47
+ "react": "19.2.0",
48
+ "tailwindcss": "^4.1.17",
49
+ "typescript": "5.9.3",
50
+ "typescript-eslint": "8.46.2"
51
+ }
52
+ }
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import { asset_get, chain_get } from '../data';
4
+
5
+ export interface AssetTileProps {
6
+ caip19: string;
7
+ onClick?: () => void;
8
+ className?: string;
9
+ children?: ReactNode;
10
+ size?: 'small' | 'medium' | 'large';
11
+ }
12
+
13
+ export const AssetTile: React.FC<AssetTileProps> = ({
14
+ caip19,
15
+ onClick,
16
+ className = '',
17
+ children,
18
+ size = 'medium'
19
+ }) => {
20
+ const asset = asset_get(caip19);
21
+ const chain = asset?.caip2 ? chain_get(asset.caip2) : undefined;
22
+
23
+ const sizeClasses = {
24
+ small: 'w-8 h-8',
25
+ medium: 'w-12 h-12',
26
+ large: 'w-16 h-16'
27
+ };
28
+
29
+ if (!asset) {
30
+ return (
31
+ <div className={`flex items-center justify-center bg-gray-600 rounded-full ${sizeClasses[size]} ${className}`}>
32
+ <span className="text-white text-xs">?</span>
33
+ </div>
34
+ );
35
+ }
36
+
37
+ return (
38
+ <div
39
+ className={`relative cursor-pointer ${className}`}
40
+ onClick={onClick}
41
+ >
42
+ <div
43
+ className={`flex items-center justify-center rounded-full ${sizeClasses[size]}`}
44
+ style={{
45
+ background: asset.gradient ? `linear-gradient(135deg, ${asset.gradient.map((g: string) => `#${g}`).join(', ')})` : 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
46
+ }}
47
+ >
48
+ <img
49
+ src={`/images/tokens/${asset.coingeckoId}.${asset.ext || 'svg'}`}
50
+ alt={asset.symbol}
51
+ className="w-3/4 h-3/4 object-contain"
52
+ style={asset.style ? JSON.parse(asset.style) as React.CSSProperties : {}}
53
+ />
54
+ </div>
55
+
56
+ {chain && (
57
+ <div className="absolute -bottom-1 -right-1 w-4 h-4 bg-black rounded-full flex items-center justify-center">
58
+ <img
59
+ src={`/images/chains/${chain.coingeckoPlatformId}.${chain.ext || 'svg'}`}
60
+ alt={chain.name}
61
+ className="w-3 h-3 rounded-full object-cover"
62
+ />
63
+ </div>
64
+ )}
65
+
66
+ {children}
67
+ </div>
68
+ );
69
+ };
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
3
+
4
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
5
+ /** Visual variant of the button */
6
+ variant?: 'primary' | 'secondary' | 'call-to-action' | 'link' | 'purple' | 'green';
7
+ /** Size of the button */
8
+ size?: 'small' | 'medium' | 'large';
9
+ /** Button content */
10
+ children: ReactNode;
11
+ }
12
+
13
+ /**
14
+ * Simple button component with different variants
15
+ * Based on SilentSwap Form.svelte and Plate.svelte styles
16
+ */
17
+ export const Button: React.FC<ButtonProps> = ({
18
+ variant = 'primary',
19
+ size = 'medium',
20
+ className = '',
21
+ children,
22
+ ...props
23
+ }) => {
24
+ const baseClasses = 'cursor-pointer select-none transition-all focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg border-none';
25
+
26
+ const variantClasses = {
27
+ primary: 'bg-[var(--color-yellow)] hover:opacity-80 text-black font-semibold focus:ring-[var(--color-yellow)]',
28
+ secondary: 'bg-[var(--color-dim-light)] hover:opacity-80 text-white focus:ring-[var(--color-dim-light)]',
29
+ 'call-to-action': 'call-to-action', // Uses CSS class from styles.css
30
+ link: 'button-link bg-transparent text-[var(--color-yellow)] p-0 hover:opacity-80',
31
+ purple: 'bg-[var(--color-purple)] hover:opacity-80 text-white focus:ring-[var(--color-purple)]',
32
+ green: 'bg-[var(--color-green)] hover:opacity-80 text-black font-semibold focus:ring-[var(--color-green)]',
33
+ };
34
+
35
+ const sizeClasses = {
36
+ small: 'px-2 py-1 text-xs',
37
+ medium: 'px-4 py-2 text-sm',
38
+ large: 'px-6 py-3 text-base',
39
+ };
40
+
41
+ // Don't apply size classes to link variant
42
+ const applySizeClasses = variant !== 'link' && variant !== 'call-to-action';
43
+
44
+ const classes = `${baseClasses} ${variantClasses[variant]} ${applySizeClasses ? sizeClasses[size] : ''} ${className}`.trim();
45
+
46
+ return (
47
+ <button className={classes} {...props}>
48
+ {children}
49
+ </button>
50
+ );
51
+ };
@@ -0,0 +1,49 @@
1
+ import React, { forwardRef } from 'react';
2
+ import type { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ export interface CardProps extends HTMLAttributes<HTMLDivElement> {
5
+ children: ReactNode;
6
+ /** Visual variant of the card */
7
+ variant?: 'default' | 'section' | 'elevated' | 'dark';
8
+ /** Whether to center the card */
9
+ centered?: boolean;
10
+ }
11
+
12
+ /**
13
+ * Simple card component with different variants
14
+ * Based on SilentSwap Form.svelte and Plate.svelte styles
15
+ */
16
+ export const Card = forwardRef<HTMLDivElement, CardProps>(({
17
+ children,
18
+ variant = 'default',
19
+ centered = false,
20
+ className = '',
21
+ style,
22
+ ...props
23
+ }, ref) => {
24
+ const baseClasses = 'rounded-lg';
25
+
26
+ const variantClasses = {
27
+ default: 'bg-[var(--color-dim-med)] p-4',
28
+ section: 'card-section', // Uses CSS class from styles.css
29
+ elevated: 'bg-[var(--color-dim-med)] p-4 shadowed-box',
30
+ dark: 'bg-[var(--color-black)] p-4',
31
+ };
32
+
33
+ const centeredClasses = centered ? 'mx-auto' : '';
34
+
35
+ const classes = `${baseClasses} ${variantClasses[variant]} ${centeredClasses} ${className}`.trim();
36
+
37
+ return (
38
+ <div
39
+ ref={ref}
40
+ className={classes}
41
+ style={style}
42
+ {...props}
43
+ >
44
+ {children}
45
+ </div>
46
+ );
47
+ });
48
+
49
+ Card.displayName = 'Card';