@lime-bundles/widget 3.2.0 → 3.2.2

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/index.js CHANGED
@@ -142,6 +142,7 @@ function bindDropdown(select) {
142
142
  let optionEls = [];
143
143
  let instance;
144
144
  function syncFromSelect() {
145
+ trigger.disabled = select.disabled;
145
146
  const opts = readOptions(select);
146
147
  const idx = select.selectedIndex;
147
148
  triggerLabel.textContent = idx >= 0 && opts[idx] ? opts[idx].label : "";
@@ -188,7 +189,7 @@ function bindDropdown(select) {
188
189
  if (rect.width === 0) return false;
189
190
  const visibleCount = Math.min(optionEls.length || 1, MAX_VISIBLE_ITEMS);
190
191
  const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
191
- const scrollable = listbox.hasAttribute("data-lb-dropdown-portal") ? null : findScrollableAncestor(trigger);
192
+ const scrollable = findScrollableAncestor(trigger);
192
193
  const clip = scrollable ? (() => {
193
194
  const r = scrollable.getBoundingClientRect();
194
195
  return { top: r.top, bottom: r.bottom };
@@ -1051,30 +1052,26 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1051
1052
  root.appendChild(pricingSection.el);
1052
1053
  const savingsBar = wc.savingsBar.visible ? renderSavingsBar2() : null;
1053
1054
  if (savingsBar) root.appendChild(savingsBar.el);
1054
- const placeholder = el("div", "lb-mix-match__price-placeholder", {
1055
- "data-price-placeholder": ""
1055
+ const cta = buildCtaButton(`Select ${requiredQty} items to unlock`);
1056
+ cta.disabled = true;
1057
+ cta.addEventListener("click", () => {
1058
+ if (cta.disabled) return;
1059
+ onAddToCart(buildCartLines(selections, bundle));
1056
1060
  });
1057
- const placeholderText = el(
1058
- "span",
1059
- "lb-mix-match__price-placeholder-text"
1060
- );
1061
- placeholderText.textContent = `Select ${requiredQty} items to see price`;
1062
- placeholder.appendChild(placeholderText);
1063
- root.appendChild(placeholder);
1064
1061
  const modal = renderModal(bundle, eligible, currency, {
1065
1062
  showSearch: wc.showSearch,
1066
1063
  showQtySelector,
1067
1064
  selections,
1068
1065
  onAdd: (product, variant, quantity) => addSelection(product, variant, quantity),
1069
- isComplete: () => selections.length >= requiredQty
1066
+ isComplete: () => selections.length >= requiredQty,
1067
+ getFocusAfterAdd: () => {
1068
+ const empty = slotsContainer.querySelector(
1069
+ ".lb-mix-match__slot--empty"
1070
+ );
1071
+ return empty ?? cta;
1072
+ }
1070
1073
  });
1071
1074
  root.appendChild(modal.el);
1072
- const cta = buildCtaButton(`Select ${requiredQty} items to unlock`);
1073
- cta.disabled = true;
1074
- cta.addEventListener("click", () => {
1075
- if (cta.disabled) return;
1076
- onAddToCart(buildCartLines(selections, bundle));
1077
- });
1078
1075
  root.appendChild(cta);
1079
1076
  root.appendChild(
1080
1077
  el("p", "lb-bundle-error", { "data-error": "", "aria-live": "polite" })
@@ -1125,7 +1122,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1125
1122
  progress.update(selections.length);
1126
1123
  pricingSection.update(selections, bundle, currency);
1127
1124
  if (savingsBar) savingsBar.update(selections, bundle, currency);
1128
- placeholder.style.display = selections.length === 0 ? "" : "none";
1129
1125
  modal.refreshCounts();
1130
1126
  updateCta();
1131
1127
  }
@@ -1207,12 +1203,14 @@ function renderProgress(requiredQty) {
1207
1203
  const wrap = el("div", "lb-mix-match__progress");
1208
1204
  const labels = el("div", "lb-mix-match__progress-labels");
1209
1205
  const count = el("span", "lb-mix-match__progress-count", {
1210
- "data-progress-count": ""
1206
+ "data-progress-count": "",
1207
+ "aria-live": "polite"
1211
1208
  });
1212
1209
  count.textContent = `0 of ${requiredQty} selected`;
1213
1210
  labels.appendChild(count);
1214
1211
  const remaining = el("span", "lb-mix-match__progress-remaining", {
1215
- "data-progress-remaining": ""
1212
+ "data-progress-remaining": "",
1213
+ "aria-live": "polite"
1216
1214
  });
1217
1215
  remaining.textContent = `${requiredQty} more to go`;
1218
1216
  labels.appendChild(remaining);
@@ -1565,14 +1563,12 @@ function renderModal(bundle, eligible, currency, handlers) {
1565
1563
  return { min: rule.min, max, cap };
1566
1564
  };
1567
1565
  let stepper = null;
1566
+ let qtyGroup = null;
1568
1567
  if (!ep.isOos && handlers.showQtySelector) {
1569
- const qtyGroup = el(
1568
+ qtyGroup = el(
1570
1569
  "div",
1571
1570
  "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group"
1572
1571
  );
1573
- const qtyLabel = el("span", "lb-bundle-variant-option-label");
1574
- qtyLabel.textContent = "Quantity";
1575
- qtyGroup.appendChild(qtyLabel);
1576
1572
  stepper = renderQtyStepper({
1577
1573
  initial: rule.min,
1578
1574
  getBounds: () => {
@@ -1581,7 +1577,6 @@ function renderModal(bundle, eligible, currency, handlers) {
1581
1577
  }
1582
1578
  });
1583
1579
  qtyGroup.appendChild(stepper.el);
1584
- info.appendChild(qtyGroup);
1585
1580
  }
1586
1581
  const row = {
1587
1582
  el: productEl,
@@ -1590,12 +1585,12 @@ function renderModal(bundle, eligible, currency, handlers) {
1590
1585
  refreshFromSelections: () => {
1591
1586
  }
1592
1587
  };
1588
+ const optionSelects = [];
1593
1589
  if (availableVariants.length > 1) {
1594
1590
  const optionNames = availableVariants[0].selectedOptions.map(
1595
1591
  (o) => o.name
1596
1592
  );
1597
1593
  const productIdTail = ep.product.id.replace(/^.*\//, "");
1598
- const optionSelects = [];
1599
1594
  const resolveVariant = (values) => availableVariants.find(
1600
1595
  (v) => v.selectedOptions.length === values.length && v.selectedOptions.every((o, i) => o.value === values[i])
1601
1596
  ) ?? null;
@@ -1704,7 +1699,13 @@ function renderModal(bundle, eligible, currency, handlers) {
1704
1699
  productEl.appendChild(info);
1705
1700
  let addBtn = null;
1706
1701
  const refreshAddState = () => {
1707
- if (!addBtn) return;
1702
+ if (!addBtn) {
1703
+ optionSelects.forEach((sel) => {
1704
+ sel.disabled = true;
1705
+ });
1706
+ if (stepper) stepper.setExternallyDisabled(true);
1707
+ return;
1708
+ }
1708
1709
  const { cap } = computeStepperBounds();
1709
1710
  const already = alreadyInBundleFor(
1710
1711
  handlers.selections,
@@ -1717,24 +1718,46 @@ function renderModal(bundle, eligible, currency, handlers) {
1717
1718
  } else {
1718
1719
  countBadge.hidden = true;
1719
1720
  }
1720
- addBtn.disabled = handlers.isComplete() || cap < rule.min;
1721
+ const productInBundle = handlers.selections.some(
1722
+ (s) => s.productId === ep.product.id
1723
+ );
1724
+ if (productInBundle) {
1725
+ addBtn.disabled = true;
1726
+ addBtn.textContent = "Added";
1727
+ addBtn.setAttribute("aria-label", `Added ${ep.product.title}`);
1728
+ productEl.classList.add("lb-mix-match__modal-product--in-bundle");
1729
+ } else {
1730
+ addBtn.textContent = "Add";
1731
+ addBtn.setAttribute("aria-label", `Add ${ep.product.title}`);
1732
+ productEl.classList.remove("lb-mix-match__modal-product--in-bundle");
1733
+ addBtn.disabled = handlers.isComplete() || cap < rule.min;
1734
+ }
1735
+ const rowDisabled = addBtn.disabled;
1736
+ optionSelects.forEach((sel) => {
1737
+ sel.disabled = rowDisabled;
1738
+ });
1739
+ if (stepper) stepper.setExternallyDisabled(rowDisabled);
1721
1740
  };
1722
1741
  if (!ep.isOos) {
1723
1742
  const actions = el("div", "lb-mix-match__modal-product-actions");
1743
+ if (qtyGroup) actions.appendChild(qtyGroup);
1724
1744
  addBtn = document.createElement("button");
1725
1745
  addBtn.type = "button";
1726
1746
  addBtn.className = "lb-mix-match__modal-add";
1727
1747
  addBtn.textContent = "Add";
1748
+ addBtn.setAttribute("aria-label", `Add ${ep.product.title}`);
1728
1749
  addBtn.addEventListener("click", () => {
1729
1750
  if (!addBtn || addBtn.disabled) return;
1730
1751
  if (handlers.isComplete()) return;
1731
1752
  const qty = stepper ? stepper.value() : rule.min;
1732
1753
  handlers.onAdd(ep.product, currentVariant, qty);
1733
1754
  if (stepper) stepper.reset(rule.min);
1755
+ const nextFocus = handlers.getFocusAfterAdd();
1756
+ if (nextFocus) lastFocused = nextFocus;
1734
1757
  close();
1735
1758
  });
1736
1759
  actions.appendChild(addBtn);
1737
- productEl.appendChild(actions);
1760
+ info.appendChild(actions);
1738
1761
  }
1739
1762
  row.refreshFromSelections = () => {
1740
1763
  if (stepper) stepper.refresh();
@@ -1826,6 +1849,7 @@ function renderQtyStepper(opts) {
1826
1849
  plus.innerHTML = STEPPER_PLUS_SVG;
1827
1850
  wrap.appendChild(plus);
1828
1851
  let current = clamp2(opts.initial, opts.getBounds());
1852
+ let externallyDisabled = false;
1829
1853
  function clamp2(n, b) {
1830
1854
  return Math.max(b.min, Math.min(b.max, n));
1831
1855
  }
@@ -1833,8 +1857,8 @@ function renderQtyStepper(opts) {
1833
1857
  const b = opts.getBounds();
1834
1858
  current = clamp2(current, b);
1835
1859
  valueEl.textContent = String(current);
1836
- minus.disabled = current <= b.min;
1837
- plus.disabled = current >= b.max;
1860
+ minus.disabled = externallyDisabled || current <= b.min;
1861
+ plus.disabled = externallyDisabled || current >= b.max;
1838
1862
  }
1839
1863
  minus.addEventListener("click", () => {
1840
1864
  const b = opts.getBounds();
@@ -1854,7 +1878,11 @@ function renderQtyStepper(opts) {
1854
1878
  current = clamp2(qty, opts.getBounds());
1855
1879
  paint();
1856
1880
  },
1857
- refresh: paint
1881
+ refresh: paint,
1882
+ setExternallyDisabled(disabled) {
1883
+ externallyDisabled = disabled;
1884
+ paint();
1885
+ }
1858
1886
  };
1859
1887
  }
1860
1888
  function buildEligibleProducts(bundle, oosBehavior) {
@@ -2501,6 +2529,13 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2501
2529
  text-decoration: underline;
2502
2530
  }
2503
2531
 
2532
+ a.lb-bundle-product-name:focus-visible {
2533
+ outline: 2px solid var(--lb-primary-color);
2534
+ outline-offset: 0;
2535
+ box-shadow: none;
2536
+ border-radius: 2px;
2537
+ }
2538
+
2504
2539
  .lb-bundle-product-price {
2505
2540
  /* --lb-product-price-display fallback is the "on" branch; Liquid sets 'none' when merchant disables. */
2506
2541
  display: var(--lb-product-price-display, block);
@@ -2574,7 +2609,7 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2574
2609
  .lb-bundle-variant-option-groups {
2575
2610
  display: flex;
2576
2611
  flex-direction: column;
2577
- gap: 12px;
2612
+ gap: 8px;
2578
2613
  margin-top: 8px;
2579
2614
  }
2580
2615
 
@@ -2596,7 +2631,23 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2596
2631
  }
2597
2632
 
2598
2633
  .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2599
- margin-top: 4px;
2634
+ margin-top: 8px;
2635
+ }
2636
+
2637
+ /* Desktop: lay variant option groups in a wrapping row inside the modal
2638
+ (Size + Color side-by-side). Mobile keeps the column stack from the
2639
+ base rule above. The 768px breakpoint mirrors bundle-mix-match.css. */
2640
+ @media (min-width: 768px) {
2641
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2642
+ flex-direction: row;
2643
+ flex-wrap: wrap;
2644
+ }
2645
+ /* Direct children only \u2014 the qty-stepper-group is also a
2646
+ .lb-bundle-variant-option-group but lives in the actions wrapper
2647
+ and must keep its natural width. */
2648
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups > .lb-bundle-variant-option-group {
2649
+ flex: 1;
2650
+ }
2600
2651
  }
2601
2652
 
2602
2653
  /* Focus-ring suppression for mouse users. A small JS helper \u2014 see
@@ -2933,6 +2984,7 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
2933
2984
  .lb-bundle-variant-select:focus-visible {
2934
2985
  outline: 2px solid var(--lb-primary-color);
2935
2986
  outline-offset: 2px;
2987
+ box-shadow: none;
2936
2988
  }
2937
2989
  `;
2938
2990
  var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
@@ -3010,7 +3062,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3010
3062
 
3011
3063
  .lb-mix-match__slot--empty:focus-visible {
3012
3064
  outline: 2px solid var(--lb-primary-color);
3013
- outline-offset: 2px;
3065
+ outline-offset: -2px;
3066
+ box-shadow: none;
3014
3067
  border-radius: 8px;
3015
3068
  }
3016
3069
 
@@ -3077,6 +3130,13 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3077
3130
  text-decoration: underline;
3078
3131
  }
3079
3132
 
3133
+ .lb-mix-match__slot--filled a.lb-mix-match__filled-title:focus-visible {
3134
+ outline: 2px solid var(--lb-primary-color);
3135
+ outline-offset: 2px;
3136
+ box-shadow: none;
3137
+ border-radius: 2px;
3138
+ }
3139
+
3080
3140
  /* .lb-mix-match__filled-variant \u2014 styled jointly with
3081
3141
  .lb-bundle-variant-badge above to keep the variant text consistent
3082
3142
  across mix-match and fixed bundle widgets. */
@@ -3122,18 +3182,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3122
3182
 
3123
3183
  .lb-mix-match__slot-remove:focus-visible {
3124
3184
  outline: 2px solid var(--lb-primary-color);
3125
- outline-offset: 2px;
3126
- }
3127
-
3128
- /* Price placeholder */
3129
- .lb-mix-match__price-placeholder {
3130
- padding: 4px 0 16px;
3131
- text-align: center;
3132
- }
3133
-
3134
- .lb-mix-match__price-placeholder-text {
3135
- font-size: 16px;
3136
- color: var(--lb-text);
3185
+ outline-offset: -2px;
3186
+ box-shadow: none;
3137
3187
  }
3138
3188
 
3139
3189
  /* === Modal Overlay === */
@@ -3164,7 +3214,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3164
3214
  border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);
3165
3215
  box-sizing: border-box;
3166
3216
  width: 100%;
3167
- max-width: 480px;
3217
+ max-width: 520px;
3168
3218
  max-height: 70vh;
3169
3219
  display: flex;
3170
3220
  flex-direction: column;
@@ -3213,7 +3263,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3213
3263
 
3214
3264
  .lb-mix-match__modal-close:focus-visible {
3215
3265
  outline: 2px solid var(--lb-primary-color);
3216
- outline-offset: 2px;
3266
+ outline-offset: 0;
3267
+ box-shadow: none;
3217
3268
  }
3218
3269
 
3219
3270
  /* === Modal Search === */
@@ -3241,9 +3292,10 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3241
3292
  color: color-mix(in srgb, var(--lb-picker-text) 50%, transparent);
3242
3293
  }
3243
3294
 
3244
- .lb-mix-match__modal-search-input:focus {
3245
- outline: none;
3246
- box-shadow: 0 0 0 1px var(--lb-primary-color);
3295
+ .lb-mix-match__modal-search-input:focus-visible {
3296
+ outline: 2px solid var(--lb-primary-color);
3297
+ outline-offset: 2px;
3298
+ box-shadow: none;
3247
3299
  }
3248
3300
 
3249
3301
  .lb-mix-match__modal-search-clear {
@@ -3274,10 +3326,12 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3274
3326
  }
3275
3327
 
3276
3328
  .lb-mix-match__modal-product {
3277
- display: flex;
3278
- align-items: center;
3279
- gap: 20px;
3280
- padding: 12px 0;
3329
+ display: grid;
3330
+ grid-template-columns: auto 1fr;
3331
+ align-items: start;
3332
+ column-gap: 20px;
3333
+ padding-top: 20px;
3334
+ padding-bottom: 16px;
3281
3335
  border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text) 7%, transparent);
3282
3336
  }
3283
3337
 
@@ -3374,6 +3428,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3374
3428
  .lb-mix-match__variant-select:focus-visible {
3375
3429
  outline: 2px solid var(--lb-primary-color);
3376
3430
  outline-offset: 2px;
3431
+ box-shadow: none;
3377
3432
  }
3378
3433
 
3379
3434
  /* Per-pick quantity stepper inside the picker modal product row.
@@ -3390,10 +3445,11 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3390
3445
  group from the unit-price / low-stock-badge above; the stepper itself
3391
3446
  has no top margin so the group can be repositioned without coupling. */
3392
3447
  .lb-mix-match__qty-stepper-group {
3393
- margin-top: 8px;
3394
- /* Group is a flex column; without an explicit width it stretches to
3395
- fill the info column so the \u2212/value/+ cells get pulled apart. Pin
3396
- to fit-content so the wrapper hugs the stepper's natural width. */
3448
+ /* Override the base .lb-bundle-variant-option-group column flex so the
3449
+ stepper child stretches on the cross-axis (vertical) \u2014 this is what
3450
+ lets the group track the Add button's height in the action row
3451
+ without pinning a fixed pixel value. */
3452
+ flex-direction: row;
3397
3453
  width: fit-content;
3398
3454
  }
3399
3455
 
@@ -3401,7 +3457,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3401
3457
  display: inline-flex;
3402
3458
  align-items: stretch;
3403
3459
  flex-shrink: 0;
3404
- height: 32px;
3405
3460
  border: var(--lb-picker-qty-stepper-border-width) solid var(--lb-picker-qty-stepper-border-color);
3406
3461
  border-radius: var(--lb-picker-qty-stepper-radius);
3407
3462
  background-color: var(--lb-picker-bg);
@@ -3428,13 +3483,10 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3428
3483
  justify-content: center;
3429
3484
  }
3430
3485
 
3431
- .lb-mix-match__qty-stepper-button:hover:not(:disabled) {
3432
- background: color-mix(in srgb, var(--lb-picker-text) 6%, transparent);
3433
- }
3434
-
3435
3486
  .lb-mix-match__qty-stepper-button:focus-visible {
3436
3487
  outline: 2px solid var(--lb-primary-color);
3437
3488
  outline-offset: -2px;
3489
+ box-shadow: none;
3438
3490
  }
3439
3491
 
3440
3492
  .lb-mix-match__qty-stepper-button:disabled {
@@ -3455,8 +3507,24 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3455
3507
  box-sizing: border-box;
3456
3508
  }
3457
3509
 
3510
+ /* Action row \u2014 qty stepper (left) + Add button (right). Sits at the
3511
+ bottom of the info column with a 12px top margin so it visually
3512
+ separates from the variant pickers / price block above. When the
3513
+ stepper is hidden, Add fills the row via flex: 1 below.
3514
+ align-items: stretch so the stepper-group tracks the Add button's
3515
+ height (driven by font size + button padding) without a pinned px. */
3516
+ .lb-mix-match__modal-product-actions {
3517
+ display: flex;
3518
+ flex-wrap: wrap;
3519
+ align-items: stretch;
3520
+ row-gap: 12px;
3521
+ column-gap: 8px;
3522
+ margin-top: 12px;
3523
+ }
3524
+
3458
3525
  .lb-mix-match__modal-add {
3459
- padding: 8px 20px;
3526
+ flex: 1;
3527
+ padding: 10px 20px;
3460
3528
  background: var(--lb-picker-add-bg);
3461
3529
  color: var(--lb-picker-add-label);
3462
3530
  border: var(--lb-picker-add-border-width) solid var(--lb-picker-add-border-color);
@@ -3465,8 +3533,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3465
3533
  font-size: 12px;
3466
3534
  font-weight: 600;
3467
3535
  cursor: pointer;
3468
- white-space: nowrap;
3469
- flex-shrink: 0;
3470
3536
  }
3471
3537
 
3472
3538
  .lb-mix-match__modal-add:hover {
@@ -3476,6 +3542,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3476
3542
  .lb-mix-match__modal-add:focus-visible {
3477
3543
  outline: 2px solid var(--lb-primary-color);
3478
3544
  outline-offset: 2px;
3545
+ box-shadow: none;
3479
3546
  }
3480
3547
 
3481
3548
  .lb-mix-match__modal-add:disabled {
@@ -3489,6 +3556,14 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3489
3556
  opacity: 0.5;
3490
3557
  }
3491
3558
 
3559
+ /* Already in a slot \u2014 prevents shoppers from filling multiple slots
3560
+ with the same product (which wouldn't qualify the discount: it
3561
+ counts distinct products, not slot occupancy). The thumb keeps
3562
+ its qty badge so the shopper sees what's already in the bundle. */
3563
+ .lb-mix-match__modal-product--in-bundle {
3564
+ opacity: 0.5;
3565
+ }
3566
+
3492
3567
  .lb-mix-match__modal-product--sold-out .lb-mix-match__modal-sold-out-label {
3493
3568
  font-size: 12px;
3494
3569
  color: inherit;
@@ -3585,12 +3660,14 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
3585
3660
  border-color: var(--lb-tier-selected-border-color);
3586
3661
  }
3587
3662
 
3588
- /* Suppress the UA default focus outline so a freshly-clicked selected
3589
- tier doesn't briefly show the 1px focus ring on top of (or in place of)
3590
- the custom selected-state outline below. Keyboard focus is still
3591
- indicated by the :focus-visible rule. */
3663
+ /* Suppress the UA default focus outline + Dawn's focus shadow so a
3664
+ freshly-clicked selected tier doesn't briefly show the 1px focus ring
3665
+ or shadow on top of (or in place of) the custom selected-state
3666
+ outline below. Keyboard focus is still indicated by the
3667
+ :focus-visible rule. */
3592
3668
  .lb-volume__tier:focus {
3593
3669
  outline: none;
3670
+ box-shadow: none;
3594
3671
  }
3595
3672
 
3596
3673
  /* Keyboard focus indicator \u2014 explicitly excludes the selected tier so
@@ -3598,7 +3675,8 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
3598
3675
  outline property when both states apply at once. */
3599
3676
  .lb-volume__tier:focus-visible:not([aria-checked="true"]) {
3600
3677
  outline: 2px solid var(--lb-primary-color);
3601
- outline-offset: 2px;
3678
+ outline-offset: -2px;
3679
+ box-shadow: none;
3602
3680
  }
3603
3681
 
3604
3682
  .lb-volume__tier[aria-checked="true"] {
@@ -3746,6 +3824,7 @@ var BUNDLE_DROPDOWN_CSS = `/**
3746
3824
  .lb-dropdown-trigger:focus-visible {
3747
3825
  outline: 2px solid var(--lb-primary-color);
3748
3826
  outline-offset: 2px;
3827
+ box-shadow: none;
3749
3828
  }
3750
3829
 
3751
3830
  .lb-dropdown-trigger[aria-expanded="true"] {