Hi All,
I want design and add functionality pdp page as per attached screenshot. Anyone help me to create pdp page design.
Request to create a PDP (product detail page) matching a provided screenshot. Clarified that the desired layout is a custom product configurator, not a standard Shopify PDP.
Key build guidance:
Constraints and tools:
Status: Guidance provided; implementation not confirmed, discussion effectively open. Image reference informs the desired design.
Hi All,
I want design and add functionality pdp page as per attached screenshot. Anyone help me to create pdp page design.
Thanks Zuhairan
I have done almost with customize code only Add-on pricing (+$3 / +$5) not showing cart page.
Can you help me to create this?
Can you help me this one
Create hidden add-on variants ($3, $5).
When checkbox is checked, use JS to auto-add the add-on variant to cart.
This makes pricing show correctly in cart and checkout.
You could go the easy route in this. So with variant metafields display the extra amount for the sizes and the checkbox selected. And where you set the price create prices for those variants including that size and selection.
Since all these are variants, you so get to add different prices for different variation mix in product admin.
Hope I was able to explain
Best
@shreys did it work on your end?
not yet, this code i used.
.custom-options-wrapper { margin: 20px 0; border: 1px solid #e5e5e5; border-radius: 8px; padding: 20px; } /\* Color Swatches \*/ .color-swatches { margin-bottom: 20px; } .color-swatches h3 { font-size: 14px; font-weight: 600; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.5px; } .color-options { display: flex; gap: 8px; flex-wrap: wrap; } .color-swatch { width: 40px; height: 40px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; transition: all 0.2s ease; position: relative; background-size: cover; background-position: center; } .color-swatch:hover { transform: scale(1.1); border-color: #333; } .color-swatch.selected { border-color: #333; box-shadow: 0 0 0 2px #fff, 0 0 0 4px #333; } .color-swatch-label { font-size: 11px; text-align: center; margin-top: 4px; color: #666; } /\* Tab Navigation \*/ .customization-tabs { display: flex; gap: 0; margin-bottom: 20px; border-bottom: 2px solid #e5e5e5; flex-wrap: wrap; } .tab-button { flex: 1; min-width: 100px; padding: 12px 20px; background: transparent; border: none; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; cursor: pointer; transition: all 0.3s ease; position: relative; color: #666; } .tab-button:hover { color: #333; } .tab-button.active { color: #c8102e; background: transparent; } .tab-button.active::after { content: ''; position: absolute; bottom: -2px; left: 0; right: 0; height: 2px; background: #c8102e; } /\* Tab Content \*/ .tab-content { display: none; } .tab-content.active { display: block; } .customization-options { margin-bottom: 20px; } .option-checkbox { display: flex; align-items: center; padding: 10px 0; cursor: pointer; } .option-checkbox input\[type="checkbox"\] { width: 18px; height: 18px; margin-right: 10px; cursor: pointer; accent-color: #c8102e; } .option-checkbox label { cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 5px; width: 100%; } .option-price { color: #666; font-size: 13px; margin-left: auto; } /\* Size Selection with Quantities \*/ .size-selection { margin-top: 20px; } .size-selection h4 { font-size: 14px; font-weight: 600; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 0.5px; } .size-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 12px; margin-bottom: 15px; } .size-item { border: 1px solid #ddd; border-radius: 8px; padding: 12px; background: #fff; transition: all 0.2s ease; } .size-item:hover { border-color: #c8102e; box-shadow: 0 2px 8px rgba(200, 16, 46, 0.1); } .size-item.has-quantity { border-color: #c8102e; background: #fff5f7; } .size-label { font-size: 16px; font-weight: 600; margin-bottom: 8px; text-align: center; color: #333; } .size-price { font-size: 12px; color: #666; text-align: center; margin-bottom: 8px; } .quantity-selector { display: flex; align-items: center; justify-content: center; gap: 8px; } .qty-btn { width: 28px; height: 28px; border: 1px solid #ddd; background: #fff; border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; color: #333; transition: all 0.2s ease; } .qty-btn:hover { background: #c8102e; color: white; border-color: #c8102e; } .qty-btn:disabled { opacity: 0.3; cursor: not-allowed; background: #f5f5f5; } .qty-input { width: 50px; height: 32px; text-align: center; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; font-weight: 600; } /\* Size upcharge info \*/ .size-upcharge-note { font-size: 12px; color: #666; margin-top: 10px; text-align: center; font-style: italic; } /\* Price Display \*/ .dynamic-price-display { margin: 20px 0; padding: 15px; background: #f9f9f9; border-radius: 6px; } .price-breakdown { font-size: 14px; } .price-row { display: flex; justify-content: space-between; margin-bottom: 8px; } .price-row.total { font-weight: 700; font-size: 18px; border-top: 2px solid #ddd; padding-top: 12px; margin-top: 12px; color: #c8102e; } .total-items { font-size: 12px; color: #666; text-align: right; margin-top: 4px; } /\* Add to Cart Button \*/ .custom-add-to-cart { width: 100%; padding: 16px; background: #c8102e; color: white; border: none; border-radius: 4px; font-size: 15px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; cursor: pointer; transition: background 0.3s ease; } .custom-add-to-cart:hover { background: #a00d25; } .custom-add-to-cart:disabled { background: #ccc; cursor: not-allowed; } @media (max-width: 768px) { .tab-button { font-size: 11px; padding: 10px 12px; } .size-grid { grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: 8px; } .color-swatch { width: 35px; height: 35px; } }{% assign color_option = product.options_with_values | where: “name”, “Color” | first %}
{% if color_option %}
<h3>Colors</h3>
<div class="color-options">
{% for value in color_option.values %}
{% assign color_name = value %}
{% assign color_handle = value | handleize %}
<div>
<button type="button"
class="color-swatch{% if forloop.first %} selected{% endif %}"
data-color="{{ color_name }}"
style="background-color: {{ value | replace: ' ', '' | downcase }};"
title="{{ color_name }}">
</button>
</div>
{% endfor %}
</div>
{% endif %}
<input
type="text"
id="customTextInput"
placeholder="Enter custom text here"
style="
width:100%;
padding:12px;
border:1px solid #ddd;
border-radius:6px;
font-size:14px;
"
<button type="button" class="tab-button active" data-tab="front">Front</button>
<button type="button" class="tab-button" data-tab="fullback">Full Back</button>
<button type="button" class="tab-button" data-tab="leftsleeve">Left Sleeve</button>
<button type="button" class="tab-button" data-tab="rightsleeve">Right Sleeve</button>
<div class="customization-options">
<div class="option-checkbox">
<input type="checkbox" id="front-printed" name="customization\[front\]" value="Left Chest Printed Logo" data-price="3">
<label for="front-printed">
<span>Left Chest Printed Logo</span>
<span class="option-price">+$3</span>
</label>
</div>
<div class="option-checkbox">
<input type="checkbox" id="front-embroidered" name="customization\[front\]" value="Left Chest Embroidered Logo" data-price="5">
<label for="front-embroidered">
<span>Left Chest Embroidered Logo</span>
<span class="option-price">+$5</span>
</label>
</div>
<div class="option-checkbox">
<input type="checkbox" id="front-full" name="customization\[front\]" value="Full Chest Printed" data-price="5">
<label for="front-full">
<span>Full Chest Printed</span>
<span class="option-price">+$5</span>
</label>
</div>
</div>
<div class="customization-options">
<div class="option-checkbox">
<input type="checkbox" id="back-printed" name="customization\[fullback\]" value="Full Back Printed Logo" data-price="5">
<label for="back-printed">
<span>Full Back Printed Logo</span>
<span class="option-price">+$5</span>
</label>
</div>
</div>
<div class="customization-options">
<div class="option-checkbox">
<input type="checkbox" id="leftsleeve-printed" name="customization\[leftsleeve\]" value="Left Sleeve Printed" data-price="3">
<label for="leftsleeve-printed">
<span>Left Sleeve Printed</span>
<span class="option-price">+$3</span>
</label>
</div>
<div class="option-checkbox">
<input type="checkbox" id="leftsleeve-embroidered" name="customization\[leftsleeve\]" value="Left Sleeve Embroidered" data-price="5">
<label for="leftsleeve-embroidered">
<span>Left Sleeve Embroidered</span>
<span class="option-price">+$5</span>
</label>
</div>
</div>
<div class="customization-options">
<div class="option-checkbox">
<input type="checkbox" id="rightsleeve-printed" name="customization\[rightsleeve\]" value="Right Sleeve Printed" data-price="3">
<label for="rightsleeve-printed">
<span>Right Sleeve Printed</span>
<span class="option-price">+$3</span>
</label>
</div>
<div class="option-checkbox">
<input type="checkbox" id="rightsleeve-embroidered" name="customization\[rightsleeve\]" value="Right Sleeve Embroidered" data-price="5">
<label for="rightsleeve-embroidered">
<span>Right Sleeve Embroidered</span>
<span class="option-price">+$5</span>
</label>
</div>
</div>
{% assign size_option = product.options_with_values | where: “name”, “Size” | first %}
{% if size_option %}
<h4>Adult - Select Quantities by Size</h4>
<div class="size-grid" id="sizeGrid">
{% for value in size_option.values %}
{% assign size_name = value %}
{% assign size_handle = value | handleize %}
{% assign upcharge = 0 %}
<div class="size-item" data-size="{{ size_name }}" data-upcharge="{{ upcharge }}">
<div class="size-label">{{ size_name }}</div>
{% if upcharge > 0 %}
<div class="size-price">+${{ upcharge }} each</div>
{% else %}
<div class="size-price"> </div>
{% endif %}
<div class="quantity-selector">
<button type="button" class="qty-btn qty-minus" data-size="{{ size_name }}">−</button>
<input type="number"
class="qty-input"
data-size="{{ size_name }}"
value="0"
min="0"
max="999"
readonly>
<button type="button" class="qty-btn qty-plus" data-size="{{ size_name }}">+</button>
</div>
</div>
{% endfor %}
</div>
<div class="size-upcharge-note">
</div>
{% endif %}
<div class="price-breakdown">
<div class="price-row">
<span>Base Price (per item):</span>
<span id="basePrice">{{ product.price | money }}</span>
</div>
<div class="price-row" id="customizationRow" style="display: none;">
<span>Customization:</span>
<span id="customizationPrice">$0.00</span>
</div>
<div class="price-row" id="sizeUpchargeRow" style="display: none;">
<span>Size Upcharges:</span>
<span id="sizeUpcharge">$0.00</span>
</div>
<div class="price-row">
<span>Subtotal:</span>
<span id="subtotalPrice">$0.00</span>
</div>
<div class="price-row total">
<span>Total Price:</span>
<span id="totalPrice">$0.00</span>
</div>
<div class="total-items" id="totalItems">0 items</div>
</div>
Add to Cart
document.addEventListener(‘DOMContentLoaded’, function() {
const customOptions = document.getElementById(‘customProductOptions’);
if (!customOptions) return;
const productPrice = {{ product.price | money_without_currency | remove: ‘,’ }};
// Tab switching
const tabButtons = customOptions.querySelectorAll(‘.tab-button’);
const tabContents = customOptions.querySelectorAll(‘.tab-content’);
tabButtons.forEach(button => {
button.addEventListener('click', function() {
const targetTab = this.dataset.tab;
tabButtons.forEach(btn => btn.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
this.classList.add('active');
document.getElementById('tab-' + targetTab).classList.add('active');
});
});
// Color selection
const colorSwatches = customOptions.querySelectorAll(‘.color-swatch’);
let selectedColor = colorSwatches.length > 0 ? colorSwatches[0].dataset.color : ‘’;
colorSwatches.forEach(swatch => {
swatch.addEventListener('click', function() {
colorSwatches.forEach(s => s.classList.remove('selected'));
this.classList.add('selected');
selectedColor = this.dataset.color;
});
});
// Quantity management
const quantities = {};
function updateQuantity(size, change) {
const input = customOptions.querySelector(\`.qty-input\[data-size="${size}"\]\`);
const sizeItem = customOptions.querySelector(\`.size-item\[data-size="${size}"\]\`);
let currentQty = parseInt(input.value) || 0;
currentQty += change;
if (currentQty < 0) currentQty = 0;
if (currentQty > 999) currentQty = 999;
input.value = currentQty;
quantities\[size\] = currentQty;
// Visual feedback
if (currentQty > 0) {
sizeItem.classList.add('has-quantity');
} else {
sizeItem.classList.remove('has-quantity');
}
calculatePrice();
}
// Plus/Minus buttons
customOptions.querySelectorAll(‘.qty-plus’).forEach(btn => {
btn.addEventListener('click', function() {
const size = this.dataset.size;
updateQuantity(size, 1);
});
});
customOptions.querySelectorAll(‘.qty-minus’).forEach(btn => {
btn.addEventListener('click', function() {
const size = this.dataset.size;
updateQuantity(size, -1);
});
});
// Direct input
customOptions.querySelectorAll(‘.qty-input’).forEach(input => {
input.addEventListener('change', function() {
const size = this.dataset.size;
let value = parseInt(this.value) || 0;
if (value < 0) value = 0;
if (value > 999) value = 999;
this.value = value;
quantities\[size\] = value;
calculatePrice();
});
});
// Price calculation - THIS IS THE KEY FIX
function calculatePrice() {
let totalQuantity = 0;
let subtotal = 0;
let customizationCostPerItem = 0;
let sizeUpchargeTotal = 0;
// Calculate total quantity and base cost
Object.keys(quantities).forEach(size => {
const qty = quantities\[size\];
if (qty > 0) {
totalQuantity += qty;
const sizeItem = customOptions.querySelector(\`.size-item\[data-size="${size}"\]\`);
const upcharge = parseFloat(sizeItem.dataset.upcharge || 0);
subtotal += (productPrice + upcharge) \* qty;
sizeUpchargeTotal += upcharge \* qty;
}
});
// Calculate customization cost per item
const checkedOptions = customOptions.querySelectorAll('input\[type="checkbox"\]:checked');
checkedOptions.forEach(option => {
customizationCostPerItem += parseFloat(option.dataset.price || 0);
});
const customizationTotal = customizationCostPerItem \* totalQuantity;
// Update display
document.getElementById('basePrice').textContent = '$' + productPrice.toFixed(2);
if (customizationTotal > 0) {
document.getElementById('customizationRow').style.display = 'flex';
document.getElementById('customizationPrice').textContent = '$' + customizationTotal.toFixed(2);
} else {
document.getElementById('customizationRow').style.display = 'none';
}
if (sizeUpchargeTotal > 0) {
document.getElementById('sizeUpchargeRow').style.display = 'flex';
document.getElementById('sizeUpcharge').textContent = '$' + sizeUpchargeTotal.toFixed(2);
} else {
document.getElementById('sizeUpchargeRow').style.display = 'none';
}
document.getElementById('subtotalPrice').textContent = '$' + subtotal.toFixed(2);
const totalPrice = subtotal + customizationTotal;
document.getElementById('totalPrice').textContent = '$' + totalPrice.toFixed(2);
document.getElementById('totalItems').textContent = totalQuantity + ' item' + (totalQuantity !== 1 ? 's' : '');
// Enable/disable add to cart button
const addToCartBtn = document.getElementById('customAddToCart');
if (totalQuantity > 0) {
addToCartBtn.disabled = false;
} else {
addToCartBtn.disabled = true;
}
return {
totalQuantity,
subtotal,
customizationTotal,
sizeUpchargeTotal,
totalPrice,
customizationCostPerItem
};
}
// Listen for changes
customOptions.querySelectorAll(‘input[type=“checkbox”]’).forEach(input => {
input.addEventListener('change', calculatePrice);
});
// Initial calculation
calculatePrice();
// UPDATED Add to Cart functionality
document.getElementById(‘customAddToCart’).addEventListener(‘click’, function() {
const prices = calculatePrice();
if (prices.totalQuantity === 0) {
alert('Please select at least one size with quantity');
return;
}
// Get custom text
const customText = document.getElementById('customTextInput').value;
// Gather all selected options
const customizations = \[\];
const checkedOptions = customOptions.querySelectorAll('input\[type="checkbox"\]:checked');
checkedOptions.forEach(option => {
customizations.push(option.value);
});
// Build size breakdown
const sizeBreakdown = \[\];
Object.keys(quantities).forEach(size => {
if (quantities\[size\] > 0) {
sizeBreakdown.push(\`${size}: ${quantities\[size\]}\`);
}
});
// MAIN FIX: Store all pricing information in properties
const properties = {
'Color': selectedColor || 'Not specified',
'Custom Text': customText || 'None',
'Sizes': sizeBreakdown.join(', '),
'Total Quantity': prices.totalQuantity.toString(),
'Customizations': customizations.join(', ') || 'None',
'\_Base_Price': '$' + productPrice.toFixed(2),
'\_Customization_Per_Item': '$' + prices.customizationCostPerItem.toFixed(2),
'\_Total_Customization': '$' + prices.customizationTotal.toFixed(2),
'\_Subtotal': '$' + prices.subtotal.toFixed(2),
'\_Total_Price': '$' + prices.totalPrice.toFixed(2)
};
const formData = {
items: \[{
id: {{ product.variants.first.id }},
quantity: prices.totalQuantity,
properties: properties
}\]
};
// Show loading state
this.textContent = 'Adding...';
this.disabled = true;
fetch('/cart/add.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
console.log('Added to cart:', data);
// Update cart attributes with total custom price
return fetch('/cart/update.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
attributes: {
'\_has_custom_pricing': 'true',
'\_custom_total': prices.totalPrice.toFixed(2)
}
})
});
})
.then(() => {
window.location.href = '/cart';
})
.catch(error => {
console.error('Error:', error);
alert('Error adding to cart. Please try again.');
this.textContent = 'Add to Cart';
this.disabled = false;
});
});
});
@shreys sent you a dm
okay got it i will do
@shreys sent you a message via the provided email