Online store2026-08-21

Shadow Style Shop — T-shirt shop with custom configurator

A print t-shirt store that needed more than a catalog: a customer needed to be able to submit their design, see it on a t-shirt before ordering, and have the price drop when they buy more pieces. All this in your own code, without a platform and without a monthly subscription.

Shadow Style Shop website homepage
Homepage — twice right at the top: a ready-made collection or your own design
The client
Shadow Style Shop
Activity
T-shirts with print, DTF printing
Area
All of Serbia
The guy
Web store with configurator
Technologies
PHP 8.3 · SQLite · Fabric.js · Service Worker
Site
shadowstyleshop.rs
Status
Live site, under active maintenance
Technology
PHP 8.3 · SQLite · Fabric.js · PWA
01

Problem and context

What Shadow Style Shop does

Shadow Style Shop sells t-shirts with a print and produces T-shirts from designs submitted by customers. Printing is done using the DTF technique on cotton t-shirts, and orders are delivered throughout Serbia — without a physical store, everything via the website and Instagram.

The catalog is divided into seven collections: Anime, Cars & Motorcycle, Random, Brands, Rock & Metal, Gucci and Stranger Things. Anime is the largest with several dozen prints, and there are over a hundred in total — a number that continues to grow, as new designs are added through the admin panel.

The custom-design flow is the part no ready-made template handled well: a customer has an image they want printed on a shirt, and before ordering he wants to see how it looks. That part was the main reason to start the site from scratch.

Featured products in the Shadow Style Shop catalog
Featured prints on homepage
02

What needed to be solved

The request was not "build a shop", but four specific things that must work together:

  • Easily expandable catalog — new prints and entire collections are added by the owner, without me and without touching the code.
  • T-shirt according to your own design — the customer uploads his image, moves and scales it on the t-shirt, chooses the color of the t-shirt and the position of the print, both for the front and for the back.
  • Non-gameable quantity discount — from three pieces up, the price per shirt drops, but the price must be calculated on the server, not in the browser.
  • One place for everything that arrives — orders, contact form messages and custom design requests can't be in three different places.

In addition, there were conditions that are not optional for a Serbian online store: complaints, right of withdrawal within 14 days, terms of use and privacy policy — all written and published, not copied from someone else's site.

T-shirt collections by category
Collections with number of designs in each
03

Catalog and product page

The homepage leads in two directions: View shop for those who choose from the collection and Create a T-shirt for those who have their own idea. Below that are highlighted products, then collections with the number of designs in each, then an explanation of the custom process in four steps.

The catalog at /shop is filtered by collection via the URL parameter, so that each collection has its own address that can be shared and indexed. Each product has its own page with a slug address, a full image of the print, a choice of size and quantity, and a clearly written condition for a quantity discount.

Product images undergo optimization during upload: JPEG, WebP and AVIF versions are created from one uploaded image, and the server chooses the format according to what the browser supports. For a catalog with over a hundred products, it's the difference between a site that opens immediately and a site that waits.

Shadow Style Shop on the phone
Same site on phone
04

Configurator — Design-your-own T-shirt

The heart of the site is the page /custom. The customer uploads their image, moves and resizes it directly through the t-shirt mockup, chooses the color of the t-shirt and the position of the print — separately for the front and back.

Under the hood, the canvas is made in Fabric.js with a self-hosted library (no CDN), and the mockup is automatically selected according to the selected item. When the customer is finished, both sides are exported as images and together with the size selection go to the server in one message.

On the server, the request receives its reference number, is recorded in the database and immediately arrives in the owner’s inbox — with both sides of the design as an attachment, so that he can respond with an offer without opening the panel. The classic form with file upload remained as a reserve under the configurator, for the customer who finds it easier.

An invisible detail that makes a difference: upload doesn't trust the file extension but checks the actual content type, and images are stored out of reach of execution. The form that receives other people's files is the most sensitive point of any store and it is treated as such.

05

Cart, quantity discount and checkout

The volume discount is a simple rule — from three shirts up the price per piece drops — but it's designed to work on two levels. In the basket, the customer immediately sees how much he is saving and how many pieces he still needs before the discount. When sending the order, the server recalculates the entire price from its database and ignores everything that came from the client, so the order note states which discount was applied and why.

It's not a theoretical precaution: a cart that trusts prices from the browser is a store where the price changes from the console. This path does not exist here.

The order goes through the cart, checkout and confirmation page, is entered into the database with items and status, and appears in the admin panel. Payment is made by cash on delivery — without cards on the site, which for this type of store means less costs, fewer obligations and no other people's secrets on the server.

06

Admin panel — everything that arrives, in one place

The owner does not get a "CMS" but a panel that does exactly what he needs: products and collections, orders with items and statuses, requests from the configurator with an overview of both sides of the design, messages from the contact form, a newsletter and settings such as the shipping price.

The messages are the most interesting part. The panel is connected to the mailbox of the store in both directions — the reply written in the panel goes as a real email and ends up in the sent mail, and the incoming mail is imported back into the correspondence. The owner can reply to the customer from the panel or from the email application, the correspondence is the same.

The same logic applies to the configurator: the request from the panel and the email that arrives to the owner are the same record, just two views of it.

07

Technical performance

The site is written in PHP 8.3, without WordPress and without any plugins that need to be maintained and updated. The data is in the SQLite database — products, categories, orders and items, messages, custom requests, newsletters, settings and SEO records per page.

Why SQLite and not MySQL: a store of this size has one writer and many readers. SQLite gives the same result with one less system that can crash, and the entire content of the site fits into one file that is copied in seconds. If the scale grows one day, moving to MySQL is a change to the data access layer, not a new site.

The addresses are clean — /shop, /custom, /product?slug=… — through their own router in index.php. Versions with a .php extension are permanently redirected to clean, but only when requested by a genuine visitor; internal server redirection is excluded, because otherwise it would create an infinite loop that kills the entire site except the homepage. That detail had crashed the site once before and has since been resolved at the server configuration level.

08

Speed, offline behavior and cookies

Images are the hardest part of the t-shirt store, so they got the most attention: WebP and AVIF versions for everyone, server format selection according to the browser header, and annual cache with an unchanging imprint in the file name. Fonts and styles are loaded so the first screen doesn't wait for things it doesn't need.

The site has a Service Worker and a PWA manifest — it can be added to the phone's home screen and survive a bad connection. The registration of workers is deliberately delayed after loading, so as not to steal bandwidth from the first view.

Analytics is set with Consent Mode v2: until the visitor consents, only necessary things are measured. The banner also offers an opt-out, and the choice is remembered and respected on the next visit. It's not cosmetic — it's the difference between a site that respects a visitor's decision and a site that ignores it.

09

SEO and visibility

Each collection, each product, and each information page has its own title, description, and canonical URL — not generated by a template, but written in the database per page. The site carries Organization and Product schema data, has a sitemap with over a thousand addresses and is verified in Google Search Console.

Minor things that are often skipped are also fixed: redirects from old and alternate URLs (/terms, /privacy-policy), robots.txt not blocking what needs to be indexed, and server rules denying access to backup files — because store source code served as plain text is an oversight that won't be noticed until it's too late.

10

Outcome

What the client received

A store the business owns. No monthly platform subscriptions, no commissions per sale, no plugins that break on the next update. The code and database are on the server, they can be copied and moved.

Practically, the day looks like this: a new print is added through the panel and is instantly in the catalog; the request from the configurator arrives by e-mail with a picture of the design and a reference number; the order is visible in the panel with all items; the answer to the customer is written where it is more convenient, and the correspondence remains the same.

The site is still under active maintenance — the Rock & Metal was added in August 2026, along with the configurator extension. What changes, changes on the live site, backed up before each change.

FAQ

Frequently asked questions

Why custom PHP and not WooCommerce or Shopify?

Because of the configurator and because of the cost. A configurator that takes someone else's image, works with the front and back, and sends a quote doesn't exist as an out-of-the-box plugin — in WooCommerce it would be a plugin via a plugin. And a store with no monthly subscription and no sales commission for this volume means that the investment is returned and remains.

Can the owner add products himself?

Yes. Products, collections, prices, images and settings go through the admin panel. The image is uploaded once, and the system itself creates optimized versions for all browsers.

How does the volume discount work?

From three pieces up, the price per T-shirt drops. In the basket, you can immediately see the savings and how many pieces are missing from the discount, and the final price is calculated by the server from its database — the price sent from the browser is ignored.

Does the site work on a phone?

It works, and it's been a requirement since the beginning — most of the traffic comes on a phones, mostly through Instagram. The configurator is especially touch-friendly, because moving a design with a finger is a different problem than moving it with a mouse.

How long does it take to build a store like this?

It depends on the scope of the catalog and whether it has a configurator. Realistically it ranges in weeks, and the preparation of product images and text, not the code, usually takes the most time.

What happens if the store expands?

The catalog can grow without any changes to the code. If the scale one day outgrows SQLite, it switches to MySQL — that's changing the data access layer, not building a new site.

Next step

Have a project that needs to work better?

Send me your goal and current situation. I will reply with a concrete scope proposal.

Start a project