@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.cjs CHANGED
@@ -150,6 +150,7 @@ function bindDropdown(select) {
150
150
  let optionEls = [];
151
151
  let instance;
152
152
  function syncFromSelect() {
153
+ trigger.disabled = select.disabled;
153
154
  const opts = readOptions(select);
154
155
  const idx = select.selectedIndex;
155
156
  triggerLabel.textContent = idx >= 0 && opts[idx] ? opts[idx].label : "";
@@ -196,7 +197,7 @@ function bindDropdown(select) {
196
197
  if (rect.width === 0) return false;
197
198
  const visibleCount = Math.min(optionEls.length || 1, MAX_VISIBLE_ITEMS);
198
199
  const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
199
- const scrollable = listbox.hasAttribute("data-lb-dropdown-portal") ? null : findScrollableAncestor(trigger);
200
+ const scrollable = findScrollableAncestor(trigger);
200
201
  const clip = scrollable ? (() => {
201
202
  const r = scrollable.getBoundingClientRect();
202
203
  return { top: r.top, bottom: r.bottom };
@@ -1044,30 +1045,26 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1044
1045
  root.appendChild(pricingSection.el);
1045
1046
  const savingsBar = wc.savingsBar.visible ? renderSavingsBar2() : null;
1046
1047
  if (savingsBar) root.appendChild(savingsBar.el);
1047
- const placeholder = el("div", "lb-mix-match__price-placeholder", {
1048
- "data-price-placeholder": ""
1048
+ const cta = buildCtaButton(`Select ${requiredQty} items to unlock`);
1049
+ cta.disabled = true;
1050
+ cta.addEventListener("click", () => {
1051
+ if (cta.disabled) return;
1052
+ onAddToCart(buildCartLines(selections, bundle));
1049
1053
  });
1050
- const placeholderText = el(
1051
- "span",
1052
- "lb-mix-match__price-placeholder-text"
1053
- );
1054
- placeholderText.textContent = `Select ${requiredQty} items to see price`;
1055
- placeholder.appendChild(placeholderText);
1056
- root.appendChild(placeholder);
1057
1054
  const modal = renderModal(bundle, eligible, currency, {
1058
1055
  showSearch: wc.showSearch,
1059
1056
  showQtySelector,
1060
1057
  selections,
1061
1058
  onAdd: (product, variant, quantity) => addSelection(product, variant, quantity),
1062
- isComplete: () => selections.length >= requiredQty
1059
+ isComplete: () => selections.length >= requiredQty,
1060
+ getFocusAfterAdd: () => {
1061
+ const empty = slotsContainer.querySelector(
1062
+ ".lb-mix-match__slot--empty"
1063
+ );
1064
+ return empty ?? cta;
1065
+ }
1063
1066
  });
1064
1067
  root.appendChild(modal.el);
1065
- const cta = buildCtaButton(`Select ${requiredQty} items to unlock`);
1066
- cta.disabled = true;
1067
- cta.addEventListener("click", () => {
1068
- if (cta.disabled) return;
1069
- onAddToCart(buildCartLines(selections, bundle));
1070
- });
1071
1068
  root.appendChild(cta);
1072
1069
  root.appendChild(
1073
1070
  el("p", "lb-bundle-error", { "data-error": "", "aria-live": "polite" })
@@ -1118,7 +1115,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1118
1115
  progress.update(selections.length);
1119
1116
  pricingSection.update(selections, bundle, currency);
1120
1117
  if (savingsBar) savingsBar.update(selections, bundle, currency);
1121
- placeholder.style.display = selections.length === 0 ? "" : "none";
1122
1118
  modal.refreshCounts();
1123
1119
  updateCta();
1124
1120
  }
@@ -1200,12 +1196,14 @@ function renderProgress(requiredQty) {
1200
1196
  const wrap = el("div", "lb-mix-match__progress");
1201
1197
  const labels = el("div", "lb-mix-match__progress-labels");
1202
1198
  const count = el("span", "lb-mix-match__progress-count", {
1203
- "data-progress-count": ""
1199
+ "data-progress-count": "",
1200
+ "aria-live": "polite"
1204
1201
  });
1205
1202
  count.textContent = `0 of ${requiredQty} selected`;
1206
1203
  labels.appendChild(count);
1207
1204
  const remaining = el("span", "lb-mix-match__progress-remaining", {
1208
- "data-progress-remaining": ""
1205
+ "data-progress-remaining": "",
1206
+ "aria-live": "polite"
1209
1207
  });
1210
1208
  remaining.textContent = `${requiredQty} more to go`;
1211
1209
  labels.appendChild(remaining);
@@ -1558,14 +1556,12 @@ function renderModal(bundle, eligible, currency, handlers) {
1558
1556
  return { min: rule.min, max, cap };
1559
1557
  };
1560
1558
  let stepper = null;
1559
+ let qtyGroup = null;
1561
1560
  if (!ep.isOos && handlers.showQtySelector) {
1562
- const qtyGroup = el(
1561
+ qtyGroup = el(
1563
1562
  "div",
1564
1563
  "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group"
1565
1564
  );
1566
- const qtyLabel = el("span", "lb-bundle-variant-option-label");
1567
- qtyLabel.textContent = "Quantity";
1568
- qtyGroup.appendChild(qtyLabel);
1569
1565
  stepper = renderQtyStepper({
1570
1566
  initial: rule.min,
1571
1567
  getBounds: () => {
@@ -1574,7 +1570,6 @@ function renderModal(bundle, eligible, currency, handlers) {
1574
1570
  }
1575
1571
  });
1576
1572
  qtyGroup.appendChild(stepper.el);
1577
- info.appendChild(qtyGroup);
1578
1573
  }
1579
1574
  const row = {
1580
1575
  el: productEl,
@@ -1583,12 +1578,12 @@ function renderModal(bundle, eligible, currency, handlers) {
1583
1578
  refreshFromSelections: () => {
1584
1579
  }
1585
1580
  };
1581
+ const optionSelects = [];
1586
1582
  if (availableVariants.length > 1) {
1587
1583
  const optionNames = availableVariants[0].selectedOptions.map(
1588
1584
  (o) => o.name
1589
1585
  );
1590
1586
  const productIdTail = ep.product.id.replace(/^.*\//, "");
1591
- const optionSelects = [];
1592
1587
  const resolveVariant = (values) => availableVariants.find(
1593
1588
  (v) => v.selectedOptions.length === values.length && v.selectedOptions.every((o, i) => o.value === values[i])
1594
1589
  ) ?? null;
@@ -1697,7 +1692,13 @@ function renderModal(bundle, eligible, currency, handlers) {
1697
1692
  productEl.appendChild(info);
1698
1693
  let addBtn = null;
1699
1694
  const refreshAddState = () => {
1700
- if (!addBtn) return;
1695
+ if (!addBtn) {
1696
+ optionSelects.forEach((sel) => {
1697
+ sel.disabled = true;
1698
+ });
1699
+ if (stepper) stepper.setExternallyDisabled(true);
1700
+ return;
1701
+ }
1701
1702
  const { cap } = computeStepperBounds();
1702
1703
  const already = alreadyInBundleFor(
1703
1704
  handlers.selections,
@@ -1710,24 +1711,46 @@ function renderModal(bundle, eligible, currency, handlers) {
1710
1711
  } else {
1711
1712
  countBadge.hidden = true;
1712
1713
  }
1713
- addBtn.disabled = handlers.isComplete() || cap < rule.min;
1714
+ const productInBundle = handlers.selections.some(
1715
+ (s) => s.productId === ep.product.id
1716
+ );
1717
+ if (productInBundle) {
1718
+ addBtn.disabled = true;
1719
+ addBtn.textContent = "Added";
1720
+ addBtn.setAttribute("aria-label", `Added ${ep.product.title}`);
1721
+ productEl.classList.add("lb-mix-match__modal-product--in-bundle");
1722
+ } else {
1723
+ addBtn.textContent = "Add";
1724
+ addBtn.setAttribute("aria-label", `Add ${ep.product.title}`);
1725
+ productEl.classList.remove("lb-mix-match__modal-product--in-bundle");
1726
+ addBtn.disabled = handlers.isComplete() || cap < rule.min;
1727
+ }
1728
+ const rowDisabled = addBtn.disabled;
1729
+ optionSelects.forEach((sel) => {
1730
+ sel.disabled = rowDisabled;
1731
+ });
1732
+ if (stepper) stepper.setExternallyDisabled(rowDisabled);
1714
1733
  };
1715
1734
  if (!ep.isOos) {
1716
1735
  const actions = el("div", "lb-mix-match__modal-product-actions");
1736
+ if (qtyGroup) actions.appendChild(qtyGroup);
1717
1737
  addBtn = document.createElement("button");
1718
1738
  addBtn.type = "button";
1719
1739
  addBtn.className = "lb-mix-match__modal-add";
1720
1740
  addBtn.textContent = "Add";
1741
+ addBtn.setAttribute("aria-label", `Add ${ep.product.title}`);
1721
1742
  addBtn.addEventListener("click", () => {
1722
1743
  if (!addBtn || addBtn.disabled) return;
1723
1744
  if (handlers.isComplete()) return;
1724
1745
  const qty = stepper ? stepper.value() : rule.min;
1725
1746
  handlers.onAdd(ep.product, currentVariant, qty);
1726
1747
  if (stepper) stepper.reset(rule.min);
1748
+ const nextFocus = handlers.getFocusAfterAdd();
1749
+ if (nextFocus) lastFocused = nextFocus;
1727
1750
  close();
1728
1751
  });
1729
1752
  actions.appendChild(addBtn);
1730
- productEl.appendChild(actions);
1753
+ info.appendChild(actions);
1731
1754
  }
1732
1755
  row.refreshFromSelections = () => {
1733
1756
  if (stepper) stepper.refresh();
@@ -1819,6 +1842,7 @@ function renderQtyStepper(opts) {
1819
1842
  plus.innerHTML = STEPPER_PLUS_SVG;
1820
1843
  wrap.appendChild(plus);
1821
1844
  let current = clamp2(opts.initial, opts.getBounds());
1845
+ let externallyDisabled = false;
1822
1846
  function clamp2(n, b) {
1823
1847
  return Math.max(b.min, Math.min(b.max, n));
1824
1848
  }
@@ -1826,8 +1850,8 @@ function renderQtyStepper(opts) {
1826
1850
  const b = opts.getBounds();
1827
1851
  current = clamp2(current, b);
1828
1852
  valueEl.textContent = String(current);
1829
- minus.disabled = current <= b.min;
1830
- plus.disabled = current >= b.max;
1853
+ minus.disabled = externallyDisabled || current <= b.min;
1854
+ plus.disabled = externallyDisabled || current >= b.max;
1831
1855
  }
1832
1856
  minus.addEventListener("click", () => {
1833
1857
  const b = opts.getBounds();
@@ -1847,7 +1871,11 @@ function renderQtyStepper(opts) {
1847
1871
  current = clamp2(qty, opts.getBounds());
1848
1872
  paint();
1849
1873
  },
1850
- refresh: paint
1874
+ refresh: paint,
1875
+ setExternallyDisabled(disabled) {
1876
+ externallyDisabled = disabled;
1877
+ paint();
1878
+ }
1851
1879
  };
1852
1880
  }
1853
1881
  function buildEligibleProducts(bundle, oosBehavior) {
@@ -2494,6 +2522,13 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2494
2522
  text-decoration: underline;
2495
2523
  }
2496
2524
 
2525
+ a.lb-bundle-product-name:focus-visible {
2526
+ outline: 2px solid var(--lb-primary-color);
2527
+ outline-offset: 0;
2528
+ box-shadow: none;
2529
+ border-radius: 2px;
2530
+ }
2531
+
2497
2532
  .lb-bundle-product-price {
2498
2533
  /* --lb-product-price-display fallback is the "on" branch; Liquid sets 'none' when merchant disables. */
2499
2534
  display: var(--lb-product-price-display, block);
@@ -2567,7 +2602,7 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2567
2602
  .lb-bundle-variant-option-groups {
2568
2603
  display: flex;
2569
2604
  flex-direction: column;
2570
- gap: 12px;
2605
+ gap: 8px;
2571
2606
  margin-top: 8px;
2572
2607
  }
2573
2608
 
@@ -2589,7 +2624,23 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2589
2624
  }
2590
2625
 
2591
2626
  .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2592
- margin-top: 4px;
2627
+ margin-top: 8px;
2628
+ }
2629
+
2630
+ /* Desktop: lay variant option groups in a wrapping row inside the modal
2631
+ (Size + Color side-by-side). Mobile keeps the column stack from the
2632
+ base rule above. The 768px breakpoint mirrors bundle-mix-match.css. */
2633
+ @media (min-width: 768px) {
2634
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2635
+ flex-direction: row;
2636
+ flex-wrap: wrap;
2637
+ }
2638
+ /* Direct children only \u2014 the qty-stepper-group is also a
2639
+ .lb-bundle-variant-option-group but lives in the actions wrapper
2640
+ and must keep its natural width. */
2641
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups > .lb-bundle-variant-option-group {
2642
+ flex: 1;
2643
+ }
2593
2644
  }
2594
2645
 
2595
2646
  /* Focus-ring suppression for mouse users. A small JS helper \u2014 see
@@ -2926,6 +2977,7 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
2926
2977
  .lb-bundle-variant-select:focus-visible {
2927
2978
  outline: 2px solid var(--lb-primary-color);
2928
2979
  outline-offset: 2px;
2980
+ box-shadow: none;
2929
2981
  }
2930
2982
  `;
2931
2983
  var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
@@ -3003,7 +3055,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3003
3055
 
3004
3056
  .lb-mix-match__slot--empty:focus-visible {
3005
3057
  outline: 2px solid var(--lb-primary-color);
3006
- outline-offset: 2px;
3058
+ outline-offset: -2px;
3059
+ box-shadow: none;
3007
3060
  border-radius: 8px;
3008
3061
  }
3009
3062
 
@@ -3070,6 +3123,13 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3070
3123
  text-decoration: underline;
3071
3124
  }
3072
3125
 
3126
+ .lb-mix-match__slot--filled a.lb-mix-match__filled-title:focus-visible {
3127
+ outline: 2px solid var(--lb-primary-color);
3128
+ outline-offset: 2px;
3129
+ box-shadow: none;
3130
+ border-radius: 2px;
3131
+ }
3132
+
3073
3133
  /* .lb-mix-match__filled-variant \u2014 styled jointly with
3074
3134
  .lb-bundle-variant-badge above to keep the variant text consistent
3075
3135
  across mix-match and fixed bundle widgets. */
@@ -3115,18 +3175,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3115
3175
 
3116
3176
  .lb-mix-match__slot-remove:focus-visible {
3117
3177
  outline: 2px solid var(--lb-primary-color);
3118
- outline-offset: 2px;
3119
- }
3120
-
3121
- /* Price placeholder */
3122
- .lb-mix-match__price-placeholder {
3123
- padding: 4px 0 16px;
3124
- text-align: center;
3125
- }
3126
-
3127
- .lb-mix-match__price-placeholder-text {
3128
- font-size: 16px;
3129
- color: var(--lb-text);
3178
+ outline-offset: -2px;
3179
+ box-shadow: none;
3130
3180
  }
3131
3181
 
3132
3182
  /* === Modal Overlay === */
@@ -3157,7 +3207,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3157
3207
  border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);
3158
3208
  box-sizing: border-box;
3159
3209
  width: 100%;
3160
- max-width: 480px;
3210
+ max-width: 520px;
3161
3211
  max-height: 70vh;
3162
3212
  display: flex;
3163
3213
  flex-direction: column;
@@ -3206,7 +3256,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3206
3256
 
3207
3257
  .lb-mix-match__modal-close:focus-visible {
3208
3258
  outline: 2px solid var(--lb-primary-color);
3209
- outline-offset: 2px;
3259
+ outline-offset: 0;
3260
+ box-shadow: none;
3210
3261
  }
3211
3262
 
3212
3263
  /* === Modal Search === */
@@ -3234,9 +3285,10 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3234
3285
  color: color-mix(in srgb, var(--lb-picker-text) 50%, transparent);
3235
3286
  }
3236
3287
 
3237
- .lb-mix-match__modal-search-input:focus {
3238
- outline: none;
3239
- box-shadow: 0 0 0 1px var(--lb-primary-color);
3288
+ .lb-mix-match__modal-search-input:focus-visible {
3289
+ outline: 2px solid var(--lb-primary-color);
3290
+ outline-offset: 2px;
3291
+ box-shadow: none;
3240
3292
  }
3241
3293
 
3242
3294
  .lb-mix-match__modal-search-clear {
@@ -3267,10 +3319,12 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3267
3319
  }
3268
3320
 
3269
3321
  .lb-mix-match__modal-product {
3270
- display: flex;
3271
- align-items: center;
3272
- gap: 20px;
3273
- padding: 12px 0;
3322
+ display: grid;
3323
+ grid-template-columns: auto 1fr;
3324
+ align-items: start;
3325
+ column-gap: 20px;
3326
+ padding-top: 20px;
3327
+ padding-bottom: 16px;
3274
3328
  border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text) 7%, transparent);
3275
3329
  }
3276
3330
 
@@ -3367,6 +3421,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3367
3421
  .lb-mix-match__variant-select:focus-visible {
3368
3422
  outline: 2px solid var(--lb-primary-color);
3369
3423
  outline-offset: 2px;
3424
+ box-shadow: none;
3370
3425
  }
3371
3426
 
3372
3427
  /* Per-pick quantity stepper inside the picker modal product row.
@@ -3383,10 +3438,11 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3383
3438
  group from the unit-price / low-stock-badge above; the stepper itself
3384
3439
  has no top margin so the group can be repositioned without coupling. */
3385
3440
  .lb-mix-match__qty-stepper-group {
3386
- margin-top: 8px;
3387
- /* Group is a flex column; without an explicit width it stretches to
3388
- fill the info column so the \u2212/value/+ cells get pulled apart. Pin
3389
- to fit-content so the wrapper hugs the stepper's natural width. */
3441
+ /* Override the base .lb-bundle-variant-option-group column flex so the
3442
+ stepper child stretches on the cross-axis (vertical) \u2014 this is what
3443
+ lets the group track the Add button's height in the action row
3444
+ without pinning a fixed pixel value. */
3445
+ flex-direction: row;
3390
3446
  width: fit-content;
3391
3447
  }
3392
3448
 
@@ -3394,7 +3450,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3394
3450
  display: inline-flex;
3395
3451
  align-items: stretch;
3396
3452
  flex-shrink: 0;
3397
- height: 32px;
3398
3453
  border: var(--lb-picker-qty-stepper-border-width) solid var(--lb-picker-qty-stepper-border-color);
3399
3454
  border-radius: var(--lb-picker-qty-stepper-radius);
3400
3455
  background-color: var(--lb-picker-bg);
@@ -3421,13 +3476,10 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3421
3476
  justify-content: center;
3422
3477
  }
3423
3478
 
3424
- .lb-mix-match__qty-stepper-button:hover:not(:disabled) {
3425
- background: color-mix(in srgb, var(--lb-picker-text) 6%, transparent);
3426
- }
3427
-
3428
3479
  .lb-mix-match__qty-stepper-button:focus-visible {
3429
3480
  outline: 2px solid var(--lb-primary-color);
3430
3481
  outline-offset: -2px;
3482
+ box-shadow: none;
3431
3483
  }
3432
3484
 
3433
3485
  .lb-mix-match__qty-stepper-button:disabled {
@@ -3448,8 +3500,24 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3448
3500
  box-sizing: border-box;
3449
3501
  }
3450
3502
 
3503
+ /* Action row \u2014 qty stepper (left) + Add button (right). Sits at the
3504
+ bottom of the info column with a 12px top margin so it visually
3505
+ separates from the variant pickers / price block above. When the
3506
+ stepper is hidden, Add fills the row via flex: 1 below.
3507
+ align-items: stretch so the stepper-group tracks the Add button's
3508
+ height (driven by font size + button padding) without a pinned px. */
3509
+ .lb-mix-match__modal-product-actions {
3510
+ display: flex;
3511
+ flex-wrap: wrap;
3512
+ align-items: stretch;
3513
+ row-gap: 12px;
3514
+ column-gap: 8px;
3515
+ margin-top: 12px;
3516
+ }
3517
+
3451
3518
  .lb-mix-match__modal-add {
3452
- padding: 8px 20px;
3519
+ flex: 1;
3520
+ padding: 10px 20px;
3453
3521
  background: var(--lb-picker-add-bg);
3454
3522
  color: var(--lb-picker-add-label);
3455
3523
  border: var(--lb-picker-add-border-width) solid var(--lb-picker-add-border-color);
@@ -3458,8 +3526,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3458
3526
  font-size: 12px;
3459
3527
  font-weight: 600;
3460
3528
  cursor: pointer;
3461
- white-space: nowrap;
3462
- flex-shrink: 0;
3463
3529
  }
3464
3530
 
3465
3531
  .lb-mix-match__modal-add:hover {
@@ -3469,6 +3535,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3469
3535
  .lb-mix-match__modal-add:focus-visible {
3470
3536
  outline: 2px solid var(--lb-primary-color);
3471
3537
  outline-offset: 2px;
3538
+ box-shadow: none;
3472
3539
  }
3473
3540
 
3474
3541
  .lb-mix-match__modal-add:disabled {
@@ -3482,6 +3549,14 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3482
3549
  opacity: 0.5;
3483
3550
  }
3484
3551
 
3552
+ /* Already in a slot \u2014 prevents shoppers from filling multiple slots
3553
+ with the same product (which wouldn't qualify the discount: it
3554
+ counts distinct products, not slot occupancy). The thumb keeps
3555
+ its qty badge so the shopper sees what's already in the bundle. */
3556
+ .lb-mix-match__modal-product--in-bundle {
3557
+ opacity: 0.5;
3558
+ }
3559
+
3485
3560
  .lb-mix-match__modal-product--sold-out .lb-mix-match__modal-sold-out-label {
3486
3561
  font-size: 12px;
3487
3562
  color: inherit;
@@ -3578,12 +3653,14 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
3578
3653
  border-color: var(--lb-tier-selected-border-color);
3579
3654
  }
3580
3655
 
3581
- /* Suppress the UA default focus outline so a freshly-clicked selected
3582
- tier doesn't briefly show the 1px focus ring on top of (or in place of)
3583
- the custom selected-state outline below. Keyboard focus is still
3584
- indicated by the :focus-visible rule. */
3656
+ /* Suppress the UA default focus outline + Dawn's focus shadow so a
3657
+ freshly-clicked selected tier doesn't briefly show the 1px focus ring
3658
+ or shadow on top of (or in place of) the custom selected-state
3659
+ outline below. Keyboard focus is still indicated by the
3660
+ :focus-visible rule. */
3585
3661
  .lb-volume__tier:focus {
3586
3662
  outline: none;
3663
+ box-shadow: none;
3587
3664
  }
3588
3665
 
3589
3666
  /* Keyboard focus indicator \u2014 explicitly excludes the selected tier so
@@ -3591,7 +3668,8 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
3591
3668
  outline property when both states apply at once. */
3592
3669
  .lb-volume__tier:focus-visible:not([aria-checked="true"]) {
3593
3670
  outline: 2px solid var(--lb-primary-color);
3594
- outline-offset: 2px;
3671
+ outline-offset: -2px;
3672
+ box-shadow: none;
3595
3673
  }
3596
3674
 
3597
3675
  .lb-volume__tier[aria-checked="true"] {
@@ -3739,6 +3817,7 @@ var BUNDLE_DROPDOWN_CSS = `/**
3739
3817
  .lb-dropdown-trigger:focus-visible {
3740
3818
  outline: 2px solid var(--lb-primary-color);
3741
3819
  outline-offset: 2px;
3820
+ box-shadow: none;
3742
3821
  }
3743
3822
 
3744
3823
  .lb-dropdown-trigger[aria-expanded="true"] {