@authhero/widget 0.32.41 → 0.33.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.
@@ -105,8 +105,8 @@ function brandingToCssVars(branding) {
105
105
  // Primary color
106
106
  if (branding.colors?.primary) {
107
107
  vars["--ah-color-primary"] = branding.colors.primary;
108
- // Generate hover variant (slightly darker)
109
- vars["--ah-color-primary-hover"] = branding.colors.primary;
108
+ // Hover variant is derived in CSS (color-mix darken) unless the theme
109
+ // sets an explicit base_hover_color; don't pin it to the base color here.
110
110
  }
111
111
  // Page background
112
112
  if (branding.colors?.page_background) {
@@ -173,7 +173,6 @@ function themeToCssVars(theme) {
173
173
  // Primary button
174
174
  if (c.primary_button) {
175
175
  vars["--ah-color-primary"] = c.primary_button;
176
- vars["--ah-color-primary-hover"] = c.primary_button;
177
176
  // Honor explicit primary_button_label when set; otherwise auto-pick.
178
177
  if (c.primary_button_label) {
179
178
  vars["--ah-color-text-on-primary"] = c.primary_button_label;
@@ -517,7 +516,7 @@ function escapeAttr(value) {
517
516
  .replace(/>/g, ">");
518
517
  }
519
518
 
520
- const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 16px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{width:var(--ah-widget-max-width, 400px);margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 24px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-subtitle, 16px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
519
+ const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 16px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{width:var(--ah-widget-max-width, 400px);margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);border:var(--ah-widget-border-width, 0) solid var(--ah-widget-border-color, transparent);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 24px);color:var(--ah-color-text-header, var(--ah-color-header, #1e212a));line-height:1.2}.description{font-size:var(--ah-font-size-subtitle, 16px);font-weight:var(--ah-font-weight-subtitle, 400);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:var(--ah-font-size-body, 14px);font-weight:var(--ah-font-weight-body, 400);color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:var(--ah-font-size-link, 14px);font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-focus-ring, var(--ah-color-link, #635dff));outline-offset:2px;border-radius:2px}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a:focus-visible{outline:2px solid var(--ah-color-focus-ring, var(--ah-color-link, #635dff));outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
521
520
 
522
521
  const AuthheroWidget = class {
523
522
  constructor(hostRef) {
@@ -636,6 +635,11 @@ const AuthheroWidget = class {
636
635
  * Aborted on screen change or component disconnect.
637
636
  */
638
637
  conditionalMediationAbort;
638
+ /**
639
+ * URL of the custom font stylesheet currently injected into the document
640
+ * head, tracked so it can be removed when the theme/branding changes.
641
+ */
642
+ loadedFontUrl;
639
643
  /**
640
644
  * Emitted when the form is submitted.
641
645
  * The consuming application should handle the submission unless autoSubmit is true.
@@ -793,6 +797,39 @@ const AuthheroWidget = class {
793
797
  applyThemeStyles() {
794
798
  const vars = mergeThemeVars(this._branding, this._theme);
795
799
  applyCssVars(this.el, vars);
800
+ this.loadCustomFont();
801
+ }
802
+ /**
803
+ * Load the custom font stylesheet referenced by the theme/branding.
804
+ * The stylesheet is expected to define the `ulp-font` family (Auth0
805
+ * convention), which the component CSS already falls back to. @font-face
806
+ * rules must live in the document (not the shadow DOM) to take effect, so
807
+ * the <link> is injected into the document head, de-duplicated by href.
808
+ */
809
+ loadCustomFont() {
810
+ if (typeof document === "undefined") {
811
+ return;
812
+ }
813
+ const fontUrl = this._theme?.fonts?.font_url || this._branding?.font?.url;
814
+ if (fontUrl === this.loadedFontUrl) {
815
+ return;
816
+ }
817
+ // Remove any previously injected font stylesheet so old fonts don't
818
+ // persist across tenant/theme switches. Match by dataset attribute
819
+ // rather than interpolating the URL into a selector string.
820
+ for (const link of Array.from(document.head.querySelectorAll("link[data-authhero-font]"))) {
821
+ link.remove();
822
+ }
823
+ this.loadedFontUrl = undefined;
824
+ if (!fontUrl) {
825
+ return;
826
+ }
827
+ const link = document.createElement("link");
828
+ link.rel = "stylesheet";
829
+ link.href = fontUrl;
830
+ link.setAttribute("data-authhero-font", fontUrl);
831
+ document.head.appendChild(link);
832
+ this.loadedFontUrl = fontUrl;
796
833
  }
797
834
  /**
798
835
  * Focus the first input field in the form.
@@ -818,6 +855,94 @@ const AuthheroWidget = class {
818
855
  }
819
856
  });
820
857
  }
858
+ /**
859
+ * Pending resolvers awaiting the next render, flushed by
860
+ * `componentDidRender`. Lets `swapScreen` wait until the new screen is
861
+ * actually in the DOM before measuring its height.
862
+ */
863
+ pendingRenderResolvers = [];
864
+ componentDidRender() {
865
+ if (this.pendingRenderResolvers.length === 0)
866
+ return;
867
+ const resolvers = this.pendingRenderResolvers;
868
+ this.pendingRenderResolvers = [];
869
+ resolvers.forEach((resolve) => resolve());
870
+ }
871
+ /**
872
+ * Resolves after Stencil flushes the next render, so `swapScreen` waits for
873
+ * the new screen to be in the DOM before measuring its height.
874
+ */
875
+ nextRender() {
876
+ return new Promise((resolve) => {
877
+ let settled = false;
878
+ const done = () => {
879
+ if (settled)
880
+ return;
881
+ settled = true;
882
+ resolve();
883
+ };
884
+ this.pendingRenderResolvers.push(done);
885
+ // Safety net: a screen swap always triggers a render here, but never
886
+ // hang the swap if one somehow doesn't fire — resolve after a couple of
887
+ // frames at the latest.
888
+ requestAnimationFrame(() => requestAnimationFrame(done));
889
+ });
890
+ }
891
+ /**
892
+ * Apply a screen swap, animating the widget card's height to the next
893
+ * screen's size (Stripe-style): lock the current height, swap the content,
894
+ * measure the new natural height, then animate between them. The content
895
+ * itself doesn't fade — the card just resizes.
896
+ *
897
+ * The card (.widget-container) is animated rather than the host because it
898
+ * carries the background, so its resize is visible; clipping its content
899
+ * (not the host) also keeps the card's drop-shadow intact.
900
+ *
901
+ * Degrades to an immediate swap when the Web Animations API is unavailable
902
+ * or the user prefers reduced motion.
903
+ */
904
+ async swapScreen(apply) {
905
+ const card = this.el.shadowRoot?.querySelector(".widget-container");
906
+ const prefersReducedMotion = typeof window.matchMedia === "function" &&
907
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
908
+ if (!card || typeof card.animate !== "function" || prefersReducedMotion) {
909
+ apply();
910
+ return;
911
+ }
912
+ // Lock the current height and clip, so swapping the content doesn't jump
913
+ // before the animation runs.
914
+ const startHeight = card.getBoundingClientRect().height;
915
+ card.style.height = `${startHeight}px`;
916
+ card.style.overflow = "hidden";
917
+ apply();
918
+ // Wait for the new screen to render before measuring its height.
919
+ await this.nextRender();
920
+ const cleanup = () => {
921
+ card.style.height = "";
922
+ card.style.overflow = "";
923
+ };
924
+ // Measure the new content's natural height without painting: toggle to
925
+ // auto, read, restore — all synchronous, so there's no visible jump.
926
+ card.style.height = "auto";
927
+ const endHeight = card.getBoundingClientRect().height;
928
+ card.style.height = `${startHeight}px`;
929
+ card.getBoundingClientRect(); // force reflow from startHeight
930
+ if (Math.abs(endHeight - startHeight) < 1) {
931
+ cleanup();
932
+ return;
933
+ }
934
+ const animation = card.animate([{ height: `${startHeight}px` }, { height: `${endHeight}px` }], { duration: 520, easing: "cubic-bezier(0.22, 1, 0.36, 1)" });
935
+ try {
936
+ await animation.finished;
937
+ }
938
+ catch {
939
+ // Animation cancelled (e.g. superseded by a newer swap); the final
940
+ // DOM state is already correct, so just fall through to cleanup.
941
+ }
942
+ finally {
943
+ cleanup();
944
+ }
945
+ }
821
946
  /**
822
947
  * Get the effective autoNavigate value (defaults to autoSubmit if not set)
823
948
  */
@@ -1120,45 +1245,55 @@ const AuthheroWidget = class {
1120
1245
  }
1121
1246
  else if (!response.ok && result.screen) {
1122
1247
  // Handle validation errors (400 response) — preserve user input
1123
- this._screen = result.screen;
1124
- this.initFormDataFromDefaults(result.screen);
1125
- this.screenChange.emit(result.screen);
1126
- this.updateDataScreenAttribute();
1127
- this.focusFirstInput();
1248
+ await this.swapScreen(() => {
1249
+ // Clear loading first so the captured "after" snapshot doesn't
1250
+ // freeze a disabled button into the cross-fade.
1251
+ this.loading = false;
1252
+ this._screen = result.screen;
1253
+ this.initFormDataFromDefaults(result.screen);
1254
+ this.screenChange.emit(result.screen);
1255
+ this.updateDataScreenAttribute();
1256
+ this.focusFirstInput();
1257
+ });
1128
1258
  }
1129
1259
  else if (result.screen) {
1130
1260
  // Next screen (success)
1131
- this._screen = result.screen;
1132
- this.formData = {};
1133
- this.initFormDataFromDefaults(result.screen);
1134
- this.screenChange.emit(result.screen);
1135
- this.updateDataScreenAttribute();
1136
- // Update screenId if returned in response
1137
- if (result.screenId) {
1138
- this.screenId = result.screenId;
1139
- }
1140
- // Persist state (especially for session storage mode)
1141
- this.persistState();
1142
- // Update URL path if navigateUrl is provided (client-side navigation)
1143
- if (result.navigateUrl && this.shouldAutoNavigate) {
1144
- window.history.pushState({ screen: result.screenId, state: this.state }, "", result.navigateUrl);
1145
- }
1146
- // Apply branding if included
1147
- if (result.branding) {
1148
- this._branding = result.branding;
1149
- this.applyThemeStyles();
1150
- }
1151
- // Update state if returned
1152
- if (result.state) {
1153
- this.state = result.state;
1261
+ await this.swapScreen(() => {
1262
+ // Clear loading first so the captured "after" snapshot doesn't
1263
+ // freeze a disabled button into the cross-fade.
1264
+ this.loading = false;
1265
+ this._screen = result.screen;
1266
+ this.formData = {};
1267
+ this.initFormDataFromDefaults(result.screen);
1268
+ this.screenChange.emit(result.screen);
1269
+ this.updateDataScreenAttribute();
1270
+ // Update screenId if returned in response
1271
+ if (result.screenId) {
1272
+ this.screenId = result.screenId;
1273
+ }
1274
+ // Persist state (especially for session storage mode)
1154
1275
  this.persistState();
1155
- }
1156
- // Perform WebAuthn ceremony if present (structured data, not script)
1157
- if (result.ceremony) {
1158
- this.performWebAuthnCeremony(result.ceremony);
1159
- }
1160
- // Focus first input on new screen
1161
- this.focusFirstInput();
1276
+ // Update URL path if navigateUrl is provided (client-side navigation)
1277
+ if (result.navigateUrl && this.shouldAutoNavigate) {
1278
+ window.history.pushState({ screen: result.screenId, state: this.state }, "", result.navigateUrl);
1279
+ }
1280
+ // Apply branding if included
1281
+ if (result.branding) {
1282
+ this._branding = result.branding;
1283
+ this.applyThemeStyles();
1284
+ }
1285
+ // Update state if returned
1286
+ if (result.state) {
1287
+ this.state = result.state;
1288
+ this.persistState();
1289
+ }
1290
+ // Perform WebAuthn ceremony if present (structured data, not script)
1291
+ if (result.ceremony) {
1292
+ this.performWebAuthnCeremony(result.ceremony);
1293
+ }
1294
+ // Focus first input on new screen
1295
+ this.focusFirstInput();
1296
+ });
1162
1297
  }
1163
1298
  else if (result.complete) {
1164
1299
  // Flow complete without redirect
@@ -39,7 +39,7 @@
39
39
  left: 16px;
40
40
  top: 50%;
41
41
  transform: translateY(-50%);
42
- font-size: 16px;
42
+ font-size: var(--ah-font-size-label, 16px);
43
43
  font-weight: var(--ah-font-weight-label, 400);
44
44
  color: var(--ah-color-text-muted, #65676e);
45
45
  pointer-events: none;
@@ -83,9 +83,9 @@ input[type="date"].input-field+.input-label {
83
83
  padding: 16px;
84
84
  font-size: 16px;
85
85
  font-family: inherit;
86
- color: var(--ah-color-text, #1e212a);
86
+ color: var(--ah-color-input-text, var(--ah-color-text, #1e212a));
87
87
  background-color: var(--ah-color-input-bg, #ffffff);
88
- border: 1px solid var(--ah-color-border, #c9cace);
88
+ border: var(--ah-input-border-width, 1px) solid var(--ah-color-border, #c9cace);
89
89
  border-radius: var(--ah-input-radius, 3px);
90
90
  outline: none;
91
91
  transition:
@@ -145,12 +145,12 @@ input[type="date"].input-field+.input-label {
145
145
  display: flex;
146
146
  align-items: center;
147
147
  justify-content: center;
148
- color: var(--ah-color-text-muted, #65676e);
148
+ color: var(--ah-color-icon, var(--ah-color-text-muted, #65676e));
149
149
  transition: color 0.15s ease;
150
150
  }
151
151
 
152
152
  .password-toggle:hover {
153
- color: var(--ah-color-text, #1e212a);
153
+ color: var(--ah-color-icon, var(--ah-color-text, #1e212a));
154
154
  }
155
155
 
156
156
  .password-toggle svg {
@@ -188,7 +188,7 @@ input[type="date"].input-field+.input-label {
188
188
  .field-link a {
189
189
  color: var(--ah-color-link, #635dff);
190
190
  text-decoration: var(--ah-link-decoration, none);
191
- font-size: 14px;
191
+ font-size: var(--ah-font-size-link, 14px);
192
192
  font-weight: var(--ah-font-weight-link, 400);
193
193
  }
194
194
 
@@ -219,7 +219,8 @@ input[type="date"].input-field+.input-label {
219
219
  }
220
220
 
221
221
  .checkbox-label {
222
- font-size: 14px;
222
+ font-size: var(--ah-font-size-body, 14px);
223
+ font-weight: var(--ah-font-weight-body, 400);
223
224
  color: var(--ah-color-text, #1e212a);
224
225
  line-height: 1.5;
225
226
  }
@@ -236,7 +237,7 @@ input[type="date"].input-field+.input-label {
236
237
  gap: 10px;
237
238
  width: 100%;
238
239
  padding: 14px 20px;
239
- font-size: 16px;
240
+ font-size: var(--ah-font-size-btn, 16px);
240
241
  font-weight: var(--ah-font-weight-btn, 400);
241
242
  font-family: inherit;
242
243
  line-height: 1.25;
@@ -262,7 +263,7 @@ input[type="date"].input-field+.input-label {
262
263
  }
263
264
 
264
265
  .btn:focus-visible {
265
- outline: 2px solid var(--ah-color-primary, #635dff);
266
+ outline: 2px solid var(--ah-color-focus-ring, var(--ah-color-primary, #635dff));
266
267
  outline-offset: 2px;
267
268
  }
268
269
 
@@ -274,14 +275,14 @@ input[type="date"].input-field+.input-label {
274
275
  }
275
276
 
276
277
  .btn-primary:not(:disabled):hover {
277
- filter: brightness(0.85);
278
+ background-color: var(--ah-color-primary-hover, color-mix(in srgb, var(--ah-color-primary, #635dff) 85%, black));
278
279
  }
279
280
 
280
281
  /* Secondary Button (Social/OIDC) */
281
282
  .btn-secondary {
282
283
  background-color: var(--ah-color-bg, #ffffff);
283
- color: var(--ah-color-text, #1e212a);
284
- border: 1px solid var(--ah-color-border, #c9cace);
284
+ color: var(--ah-btn-secondary-text, var(--ah-color-text, #1e212a));
285
+ border: var(--ah-btn-border-width, 1px) solid var(--ah-btn-secondary-border, var(--ah-color-border, #c9cace));
285
286
  }
286
287
 
287
288
  .btn-secondary:not(:disabled):hover {
@@ -321,11 +322,11 @@ input[type="date"].input-field+.input-label {
321
322
  padding: 12px 8px;
322
323
  font-size: var(--ah-font-size-body, 16px);
323
324
  font-family: var(--ah-font-family, inherit);
324
- border: 1px solid var(--ah-color-border, #c9cace);
325
+ border: var(--ah-input-border-width, 1px) solid var(--ah-color-border, #c9cace);
325
326
  border-right: none;
326
- border-radius: var(--ah-border-radius-input, 4px) 0 0 var(--ah-border-radius-input, 4px);
327
- background-color: var(--ah-color-surface, #fff);
328
- color: var(--ah-color-text, #1e212a);
327
+ border-radius: var(--ah-input-radius, 4px) 0 0 var(--ah-input-radius, 4px);
328
+ background-color: var(--ah-color-input-bg, var(--ah-color-surface, #fff));
329
+ color: var(--ah-color-input-text, var(--ah-color-text, #1e212a));
329
330
  cursor: pointer;
330
331
  appearance: none;
331
332
  -webkit-appearance: none;
@@ -352,7 +353,7 @@ input[type="date"].input-field+.input-label {
352
353
  }
353
354
 
354
355
  .phone-input-wrapper .input-field {
355
- border-radius: 0 var(--ah-border-radius-input, 4px) var(--ah-border-radius-input, 4px) 0;
356
+ border-radius: 0 var(--ah-input-radius, 4px) var(--ah-input-radius, 4px) 0;
356
357
  }
357
358
 
358
359
  .phone-input-wrapper .input-label.floating,
@@ -450,9 +451,9 @@ input[type="date"].input-field+.input-label {
450
451
  ========================================================================== */
451
452
 
452
453
  .text-title {
453
- font-size: 20px;
454
- font-weight: 400;
455
- color: var(--ah-color-text, #1e212a);
454
+ font-size: var(--ah-font-size-title, 20px);
455
+ font-weight: var(--ah-font-weight-title, 400);
456
+ color: var(--ah-color-text-header, var(--ah-color-header, #1e212a));
456
457
  margin: 8px 0;
457
458
  line-height: 1.3;
458
459
  }
@@ -462,7 +463,8 @@ input[type="date"].input-field+.input-label {
462
463
  }
463
464
 
464
465
  .text-description {
465
- font-size: 14px;
466
+ font-size: var(--ah-font-size-subtitle, 14px);
467
+ font-weight: var(--ah-font-weight-subtitle, 400);
466
468
  color: var(--ah-color-text-muted, #65676e);
467
469
  margin: 4px 0;
468
470
  line-height: 1.5;
@@ -495,7 +497,7 @@ input[type="date"].input-field+.input-label {
495
497
  .link {
496
498
  color: var(--ah-color-link, #635dff);
497
499
  text-decoration: var(--ah-link-decoration, none);
498
- font-size: 14px;
500
+ font-size: var(--ah-font-size-link, 14px);
499
501
  transition: color 0.15s ease;
500
502
  }
501
503
 
@@ -504,7 +506,7 @@ input[type="date"].input-field+.input-label {
504
506
  }
505
507
 
506
508
  .link:focus-visible {
507
- outline: 2px solid var(--ah-color-link, #635dff);
509
+ outline: 2px solid var(--ah-color-focus-ring, var(--ah-color-link, #635dff));
508
510
  outline-offset: 2px;
509
511
  border-radius: 2px;
510
512
  }
@@ -568,7 +570,8 @@ input[type="date"].input-field+.input-label {
568
570
  ========================================================================== */
569
571
 
570
572
  .rich-text {
571
- font-size: 14px;
573
+ font-size: var(--ah-font-size-body, 14px);
574
+ font-weight: var(--ah-font-weight-body, 400);
572
575
  line-height: 1.5;
573
576
  color: var(--ah-color-text, #1e212a);
574
577
  }
@@ -26,6 +26,7 @@
26
26
  margin: 0 auto;
27
27
  background-color: var(--ah-color-bg, #ffffff);
28
28
  border-radius: var(--ah-widget-radius, 5px);
29
+ border: var(--ah-widget-border-width, 0) solid var(--ah-widget-border-color, transparent);
29
30
  box-shadow: var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));
30
31
  box-sizing: border-box;
31
32
  }
@@ -77,12 +78,13 @@
77
78
  font-weight: var(--ah-font-weight-title, 700);
78
79
  text-align: var(--ah-title-align, center);
79
80
  margin: var(--ah-title-margin, 24px 0 24px);
80
- color: var(--ah-color-header, #1e212a);
81
+ color: var(--ah-color-text-header, var(--ah-color-header, #1e212a));
81
82
  line-height: 1.2;
82
83
  }
83
84
 
84
85
  .description {
85
86
  font-size: var(--ah-font-size-subtitle, 16px);
87
+ font-weight: var(--ah-font-weight-subtitle, 400);
86
88
  text-align: var(--ah-title-align, center);
87
89
  margin: var(--ah-description-margin, 0 0 8px);
88
90
  color: var(--ah-color-text, #1e212a);
@@ -185,14 +187,15 @@ form {
185
187
  }
186
188
 
187
189
  .link-wrapper {
188
- font-size: 14px;
190
+ font-size: var(--ah-font-size-body, 14px);
191
+ font-weight: var(--ah-font-weight-body, 400);
189
192
  color: var(--ah-color-text, #1e212a);
190
193
  }
191
194
 
192
195
  .link {
193
196
  color: var(--ah-color-link, #635dff);
194
197
  text-decoration: var(--ah-link-decoration, none);
195
- font-size: 14px;
198
+ font-size: var(--ah-font-size-link, 14px);
196
199
  font-weight: var(--ah-font-weight-link, 400);
197
200
  transition: color 150ms ease;
198
201
  }
@@ -202,7 +205,7 @@ form {
202
205
  }
203
206
 
204
207
  .link:focus-visible {
205
- outline: 2px solid var(--ah-color-link, #635dff);
208
+ outline: 2px solid var(--ah-color-focus-ring, var(--ah-color-link, #635dff));
206
209
  outline-offset: 2px;
207
210
  border-radius: 2px;
208
211
  }
@@ -231,7 +234,7 @@ form {
231
234
  }
232
235
 
233
236
  .widget-footer a:focus-visible {
234
- outline: 2px solid var(--ah-color-link, #635dff);
237
+ outline: 2px solid var(--ah-color-focus-ring, var(--ah-color-link, #635dff));
235
238
  outline-offset: 2px;
236
239
  border-radius: 2px;
237
240
  }