@rhinestone/deposit-modal 0.3.0-alpha.11 → 0.3.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -284,6 +284,11 @@
284
284
  /* Cap the modal at 600px (or viewport-40 on tiny screens) so long lists
285
285
  scroll inside instead of growing the modal. */
286
286
  max-height: min(600px, calc(100vh - 40px));
287
+ /* Explicit 0 (not the `auto` default) so the min-height transition
288
+ below has a definite starting value when :has() rules later raise
289
+ it to the iframe-mounted height. Transitions from `auto` are
290
+ unreliable in browsers and tend to apply instantly. */
291
+ min-height: 0;
287
292
  display: flex;
288
293
  flex-direction: column;
289
294
  margin: 20px;
@@ -292,7 +297,18 @@
292
297
  border: 1px solid var(--rs-border);
293
298
  border-radius: var(--rs-radius-lg);
294
299
  transform: scale(0.95);
295
- transition: transform 0.2s ease;
300
+ transition:
301
+ transform 0.2s ease,
302
+ /* Smooth resize when the user moves between steps with different
303
+ widths (e.g. ConnectStep 400 → Connect-via-Swapped 469) instead of
304
+ a hard pop. */
305
+ max-width 240ms cubic-bezier(0.22, 1, 0.36, 1),
306
+ /* Same for height: when a flow starts that pre-reserves the iframe
307
+ size (via the min-height :has() rules below), grow smoothly from
308
+ the previous step's height instead of jumping. Min-height is on
309
+ modal-content rather than modal-body so the transitioned property
310
+ lives on an element that survives step swaps. */
311
+ min-height 280ms cubic-bezier(0.22, 1, 0.36, 1);
296
312
  }
297
313
 
298
314
  .rs-modal-content[data-theme="dark"] {
@@ -1300,6 +1316,11 @@
1300
1316
  justify-content: center;
1301
1317
  gap: 4px;
1302
1318
  width: 100%;
1319
+ /* `border-box` so the 16px side padding counts INTO the 100% width
1320
+ instead of adding 32px on top of it (which otherwise pushes the
1321
+ footer past containers with their own horizontal padding — e.g.
1322
+ the tracker — and paints a phantom horizontal scrollbar). */
1323
+ box-sizing: border-box;
1303
1324
  padding: 0 16px 16px;
1304
1325
  font-size: 11px;
1305
1326
  font-weight: 500;
@@ -1660,6 +1681,21 @@
1660
1681
  manages the vertical rhythm between body slot and powered-by. */
1661
1682
  padding: 0;
1662
1683
  gap: 16px;
1684
+ /* Each screen fades in on mount so step-to-step transitions feel
1685
+ progressive instead of a hard swap. Short enough that the user
1686
+ doesn't perceive a delay; long enough to look intentional. */
1687
+ animation: rs-screen-enter 180ms ease-out;
1688
+ }
1689
+
1690
+ @keyframes rs-screen-enter {
1691
+ from {
1692
+ opacity: 0;
1693
+ transform: translateY(4px);
1694
+ }
1695
+ to {
1696
+ opacity: 1;
1697
+ transform: translateY(0);
1698
+ }
1663
1699
  }
1664
1700
 
1665
1701
  /* Body slot — direct child of .rs-screen. Always uses a 16px internal
@@ -2782,7 +2818,7 @@
2782
2818
  .rs-withdraw-dropdown-arrow {
2783
2819
  width: 24px;
2784
2820
  height: 24px;
2785
- color: var(--rs-icon-secondary);
2821
+ color: #9f9fa9;
2786
2822
  transition: transform 0.15s;
2787
2823
  }
2788
2824
 
@@ -3085,7 +3121,7 @@
3085
3121
  display: flex;
3086
3122
  align-items: center;
3087
3123
  gap: 8px;
3088
- padding-top: 12px;
3124
+ padding-top: 8px;
3089
3125
  font-size: 12px;
3090
3126
  color: var(--rs-muted);
3091
3127
  }
@@ -3205,7 +3241,7 @@
3205
3241
  .rs-deposit-address-dropdown-chevron {
3206
3242
  width: 24px;
3207
3243
  height: 24px;
3208
- color: var(--rs-icon-secondary);
3244
+ color: #9f9fa9;
3209
3245
  margin-left: auto;
3210
3246
  flex-shrink: 0;
3211
3247
  transition: transform 0.15s ease;
@@ -3226,10 +3262,10 @@
3226
3262
  border: 1px solid var(--rs-surface-subtle);
3227
3263
  border-radius: var(--rs-radius-sm);
3228
3264
  box-shadow: var(--rs-shadow-dropdown);
3229
- padding: 12px;
3265
+ padding: 8px;
3230
3266
  display: flex;
3231
3267
  flex-direction: column;
3232
- gap: 16px;
3268
+ gap: 0;
3233
3269
  max-height: 240px;
3234
3270
  overflow-y: auto;
3235
3271
  overscroll-behavior: none;
@@ -3241,8 +3277,8 @@
3241
3277
  align-items: center;
3242
3278
  gap: 6px;
3243
3279
  width: 100%;
3244
- padding: 0;
3245
- border-radius: calc(var(--rs-radius-sm) - 2px);
3280
+ padding: 8px 4px;
3281
+ border-radius: 4px;
3246
3282
  border: none;
3247
3283
  background: transparent;
3248
3284
  color: var(--rs-foreground);
@@ -3251,12 +3287,12 @@
3251
3287
  line-height: normal;
3252
3288
  cursor: pointer;
3253
3289
  text-align: left;
3254
- transition: opacity 0.1s;
3290
+ transition: background-color 0.1s;
3255
3291
  font-family: inherit;
3256
3292
  }
3257
3293
 
3258
3294
  .rs-deposit-address-dropdown-item:hover {
3259
- opacity: 0.7;
3295
+ background: var(--rs-surface);
3260
3296
  }
3261
3297
 
3262
3298
  .rs-deposit-address-dropdown-item--active {
@@ -3323,6 +3359,51 @@
3323
3359
  word-break: break-all;
3324
3360
  }
3325
3361
 
3362
+ /* Visually hidden but exposed to assistive tech — used by the skeleton's
3363
+ "Preparing…" status, whose visual placeholders are all aria-hidden. */
3364
+ .rs-sr-only {
3365
+ position: absolute;
3366
+ width: 1px;
3367
+ height: 1px;
3368
+ padding: 0;
3369
+ margin: -1px;
3370
+ overflow: hidden;
3371
+ clip: rect(0, 0, 0, 0);
3372
+ white-space: nowrap;
3373
+ border: 0;
3374
+ }
3375
+
3376
+ /* Loading placeholders for DepositAddressSkeleton — grey blocks that pulse
3377
+ while the smart account / deposit address are being prepared. Uses
3378
+ --color-gray4 (zinc-200 light / zinc-800 dark) so it reads on the well in
3379
+ both themes. */
3380
+ .rs-skeleton {
3381
+ background: var(--color-gray4);
3382
+ border-radius: 8px;
3383
+ animation: rs-skeleton-pulse 1.6s ease-in-out infinite;
3384
+ }
3385
+
3386
+ .rs-skeleton-qr {
3387
+ width: 200px;
3388
+ height: 200px;
3389
+ }
3390
+
3391
+ .rs-skeleton-address {
3392
+ height: 12px;
3393
+ margin: 0 16px 12px;
3394
+ border-radius: 8px;
3395
+ }
3396
+
3397
+ @keyframes rs-skeleton-pulse {
3398
+ 0%,
3399
+ 100% {
3400
+ opacity: 1;
3401
+ }
3402
+ 50% {
3403
+ opacity: 0.5;
3404
+ }
3405
+ }
3406
+
3326
3407
  .rs-deposit-address-copy {
3327
3408
  display: flex;
3328
3409
  align-items: center;
@@ -3990,8 +4071,8 @@
3990
4071
  width: 100%;
3991
4072
  display: flex;
3992
4073
  flex-direction: column;
3993
- gap: 16px;
3994
- padding: 16px 12px;
4074
+ gap: 0;
4075
+ padding: 12px 8px;
3995
4076
  background: var(--rs-surface-subtle);
3996
4077
  border: none;
3997
4078
  border-radius: 8px;
@@ -4106,12 +4187,38 @@
4106
4187
  white-space: nowrap;
4107
4188
  }
4108
4189
 
4190
+ /* Collapsing wrapper that animates the detail block between closed/open. */
4191
+ .rs-history-card-panel {
4192
+ display: grid;
4193
+ grid-template-rows: 0fr;
4194
+ transition: grid-template-rows 0.25s ease;
4195
+ }
4196
+
4197
+ .rs-history-card--expanded .rs-history-card-panel {
4198
+ grid-template-rows: 1fr;
4199
+ }
4200
+
4201
+ .rs-history-card-panel-inner {
4202
+ overflow: hidden;
4203
+ min-height: 0;
4204
+ /* Keep the collapsed panel (and its tx link) out of the tab order.
4205
+ visibility stays "visible" through the collapse so the content is
4206
+ present during the height animation, then flips hidden at the end. */
4207
+ visibility: hidden;
4208
+ transition: visibility 0.25s;
4209
+ }
4210
+
4211
+ .rs-history-card--expanded .rs-history-card-panel-inner {
4212
+ visibility: visible;
4213
+ }
4214
+
4109
4215
  /* Expanded detail block under the row. */
4110
4216
  .rs-history-card-details {
4111
4217
  display: flex;
4112
4218
  flex-direction: column;
4113
4219
  gap: 8px;
4114
4220
  width: 100%;
4221
+ padding-top: 16px;
4115
4222
  }
4116
4223
 
4117
4224
  .rs-history-card-link {
@@ -4601,3 +4708,431 @@
4601
4708
  border: 1px solid var(--rs-border);
4602
4709
  background-color: var(--rs-background);
4603
4710
  }
4711
+
4712
+ /* =============================================================================
4713
+ Fiat On-Ramp (Swapped iframe flow)
4714
+ ============================================================================= */
4715
+
4716
+ /* `.rs-fiat-onramp` is a marker class on the .rs-screen root so the
4717
+ modal-content `:has()` rules below can size correctly. Layout (padding,
4718
+ gap, flex direction) comes from .rs-screen itself, identical to
4719
+ ConnectStep — keeps the top of the page visually unchanged across the
4720
+ payment-method picker → Swapped iframe transition. */
4721
+
4722
+ .rs-fiat-onramp-banner {
4723
+ display: flex;
4724
+ flex-direction: column;
4725
+ gap: 2px;
4726
+ padding: 8px 12px;
4727
+ border-radius: var(--rs-radius-sm);
4728
+ font-size: 12px;
4729
+ line-height: 1.35;
4730
+ border: 1px solid var(--rs-border);
4731
+ }
4732
+
4733
+ .rs-fiat-onramp-banner--info {
4734
+ background: var(--rs-background-secondary);
4735
+ color: var(--rs-foreground);
4736
+ }
4737
+
4738
+ .rs-fiat-onramp-banner--success {
4739
+ background: rgba(34, 197, 94, 0.08);
4740
+ border-color: rgba(34, 197, 94, 0.35);
4741
+ color: var(--rs-foreground);
4742
+ }
4743
+
4744
+ .rs-fiat-onramp-banner--warning {
4745
+ background: rgba(234, 179, 8, 0.08);
4746
+ border-color: rgba(234, 179, 8, 0.4);
4747
+ color: var(--rs-foreground);
4748
+ }
4749
+
4750
+ .rs-fiat-onramp-banner-title {
4751
+ font-weight: 600;
4752
+ }
4753
+
4754
+ .rs-fiat-onramp-banner-detail {
4755
+ color: var(--rs-muted);
4756
+ }
4757
+
4758
+ /* Iframe wrap sized to Swapped's documented dimensions — 400×482 for the
4759
+ fiat on-ramp, 445×585 for Connect (overrides below). Centered horizontally
4760
+ inside the modal so the step body can have header padding. */
4761
+ .rs-fiat-onramp-iframe-wrap {
4762
+ position: relative;
4763
+ width: 100%;
4764
+ max-width: 400px;
4765
+ min-height: 482px;
4766
+ margin: 0 auto;
4767
+ border-radius: var(--rs-radius);
4768
+ overflow: hidden;
4769
+ }
4770
+
4771
+ .rs-fiat-onramp[data-variant="connect"] .rs-fiat-onramp-iframe-wrap {
4772
+ max-width: 445px;
4773
+ min-height: 585px;
4774
+ }
4775
+
4776
+ .rs-fiat-onramp-iframe {
4777
+ display: block;
4778
+ width: 100%;
4779
+ height: 482px;
4780
+ min-height: 482px;
4781
+ border: 0;
4782
+ background: var(--rs-background);
4783
+ }
4784
+
4785
+ .rs-fiat-onramp[data-variant="connect"] .rs-fiat-onramp-iframe {
4786
+ height: 585px;
4787
+ min-height: 585px;
4788
+ }
4789
+
4790
+ .rs-fiat-onramp-loading,
4791
+ .rs-fiat-onramp-error {
4792
+ position: absolute;
4793
+ inset: 0;
4794
+ display: flex;
4795
+ flex-direction: column;
4796
+ align-items: center;
4797
+ justify-content: center;
4798
+ gap: 12px;
4799
+ text-align: center;
4800
+ background: var(--rs-background);
4801
+ color: var(--rs-foreground);
4802
+ padding: 16px;
4803
+ }
4804
+
4805
+ .rs-fiat-onramp-retry {
4806
+ padding: 6px 14px;
4807
+ border: 1px solid var(--rs-border);
4808
+ border-radius: var(--rs-radius-sm);
4809
+ background: var(--rs-background-secondary);
4810
+ color: var(--rs-foreground);
4811
+ font-size: 12px;
4812
+ font-weight: 500;
4813
+ font-family: inherit;
4814
+ cursor: pointer;
4815
+ }
4816
+
4817
+ .rs-fiat-onramp-retry:hover {
4818
+ background: var(--rs-border);
4819
+ }
4820
+
4821
+ /* =============================================================================
4822
+ Exchange picker (Swapped Connect)
4823
+ ============================================================================= */
4824
+
4825
+ .rs-exchange-grid {
4826
+ display: grid;
4827
+ grid-template-columns: repeat(2, minmax(0, 1fr));
4828
+ gap: 8px;
4829
+ }
4830
+
4831
+ .rs-exchange-card {
4832
+ display: flex;
4833
+ min-width: 0;
4834
+ min-height: 104px;
4835
+ flex-direction: column;
4836
+ align-items: center;
4837
+ justify-content: center;
4838
+ gap: 10px;
4839
+ padding: 12px 8px;
4840
+ border: 1px solid var(--rs-background-secondary);
4841
+ border-radius: 8px;
4842
+ background: var(--rs-background-secondary);
4843
+ color: var(--rs-foreground);
4844
+ font-family: inherit;
4845
+ cursor: pointer;
4846
+ transition:
4847
+ background-color 0.15s,
4848
+ border-color 0.15s;
4849
+ }
4850
+
4851
+ .rs-exchange-card:hover:not(:disabled) {
4852
+ background: var(--rs-surface-hover);
4853
+ border-color: var(--rs-surface-hover);
4854
+ }
4855
+
4856
+ .rs-exchange-card:focus-visible {
4857
+ outline: 2px solid var(--rs-border-accent);
4858
+ outline-offset: 2px;
4859
+ }
4860
+
4861
+ .rs-exchange-card-logo {
4862
+ display: inline-flex;
4863
+ align-items: center;
4864
+ justify-content: center;
4865
+ width: 44px;
4866
+ height: 44px;
4867
+ border-radius: 10px;
4868
+ background: var(--rs-surface);
4869
+ color: #71717b;
4870
+ overflow: hidden;
4871
+ }
4872
+
4873
+ .rs-exchange-card-logo img,
4874
+ .rs-exchange-card-logo svg {
4875
+ width: 28px;
4876
+ height: 28px;
4877
+ display: block;
4878
+ object-fit: contain;
4879
+ }
4880
+
4881
+ .rs-exchange-card-name {
4882
+ width: 100%;
4883
+ min-width: 0;
4884
+ font-size: 14px;
4885
+ font-weight: 500;
4886
+ line-height: 1.25;
4887
+ color: var(--rs-foreground);
4888
+ text-align: center;
4889
+ overflow: hidden;
4890
+ text-overflow: ellipsis;
4891
+ white-space: nowrap;
4892
+ }
4893
+
4894
+ .rs-exchange-select-state {
4895
+ min-height: 184px;
4896
+ display: flex;
4897
+ flex-direction: column;
4898
+ align-items: center;
4899
+ justify-content: center;
4900
+ gap: 12px;
4901
+ text-align: center;
4902
+ color: var(--rs-foreground);
4903
+ }
4904
+
4905
+ .rs-exchange-select-state-title {
4906
+ font-size: 14px;
4907
+ font-weight: 600;
4908
+ line-height: 1.35;
4909
+ }
4910
+
4911
+ /* =============================================================================
4912
+ Modal sizing for the Swapped iframe step
4913
+ =============================================================================
4914
+ Modal stays at the default 400px width for the fiat variant (matches
4915
+ Swapped's 400×482 iframe edge-to-edge). Connect needs 445px because its
4916
+ iframe is 445×585. Height grows with the Rhinestone chrome (header
4917
+ icon/title/subtitle + iframe + PoweredBy); cap to viewport so smaller
4918
+ laptops get a scrollable modal-body instead of overflowing the screen. */
4919
+
4920
+ /* Connect's iframe is 445×585 (vs fiat's 400×482). To preserve the same
4921
+ 12px horizontal padding as ConnectStep while still giving Swapped's
4922
+ Connect iframe its documented width, widen the modal by 24px (12 each
4923
+ side) → 469px. Applied from the moment the user enters exchange-connect
4924
+ mode (`data-flow-mode` on rs-modal-body) — not just the iframe step —
4925
+ so the modal width is stable across SetupStep → SwappedIframeStep.
4926
+ Fiat keeps the default 400px modal; its iframe renders at 376px
4927
+ content-width, matching the visual width of ConnectStep's list rows. */
4928
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="exchange-connect"]) {
4929
+ max-width: 469px;
4930
+ }
4931
+
4932
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="fiat-onramp"]),
4933
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="exchange-connect"]) {
4934
+ max-height: calc(100vh - 40px);
4935
+ }
4936
+
4937
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="fiat-onramp"]) .rs-modal-body,
4938
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="exchange-connect"]) .rs-modal-body {
4939
+ /* `overflow-x: hidden` is explicit because a `visible` x-axis with an
4940
+ `auto` y-axis gets promoted to `auto` on both axes, which paints a
4941
+ phantom horizontal scrollbar even when content fits horizontally. */
4942
+ overflow: hidden auto;
4943
+ }
4944
+
4945
+ /* Pre-size the modal-content to the eventual iframe-mounted height for
4946
+ the iframe-phase only:
4947
+ - `[data-step-type="setup"]` — brief pre-flight before SwappedIframeStep mounts
4948
+ - `:has(...) .rs-fiat-onramp` — SwappedIframeStep's iframe phase is in DOM
4949
+ The tracker phase (.rs-swapped-tracker) is naturally shorter and is
4950
+ excluded so the modal shrinks down to its content instead of leaving
4951
+ a tall empty area below the tracker. Applied on modal-content (not
4952
+ modal-body) so the transition on its min-height animates smoothly
4953
+ when the iframe→tracker phase swap fires. */
4954
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="fiat-onramp"][data-step-type="setup"]),
4955
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="fiat-onramp"] .rs-fiat-onramp) {
4956
+ min-height: 691px;
4957
+ }
4958
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="exchange-connect"][data-step-type="setup"]),
4959
+ .rs-modal-content:has(.rs-modal-body[data-flow-mode="exchange-connect"] .rs-fiat-onramp) {
4960
+ min-height: 794px;
4961
+ }
4962
+
4963
+ /* =============================================================================
4964
+ Custom order tracker
4965
+ =============================================================================
4966
+ Shown after the user creates an order with Swapped — replaces the iframe
4967
+ until the deposit-processor reports bridge-complete. */
4968
+ /* Layout lives in rs-screen now — tracker just contributes the entrance
4969
+ animation and a marker class for any future tracker-only tweaks. The
4970
+ icon + title come from the shared BodyHeader so the header position
4971
+ matches ConnectStep and SwappedIframeStep exactly. */
4972
+ .rs-swapped-tracker {
4973
+ animation: rs-swapped-tracker-enter 250ms ease-out;
4974
+ }
4975
+
4976
+ @keyframes rs-swapped-tracker-enter {
4977
+ from {
4978
+ opacity: 0;
4979
+ }
4980
+ to {
4981
+ opacity: 1;
4982
+ }
4983
+ }
4984
+
4985
+ @keyframes rs-tracker-step-complete {
4986
+ 0% {
4987
+ transform: scale(0.72);
4988
+ }
4989
+ 60% {
4990
+ transform: scale(1.08);
4991
+ }
4992
+ 100% {
4993
+ transform: scale(1);
4994
+ }
4995
+ }
4996
+
4997
+ .rs-swapped-tracker-steps {
4998
+ list-style: none;
4999
+ padding: 0;
5000
+ margin: 0;
5001
+ display: flex;
5002
+ flex-direction: column;
5003
+ flex-grow: 1;
5004
+ }
5005
+
5006
+ .rs-swapped-tracker-step {
5007
+ position: relative;
5008
+ display: flex;
5009
+ align-items: center;
5010
+ justify-content: space-between;
5011
+ gap: 12px;
5012
+ padding: 16px 0;
5013
+ }
5014
+
5015
+ /* Dotted vertical connector between adjacent step markers. */
5016
+ .rs-swapped-tracker-step + .rs-swapped-tracker-step::before {
5017
+ content: "";
5018
+ position: absolute;
5019
+ top: -14px;
5020
+ right: 11px;
5021
+ width: 0;
5022
+ height: 28px;
5023
+ border-left: 1.5px dotted #d4d4d8;
5024
+ }
5025
+
5026
+ .rs-swapped-tracker-step-label {
5027
+ font-size: 15px;
5028
+ color: var(--rs-foreground);
5029
+ flex-grow: 1;
5030
+ }
5031
+
5032
+ .rs-swapped-tracker-step-marker {
5033
+ width: 24px;
5034
+ height: 24px;
5035
+ border-radius: 50%;
5036
+ flex-shrink: 0;
5037
+ display: flex;
5038
+ align-items: center;
5039
+ justify-content: center;
5040
+ background: transparent;
5041
+ border: 1.5px solid #d4d4d8;
5042
+ color: transparent;
5043
+ transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
5044
+ }
5045
+
5046
+ .rs-swapped-tracker-step--active .rs-swapped-tracker-step-marker {
5047
+ border-color: #d4d4d8;
5048
+ background: transparent;
5049
+ /* Spinner SVG uses currentColor; base marker class sets color:transparent
5050
+ for the (hidden) ring glyph — re-establish a visible color here so the
5051
+ active-state spinner actually paints. */
5052
+ color: #18181b;
5053
+ }
5054
+
5055
+ .rs-swapped-tracker-step--active .rs-swapped-tracker-step-marker .rs-spinner {
5056
+ width: 14px;
5057
+ height: 14px;
5058
+ }
5059
+
5060
+ /* Satisfying scale-pop when a step ticks complete — the color transition
5061
+ alone is too subtle to register as "that just happened". */
5062
+ .rs-swapped-tracker-step--complete .rs-swapped-tracker-step-marker {
5063
+ animation: rs-tracker-step-complete 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
5064
+ background: #18181b;
5065
+ border-color: #18181b;
5066
+ color: #fff;
5067
+ }
5068
+
5069
+ .rs-swapped-tracker-step--complete .rs-swapped-tracker-step-marker svg {
5070
+ width: 14px;
5071
+ height: 14px;
5072
+ stroke-width: 3;
5073
+ }
5074
+
5075
+ /* Failed step marker — terminal processor failure or cancellation. Red in
5076
+ both themes (the failure color reads on light and dark). */
5077
+ .rs-swapped-tracker-step--failed .rs-swapped-tracker-step-marker {
5078
+ background: #fb2c36;
5079
+ border-color: #fb2c36;
5080
+ color: #fff;
5081
+ }
5082
+
5083
+ .rs-swapped-tracker-step--failed .rs-swapped-tracker-step-marker svg {
5084
+ width: 14px;
5085
+ height: 14px;
5086
+ stroke-width: 3;
5087
+ }
5088
+
5089
+ /* Terminal message block shown below the steps on failure / cancellation. */
5090
+ .rs-swapped-tracker-terminal {
5091
+ display: flex;
5092
+ flex-direction: column;
5093
+ align-items: center;
5094
+ gap: 12px;
5095
+ text-align: center;
5096
+ }
5097
+
5098
+ .rs-swapped-tracker-terminal-message {
5099
+ margin: 0;
5100
+ font-size: 14px;
5101
+ line-height: 1.4;
5102
+ color: var(--rs-foreground-secondary, #71717a);
5103
+ }
5104
+
5105
+ .rs-swapped-tracker-terminal--failed .rs-swapped-tracker-terminal-message {
5106
+ color: #fb2c36;
5107
+ }
5108
+
5109
+ .rs-swapped-tracker-retry {
5110
+ padding: 8px 16px;
5111
+ border: 1px solid var(--rs-border);
5112
+ border-radius: var(--rs-radius-sm);
5113
+ background: var(--rs-background-secondary);
5114
+ color: var(--rs-foreground);
5115
+ font-size: 13px;
5116
+ font-weight: 500;
5117
+ font-family: inherit;
5118
+ cursor: pointer;
5119
+ }
5120
+
5121
+ .rs-swapped-tracker-retry:hover {
5122
+ background: var(--rs-border);
5123
+ }
5124
+
5125
+ .rs-modal[data-theme="dark"] .rs-swapped-tracker-step + .rs-swapped-tracker-step::before {
5126
+ border-left-color: #3f3f46;
5127
+ }
5128
+
5129
+ .rs-modal[data-theme="dark"] .rs-swapped-tracker-step--pending .rs-swapped-tracker-step-marker,
5130
+ .rs-modal[data-theme="dark"] .rs-swapped-tracker-step--active .rs-swapped-tracker-step-marker {
5131
+ border-color: #3f3f46;
5132
+ }
5133
+
5134
+ .rs-modal[data-theme="dark"] .rs-swapped-tracker-step--complete .rs-swapped-tracker-step-marker {
5135
+ background: #fafafa;
5136
+ border-color: #fafafa;
5137
+ color: #18181b;
5138
+ }
@@ -153,6 +153,16 @@ interface RouteConfig {
153
153
  sourceChains?: number[];
154
154
  sourceTokens?: string[];
155
155
  }
156
+ interface FiatPaymentMethodOption {
157
+ /** Swapped's payment-method slug (e.g. `creditcard`, `applepay`, `bank-transfer`). */
158
+ method: string;
159
+ /** Row label shown in ConnectStep. */
160
+ label: string;
161
+ /** Optional sublabel under the row. */
162
+ sublabel?: string;
163
+ /** Optional icon name: `card` | `apple` | `bank`. Defaults to `card`. */
164
+ icon?: "card" | "apple" | "bank";
165
+ }
156
166
  interface DepositModalProps {
157
167
  dappWalletClient?: WalletClient | null;
158
168
  dappPublicClient?: PublicClient | null;
@@ -197,6 +207,21 @@ interface DepositModalProps {
197
207
  * entry screen.
198
208
  */
199
209
  dappImports?: DappImportsConfig;
210
+ /** Show fiat on-ramp option(s) that embed Swapped's iframe. Default: false. */
211
+ enableFiatOnramp?: boolean;
212
+ /** Show the Transfer Crypto / QR row. Default: true. */
213
+ enableQrTransfer?: boolean;
214
+ /**
215
+ * One row per Swapped payment method (Pay with Card / Apple Pay / Bank
216
+ * Transfer). When set, replaces the generic "Pay with Card" row.
217
+ */
218
+ fiatOnrampMethods?: FiatPaymentMethodOption[];
219
+ /**
220
+ * Show a "Fund from Exchange" row that opens Swapped Connect — the user
221
+ * picks their CEX (Coinbase, Binance, Kraken…) or wallet inside the iframe
222
+ * and Swapped pulls the crypto on-chain to the smart account. Default: false.
223
+ */
224
+ enableExchangeConnect?: boolean;
200
225
  onRequestConnect?: () => void;
201
226
  theme?: DepositModalTheme;
202
227
  uiConfig?: DepositModalUIConfig;