@coras-io/embed 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +8 -7
  3. package/dist/components/accordion/accordion-item/accordion-item.styles.js +1 -1
  4. package/dist/components/alert/alert.styles.js +14 -14
  5. package/dist/components/attraction-card/base/attraction-card.styles.js +7 -3
  6. package/dist/components/attraction-discovery/attraction-discovery.styles.js +1 -1
  7. package/dist/components/attraction-header/attraction-header.styles.js +1 -1
  8. package/dist/components/attraction-option/attraction-option.styles.js +4 -4
  9. package/dist/components/basket/basket.styles.js +27 -26
  10. package/dist/components/basket-empty/basket-empty.styles.js +1 -0
  11. package/dist/components/basket-item/basket-item.component.js +10 -4
  12. package/dist/components/basket-item/basket-item.styles.js +9 -3
  13. package/dist/components/basket-item-ticket/basket-item-ticket.styles.js +1 -3
  14. package/dist/components/basket-mobile/basket-mobile.styles.js +5 -5
  15. package/dist/components/better-carousel/better-carousel.styles.js +2 -0
  16. package/dist/components/bottom-drawer/bottom-drawer.styles.js +6 -4
  17. package/dist/components/button/button.component.d.ts +0 -1
  18. package/dist/components/button/button.component.js +1 -8
  19. package/dist/components/button/button.constants.d.ts +1 -1
  20. package/dist/components/button/button.constants.js +1 -1
  21. package/dist/components/button/button.styles.js +66 -39
  22. package/dist/components/calendar/base/calendar.styles.js +3 -5
  23. package/dist/components/calendar/multi/multi.styles.js +7 -0
  24. package/dist/components/category-tag/category-tag.styles.js +10 -7
  25. package/dist/components/checkbox/checkbox.styles.js +2 -2
  26. package/dist/components/chip/chip.styles.js +2 -2
  27. package/dist/components/collect-fida/collect-fida.component.js +5 -3
  28. package/dist/components/collect-fida/collect-fida.styles.js +9 -16
  29. package/dist/components/crypto-auth-modal/crypto-auth-modal.component.js +6 -4
  30. package/dist/components/crypto-auth-modal/crypto-auth-modal.styles.js +2 -2
  31. package/dist/components/dialog/dialog.styles.js +10 -17
  32. package/dist/components/drawer/drawer.component.js +6 -0
  33. package/dist/components/drawer/drawer.styles.js +17 -3
  34. package/dist/components/dropdown/dropdown.component.js +7 -0
  35. package/dist/components/dropdown/dropdown.styles.js +3 -6
  36. package/dist/components/expiration-timer/expiration-timer.styles.js +3 -3
  37. package/dist/components/explore-card/explore-card.styles.js +1 -1
  38. package/dist/components/filter-chip/filter-chip.styles.js +11 -9
  39. package/dist/components/filters-drawer/filters-drawer.component.js +1 -1
  40. package/dist/components/filters-drawer/filters-drawer.styles.js +6 -5
  41. package/dist/components/image/image.styles.js +3 -3
  42. package/dist/components/image-chip/image-chip.styles.js +1 -1
  43. package/dist/components/input-field/input-field.styles.js +22 -17
  44. package/dist/components/itinerary/itinerary-item/itinerary-item.styles.js +1 -1
  45. package/dist/components/joined-fields/joined-fields.styles.js +10 -6
  46. package/dist/components/loading-spinner/loading-spinner.styles.js +1 -1
  47. package/dist/components/location-image/location-image.component.d.ts +6 -0
  48. package/dist/components/location-image/location-image.component.js +15 -2
  49. package/dist/components/location-image/location-image.styles.js +1 -1
  50. package/dist/components/locations-results/locations-results.styles.js +4 -6
  51. package/dist/components/marketing-tag/marketing-tag.styles.js +1 -1
  52. package/dist/components/message-card/message-card.styles.js +6 -6
  53. package/dist/components/option-group/option-chip/option-chip.styles.js +4 -3
  54. package/dist/components/order-confirmation/order-confirmation-item/order-confirmation-item.styles.js +9 -0
  55. package/dist/components/order-confirmation/order-confirmation.component.js +4 -3
  56. package/dist/components/order-confirmation/order-confirmation.styles.js +1 -1
  57. package/dist/components/otp/otp.styles.js +6 -6
  58. package/dist/components/overlay/overlay.styles.js +5 -1
  59. package/dist/components/pay-in/pay-in.styles.js +2 -2
  60. package/dist/components/payment-fiat-method/payment-fiat-method.styles.js +20 -4
  61. package/dist/components/payment-option/payment-option.styles.js +10 -12
  62. package/dist/components/performance-time/performance-time.styles.js +2 -2
  63. package/dist/components/phone-country-code/phone-country-code.styles.js +1 -1
  64. package/dist/components/points/points.styles.js +1 -1
  65. package/dist/components/search-button/search-button.styles.js +15 -7
  66. package/dist/components/seated-selector/seated-selector.component.d.ts +2 -0
  67. package/dist/components/seated-selector/seated-selector.component.js +53 -10
  68. package/dist/components/seated-selector/seated-selector.styles.js +77 -14
  69. package/dist/components/seats-io/seats-io.component.d.ts +2 -0
  70. package/dist/components/seats-io/seats-io.component.js +25 -2
  71. package/dist/components/seats-io/seats-io.constants.d.ts +2 -1
  72. package/dist/components/seats-io/seats-io.constants.js +6 -1
  73. package/dist/components/seats-io/seats-io.transform.d.ts +8 -2
  74. package/dist/components/seats-io/seats-io.transform.js +12 -4
  75. package/dist/components/select-list/select-list.styles.js +1 -0
  76. package/dist/components/select-list/select-option/select-option.styles.js +3 -3
  77. package/dist/components/sort-dropdown/sort-dropdown.styles.js +1 -1
  78. package/dist/components/sort-list/sort-list.styles.js +1 -1
  79. package/dist/components/sport-options/sport-option/sport-option.styles.js +2 -2
  80. package/dist/components/star-rating/star-rating.component.d.ts +1 -0
  81. package/dist/components/star-rating/star-rating.component.js +19 -23
  82. package/dist/components/star-rating/star-rating.styles.js +32 -6
  83. package/dist/components/tab/tab.styles.js +1 -1
  84. package/dist/components/tabs/tabs.styles.js +1 -1
  85. package/dist/components/tag/tag.styles.js +7 -7
  86. package/dist/components/text-section/text-section.styles.js +2 -1
  87. package/dist/components/time-tag/time-tag.styles.js +1 -2
  88. package/dist/components/tooltip/tooltip.styles.js +3 -3
  89. package/dist/components/tour-ticket-type-selector/tour-ticket-type/tour-ticket-type.styles.js +2 -2
  90. package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.component.js +1 -1
  91. package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.styles.js +1 -10
  92. package/dist/components/transfer-modal/transfer-modal.component.js +3 -3
  93. package/dist/components/transfer-modal/transfer-modal.styles.js +6 -8
  94. package/dist/containers/basket/basket.component.d.ts +1 -1
  95. package/dist/containers/basket/basket.component.js +56 -25
  96. package/dist/containers/footer/footer.component.d.ts +2 -0
  97. package/dist/containers/footer/footer.component.js +8 -0
  98. package/dist/containers/footer/footer.styles.js +12 -0
  99. package/dist/containers/navbar/navbar.component.d.ts +6 -1
  100. package/dist/containers/navbar/navbar.component.js +41 -9
  101. package/dist/containers/navbar/navbar.styles.js +47 -13
  102. package/dist/contexts/basket/basket.provider.d.ts +1 -0
  103. package/dist/contexts/basket/basket.provider.js +31 -4
  104. package/dist/contexts/properties.context.d.ts +4 -4
  105. package/dist/core/api/middlewares/booking-session/booking-session.constants.d.ts +1 -0
  106. package/dist/core/api/middlewares/booking-session/booking-session.constants.js +1 -0
  107. package/dist/core/api/middlewares/booking-session/booking-session.js +18 -27
  108. package/dist/core/api/middlewares/booking-session/booking-session.utils.d.ts +4 -4
  109. package/dist/core/api/middlewares/booking-session/booking-session.utils.js +13 -10
  110. package/dist/core/api/queries.js +3 -2
  111. package/dist/core/color-scheme.d.ts +14 -0
  112. package/dist/core/color-scheme.js +48 -0
  113. package/dist/core/currency/constants.d.ts +1 -1
  114. package/dist/core/currency/constants.js +4 -3
  115. package/dist/core/i18n/date.js +20 -9
  116. package/dist/core/i18n/locale-codes.d.ts +2 -2
  117. package/dist/core/i18n/locale-codes.js +4 -0
  118. package/dist/core/images/constants.d.ts +0 -5
  119. package/dist/core/images/constants.js +0 -5
  120. package/dist/core/images/helpers.d.ts +8 -0
  121. package/dist/core/images/helpers.js +14 -6
  122. package/dist/core/images/srcset.js +2 -2
  123. package/dist/core/storybook/demo.d.ts +4 -0
  124. package/dist/core/storybook/demo.js +17 -0
  125. package/dist/core/types/locales.d.ts +7 -0
  126. package/dist/core/utils/datetime.js +10 -4
  127. package/dist/core/utils/locale.d.ts +8 -1
  128. package/dist/core/utils/locale.js +10 -0
  129. package/dist/helpers/helpers.d.ts +1 -1
  130. package/dist/index.d.ts +2 -3
  131. package/dist/index.js +1 -2
  132. package/dist/locales/ar-SA.d.ts +279 -0
  133. package/dist/locales/ar-SA.js +288 -0
  134. package/dist/locales/da-DK.d.ts +100 -82
  135. package/dist/locales/da-DK.js +104 -89
  136. package/dist/locales/de-DE.d.ts +100 -82
  137. package/dist/locales/de-DE.js +104 -89
  138. package/dist/locales/en-IE.d.ts +250 -232
  139. package/dist/locales/en-IE.js +254 -239
  140. package/dist/locales/en-US.d.ts +100 -82
  141. package/dist/locales/en-US.js +107 -92
  142. package/dist/locales/es-ES.d.ts +100 -82
  143. package/dist/locales/es-ES.js +104 -89
  144. package/dist/locales/fr-FR.d.ts +100 -82
  145. package/dist/locales/fr-FR.js +104 -89
  146. package/dist/locales/fr-MA.d.ts +100 -82
  147. package/dist/locales/fr-MA.js +104 -89
  148. package/dist/locales/is-IS.d.ts +100 -82
  149. package/dist/locales/is-IS.js +104 -89
  150. package/dist/locales/it-IT.d.ts +100 -82
  151. package/dist/locales/it-IT.js +104 -89
  152. package/dist/locales/ms-MY.d.ts +100 -82
  153. package/dist/locales/ms-MY.js +106 -91
  154. package/dist/locales/nb-NO.d.ts +100 -82
  155. package/dist/locales/nb-NO.js +104 -89
  156. package/dist/locales/pl-PL.d.ts +100 -82
  157. package/dist/locales/pl-PL.js +104 -89
  158. package/dist/locales/pt-PT.d.ts +100 -82
  159. package/dist/locales/pt-PT.js +104 -89
  160. package/dist/locales/sv-SE.d.ts +100 -82
  161. package/dist/locales/sv-SE.js +104 -89
  162. package/dist/locales/th-TH.d.ts +100 -82
  163. package/dist/locales/th-TH.js +104 -89
  164. package/dist/locales/tr-TR.d.ts +100 -82
  165. package/dist/locales/tr-TR.js +104 -89
  166. package/dist/locales/zh-CN.d.ts +100 -82
  167. package/dist/locales/zh-CN.js +105 -90
  168. package/dist/locales/zh-HK.d.ts +279 -0
  169. package/dist/locales/zh-HK.js +288 -0
  170. package/dist/mixins/coinbase.mixin.js +1 -1
  171. package/dist/mixins/error-modal.styles.js +2 -2
  172. package/dist/mixins/location-search/location-search.js +3 -3
  173. package/dist/mixins/location-search/location-search.styles.js +6 -5
  174. package/dist/sdk/chrome.d.ts +9 -3
  175. package/dist/sdk/chrome.js +21 -9
  176. package/dist/sdk/element-adapter.js +11 -8
  177. package/dist/sdk/mount.d.ts +3 -3
  178. package/dist/sdk/mount.js +153 -48
  179. package/dist/sdk/navigation-intent.js +3 -0
  180. package/dist/sdk/page-registry.d.ts +14 -2
  181. package/dist/sdk/page-registry.js +33 -13
  182. package/dist/sdk/theme.d.ts +2 -16
  183. package/dist/sdk/theme.js +12 -44
  184. package/dist/sdk/types.d.ts +59 -30
  185. package/dist/sdk/url.d.ts +24 -3
  186. package/dist/sdk/url.js +66 -27
  187. package/dist/sdk/validation.d.ts +0 -7
  188. package/dist/sdk/validation.js +104 -48
  189. package/dist/styles/contrast-fixtures.d.ts +0 -5
  190. package/dist/styles/contrast-fixtures.js +79 -9
  191. package/dist/styles/contrast.js +4 -3
  192. package/dist/styles/defaults/component.js +17 -1
  193. package/dist/styles/primitives.d.ts +0 -10
  194. package/dist/styles/primitives.js +2 -12
  195. package/dist/styles/tokens.d.ts +1 -1
  196. package/dist/styles/tokens.generated.d.ts +1 -1
  197. package/dist/styles/tokens.generated.js +1 -1
  198. package/dist/styles/tokens.metadata.d.ts +1 -1
  199. package/dist/styles/tokens.metadata.js +378 -90
  200. package/dist/views/details-page/details-page.component.d.ts +5 -5
  201. package/dist/views/details-page/details-page.component.js +77 -80
  202. package/dist/views/details-page/details-page.styles.js +64 -9
  203. package/dist/views/help-page/help-page.component.d.ts +1 -0
  204. package/dist/views/help-page/help-page.component.js +11 -4
  205. package/dist/views/help-page/help-page.styles.js +21 -8
  206. package/dist/views/landing-page/landing-page.component.d.ts +4 -4
  207. package/dist/views/landing-page/landing-page.component.js +65 -65
  208. package/dist/views/landing-page/landing-page.styles.js +70 -3
  209. package/dist/views/payment-page/partials/activity-details/activity-details.styles.js +10 -33
  210. package/dist/views/payment-page/partials/billing-address/billing-address.styles.js +4 -9
  211. package/dist/views/payment-page/partials/confirmation/confirmation.styles.js +6 -6
  212. package/dist/views/payment-page/partials/hotel-pickup-list/hotel-pickup-list.styles.js +4 -4
  213. package/dist/views/payment-page/partials/payment-type-selector/payment-type-selector.styles.js +1 -0
  214. package/dist/views/payment-page/partials/stripe/stripe.component.d.ts +1 -1
  215. package/dist/views/payment-page/partials/stripe/stripe.component.js +11 -12
  216. package/dist/views/payment-page/partials/stripe/stripe.constants.d.ts +2 -15
  217. package/dist/views/payment-page/partials/stripe/stripe.constants.js +4 -17
  218. package/dist/views/payment-page/partials/stripe/stripe.styles.js +16 -23
  219. package/dist/views/payment-page/partials/stripe/stripe.utils.d.ts +25 -14
  220. package/dist/views/payment-page/partials/stripe/stripe.utils.js +111 -62
  221. package/dist/views/payment-page/partials/terms-checkbox/terms-checkbox.styles.js +1 -1
  222. package/dist/views/payment-page/payment-page.component.d.ts +1 -0
  223. package/dist/views/payment-page/payment-page.component.js +19 -9
  224. package/dist/views/payment-page/payment-page.styles.js +37 -32
  225. package/dist/views/payment-page/payment-page.utils.d.ts +7 -0
  226. package/dist/views/payment-page/payment-page.utils.js +29 -14
  227. package/dist/views/payment-page/payment-page.validators.js +9 -3
  228. package/dist/views/payment-page/services/payment-api.service.d.ts +1 -1
  229. package/dist/views/search-page/search-page.component.d.ts +1 -0
  230. package/dist/views/search-page/search-page.component.js +12 -6
  231. package/dist/views/search-page/search-page.styles.js +24 -2
  232. package/dist/views/suggestion-widget/suggestion-widget.styles.js +1 -1
  233. package/package.json +63 -76
  234. package/dist/custom-elements.json +0 -37397
  235. package/dist/sdk/meta.d.ts +0 -7
  236. package/dist/sdk/meta.js +0 -9
  237. package/dist/styles/tokens.css +0 -359
@@ -1,43 +1,30 @@
1
- /**
2
- * Constants for the Stripe payment component.
3
- */
4
- /** Valid Stripe publishable key prefixes. */
5
1
  export declare const STRIPE_KEY_PREFIXES: readonly ["pk_test_", "pk_live_"];
6
- /** Minimum length for a valid Stripe publishable key. */
7
2
  export declare const STRIPE_KEY_MIN_LENGTH = 32;
8
- /** Debounce delay for portal position updates (ms). ~60fps */
9
- export declare const PORTAL_UPDATE_DEBOUNCE_MS = 16;
10
- /** Default timeout before initial portal positioning (ms). */
11
3
  export declare const INITIAL_PORTAL_UPDATE_DELAY_MS = 50;
12
- /** CSS class names used by the component. */
13
4
  export declare const CSS_CLASSES: {
14
5
  readonly STRIPE_PLACEHOLDER: "stripe-placeholder";
15
6
  readonly ERROR: "error";
16
7
  readonly LOADING: "loading";
17
8
  };
18
- /** Default Stripe element style configuration. */
19
9
  export declare const DEFAULT_STRIPE_STYLE: {
20
10
  readonly fontSize: "16px";
21
11
  readonly lineHeight: "52px";
22
12
  readonly fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif";
23
13
  };
24
- /** Placeholder text for card input fields. */
25
14
  export declare const PLACEHOLDERS: {
26
15
  readonly CARD_NUMBER: "1234 1234 1234 1234";
27
16
  readonly CARD_EXPIRY: "MM / YY";
28
17
  readonly CARD_CVC: "CVC";
29
18
  };
30
- /** CSS variable names for theming. */
31
19
  export declare const CSS_VARS: {
32
20
  readonly FIELD_INPUT_COLOR: "--field-input-color";
33
21
  readonly FIELD_INPUT_PLACEHOLDER_COLOR: "--field-input-placeholder-color";
34
22
  readonly FIELD_INPUT_ERROR_COLOR: "--field-input-error-color";
35
- readonly SURFACE: "--coras-color-surface";
23
+ readonly SURFACE: "--coras-color-field";
36
24
  readonly PRIMARY: "--coras-color-primary";
37
25
  readonly FONT_FAMILY: "--coras-font-family-sans";
38
- readonly BORDER_RADIUS: "--coras-radius-sm";
26
+ readonly BORDER_RADIUS: "--coras-radius-field";
39
27
  };
40
- /** Default colors when CSS variables are not set. */
41
28
  export declare const DEFAULT_COLORS: {
42
29
  readonly TEXT: "#32325d";
43
30
  readonly PLACEHOLDER: "#aab7c4";
@@ -1,45 +1,32 @@
1
- /**
2
- * Constants for the Stripe payment component.
3
- */
4
- /** Valid Stripe publishable key prefixes. */
5
1
  export const STRIPE_KEY_PREFIXES = ["pk_test_", "pk_live_"];
6
- /** Minimum length for a valid Stripe publishable key. */
7
2
  export const STRIPE_KEY_MIN_LENGTH = 32;
8
- /** Debounce delay for portal position updates (ms). ~60fps */
9
- export const PORTAL_UPDATE_DEBOUNCE_MS = 16;
10
- /** Default timeout before initial portal positioning (ms). */
11
3
  export const INITIAL_PORTAL_UPDATE_DELAY_MS = 50;
12
- /** CSS class names used by the component. */
13
4
  export const CSS_CLASSES = {
14
5
  STRIPE_PLACEHOLDER: "stripe-placeholder",
15
6
  ERROR: "error",
16
7
  LOADING: "loading",
17
8
  };
18
- /** Default Stripe element style configuration. */
19
9
  export const DEFAULT_STRIPE_STYLE = {
20
10
  fontSize: "16px",
21
11
  lineHeight: "52px",
22
12
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
23
13
  };
24
- /** Placeholder text for card input fields. */
25
14
  export const PLACEHOLDERS = {
26
15
  CARD_NUMBER: "1234 1234 1234 1234",
27
16
  CARD_EXPIRY: "MM / YY",
28
17
  CARD_CVC: "CVC",
29
18
  };
30
- /** CSS variable names for theming. */
31
19
  export const CSS_VARS = {
32
20
  FIELD_INPUT_COLOR: "--field-input-color",
33
21
  FIELD_INPUT_PLACEHOLDER_COLOR: "--field-input-placeholder-color",
34
22
  FIELD_INPUT_ERROR_COLOR: "--field-input-error-color",
35
- // Read for the Stripe Elements `appearance` so the hosted iframe matches the
36
- // embed's surface, brand colour, and (light/dark) scheme.
37
- SURFACE: "--coras-color-surface",
23
+ SURFACE: "--coras-color-field",
38
24
  PRIMARY: "--coras-color-primary",
39
25
  FONT_FAMILY: "--coras-font-family-sans",
40
- BORDER_RADIUS: "--coras-radius-sm",
26
+ // The Stripe card element sits in the same row as the embed's own text
27
+ // fields, so it takes the field radius rather than the general radius scale.
28
+ BORDER_RADIUS: "--coras-radius-field",
41
29
  };
42
- /** Default colors when CSS variables are not set. */
43
30
  export const DEFAULT_COLORS = {
44
31
  TEXT: "#32325d",
45
32
  PLACEHOLDER: "#aab7c4",
@@ -11,10 +11,9 @@ export default css `
11
11
  }
12
12
 
13
13
  .stripe-placeholder {
14
- border: 1px solid
15
- var(--field-input-border-color, var(--coras-color-neutral-400));
16
- border-radius: var(--field-input-border-radius, 4px);
17
- background-color: var(--coras-color-surface);
14
+ border: 1px solid var(--field-input-border-color);
15
+ border-radius: var(--field-input-border-radius);
16
+ background-color: var(--coras-color-field);
18
17
  min-height: 3.2rem;
19
18
  box-sizing: border-box;
20
19
  padding-inline: 1rem;
@@ -24,12 +23,9 @@ export default css `
24
23
  }
25
24
 
26
25
  .stripe-placeholder:focus-within {
27
- border-color: var(--field-input-focus-border-color, #3b82f6);
26
+ border-color: var(--field-input-focused-border-color);
28
27
  box-shadow: 0 0 0 3px
29
- var(
30
- --field-input-focus-shadow-color,
31
- color-mix(in srgb, var(--coras-color-primary-500), transparent 90%)
32
- );
28
+ color-mix(in srgb, var(--coras-color-primary), transparent 90%);
33
29
  outline: none;
34
30
  }
35
31
 
@@ -42,34 +38,31 @@ export default css `
42
38
 
43
39
  .card-field-container label {
44
40
  display: block;
45
- margin-bottom: var(--field-label-bottom-margin, 0.5rem);
46
- font-size: var(--field-label-font-size, 0.875rem);
47
- line-height: var(--field-label-line-height, 1.25rem);
48
- color: var(--field-label-color, #4b5563);
41
+ margin-bottom: var(--field-label-bottom-margin);
42
+ font-size: var(--field-label-font-size);
43
+ line-height: var(--field-label-line-height);
44
+ color: var(--field-label-color);
49
45
  }
50
46
 
51
47
  .stripe-element {
52
- border: 1px solid var(--field-input-border-color, #d1d5db);
53
- border-radius: var(--field-input-border-radius, 0.25rem);
48
+ border: 1px solid var(--field-input-border-color);
49
+ border-radius: var(--field-input-border-radius);
54
50
  padding: 1rem;
55
- background-color: var(--coras-color-surface);
51
+ background-color: var(--coras-color-field);
56
52
  min-height: 3.2rem;
57
53
  box-sizing: border-box;
58
54
  }
59
55
 
60
56
  .stripe-element-error {
61
- border-color: var(--field-input-error-color, #ef4444);
57
+ border-color: var(--field-input-error-color);
62
58
  }
63
59
 
64
60
  .stripe-placeholder.error {
65
- border-color: var(--field-input-error-color, #ef4444);
61
+ border-color: var(--field-input-error-color);
66
62
  }
67
63
 
68
64
  .stripe-placeholder.disabled {
69
- background-color: var(
70
- --field-input-disabled-bg,
71
- var(--coras-color-neutral-50)
72
- );
65
+ background-color: var(--coras-color-neutral-50);
73
66
  cursor: not-allowed;
74
67
  opacity: 0.6;
75
68
  }
@@ -95,7 +88,7 @@ export default css `
95
88
  }
96
89
 
97
90
  .error-message {
98
- color: var(--field-input-error-color, #ef4444);
91
+ color: var(--field-input-error-color);
99
92
  font-size: 0.875rem;
100
93
  margin-top: 0.5rem;
101
94
  }
@@ -12,20 +12,21 @@ import type { StripeKeyValidationResult, ThemeColors, StripeElementStyle } from
12
12
  */
13
13
  export declare function validateStripeKey(key: string): StripeKeyValidationResult;
14
14
  /**
15
- * Extracts theme colors from computed styles or returns defaults.
15
+ * Extracts theme colors from the host's resolved tokens or returns defaults.
16
16
  *
17
- * @param computedStyle - The computed style object from getComputedStyle()
18
- * @returns Theme colors for Stripe elements
17
+ * @param host - The element whose Coras tokens theme the Stripe elements
18
+ * @returns Theme colors for Stripe elements, as concrete `rgb(...)` values
19
19
  */
20
- export declare function extractThemeColors(computedStyle: CSSStyleDeclaration): ThemeColors;
20
+ export declare function extractThemeColors(host: HTMLElement): ThemeColors;
21
21
  /**
22
22
  * Builds a Stripe Elements `appearance` from the resolved Coras tokens, so the
23
23
  * hosted card iframe matches the embed's surface, brand colour, and colour
24
- * scheme. The `night` theme and a dark `colorBackground` are selected
25
- * automatically when the surface resolves to a dark colour (the light-dark()
26
- * tokens already follow the mount's `color-scheme`).
24
+ * scheme. Colours and the radius are resolved to concrete values Stripe accepts
25
+ * (see `withTokenResolver`); the `night` theme and a dark `colorBackground` are
26
+ * selected automatically when the surface resolves to a dark colour, which the
27
+ * probe evaluates against the mount's `color-scheme`.
27
28
  */
28
- export declare function buildStripeAppearance(computed: CSSStyleDeclaration): Appearance;
29
+ export declare function buildStripeAppearance(host: HTMLElement): Appearance;
29
30
  /**
30
31
  * Creates a Stripe element style configuration based on theme colors.
31
32
  *
@@ -43,6 +44,12 @@ export declare function createPortalElement(className: string): HTMLElement;
43
44
  /**
44
45
  * Positions a portal element to match a placeholder's position.
45
46
  *
47
+ * The coordinates are document-relative, so a host page that scrolls the window
48
+ * moves the portal with the document and every recomputed value is identical.
49
+ * Only a scroll in an ancestor container (the Studio preview frame) actually
50
+ * moves the placeholder under the portal. Tracking runs every frame, so an
51
+ * unchanged position returns without writing and costs that host page nothing.
52
+ *
46
53
  * @param placeholder - The placeholder element inside Shadow DOM
47
54
  * @param portal - The portal element in the document body
48
55
  * @param zIndex - The z-index value for the portal
@@ -50,14 +57,18 @@ export declare function createPortalElement(className: string): HTMLElement;
50
57
  */
51
58
  export declare function positionPortalElement(placeholder: HTMLElement | null, portal: HTMLElement | null, zIndex: number, inlinePadding?: number): void;
52
59
  /**
53
- * Creates a debounced function that uses requestAnimationFrame.
54
- * Useful for DOM operations that should be batched with the browser's paint cycle.
60
+ * Coalesces repeated calls into one run in the next animation frame.
61
+ *
62
+ * Portal tracking has to keep up with a scroll that is still in progress. A
63
+ * trailing debounce cannot: each scroll event restarts its timer, so the portals
64
+ * hold their stale position for the whole gesture and only catch up once the
65
+ * scroll stops. Running on every frame that had at least one call matches the
66
+ * rate the portals are painted at.
55
67
  *
56
- * @param fn - The function to debounce
57
- * @param delay - Debounce delay in milliseconds
58
- * @returns Object with the debounced function and a cancel method
68
+ * @param fn - The function to run, at most once per frame
69
+ * @returns Object with the throttled function and a cancel method
59
70
  */
60
- export declare function createDebouncedRAF(fn: () => void, delay: number): {
71
+ export declare function createFrameThrottle(fn: () => void): {
61
72
  execute: () => void;
62
73
  cancel: () => void;
63
74
  };
@@ -42,21 +42,56 @@ export function validateStripeKey(key) {
42
42
  return { isValid: true };
43
43
  }
44
44
  /**
45
- * Extracts theme colors from computed styles or returns defaults.
45
+ * Runs `use` with a resolver that turns the Coras token expressions
46
+ * (`var()`, `light-dark()`, `calc()`) into the concrete `rgb(...)` / px strings
47
+ * Stripe accepts. Stripe silently drops a `light-dark(...)` colour and a
48
+ * `calc(.../)` length, so every token value must be resolved before it is handed
49
+ * to Stripe (otherwise the card iframe falls back to Stripe's own defaults).
46
50
  *
47
- * @param computedStyle - The computed style object from getComputedStyle()
48
- * @returns Theme colors for Stripe elements
51
+ * Each value is applied to a hidden probe mounted in the host's own scope, so it
52
+ * inherits the same custom properties and `color-scheme`, then read back from
53
+ * `getComputedStyle`. An empty input is returned unchanged so callers keep their
54
+ * own fallbacks.
49
55
  */
50
- export function extractThemeColors(computedStyle) {
51
- return {
52
- textColor: computedStyle.getPropertyValue(CSS_VARS.FIELD_INPUT_COLOR).trim() ||
53
- DEFAULT_COLORS.TEXT,
54
- placeholderColor: computedStyle
55
- .getPropertyValue(CSS_VARS.FIELD_INPUT_PLACEHOLDER_COLOR)
56
- .trim() || DEFAULT_COLORS.PLACEHOLDER,
57
- errorColor: computedStyle.getPropertyValue(CSS_VARS.FIELD_INPUT_ERROR_COLOR).trim() ||
56
+ function withTokenResolver(host, use) {
57
+ const probe = document.createElement("span");
58
+ probe.style.position = "absolute";
59
+ probe.style.visibility = "hidden";
60
+ probe.style.pointerEvents = "none";
61
+ const root = host.shadowRoot ?? host;
62
+ root.appendChild(probe);
63
+ try {
64
+ return use((value, as) => {
65
+ if (!value)
66
+ return value;
67
+ if (as === "color") {
68
+ probe.style.color = value;
69
+ return getComputedStyle(probe).color || value;
70
+ }
71
+ probe.style.borderTopLeftRadius = value;
72
+ return getComputedStyle(probe).borderTopLeftRadius || value;
73
+ });
74
+ }
75
+ finally {
76
+ probe.remove();
77
+ }
78
+ }
79
+ /**
80
+ * Extracts theme colors from the host's resolved tokens or returns defaults.
81
+ *
82
+ * @param host - The element whose Coras tokens theme the Stripe elements
83
+ * @returns Theme colors for Stripe elements, as concrete `rgb(...)` values
84
+ */
85
+ export function extractThemeColors(host) {
86
+ const computed = getComputedStyle(host);
87
+ const read = (name) => computed.getPropertyValue(name).trim();
88
+ return withTokenResolver(host, (resolve) => ({
89
+ textColor: resolve(read(CSS_VARS.FIELD_INPUT_COLOR), "color") || DEFAULT_COLORS.TEXT,
90
+ placeholderColor: resolve(read(CSS_VARS.FIELD_INPUT_PLACEHOLDER_COLOR), "color") ||
91
+ DEFAULT_COLORS.PLACEHOLDER,
92
+ errorColor: resolve(read(CSS_VARS.FIELD_INPUT_ERROR_COLOR), "color") ||
58
93
  DEFAULT_COLORS.ERROR,
59
- };
94
+ }));
60
95
  }
61
96
  /** True when a resolved CSS colour (rgb()/rgba()/#hex) reads as dark. */
62
97
  function isDarkColor(color) {
@@ -69,26 +104,31 @@ function isDarkColor(color) {
69
104
  /**
70
105
  * Builds a Stripe Elements `appearance` from the resolved Coras tokens, so the
71
106
  * hosted card iframe matches the embed's surface, brand colour, and colour
72
- * scheme. The `night` theme and a dark `colorBackground` are selected
73
- * automatically when the surface resolves to a dark colour (the light-dark()
74
- * tokens already follow the mount's `color-scheme`).
107
+ * scheme. Colours and the radius are resolved to concrete values Stripe accepts
108
+ * (see `withTokenResolver`); the `night` theme and a dark `colorBackground` are
109
+ * selected automatically when the surface resolves to a dark colour, which the
110
+ * probe evaluates against the mount's `color-scheme`.
75
111
  */
76
- export function buildStripeAppearance(computed) {
112
+ export function buildStripeAppearance(host) {
113
+ const computed = getComputedStyle(host);
77
114
  const read = (name) => computed.getPropertyValue(name).trim();
78
- const surface = read(CSS_VARS.SURFACE) || DEFAULT_COLORS.BACKGROUND;
79
- return {
80
- theme: isDarkColor(surface) ? "night" : "stripe",
81
- variables: {
82
- colorBackground: surface,
83
- colorText: read(CSS_VARS.FIELD_INPUT_COLOR) || DEFAULT_COLORS.TEXT,
84
- colorTextPlaceholder: read(CSS_VARS.FIELD_INPUT_PLACEHOLDER_COLOR) ||
85
- DEFAULT_COLORS.PLACEHOLDER,
86
- colorDanger: read(CSS_VARS.FIELD_INPUT_ERROR_COLOR) || DEFAULT_COLORS.ERROR,
87
- colorPrimary: read(CSS_VARS.PRIMARY) || undefined,
88
- fontFamily: read(CSS_VARS.FONT_FAMILY) || undefined,
89
- borderRadius: read(CSS_VARS.BORDER_RADIUS) || undefined,
90
- },
91
- };
115
+ return withTokenResolver(host, (resolve) => {
116
+ const color = (name) => resolve(read(name), "color");
117
+ const surface = color(CSS_VARS.SURFACE) || DEFAULT_COLORS.BACKGROUND;
118
+ return {
119
+ theme: isDarkColor(surface) ? "night" : "stripe",
120
+ variables: {
121
+ colorBackground: surface,
122
+ colorText: color(CSS_VARS.FIELD_INPUT_COLOR) || DEFAULT_COLORS.TEXT,
123
+ colorTextPlaceholder: color(CSS_VARS.FIELD_INPUT_PLACEHOLDER_COLOR) ||
124
+ DEFAULT_COLORS.PLACEHOLDER,
125
+ colorDanger: color(CSS_VARS.FIELD_INPUT_ERROR_COLOR) || DEFAULT_COLORS.ERROR,
126
+ colorPrimary: color(CSS_VARS.PRIMARY) || undefined,
127
+ fontFamily: read(CSS_VARS.FONT_FAMILY) || undefined,
128
+ borderRadius: resolve(read(CSS_VARS.BORDER_RADIUS), "length") || undefined,
129
+ },
130
+ };
131
+ });
92
132
  }
93
133
  /**
94
134
  * Creates a Stripe element style configuration based on theme colors.
@@ -130,6 +170,12 @@ export function createPortalElement(className) {
130
170
  /**
131
171
  * Positions a portal element to match a placeholder's position.
132
172
  *
173
+ * The coordinates are document-relative, so a host page that scrolls the window
174
+ * moves the portal with the document and every recomputed value is identical.
175
+ * Only a scroll in an ancestor container (the Studio preview frame) actually
176
+ * moves the placeholder under the portal. Tracking runs every frame, so an
177
+ * unchanged position returns without writing and costs that host page nothing.
178
+ *
133
179
  * @param placeholder - The placeholder element inside Shadow DOM
134
180
  * @param portal - The portal element in the document body
135
181
  * @param zIndex - The z-index value for the portal
@@ -147,51 +193,54 @@ export function positionPortalElement(placeholder, portal, zIndex, inlinePadding
147
193
  // Calculate padding in pixels (assuming 16px base font size)
148
194
  const paddingPx = inlinePadding * 16;
149
195
  const totalPadding = paddingPx * 2;
196
+ const top = `${rect.top + window.scrollY}px`;
197
+ const left = `${rect.left + window.scrollX}px`;
198
+ const width = `${rect.width - totalPadding}px`;
199
+ const height = `${rect.height}px`;
200
+ if (portal.style.display === "" &&
201
+ portal.style.top === top &&
202
+ portal.style.left === left &&
203
+ portal.style.width === width &&
204
+ portal.style.height === height) {
205
+ return;
206
+ }
150
207
  portal.style.display = "";
151
208
  portal.style.position = "absolute";
152
- portal.style.top = `${rect.top + window.scrollY}px`;
153
- portal.style.left = `${rect.left + window.scrollX}px`;
154
- portal.style.width = `${rect.width - totalPadding}px`;
155
- portal.style.height = `${rect.height}px`;
209
+ portal.style.top = top;
210
+ portal.style.left = left;
211
+ portal.style.width = width;
212
+ portal.style.height = height;
156
213
  portal.style.zIndex = `${zIndex}`;
157
214
  portal.style.margin = "0";
158
215
  portal.style.paddingInline = `${inlinePadding}rem`;
159
216
  }
160
217
  /**
161
- * Creates a debounced function that uses requestAnimationFrame.
162
- * Useful for DOM operations that should be batched with the browser's paint cycle.
218
+ * Coalesces repeated calls into one run in the next animation frame.
219
+ *
220
+ * Portal tracking has to keep up with a scroll that is still in progress. A
221
+ * trailing debounce cannot: each scroll event restarts its timer, so the portals
222
+ * hold their stale position for the whole gesture and only catch up once the
223
+ * scroll stops. Running on every frame that had at least one call matches the
224
+ * rate the portals are painted at.
163
225
  *
164
- * @param fn - The function to debounce
165
- * @param delay - Debounce delay in milliseconds
166
- * @returns Object with the debounced function and a cancel method
226
+ * @param fn - The function to run, at most once per frame
227
+ * @returns Object with the throttled function and a cancel method
167
228
  */
168
- export function createDebouncedRAF(fn, delay) {
169
- let timeoutId = null;
229
+ export function createFrameThrottle(fn) {
170
230
  let rafId = null;
171
231
  const execute = () => {
172
- if (timeoutId !== null) {
173
- clearTimeout(timeoutId);
174
- }
175
- timeoutId = setTimeout(() => {
176
- if (rafId !== null) {
177
- cancelAnimationFrame(rafId);
178
- }
179
- rafId = requestAnimationFrame(() => {
180
- fn();
181
- rafId = null;
182
- });
183
- timeoutId = null;
184
- }, delay);
232
+ if (rafId !== null)
233
+ return;
234
+ rafId = requestAnimationFrame(() => {
235
+ rafId = null;
236
+ fn();
237
+ });
185
238
  };
186
239
  const cancel = () => {
187
- if (timeoutId !== null) {
188
- clearTimeout(timeoutId);
189
- timeoutId = null;
190
- }
191
- if (rafId !== null) {
192
- cancelAnimationFrame(rafId);
193
- rafId = null;
194
- }
240
+ if (rafId === null)
241
+ return;
242
+ cancelAnimationFrame(rafId);
243
+ rafId = null;
195
244
  };
196
245
  return { execute, cancel };
197
246
  }
@@ -7,6 +7,6 @@ export default css `
7
7
  .terms-container {
8
8
  display: flex;
9
9
  flex-direction: column;
10
- gap: var(--spacing-xs);
10
+ gap: 0.25rem;
11
11
  }
12
12
  `;
@@ -204,6 +204,7 @@ export declare class CorasPaymentPage extends CorasPaymentPage_base {
204
204
  private getShortestExpirationSeconds;
205
205
  private getTotalQuantity;
206
206
  private getTotalPriceFormatted;
207
+ private renderPaymentSkeleton;
207
208
  private renderContent;
208
209
  private getLoyaltyAccountUrl;
209
210
  private renderSuccess;
@@ -54,6 +54,7 @@ import { FormValidationController } from "./controllers/form-validation.controll
54
54
  import { LayoutController } from "./controllers/layout.controller.js";
55
55
  import { CryptoAuthController } from "./controllers/crypto-auth.controller.js";
56
56
  import { CryptoTransferController, } from "./controllers/crypto-transfer.controller.js";
57
+ import { getApiLanguageCode } from "../../core/utils/locale.js";
57
58
  /**
58
59
  * Payment page component for processing checkout and payments.
59
60
  *
@@ -169,7 +170,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
169
170
  this.countriesTask = new Task(this, {
170
171
  autoRun: false,
171
172
  task: async () => {
172
- const language_code = this.locale.split("-")[0] || "en";
173
+ const language_code = getApiLanguageCode(this.locale);
173
174
  const { data, error } = await this.getApiService().fetchCountries(language_code);
174
175
  if (error) {
175
176
  console.error("Error fetching countries:", error);
@@ -254,7 +255,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
254
255
  this.reservationsCheckTask = new Task(this, {
255
256
  autoRun: false,
256
257
  task: async () => {
257
- const bookingSession = getBookingSession();
258
+ const bookingSession = getBookingSession(this.distributorID);
258
259
  // No session means there is nothing to check out: a legitimate empty
259
260
  // state (lost/expired session, or the branding preview), not an error.
260
261
  // Returning empty keeps the error modal reserved for real API failures.
@@ -271,7 +272,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
271
272
  this.reservationsTask = new Task(this, {
272
273
  autoRun: false,
273
274
  task: async () => {
274
- const bookingSession = getBookingSession();
275
+ const bookingSession = getBookingSession(this.distributorID);
275
276
  if (!bookingSession) {
276
277
  return {
277
278
  reservations: null,
@@ -1161,7 +1162,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
1161
1162
  this.scrollToFirstError();
1162
1163
  return null;
1163
1164
  }
1164
- const bookingSession = getBookingSession();
1165
+ const bookingSession = getBookingSession(this.distributorID);
1165
1166
  if (!bookingSession) {
1166
1167
  console.error("Cannot confirm reservation: booking session is missing.");
1167
1168
  return null;
@@ -1210,7 +1211,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
1210
1211
  this.userId = responseData.user_id;
1211
1212
  this.pendingClientSecret = responseData.client_secret ?? null;
1212
1213
  // Invalidate booking session so a page refresh cannot replay the reservation
1213
- removeBookingSession();
1214
+ removeBookingSession(this.distributorID);
1214
1215
  return responseData;
1215
1216
  }
1216
1217
  return null;
@@ -1441,6 +1442,15 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
1441
1442
  currency: this.currency,
1442
1443
  });
1443
1444
  }
1445
+ renderPaymentSkeleton() {
1446
+ return html `
1447
+ <div class="payment-skeleton" aria-hidden="true">
1448
+ ${Array.from({ length: 5 }, () => html `<div class="coras-skeleton sk-field"></div>`)}
1449
+ <div class="coras-skeleton sk-block"></div>
1450
+ </div>
1451
+ <span class="coras-sr-only" role="status">${msg("Loading")}</span>
1452
+ `;
1453
+ }
1444
1454
  renderContent() {
1445
1455
  if (this.paymentSuccessful) {
1446
1456
  return this.renderSuccess();
@@ -1449,8 +1459,8 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
1449
1459
  return this.renderFailure();
1450
1460
  }
1451
1461
  return this.configTask.render({
1452
- initial: () => html `<div class="loading">${msg("Loading...")}</div>`,
1453
- pending: () => html `<div class="loading">${msg("Loading...")}</div>`,
1462
+ initial: () => this.renderPaymentSkeleton(),
1463
+ pending: () => this.renderPaymentSkeleton(),
1454
1464
  error: () => {
1455
1465
  this.showErrorModalOnce();
1456
1466
  return html ``;
@@ -1513,7 +1523,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
1513
1523
  // reservations below - the check response only carries requirements, and a
1514
1524
  // basket of plain tickets returns no per-reservation entries.
1515
1525
  return this.reservationsCheckTask.render({
1516
- pending: () => html `<div class="loading">${msg("Loading...")}</div>`,
1526
+ pending: () => this.renderPaymentSkeleton(),
1517
1527
  error: () => {
1518
1528
  this.showErrorModalOnce();
1519
1529
  return html ``;
@@ -1523,7 +1533,7 @@ let CorasPaymentPage = class CorasPaymentPage extends CoinbaseMixin(ErrorModalMi
1523
1533
  }
1524
1534
  renderReservations() {
1525
1535
  return this.reservationsTask.render({
1526
- pending: () => html `<div class="loading">${msg("Loading...")}</div>`,
1536
+ pending: () => this.renderPaymentSkeleton(),
1527
1537
  complete: (data) => {
1528
1538
  // Nothing to check out: real customers are guided back to browse;
1529
1539
  // the branding preview renders the full form (with Pay now disabled).