I am making the modal for select the spac and quantity in online web shop:
the following is the related javascript css and html:
$(document).ready(function () {
  const modal = $('#spec-modal-product-spec-modal');
  const variantsContainer = modal.find('#spec-modal-variants-container');
  const qtyInput = modal.find('#spec-modal-qty');
  const qtyMinusBtn = modal.find('.spec-modal-qty-minus');
  const qtyPlusBtn = modal.find('.spec-modal-qty-plus');
  const modalCloseBtn = modal.find('.spec-modal-close');
  const modalForm = modal.find('#spec-modal-product-spec-form');
  const modalError = modal.find('.spec-modal-error');

  let currentProductData = null;
  let activeAddToCartBtn = null; // store clicked button to restore UI

  function buildVariantOptions(variants) {
    if (!Array.isArray(variants) || variants.length === 0) {
      variantsContainer.html('');
      return;
    }
    
    // Trim and split attr_name
    const attrNames = variants[0].attr_name ? variants[0].attr_name.split(',').map(s => s.trim()) : [];
    if (attrNames.length === 0) {
      variantsContainer.html('');
      return;
    }
    
    let attrValuesByName = {};
    attrNames.forEach(attr => attrValuesByName[attr] = new Set());

    variants.forEach(variant => {
      if (!variant.variant_values) return;
      const vals = variant.variant_values.split(',').map(s => s.trim());

      vals.forEach((val, i) => {
        if (attrNames[i]) attrValuesByName[attrNames[i]].add(val);
      });
    });

    if (Object.values(attrValuesByName).every(set => set.size === 0)) {
      variantsContainer.html('');
      return;
    }

    let html = '';
    attrNames.forEach((attrName, groupIndex) => {
      html += `<div class="spec-modal-variant-group" data-variant-attribute="${attrName}">
        <label class="spec-modal-variant-label">${attrName}:</label>
        <div class="spec-modal-variant-options" role="radiogroup" aria-label="${attrName} options">`;
      Array.from(attrValuesByName[attrName]).forEach((val, optionIndex) => {
        const optionId = `spec-modal-variant-${attrName}-${optionIndex}`;
        html += `
          <label class="spec-modal-variant-option" for="${optionId}" tabindex="0" role="radio" aria-checked="false" data-variant-attr="${attrName}" data-variant-val="${val}">
            <input type="radio" name="spec-modal-variant-${attrName}" id="${optionId}" value="${val}">
            ${val}
          </label>`;
      });
      html += '</div></div>';
    });
    variantsContainer.html(html);

    function updateSelectedStyles() {
      variantsContainer.find('.spec-modal-variant-option').each(function () {
        var label = $(this);
        var input = label.find('input[type="radio"]');
        if (input.prop('checked')) {
          label.addClass('selected').attr('aria-checked', 'true');
        } else {
          label.removeClass('selected').attr('aria-checked', 'false');
        }
      });
    }
    updateSelectedStyles();

    variantsContainer.find('.spec-modal-variant-option').on('click', function () {
      var input = $(this).find('input[type="radio"]');
      input.prop('checked', true).trigger('change');
      updateSelectedStyles();
      updatePriceInfo();
    }).on('keydown', function (e) {
      if (e.key === ' ' || e.key === 'Enter') {
        e.preventDefault();
        $(this).trigger('click');
      }
    });

    variantsContainer.find('input[type="radio"]').on('change', function(){
      updateSelectedStyles();
      updatePriceInfo();
    });
  }

  function updatePriceInfo() {
    if (!currentProductData) return;

    // Find selected attributes
    let selectedAttributes = {};
    let allSelected = true;
    variantsContainer.find('.spec-modal-variant-group').each(function () {
      const attrName = $(this).data('variant-attribute');
      const checkedInput = $(this).find('input[type=radio]:checked');
      if (checkedInput.length === 0) {
        allSelected = false;
        return false;
      }
      selectedAttributes[attrName] = checkedInput.val();
    });

    const unitPriceEl = $('#spec-modal-unit-price');
    const discountPercentEl = $('#spec-modal-discount-percent');
    const totalPriceEl = $('#spec-modal-total-price');
    const qty = parseInt(qtyInput.val()) || 1;

    if (!allSelected) {
      unitPriceEl.text('—');
      discountPercentEl.text('—');
      totalPriceEl.text('—');
      return;
    }

    // Find matched variant
    let matchedVariant = currentProductData.variants.find(variant => {
      if (!variant.attr_name || !variant.variant_values) return false;
      const attrNames = variant.attr_name.split(',').map(s => s.trim());
      const attrValues = variant.variant_values.split(',').map(s => s.trim());
      for (let i = 0; i < attrNames.length; i++) {
        if (selectedAttributes[attrNames[i]] !== attrValues[i]) return false;
      }
      return true;
    });

    if (!matchedVariant) {
      unitPriceEl.text('—');
      discountPercentEl.text('—');
      totalPriceEl.text('—');
      return;
    }

    const originalPrice = Number(matchedVariant.price) || 0;
    const specialPrice = Number(matchedVariant.special_price) || originalPrice;

    const discountPercent = originalPrice > 0 && specialPrice < originalPrice 
      ? Math.round((1 - specialPrice / originalPrice) * 100) 
      : 0;

    const totalPrice = specialPrice * qty;

    function formatHKD(price) {
      return 'HK$' + price.toFixed(2);
    }

    unitPriceEl.text(formatHKD(specialPrice));
    discountPercentEl.text(discountPercent > 0 ? `-${discountPercent}%` : '無折扣');
    totalPriceEl.text(formatHKD(totalPrice));
  }

  function openModal(productData, clickedBtn) {
    currentProductData = productData;
    activeAddToCartBtn = clickedBtn;

    if (productData.hasMultipleVariants) {
      buildVariantOptions(productData.variants);
      variantsContainer.show();
    } else {
      variantsContainer.hide().html('');
    }

    const min = Number(productData.min) || 1;
    const step = Number(productData.step) || 1;
    const max = Number(productData.max) || null;

    qtyInput.attr('min', min);
    qtyInput.attr('step', step);
    qtyInput.val(min);
    if (max) {
      qtyInput.attr('max', max);
    } else {
      qtyInput.removeAttr('max');
    }

    modalError.text('');
    modal.fadeIn(200);
    $('body').css('overflow', 'hidden');

    updatePriceInfo();
  }

  function closeModal() {
    modal.fadeOut(200);
    $('body').css('overflow', '');
    currentProductData = null;
    activeAddToCartBtn = null;
    variantsContainer.html('');
    modalError.text('');

    $('#spec-modal-unit-price').text('HK$0.00');
    $('#spec-modal-discount-percent').text('-0%');
    $('#spec-modal-total-price').text('HK$0.00');
  }

  // Quantity buttons logic
  qtyMinusBtn.on('click', function () {
    let val = parseInt(qtyInput.val()) || 1;
    const min = parseInt(qtyInput.attr('min')) || 1;
    const step = parseInt(qtyInput.attr('step')) || 1;
    val = Math.max(min, val - step);
    qtyInput.val(val);
    updatePriceInfo();
  });

  qtyPlusBtn.on('click', function () {
    let val = parseInt(qtyInput.val()) || 1;
    const step = parseInt(qtyInput.attr('step')) || 1;
    const max = parseInt(qtyInput.attr('max'));
    val = val + step;
    if (!isNaN(max)) val = Math.min(max, val);
    qtyInput.val(val);
    updatePriceInfo();
  });

  qtyInput.on('input change', function () {
    let val = +qtyInput.val();
    const min = +qtyInput.attr('min');
    const max = +qtyInput.attr('max');
    const step = +qtyInput.attr('step');
    if (isNaN(val) || val < min) val = min;
    if (!isNaN(max) && val > max) val = max;
    if (step > 1) val = Math.round(val / step) * step;
    qtyInput.val(val);
    updatePriceInfo();
  });

  modalCloseBtn.on('click', closeModal);
  modal.on('click', function (e) {
    if (e.target === this) closeModal();
  });

  modalForm.on('submit', function (e) {
    e.preventDefault();
    modalError.text('');

    if (!currentProductData) {
      modalError.text('產品資料缺失');
      return;
    }

    let selectedAttributes = {};
    let valid = true;
    variantsContainer.find('.spec-modal-variant-group').each(function () {
      const attrName = $(this).data('variant-attribute');
      const checkedInput = $(this).find('input[type=radio]:checked');
      if (checkedInput.length === 0) {
        modalError.text(`請選擇${attrName}`);
        valid = false;
        return false;
      }
      selectedAttributes[attrName] = checkedInput.val();
    });
    if (!valid) return;

    let qty = parseInt(qtyInput.val());
    const minQty = Number(currentProductData.min) || 1;
    const maxQty = Number(currentProductData.max);
    if (isNaN(qty) || qty < minQty) {
      modalError.text(`數量不能小於 ${minQty}`);
      return;
    }
    if (!isNaN(maxQty) && maxQty > 0 && qty > maxQty) {
      modalError.text(`數量不能超過最大限制 ${maxQty}`);
      return;
    }

    let matchedVariant = null;
    if (currentProductData.hasMultipleVariants) {
      matchedVariant = currentProductData.variants.find(variant => {
        if (!variant.attr_name || !variant.variant_values) return false;
        const attrNames = variant.attr_name.split(',').map(s => s.trim());
        const attrValues = variant.variant_values.split(',').map(s => s.trim());
        for (let i = 0; i < attrNames.length; i++) {
          if (selectedAttributes[attrNames[i]] !== attrValues[i]) return false;
        }
        return true;
      });
    } else {
      matchedVariant = currentProductData.variants && currentProductData.variants.length > 0
                   ? currentProductData.variants[0]
                   : null;
    }

    if (!matchedVariant) {
      modalError.text('請選擇有效的產品規格');
      return;
    }

    closeModal();

    // Your AJAX add to cart logic here (update base_url, csrfName, csrfHash, is_loggedin accordingly)
    if (!matchedVariant.id) {
      Swal.fire({
        icon: 'error',
        title: '錯誤',
        text: '無法找到匹配的產品規格，請重試',
      });
      return;
    }
  
    $.ajax({
      type: 'POST',
      url: base_url + 'cart/manage',
      data: {
        product_variant_id: matchedVariant.id,
        qty: qty,
        is_saved_for_later: false,
        [csrfName]: csrfHash,
      },
      dataType: 'json',
      success: function (result) {
        csrfName = result.csrfName;
        csrfHash = result.csrfHash;
        if (!result.error) {
          Toast.fire({
            icon: 'success',
            title: result.message
          });
          $('.cart_count').each(function () {
            $(this).text(result.data.cart_count);
          });

          display_cart(result.data.items);
        } else {
          if (is_loggedin == 0) {
            Toast.fire({
              icon: 'success',
              title: "已加入至購物車"
            });
            var cart_item = {
              product_variant_id: matchedVariant.id,
              name: currentProductData.title || '',
              short_description: currentProductData.description || '',
              qty: qty,
              image: currentProductData.image || '',
              price: currentProductData.price
            };
            var cart = localStorage.getItem("cart");
            cart = cart ? JSON.parse(cart) : [];
            cart.push(cart_item);
            localStorage.setItem("cart", JSON.stringify(cart));
            display_cart(cart);
          } else {
            Toast.fire({
              icon: 'error',
              title: result.message
            });
          }
        }
      },
      error: function () {
        Toast.fire({
          icon: 'error',
          title: '伺服器錯誤，請稍後重試'
        });
      }
    });
  });

  // Bind popup open on all add_to_cart buttons
  $(document).on('click', '.add_to_cart', function (e) {
    e.preventDefault();
    const btn = $(this);

    // Parse variants JSON from data attribute
    let variantsData = [];
    try {
      variantsData = btn.attr('data-variants') ? JSON.parse(btn.attr('data-variants')) : [];
    } catch {
      variantsData = [];
    }

    const productData = {
      id: btn.data('product-id'),
      title: btn.data('product-title'),
      name: btn.data('product-title'),
      price: btn.data('product-price'),
      image: btn.data('product-image'),
      min: Number(btn.data('min')) || 1,
      step: Number(btn.data('step')) || 1,
      max: Number(btn.data('max')) || null,
      variants: variantsData,
      description: btn.data('product-description') || '',
      hasMultipleVariants: btn.data('has-multiple-variants') === 1 || btn.data('has-multiple-variants') === '1',
    };

    openModal(productData, btn);
  });

});

/* Modal overlay */
.spec-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  display: none; /* initially hidden */
  justify-content: center;
  align-items: center;
  z-index: 1050;
  overflow: hidden;
  animation: spec-modal-fadeIn 0.3s ease forwards;
}

/* Modal window */
.spec-modal-window {
  background: #fff;
  border-radius: 12px;
  max-height: 90vh;
  max-width: 480px;
  width: 90%;
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateY(20px);
  animation: spec-modal-slideFadeIn 0.3s forwards;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);

  /* Center on desktop/tablet */
  position: relative;
  top: auto;
  bottom: auto;
  margin: auto;
}

/* Modal header */
.spec-modal-header {
  padding: 16px 24px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.spec-modal-header h2 {
  margin: 0;
  font-weight: 700;
  font-size: 1.25rem;
}

.spec-modal-close {
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  line-height: 1;
}

/* Modal content */
.spec-modal-content {
  padding: 24px;
  overflow-y: auto;
  flex-grow: 1;
}

/* Quantity input styles */
.spec-modal-quantity-input {
  display: flex;
  align-items: center;
  max-width: 180px;
  margin-top: 8px;
}

.spec-modal-qty-btn {
  background-color: #e2e8f0;
  border: none;
  width: 36px;
  height: 36px;
  font-size: 1.5rem;
  cursor: pointer;
  border-radius: 50%;
  line-height: 1;
  user-select: none;
}

.spec-modal-qty-btn:hover {
  background-color: #cbd5e1;
}

#spec-modal-qty {
  width: 64px;
  height: 36px;
  text-align: center;
  font-size: 1rem;
  border: 1px solid #ccc;
  margin: 0 8px;
  border-radius: 6px;
}

#spec-modal-variants-container {
  margin-bottom: 18px;
}

/* Variant option styles */
.spec-modal-variant-group {
  margin-bottom: 12px;
}

.spec-modal-variant-label {
  display: block;
  font-weight: 600;
  margin-bottom: 4px;
}

.spec-modal-variant-options {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.spec-modal-variant-option {
  border: 1px solid #b0bec5;
  border-radius: 8px;
  padding: 6px 10px;
  cursor: pointer;
  user-select: none;
  font-size: 0.875rem;
  background-color: #f8fafc;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.spec-modal-variant-option.selected,
.spec-modal-variant-option:hover {
  border-color: #3b82f6;
  background-color: #dbeafe;
}

.spec-modal-variant-option input[type="radio"] {
  display: none;
}

/* Modal error */
.spec-modal-error {
  color: red;
  margin-top: 8px;
  min-height: 1.5em;
}

/* Price Info Section */
#spec-modal-discount-price-info {
  border-top: 1px solid #e0e0e0;
  padding-top: 12px;
  font-size: 1rem;
  color: #333;
  margin-top: 16px;
  margin-bottom: 16px;
}

.spec-modal-price-info-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
}

.spec-modal-unit-price-value {
  color: #e74c3c;
  font-weight: 700;
}

.spec-modal-discount-percent-value {
  background-color: #e67e22;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-weight: 700;
  font-size: 0.9rem;
  white-space: nowrap;
}

.spec-modal-total-price-row {
  margin-top: 8px;
  font-weight: 700;
  font-size: 1.1rem;
  color: #222;
  border-top: 1px solid #e0e0e0;
  padding-top: 8px;
}

/* Utility classes */
.spec-modal-mt-16 {
  margin-top: 16px;
}
.spec-modal-mt-24 {
  margin-top: 24px;
}

.spec-modal-btn {
  padding: 12px 24px;
  border: none;
  cursor: pointer;
  border-radius: 6px;
  font-weight: 600;
  font-size: 1rem;
}

.spec-modal-btn-main {
  background-color: #3b82f6;
  color: white;
}

.spec-modal-btn-main:hover {
  background-color: #2563eb;
}

/* Responsive styling */

/* Tablets & below */
@media (max-width: 767.98px) {
  #spec-modal-discount-price-info {
    font-size: 0.95rem;
  }
  .spec-modal-price-info-row {
    margin-bottom: 4px;
  }
}

/* Mobile devices */
@media (max-width: 479.98px) {
  .spec-modal-overlay {
    align-items: flex-start;
    padding: 12px 12px 30px;
  }
  .spec-modal-window {
    border-radius: 16px 16px 0 0;
    width: 100%;
    max-width: 100%;
    max-height: 100vh;
    height: 100vh;
    transform: translateY(0);
    animation: none; /* remove slide animation for mobile */
    margin: 0;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
  }
  .spec-modal-content {
    padding: 16px 20px;
    overflow-y: auto;
  }
  .spec-modal-header {
    padding: 16px 20px;
  }
  #spec-modal-discount-price-info {
    font-size: 0.9rem;
  }
  .spec-modal-price-info-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
  .spec-modal-price-info-row > div:last-child {
    align-self: flex-end;
  }
  .spec-modal-total-price-row {
    font-size: 1rem;
  }
}

<div id="spec-modal-product-spec-modal" class="spec-modal-overlay" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="spec-modal-modalTitle">
        <div class="spec-modal-window" role="document">
            <header class="spec-modal-header">
            <h2 id="spec-modal-modalTitle">選擇產品規格和數量</h2>
            <button type="button" aria-label="Close modal" class="spec-modal-close">&times;</button>
            </header>
            <main class="spec-modal-content">
            <form id="spec-modal-product-spec-form">
                <div id="spec-modal-variants-container">
                <!-- Variant options injected dynamically here -->
                </div>

                <label for="spec-modal-qty" class="spec-modal-mt-16">數量:</label>
                <div class="spec-modal-quantity-input">
                <button type="button" class="spec-modal-qty-btn spec-modal-qty-minus" aria-label="減少數量">−</button>
                <input type="number" id="spec-modal-qty" name="qty" value="1" min="1" step="1" />
                <button type="button" class="spec-modal-qty-btn spec-modal-qty-plus" aria-label="增加數量">+</button>
                </div>

                <!-- Price Info Section - added -->
                <div id="spec-modal-discount-price-info" aria-live="polite" aria-atomic="true">
                <div class="spec-modal-price-info-row">
                    <div class="spec-modal-unit-price-label">單價：</div>
                    <div class="spec-modal-unit-price-value" id="spec-modal-unit-price">HK$0.00</div>
                </div>
                <div class="spec-modal-price-info-row">
                    <div class="spec-modal-discount-percent-label">折扣：</div>
                    <div class="spec-modal-discount-percent-value" id="spec-modal-discount-percent">-0%</div>
                </div>
                <div class="spec-modal-price-info-row spec-modal-total-price-row">
                    <div class="spec-modal-total-price-label">合計：</div>
                    <div class="spec-modal-total-price-value" id="spec-modal-total-price">HK$0.00</div>
                </div>
                </div>

                <div class="spec-modal-error" aria-live="polite" style="color:red; margin-top: 8px;"></div>
                <button type="submit" class="spec-modal-btn spec-modal-btn-main spec-modal-mt-24" style="width:100%">確認加入購物車</button>
            </form>
            </main>
        </div>
    </div>

And I found the problem is the modal content cannot show normal on screen, just the overlay can be show, why and how to fix it?