Shopify 2.0 Portfolio
WinSport
Theme
End-to-end Shopify theme development — from Dawn base to production-ready store
10 Phases · Custom Design · Zero Dependencies
Project Overview
The Numbers
Structure → Design → Sections → Metafields → App Data → VAT Pricing → JS Interactions → Performance → Git → Docs
Tech Stack
Built With
Platform
Shopify 2.0
Online Store 2.0 architecture with JSON templates and section-everywhere support
Templating
Liquid
Shopify's templating language with filters, conditional rendering, and metafield access
JavaScript
Vanilla JS
Web Components, custom elements, zero jQuery — event delegation and module pattern
CSS
Mobile-First
Responsive design, CSS custom properties, no preprocessors
Base Theme
Dawn v15.4.1
Shopify's reference theme — fully customized and extended
Tooling
Shopify CLI
Local development, theme push/pull, Lighthouse audits
Phase 2
Before: Dawn Default
Stock Dawn theme — generic, no brand identity
Phase 2
After: WinSport Design
Custom luxury sports aesthetic — Cormorant + Work Sans, beige & navy palette
Phase 2
Design System
Cormorant Garamond
Work Sans — UI & Body
- Pixel-accurate Figma implementation
- Mobile-first responsive approach
- No page builders — all code
- CSS custom properties for theming
- Consistent spacing & typography scale
Phase 3
Dynamic Blocks
- Custom sections with {% schema %} blocks
- Drag-and-drop reordering in admin
- JSON templates for section-everywhere
- Specs showcase section on PDP
- Product Features with dynamic content
Phase 4
Product Metafields
Conditional Rendering on PDP
- care_instructions — multi-line text
- difficulty_rating — numeric scale
- is_new_arrival — boolean badge
- Show only if filled — zero output when empty
- Collapsible accordion UI pattern
Phase 4
Admin Setup
Settings → Custom data → Products — merchant-friendly setup
Phase 5
App Data: Collection Badge
Product Card with App Badge
- Simulates external app data via app_data.* namespace
- Double guard: badge_enabled + badge_text
- Defensive coding — zero errors if app removed
- Positioned independently from Sale/Sold Out badges
Phase 5
App Data: PDP Certifications
JSON Metafield → Structured UI
- extra_info JSON: warranty, origin, certifications[]
- Each key checked independently before render
- Certifications as pill tags via
for cert in
- App-uninstall safe — graceful degradation
Phase 6
Custom VAT Pricing
Inc/Exc Tax Toggle on PDP
- Per-product custom.show_tax boolean metafield
- Price incl. VAT & excl. VAT display
- Sale price support with compare_at_price
- Consistent across PDP, Collection, Cart
Phase 6
Price Logic
{%- assign show_tax = product.metafields.custom.show_tax.value -%}
{%- assign tax_rate = settings.tax_rate | divided_by: 100.0 -%}
{%- if show_tax -%}
{%- assign price_inc = price | times: 1 -%}
{%- assign price_exc = price | divided_by: tax_factor -%}
<span class="price--inc">{{ price_inc | money }} incl. VAT</span>
<span class="price--exc">{{ price_exc | money }} excl. VAT</span>
{%- endif -%}
{%- if compare_at_price > price -%}
<s class="price--was">{{ compare_at_price | money }}</s>
{%- endif -%}
Phase 7
JavaScript Interactions
Web Component
Product Tabs
Custom <product-tabs> element with keyboard navigation, ARIA attributes, and section rendering API
Web Component
Variant Picker
Dynamic variant selection updating price, availability, and images without page reload
Web Component
Sticky ATC
Intersection Observer-based sticky Add to Cart bar — appears on scroll past main button
Zero jQuery · Custom Elements API · Event Delegation · data-* Attributes
Phase 7
Product Tabs
Description, Specifications, Shipping — accessible tab navigation with ARIA roles
Phase 7
Variant Picker & Sticky ATC
Phase 7
Web Components Pattern
class ProductTabs extends HTMLElement {
constructor() {
super();
this.buttons = this.querySelectorAll('[role="tab"]');
this.panels = this.querySelectorAll('[role="tabpanel"]');
}
connectedCallback() {
this.addEventListener('click', this.handleClick);
this.addEventListener('keydown', this.handleKeydown);
}
switchTab(target) {
this.buttons.forEach(btn => btn.setAttribute('aria-selected', 'false'));
target.setAttribute('aria-selected', 'true');
}
}
customElements.define('product-tabs', ProductTabs);
Phase 8
Performance Optimizations
CSS
Deferred Styles
Non-critical CSS loaded asynchronously via media="print" swap pattern, eliminating render-blocking
Layout
Content Visibility
content-visibility: auto on below-fold sections, reducing initial rendering work by ~40%
Images
LCP Optimization
Hero image preload with fetchpriority="high", responsive srcset, WebP via Shopify CDN
Lazy Load
Smart Loading
loading="lazy" below fold, eager for LCP — no lazy on hero images
CLS
Layout Stability
Explicit width/height on all images, aspect-ratio CSS, no above-fold shifts
DOM
Minimal Markup
Semantic HTML, reduced wrapper nesting, efficient Liquid loops
Phase 8
Render-Blocking Reduction
<link rel="stylesheet" href="component-card.css"
media="print" onload="this.media='all'">
<link rel="preload" as="image" fetchpriority="high"
href="{{ hero_image | image_url: width: 1200 }}"
imagesrcset="...">
.section--below-fold {
content-visibility: auto;
contain-intrinsic-size: auto 500px;
}
Every optimization follows Shopify best practices — no external dependencies needed
Summary
Skills Demonstrated
1
Theme Architecture — sections, snippets, JSON templates
2
Design Implementation — Figma to Liquid, mobile-first
3
Shopify 2.0 — dynamic blocks, admin customization
4
Metafields — text, boolean, JSON, conditional rendering
5
App Integration — resilient app-data handling
6
Custom Pricing — VAT logic across all surfaces
7
Vanilla JS — Web Components, ARIA, event delegation
8
Performance — deferred CSS, LCP, content-visibility
WinSport Portfolio Theme
Shopify 2.0 · Liquid · Vanilla JS · Mobile-First · Dawn v15.4.1
Press Space or click to navigate · Auto-advances every 6 seconds