JAVASCRIPTshopifybeginner
Shopify Size Chart Modal
Add a size chart popup to help customers choose the right size
Faisal Yaqoob
November 24, 2025
#shopify#size-chart#modal#apparel#ux
Code
javascript
1 // Size Chart Modal Class 2 class SizeChartModal { 3 constructor(options = {}) { 4 this.trigger = options.trigger || '[data-size-chart-trigger]'; 5 this.modalId = options.modalId || 'size-chart-modal'; 6 this.modal = null; 7
8 this.init(); 9 } 10
11 init() { 12 // Create modal 13 this.createModal(); 14
15 // Bind trigger buttons 16 document.addEventListener('click', (e) => { 17 if (e.target.matches(this.trigger) || e.target.closest(this.trigger)) { 18 e.preventDefault(); 19 this.open(); 20 } 21 }); 22 } 23
24 createModal() { 25 this.modal = document.createElement('div'); 26 this.modal.id = this.modalId; 27 this.modal.className = 'size-chart-modal'; 28
29 this.modal.innerHTML = ` 30 <div class="size-chart-overlay" data-close-modal></div> 31 <div class="size-chart-content"> 32 <button class="size-chart-close" data-close-modal aria-label="Close">×</button> 33
34 <div class="size-chart-header"> 35 <h2>Size Chart</h2> 36 <p>Find your perfect fit</p> 37 </div> 38
39 <div class="size-chart-body"> 40 <!-- Size tabs --> 41 <div class="size-tabs"> 42 <button class="size-tab active" data-tab="women">Women's</button> 43 <button class="size-tab" data-tab="men">Men's</button> 44 <button class="size-tab" data-tab="kids">Kids</button> 45 </div> 46
47 <!-- Women's Size Chart --> 48 <div class="size-table-wrapper active" data-content="women"> 49 <div class="size-table-scroll"> 50 <table class="size-table"> 51 <thead> 52 <tr> 53 <th>Size</th> 54 <th>US</th> 55 <th>Bust (in)</th> 56 <th>Waist (in)</th> 57 <th>Hips (in)</th> 58 </tr> 59 </thead> 60 <tbody> 61 <tr> 62 <td>XS</td> 63 <td>0-2</td> 64 <td>32-33</td> 65 <td>24-25</td> 66 <td>34-35</td> 67 </tr> 68 <tr> 69 <td>S</td> 70 <td>4-6</td> 71 <td>34-35</td> 72 <td>26-27</td> 73 <td>36-37</td> 74 </tr> 75 <tr> 76 <td>M</td> 77 <td>8-10</td> 78 <td>36-37</td> 79 <td>28-29</td> 80 <td>38-39</td> 81 </tr> 82 <tr> 83 <td>L</td> 84 <td>12-14</td> 85 <td>38-40</td> 86 <td>30-32</td> 87 <td>40-42</td> 88 </tr> 89 <tr> 90 <td>XL</td> 91 <td>16-18</td> 92 <td>42-44</td> 93 <td>34-36</td> 94 <td>44-46</td> 95 </tr> 96 </tbody> 97 </table> 98 </div> 99 </div> 100
101 <!-- Men's Size Chart --> 102 <div class="size-table-wrapper" data-content="men"> 103 <div class="size-table-scroll"> 104 <table class="size-table"> 105 <thead> 106 <tr> 107 <th>Size</th> 108 <th>Chest (in)</th> 109 <th>Waist (in)</th> 110 <th>Sleeve (in)</th> 111 </tr> 112 </thead> 113 <tbody> 114 <tr> 115 <td>S</td> 116 <td>34-36</td> 117 <td>28-30</td> 118 <td>32-33</td> 119 </tr> 120 <tr> 121 <td>M</td> 122 <td>38-40</td> 123 <td>32-34</td> 124 <td>33-34</td> 125 </tr> 126 <tr> 127 <td>L</td> 128 <td>42-44</td> 129 <td>36-38</td> 130 <td>34-35</td> 131 </tr> 132 <tr> 133 <td>XL</td> 134 <td>46-48</td> 135 <td>40-42</td> 136 <td>35-36</td> 137 </tr> 138 <tr> 139 <td>XXL</td> 140 <td>50-52</td> 141 <td>44-46</td> 142 <td>36-37</td> 143 </tr> 144 </tbody> 145 </table> 146 </div> 147 </div> 148
149 <!-- Kids Size Chart --> 150 <div class="size-table-wrapper" data-content="kids"> 151 <div class="size-table-scroll"> 152 <table class="size-table"> 153 <thead> 154 <tr> 155 <th>Size</th> 156 <th>Age</th> 157 <th>Height (in)</th> 158 <th>Weight (lbs)</th> 159 </tr> 160 </thead> 161 <tbody> 162 <tr> 163 <td>XS</td> 164 <td>4-5</td> 165 <td>41-44</td> 166 <td>34-41</td> 167 </tr> 168 <tr> 169 <td>S</td> 170 <td>6-7</td> 171 <td>45-49</td> 172 <td>42-52</td> 173 </tr> 174 <tr> 175 <td>M</td> 176 <td>8-10</td> 177 <td>50-54</td> 178 <td>53-70</td> 179 </tr> 180 <tr> 181 <td>L</td> 182 <td>12-14</td> 183 <td>55-61</td> 184 <td>71-100</td> 185 </tr> 186 </tbody> 187 </table> 188 </div> 189 </div> 190
191 <!-- Measurement Guide --> 192 <div class="measurement-guide"> 193 <h3>How to Measure</h3> 194 <div class="measurement-tips"> 195 <div class="measurement-tip"> 196 <strong>Bust/Chest:</strong> Measure around the fullest part 197 </div> 198 <div class="measurement-tip"> 199 <strong>Waist:</strong> Measure around the narrowest part 200 </div> 201 <div class="measurement-tip"> 202 <strong>Hips:</strong> Measure around the fullest part 203 </div> 204 </div> 205 </div> 206 </div> 207 </div> 208 `; 209
210 document.body.appendChild(this.modal); 211
212 // Bind close events 213 this.modal.querySelectorAll('[data-close-modal]').forEach(el => { 214 el.addEventListener('click', () => this.close()); 215 }); 216
217 // Tab switching 218 this.initTabs(); 219 } 220
221 initTabs() { 222 const tabs = this.modal.querySelectorAll('.size-tab'); 223 const contents = this.modal.querySelectorAll('.size-table-wrapper'); 224
225 tabs.forEach(tab => { 226 tab.addEventListener('click', () => { 227 const target = tab.dataset.tab; 228
229 // Update active tab 230 tabs.forEach(t => t.classList.remove('active')); 231 tab.classList.add('active'); 232
233 // Update visible content 234 contents.forEach(content => { 235 if (content.dataset.content === target) { 236 content.classList.add('active'); 237 } else { 238 content.classList.remove('active'); 239 } 240 }); 241 }); 242 }); 243 } 244
245 open() { 246 this.modal.classList.add('is-active'); 247 document.body.style.overflow = 'hidden'; 248
249 // Trigger analytics event 250 if (typeof window.dataLayer !== 'undefined') { 251 window.dataLayer.push({ 252 'event': 'size_chart_opened' 253 }); 254 } 255 } 256
257 close() { 258 this.modal.classList.remove('is-active'); 259 document.body.style.overflow = ''; 260 } 261 } 262
263 // Initialize 264 document.addEventListener('DOMContentLoaded', () => { 265 new SizeChartModal(); 266 });
Shopify Size Chart Modal
Create a professional size chart modal that helps customers select the correct size, reducing returns and improving customer satisfaction.
// Size Chart Modal Class
class SizeChartModal {
constructor(options = {}) {
this.trigger = options.trigger || '[data-size-chart-trigger]';
this.modalId = options.modalId || 'size-chart-modal';
this.modal = null;
this.init();
}
init() {
// Create modal
this.createModal();
// Bind trigger buttons
document.addEventListener('click', (e) => {
if (e.target.matches(this.trigger) || e.target.closest(this.trigger)) {
e.preventDefault();
this.open();
}
});
}
createModal() {
this.modal = document.createElement('div');
this.modal.id = this.modalId;
this.modal.className = 'size-chart-modal';
this.modal.innerHTML = `
<div class="size-chart-overlay" data-close-modal></div>
<div class="size-chart-content">
<button class="size-chart-close" data-close-modal aria-label="Close">×</button>
<div class="size-chart-header">
<h2>Size Chart</h2>
<p>Find your perfect fit</p>
</div>
<div class="size-chart-body">
<!-- Size tabs -->
<div class="size-tabs">
<button class="size-tab active" data-tab="women">Women's</button>
<button class="size-tab" data-tab="men">Men's</button>
<button class="size-tab" data-tab="kids">Kids</button>
</div>
<!-- Women's Size Chart -->
<div class="size-table-wrapper active" data-content="women">
<div class="size-table-scroll">
<table class="size-table">
<thead>
<tr>
<th>Size</th>
<th>US</th>
<th>Bust (in)</th>
<th>Waist (in)</th>
<th>Hips (in)</th>
</tr>
</thead>
<tbody>
<tr>
<td>XS</td>
<td>0-2</td>
<td>32-33</td>
<td>24-25</td>
<td>34-35</td>
</tr>
<tr>
<td>S</td>
<td>4-6</td>
<td>34-35</td>
<td>26-27</td>
<td>36-37</td>
</tr>
<tr>
<td>M</td>
<td>8-10</td>
<td>36-37</td>
<td>28-29</td>
<td>38-39</td>
</tr>
<tr>
<td>L</td>
<td>12-14</td>
<td>38-40</td>
<td>30-32</td>
<td>40-42</td>
</tr>
<tr>
<td>XL</td>
<td>16-18</td>
<td>42-44</td>
<td>34-36</td>
<td>44-46</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Men's Size Chart -->
<div class="size-table-wrapper" data-content="men">
<div class="size-table-scroll">
<table class="size-table">
<thead>
<tr>
<th>Size</th>
<th>Chest (in)</th>
<th>Waist (in)</th>
<th>Sleeve (in)</th>
</tr>
</thead>
<tbody>
<tr>
<td>S</td>
<td>34-36</td>
<td>28-30</td>
<td>32-33</td>
</tr>
<tr>
<td>M</td>
<td>38-40</td>
<td>32-34</td>
<td>33-34</td>
</tr>
<tr>
<td>L</td>
<td>42-44</td>
<td>36-38</td>
<td>34-35</td>
</tr>
<tr>
<td>XL</td>
<td>46-48</td>
<td>40-42</td>
<td>35-36</td>
</tr>
<tr>
<td>XXL</td>
<td>50-52</td>
<td>44-46</td>
<td>36-37</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Kids Size Chart -->
<div class="size-table-wrapper" data-content="kids">
<div class="size-table-scroll">
<table class="size-table">
<thead>
<tr>
<th>Size</th>
<th>Age</th>
<th>Height (in)</th>
<th>Weight (lbs)</th>
</tr>
</thead>
<tbody>
<tr>
<td>XS</td>
<td>4-5</td>
<td>41-44</td>
<td>34-41</td>
</tr>
<tr>
<td>S</td>
<td>6-7</td>
<td>45-49</td>
<td>42-52</td>
</tr>
<tr>
<td>M</td>
<td>8-10</td>
<td>50-54</td>
<td>53-70</td>
</tr>
<tr>
<td>L</td>
<td>12-14</td>
<td>55-61</td>
<td>71-100</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Measurement Guide -->
<div class="measurement-guide">
<h3>How to Measure</h3>
<div class="measurement-tips">
<div class="measurement-tip">
<strong>Bust/Chest:</strong> Measure around the fullest part
</div>
<div class="measurement-tip">
<strong>Waist:</strong> Measure around the narrowest part
</div>
<div class="measurement-tip">
<strong>Hips:</strong> Measure around the fullest part
</div>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(this.modal);
// Bind close events
this.modal.querySelectorAll('[data-close-modal]').forEach(el => {
el.addEventListener('click', () => this.close());
});
// Tab switching
this.initTabs();
}
initTabs() {
const tabs = this.modal.querySelectorAll('.size-tab');
const contents = this.modal.querySelectorAll('.size-table-wrapper');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const target = tab.dataset.tab;
// Update active tab
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// Update visible content
contents.forEach(content => {
if (content.dataset.content === target) {
content.classList.add('active');
} else {
content.classList.remove('active');
}
});
});
});
}
open() {
this.modal.classList.add('is-active');
document.body.style.overflow = 'hidden';
// Trigger analytics event
if (typeof window.dataLayer !== 'undefined') {
window.dataLayer.push({
'event': 'size_chart_opened'
});
}
}
close() {
this.modal.classList.remove('is-active');
document.body.style.overflow = '';
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
new SizeChartModal();
});
Liquid Template
<!-- Size Chart Trigger Link -->
<div class="product-size-selector">
<label>Size:</label>
<select name="Size" class="product-size-select">
{% for option in product.options_with_values %}
{% if option.name == 'Size' %}
{% for value in option.values %}
<option value="{{ value }}">{{ value }}</option>
{% endfor %}
{% endif %}
{% endfor %}
</select>
<a href="#" class="size-chart-link" data-size-chart-trigger>
<svg width="16" height="16" viewBox="0 0 16 16">
<path d="M0 0h6v6H0V0zm0 10h6v6H0v-6zM10 0h6v6h-6V0zm0 10h6v6h-6v-6z"/>
</svg>
Size Chart
</a>
</div>
CSS Styling
/* Size Chart Link */
.size-chart-link {
display: inline-flex;
align-items: center;
gap: 5px;
color: #0066cc;
text-decoration: none;
font-size: 14px;
margin-left: 10px;
}
.size-chart-link:hover {
text-decoration: underline;
}
/* Modal */
.size-chart-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
display: none;
}
.size-chart-modal.is-active {
display: block;
}
.size-chart-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
cursor: pointer;
}
.size-chart-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 8px;
max-width: 800px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.size-chart-close {
position: absolute;
top: 15px;
right: 15px;
background: none;
border: none;
font-size: 30px;
cursor: pointer;
line-height: 1;
z-index: 1;
}
.size-chart-header {
padding: 30px 30px 20px;
border-bottom: 1px solid #eee;
}
.size-chart-header h2 {
margin: 0 0 5px;
font-size: 24px;
}
.size-chart-header p {
margin: 0;
color: #666;
}
.size-chart-body {
padding: 30px;
}
/* Tabs */
.size-tabs {
display: flex;
gap: 10px;
margin-bottom: 25px;
border-bottom: 2px solid #eee;
}
.size-tab {
padding: 10px 20px;
background: none;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
font-size: 15px;
font-weight: 500;
color: #666;
margin-bottom: -2px;
transition: all 0.3s;
}
.size-tab:hover {
color: #000;
}
.size-tab.active {
color: #000;
border-bottom-color: #000;
}
/* Size Table */
.size-table-wrapper {
display: none;
}
.size-table-wrapper.active {
display: block;
}
.size-table-scroll {
overflow-x: auto;
}
.size-table {
width: 100%;
border-collapse: collapse;
}
.size-table th,
.size-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}
.size-table th {
background: #f9f9f9;
font-weight: 600;
font-size: 14px;
}
.size-table td {
font-size: 14px;
}
.size-table tbody tr:hover {
background: #f5f5f5;
}
/* Measurement Guide */
.measurement-guide {
margin-top: 30px;
padding: 20px;
background: #f9f9f9;
border-radius: 6px;
}
.measurement-guide h3 {
margin: 0 0 15px;
font-size: 16px;
}
.measurement-tips {
display: grid;
gap: 10px;
}
.measurement-tip {
font-size: 14px;
}
.measurement-tip strong {
display: inline-block;
min-width: 100px;
color: #000;
}
/* Responsive */
@media (max-width: 768px) {
.size-chart-content {
width: 95%;
max-height: 95vh;
}
.size-chart-header,
.size-chart-body {
padding: 20px;
}
.size-tabs {
flex-wrap: wrap;
}
.size-tab {
padding: 8px 15px;
font-size: 14px;
}
.size-table th,
.size-table td {
padding: 8px;
font-size: 13px;
}
}
Dynamic Size Chart from Metafields
<!-- Load size chart data from product metafields -->
{% if product.metafields.custom.size_chart %}
<script>
window.productSizeChart = {{ product.metafields.custom.size_chart }};
</script>
{% endif %}
// Render custom size chart from metafield
if (window.productSizeChart) {
const sizeChartData = JSON.parse(window.productSizeChart);
// Render dynamic table from data
}
Features
- Tabbed Interface: Easy navigation between categories
- Responsive Tables: Horizontal scroll on mobile
- Measurement Guide: Helps customers measure correctly
- Professional Design: Clean, modern appearance
- Keyboard Accessible: ESC to close
- Mobile Optimized: Perfect on all devices
- Analytics Ready: Track usage for insights
- Customizable: Easy to modify for your sizes
Related Snippets
Shopify Product Quick View Modal
Add a quick view popup to preview products without leaving the page
JAVASCRIPTshopifyintermediate
javascriptPreview
// Quick View Modal Class
class ProductQuickView {
constructor() {
this.modal = null;
...#shopify#product#modal+2
11/17/2025
View
Shopify Wishlist with LocalStorage
Add wishlist/favorites functionality without requiring a Shopify app
JAVASCRIPTshopifyintermediate
javascriptPreview
// Wishlist Class
class Wishlist {
constructor(options = {}) {
this.storageKey = options.storageKey || 'shopify_wishlist';
...#shopify#wishlist#favorites+1
11/28/2025
View
Shopify AJAX Collection Filters
Add dynamic filtering to collection pages without page reloads
JAVASCRIPTshopifyadvanced
javascriptPreview
// Collection Filters Class
class CollectionFilters {
constructor(options = {}) {
this.container = document.querySelector(options.container || '[data-collection-container]');
...#shopify#filters#ajax+2
11/27/2025
View