I worked in a full-time position as a Front-end Shopify Developer for Bristol-based agency Thought & Mortar from June 2024 until its closure in March 2026.
My duties included:
- Developing custom solutions to meet the needs of a wide variety of clients through bespoke theme features;
- Pushing the boundaries of Shopify with custom Liquid and JavaScript to overcome unique challenges;
- Translating project briefs into comprehensive technical scope documents;
- Bringing Figma designs to life with responsive CSS;
- Managing multiple CI/CD Git-to-Shopify pipelines at once;
- Writing approachable client documentation which also breaks down technical details to help clients feel empowered;
- Using JavaScript within Google Scripts to manipulate spreadsheets ready for import;
- Helping businesses set up their first E-commerce store, migrate from other systems like Adobe Commerse and eBay, or update from Shopify themes from 1.0 to 2.0;
- Utilising AI as a tool to enhance my work and increase productivity, while keeping the core of my work distinctly human.
PerfectFit
PerfectFit were on Adobe Commerce and overly reliant on customers using their 'Harness Calculator' to find the right size harness for their dog. The customer would input their dog's breed/measurements and the calculator would link them to the appropriate harness, but there wasn't any recognition of the dog's breed or specific measurements after that, so it was easy to get confused as to which harness was recommended.

Dog breeds products
As part of their move from Adobe Commerce to Shopify and the 'Habitat' theme, I theorised and implemented 'dog breed' products. We added them as products to their Shopify store, but they are never intended to actually be purchased. Instead, they are just a vector for purchasing the appropriate harnesses for that size of dog breed without disrupting the product structure PerfectFit were used to.
'Fit' metaobjects within a metafield store which size dog should have which sized harness, and when the 'Harness selector' inputs are changed by the customer, the Section Rendering API updates the UI around the appropriate harness.
An important SEO benefit was that it gave PerfectFit the perfect pages to rank for searches like 'harness for border collie' or 'husky harness'.
Challenges that I overcame implementing this system included:
- Setting up a query string system so that customers can return to the breed/size/puller/variant that they were viewing previously;
- Ensuring the customer stays on the same color option when moving between harnesses;
- Updating the variant image in the gallery when moving between harnesses and variants;
- Customizing the Google Structured Data so dog breed harnesses appear as a `productGroup`, with the harnesses as products within it.
Harness finder
We also kept the Harness Finder available as an additional avenue for customers to find the correct harness.
I coded the new version from scratch to work with the new dog breed pages, and added new Google Analytics 4 events that fire when a customer succeeds or fails to get a result.
These events carry all the form data as parameters, allowing PerfectFit to see which breeds/sizes are most commonly searched for. This will help inform them which new dog breeds they should add to the store and which new fits they should add for existing breeds.
Collection template promo cards
T&M's previous system for promo cards had clients creating a metaobject entry with the cards' text/layout/colours etc as inputs, assigning the metaobject to a collection's metafield, then assigning the promo card an order in the grid using the section settings.
As the card's settings were assigned outside of the theme, if you changed a setting like its layout, you would need to then open the theme to see what your change looked like.
I created a new system for PerfectFit which ditched metaobjects or metafields. Promo cards are added as theme blocks to the product grid of a collection template. The promo card block has layout/order/shape/colour settings. Any number of buttons, images, text, or graphics can be nested as blocks within the promo block in any order.
The promo cards don't interfere with the pagination as I added an additional loop to the section which fills the gap on pages where the promo cards aren't rendered.

Bristol Blue Glass
Thought & Mortar redesigned the Bristol Blue Glass store, when we moved them onto the 'Symmetry' Shopify theme.
They were paying a monthly fee for a gift wrap app which didn't offer all the functionality they needed, so I created a new system using Shopify Liquid and JavaScript.
Customers can now purchase the following services on each product page:
- Engraving — With settings for the text, font family, and text colour;
- Gift wrapping;
- A greetings card message.
To the extra cost to the order, hidden items are added to the cart for each selected service. Although these items can't be hidden in the checkout, it is clear which service is for which product thanks to line item properties on the main product.
If the main product is removed from the cart, any services associated with it are also removed automatically.
As the system relies on JavaScript to work, the services are hidden to any users with JavaScript disabled.

Wow Shelving
As part of their migration from eBay to the 'Symmetry' Shopify theme, I implemented custom features including:
- A VAT toggle in the header which uses local storage to keep track of the customer's preference;
- A tile calculator which updates the customer's item quantity based on the area they intend to cover;
- A discount if the customer is picking up the order from the store — Uses JavaScript to add or remove a discount code depending on the customer's selection.
Lillicoco
I was one of several developers to work on the Lillicoco store.
Using Liquid and JavaScript without any apps, I created a resizing block for their ring product pages.
As with the Bristol Blue Glass services, the cost of a ring resize is added to the order by adding a hidden item to the cart. The boundaries and cost of the resizing varies from ring to ring, so I created different variants for different costs within the 'Ring Resize' product.
Another feature I worked on was ensuring that orders, which would have missed the free delivery threshold because of layaway, actually retain their free delivery by adding a hidden item to the cart with JS and using an app's custom shipping rules.
Whenever an item is added or removed from cart, the code calculates what the total cost of the order will be after that change, then checks if the total is under the threshold with layaway but over the threshold without layaway.
