Anatomy of a tartan
Tartan patterns are more structured than they appear. The weave is built from repeating horizontal and vertical bands of colored threads, with the same color sequence and spacing in both directions. Where bands cross, new visual colors emerge. A diagonal twill texture also runs through the fabric, which creates an optical effect.
The 5,000+ tartans on tartanify.com are stored as structured thread-count strings. Each tartan is described by a palette of hex colors and a thread-count sequence. That data is enough to compute the exact width of every stripe and to construct the full pattern programmatically.
From thread counts to SVG tiles
The foundation of the SVG tile is two layered squares. One square draws horizontal stripes, the other draws the same square rotated 90 degrees so its stripes run vertically. Stacked directly, the vertical layer would fully cover the horizontal one. To reveal the weave pattern, an SVG mask is applied to the top layer. The mask's coordinate system is objectBoundingBox, and with width and height set to 1, the mask covers the entire square.
The actual alternating stitch pattern is generated inside the mask itself. This is done with a pattern tile that acts like the repeating weave structure. In the pattern definition, patternUnits is switched from the default objectBoundingBox to userSpaceOnUse, so the dimensions are expressed in pixels, giving pixel-exact control over the weave repeat.
Building a dynamic tile component
In React, the tile is a self-contained component. A defs group holds the pattern, stripes, and mask definitions. The tartan itself is modeled as an array of stripe objects, each with a fill hex value and a size number.
The raw source data uses a two-string format: Palette and Threadcount. Parsing those strings into the stripe array is handled outside the render logic. Once transformed, the array is passed to the tile component, which renders the SVG structure. Because the whole pattern is data-driven, converting new entries from the dataset to SVG becomes a single pipeline step rather than a manual drawing process.
Serving and downloading the pattern
Background image usage
Inline SVG can't be assigned directly to a CSS background-image property. The workaround is to make the SVG a data URI and reference it as an encoded background string.
There are two complications in that approach. First, the React tile must be converted from its virtual DOM form into a plain markup string. ReactDOMServer.renderToStaticMarkup handles that. Critically, it works in both the browser and the Node server environment, which matters because the pages are server-rendered through Gatsby.
Second, the markup string contains hex color codes that begin with #. In a URL, that symbol starts a fragment identifier, which would break the data URI. Every # in the SVG string must therefore be escaped with encodeURIComponent.
SVG download link
Making the tile downloadable is handled by a dedicated link component. It receives the encoded SVG string and a filename. The anchor uses the download attribute, which prompts the browser to save the file with the suggested name rather than navigating to it.
High-resolution PNG output
PNG download links work by drawing the SVG onto a canvas element. Because the component may execute on the server, the drawing logic runs inside the useEffect hook, ensuring the canvas work only happens in the browser. The canvas dimensions are set to twice the tile size, and the drawing context is scaled by a factor of two. This produces a PNG at double resolution for a sharp image on high-density displays.
Once the tile is drawn, the canvas toDataUrl() method returns it as a data URI. That URI is then set as the anchor's href attribute.
Static site generation with Gatsby
Tartanify.com is a static site built with Gatsby. All tartan entries lived in a large CSV file, so the site needed plugins to turn that raw data into queryable nodes. The gatsby-source-filesystem plugin reads the contents of the /src/data directory, and gatsby-transformer-csv parses each row into JSON objects.
Because the dataset has over 5,000 rows, unique slugs and names are essential. The onCreateNode Gatsby Node API adds these fields as new nodes are created. Many tartan variants share the same name, which makes the unique slug necessary for stable URLs.
Individual tartan pages are generated in gatsby-node.js via the createPages API. Each tartan page needs its previous and next siblings so users can navigate through the full collection. Those adjacent entries are found by querying the neighboring edges and storing the result in the page context.
The index list is organized alphabetically. Tartans are grouped by their starting letter, and each letter's group is paginated with a maximum of 60 entries per page. The page count per letter varies considerably; the letter "a" needs four pages, while "m" tops out at fifteen because so many traditional tartan names start with "Mac."
The pagination logic keeps cross-letter navigation consistent. The letter b page must know that the previous set ends at tartans/a/4, not at tartans/a. A running counter tracks the last index for the previous letter so the next page's "previous" link always points to the proper page.
The index pages themselves use a dedicated navigation component for next/previous traversal. The tartan detail template re-uses the same tile, download, and navigation components from the first part of the build, so there's no duplication between the generation and display layers.



