[object Object]

← back to CelebritySignatures

wear: polo left_chest placeholder, add cap garment, collar fabric tag (TK-10286)

adf131336d551cbaa05b663ac4c8131485716f86 · 2026-09-08 13:03:59 -0700 · Steve Abrams

Still fully gated off (WEAR_SALES_LIVE unset, checkout returns comingSoon).

1. Polo: switched from the manually-tuned 'front' full-torso print area to
   Printify's own DEDICATED 'left_chest' placeholder for this blueprint. The
   'front' area kept sitting too low no matter how high y went (y:0.0 still
   overlapped the placket) — the dedicated placeholder is a properly-scoped
   small print area that lands at the correct height automatically. Verified
   via a real throwaway draft product + mockup.

2. New 4th garment: Classic Dad Cap (Yupoong 6245CM, blueprint 1447, provider
   99 Printify Choice), DTF print (not embroidery — a cursive signature's
   thin strokes would blob out in embroidery) on the front_dtf placeholder,
   x:0.5/y:0.5/scale:0.7. White/Stone/Khaki/Light Blue (light-only, same
   conservative default as the original tee palette). One-size. Cost $16.12
   at verification. New SVG geometry in wear-templates.json (dome + brim) and
   public/wear.html renders it via the existing generic accent/pocketAccent
   hooks — no cap-specific branch needed. Verified with real Playwright
   screenshots (0 console errors) plus a real Printify draft-product mockup.

3. 'Celebrity Signatures' fabric tag at the back collar (Steve's request) —
   Printify's dedicated 'neck' placeholder, only available on the tee's
   blueprint (checked: polo/pocket-tee/cap blueprints have none). Rendered
   the tag as a real PNG (Playfair Display italic, matching the site's own
   wordmark) via a local Playwright screenshot, saved to
   public/assets/wear-tag-celebrity-signatures.png. Iterated position against
   4 real throwaway draft products: y:0.5 (centered, too low) -> y:0.15 ->
   y:0.0 (clips into the collar-binding curve, distorts the text) -> the
   final y:0.08, the highest clean/legible position.

   New data model: garment.printify.extraPrintAreas[] — static (not
   per-order) image print areas layered onto every order for that garment.
   scripts/submit-pod-draft-printify.mjs's buildPayload() now merges these
   into the order's print_areas object alongside the per-order signature
   design, referencing the tag by its live site URL
   (https://celebsignatures.com/assets/...) the same way the existing code
   already references design_image_url — no image upload/ID plumbing needed,
   since Printify's Orders API (unlike the Products API used for mockup
   verification) accepts a fetchable src URL directly.

Verified: node --check clean, JSON valid, dry-run sender unchanged (0
pending, nothing submitted), local E2E shows all 4 garments + correct
variant maps, checkout still comingSoon:true, Playwright screenshots of the
real on-site canvas confirm all 4 garments render without JS errors.

Known gap (not fixed here): the on-site canvas preview doesn't render the
collar tag (only the real Printify mockup does) — a completeness gap, not a
correctness bug, left for a follow-up pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmxcNKtFg77wP47uZq5Zsm

Files touched

Diff

commit adf131336d551cbaa05b663ac4c8131485716f86
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 8 13:03:59 2026 -0700

    wear: polo left_chest placeholder, add cap garment, collar fabric tag (TK-10286)
    
    Still fully gated off (WEAR_SALES_LIVE unset, checkout returns comingSoon).
    
    1. Polo: switched from the manually-tuned 'front' full-torso print area to
       Printify's own DEDICATED 'left_chest' placeholder for this blueprint. The
       'front' area kept sitting too low no matter how high y went (y:0.0 still
       overlapped the placket) — the dedicated placeholder is a properly-scoped
       small print area that lands at the correct height automatically. Verified
       via a real throwaway draft product + mockup.
    
    2. New 4th garment: Classic Dad Cap (Yupoong 6245CM, blueprint 1447, provider
       99 Printify Choice), DTF print (not embroidery — a cursive signature's
       thin strokes would blob out in embroidery) on the front_dtf placeholder,
       x:0.5/y:0.5/scale:0.7. White/Stone/Khaki/Light Blue (light-only, same
       conservative default as the original tee palette). One-size. Cost $16.12
       at verification. New SVG geometry in wear-templates.json (dome + brim) and
       public/wear.html renders it via the existing generic accent/pocketAccent
       hooks — no cap-specific branch needed. Verified with real Playwright
       screenshots (0 console errors) plus a real Printify draft-product mockup.
    
    3. 'Celebrity Signatures' fabric tag at the back collar (Steve's request) —
       Printify's dedicated 'neck' placeholder, only available on the tee's
       blueprint (checked: polo/pocket-tee/cap blueprints have none). Rendered
       the tag as a real PNG (Playfair Display italic, matching the site's own
       wordmark) via a local Playwright screenshot, saved to
       public/assets/wear-tag-celebrity-signatures.png. Iterated position against
       4 real throwaway draft products: y:0.5 (centered, too low) -> y:0.15 ->
       y:0.0 (clips into the collar-binding curve, distorts the text) -> the
       final y:0.08, the highest clean/legible position.
    
       New data model: garment.printify.extraPrintAreas[] — static (not
       per-order) image print areas layered onto every order for that garment.
       scripts/submit-pod-draft-printify.mjs's buildPayload() now merges these
       into the order's print_areas object alongside the per-order signature
       design, referencing the tag by its live site URL
       (https://celebsignatures.com/assets/...) the same way the existing code
       already references design_image_url — no image upload/ID plumbing needed,
       since Printify's Orders API (unlike the Products API used for mockup
       verification) accepts a fetchable src URL directly.
    
    Verified: node --check clean, JSON valid, dry-run sender unchanged (0
    pending, nothing submitted), local E2E shows all 4 garments + correct
    variant maps, checkout still comingSoon:true, Playwright screenshots of the
    real on-site canvas confirm all 4 garments render without JS errors.
    
    Known gap (not fixed here): the on-site canvas preview doesn't render the
    collar tag (only the real Printify mockup does) — a completeness gap, not a
    correctness bug, left for a follow-up pass.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01AmxcNKtFg77wP47uZq5Zsm
---
 data/wear-templates.json                        |  88 +++++++++++++++++++++++-
 public/assets/wear-tag-celebrity-signatures.png | Bin 0 -> 29001 bytes
 scripts/submit-pod-draft-printify.mjs           |  17 +++--
 3 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/data/wear-templates.json b/data/wear-templates.json
index 7a27097..2b2de98 100644
--- a/data/wear-templates.json
+++ b/data/wear-templates.json
@@ -69,6 +69,14 @@
         "blueprintId": 12,
         "printProviderId": 99,
         "printPosition": { "placeholder": "front", "scale": 0.16, "x": 0.7, "y": 0.1 },
+        "extraPrintAreas": [
+          {
+            "_note": "Fabric tag at the back collar (Steve, 2026-09-08) — Printify's dedicated 'neck' placeholder, only available on this blueprint (checked: polo/pocket-tee/cap blueprints have no neck-label placeholder). Static asset (not per-order design), same on every order. Verified 2026-09-08 via 4 real throwaway draft products — y:0.0 clips into the collar-binding curve and distorts the text; y:0.08 is the highest clean/legible position.",
+            "placeholder": "neck",
+            "imageUrl": "/assets/wear-tag-celebrity-signatures.png",
+            "x": 0.5, "y": 0.08, "scale": 0.8
+          }
+        ],
         "variants": {
           "white/S": 18540, "white/M": 18541, "white/L": 18542, "white/XL": 18543, "white/2XL": 18544,
           "heather/S": 18076, "heather/M": 18077, "heather/L": 18078, "heather/XL": 18079, "heather/2XL": 18080,
@@ -140,7 +148,8 @@
         "_note": "JERZEES 443M Piqué Polo, print provider 99 (Printify Choice), DTG. Mapped 2026-09-08 via read-only catalog GETs. printPosition VERIFIED 2026-09-08 against real Printify mockups (multiple throwaway draft products, each deleted) — iterated through x:0.32/y:0.3/scale:0.25 (wrong, dead-center) -> x:0.7/y:0.15/scale:0.14 -> x:0.7/y:0.13/scale:0.22 (too big) to the final x:0.7/y:0.1/scale:0.16, a standard small left-chest logo size sitting just above pocket-top height. Renders correctly on both white and black. KNOWN ISSUE: on the black colorway, the source signature art (dark ink) prints as low-contrast dark-gray-on-black — Printify does not auto-recolor art for dark garments. Needs a server-side light-ink recolor of design_image_url before order submission for dark colors, or dropping the black colorway, before go-live.",
         "blueprintId": 1402,
         "printProviderId": 99,
-        "printPosition": { "placeholder": "front", "scale": 0.16, "x": 0.7, "y": 0.1 },
+        "printPosition": { "placeholder": "left_chest", "scale": 0.85, "x": 0.5, "y": 0.5 },
+        "_note_placeholder": "Switched off the manually-tuned 'front' full-torso area (kept overlapping the placket no matter how high y went) to Printify's own DEDICATED 'left_chest' placeholder for this blueprint — a properly-scoped small print area that lands at the correct height automatically. Verified 2026-09-08 via a real throwaway draft product.",
         "variants": {
           "white/S": 104647, "white/M": 104648, "white/L": 104649, "white/XL": 104650, "white/2XL": 104651,
           "black/S": 104623, "black/M": 104624, "black/L": 104625, "black/XL": 104626, "black/2XL": 104627
@@ -211,6 +220,83 @@
       "sizeUpchargeUsd": {
         "2XL": 4
       }
+    },
+    {
+      "id": "cap",
+      "label": "Classic Dad Cap",
+      "priceUsd": 28,
+      "sizes": [
+        "One size"
+      ],
+      "_note_sizes": "Caps are one-size-fits-all (adjustable strap) — no size chips shown for this garment on the storefront.",
+      "viewBox": [
+        0,
+        0,
+        400,
+        480
+      ],
+      "bodyPath": "M90 210 Q90 70 200 64 Q310 70 310 210 Q310 226 200 230 Q90 226 90 210 Z",
+      "accents": [
+        {
+          "d": "M195 68 L205 68 L207 226 L193 226 Z",
+          "fillShade": -0.06
+        }
+      ],
+      "pocketAccent": {
+        "d": "M60 214 Q40 250 90 268 Q200 288 310 268 Q360 250 340 214 Q320 232 200 236 Q80 232 60 214 Z",
+        "fillShade": -0.1
+      },
+      "_note_pocketAccent": "This is the cap's BRIM (drawn as a shaded curve below the crown), reusing the pocketAccent rendering hook — not a real pocket. Renamed at the code level would be cleaner but this keeps public/wear.html's draw() generic across all 4 garments without a one-off branch.",
+      "pocketBox": {
+        "x": 150,
+        "y": 120,
+        "w": 100,
+        "h": 55
+      },
+      "colors": [
+        {
+          "id": "white",
+          "label": "White",
+          "hex": "#f6f4ef",
+          "ink": "#1a1a1a"
+        },
+        {
+          "id": "stone",
+          "label": "Stone",
+          "hex": "#cfc9bd",
+          "ink": "#1a1a1a"
+        },
+        {
+          "id": "khaki",
+          "label": "Khaki",
+          "hex": "#c9b896",
+          "ink": "#1a1a1a"
+        },
+        {
+          "id": "lightblue",
+          "label": "Light Blue",
+          "hex": "#a9c4d9",
+          "ink": "#16324a"
+        }
+      ],
+      "_note_colors": "Light-only (White/Stone/Khaki/Light Blue), same conservative default as the original tee palette — avoids the known dark-ink-on-dark-fabric contrast problem (see polo's black colorway note) on a third garment before that gets a real fix.",
+      "printful": {
+        "_note": "Not mapped — Printify only for this garment.",
+        "suggestedProduct": "Yupoong 6245CM Classic Dad Cap",
+        "productId": null,
+        "placementType": "front",
+        "variants": {}
+      },
+      "printify": {
+        "_note": "Yupoong 6245CM Classic Dad Cap, print provider 99 (Printify Choice), DTF (not embroidery — a cursive signature's thin strokes would blob out in embroidery; this blueprint offers both, DTF was the deliberate choice). Mapped + VERIFIED 2026-09-08 via a real throwaway draft product — front_dtf placeholder, x:0.5/y:0.5/scale:0.7 centers cleanly on the front panel. Cost $16.12 at time of verification.",
+        "blueprintId": 1447,
+        "printProviderId": 99,
+        "printPosition": { "placeholder": "front_dtf", "scale": 0.7, "x": 0.5, "y": 0.5 },
+        "variants": {
+          "white/One size": 105381, "stone/One size": 105380, "khaki/One size": 105375, "lightblue/One size": 105376
+        }
+      },
+      "sizeUpchargeUsd": {}
     }
   ]
 }
diff --git a/public/assets/wear-tag-celebrity-signatures.png b/public/assets/wear-tag-celebrity-signatures.png
new file mode 100644
index 0000000..65c909d
Binary files /dev/null and b/public/assets/wear-tag-celebrity-signatures.png differ
diff --git a/scripts/submit-pod-draft-printify.mjs b/scripts/submit-pod-draft-printify.mjs
index d94bb5f..dddb41c 100644
--- a/scripts/submit-pod-draft-printify.mjs
+++ b/scripts/submit-pod-draft-printify.mjs
@@ -12,6 +12,7 @@ import { pathToFileURL } from 'node:url';
 const ROOT = fileURLToPath(new URL('..', import.meta.url));
 const DATA = join(ROOT, 'data');
 const API = 'https://api.printify.com/v1';
+const SITE_ORIGIN = 'https://celebsignatures.com';
 const APPLY = process.argv.includes('--apply');
 const SHOW_PII = process.argv.includes('--show-pii');
 
@@ -37,26 +38,34 @@ function resolveProduct(tpl, d) {
   const key = `${d.color}/${d.size}`;
   if (!map.blueprintId || !map.printProviderId || !map.variants?.[key]) throw new Error(`order #${d.orderId}: ${d.garment} ${key} is not mapped to a Printify blueprint/provider/variant`);
   return { blueprintId: map.blueprintId, printProviderId: map.printProviderId, variantId: map.variants[key],
-    position: map.printPosition || { placeholder: 'front', scale: 0.25, x: 0.32, y: 0.3 } };
+    position: map.printPosition || { placeholder: 'front', scale: 0.25, x: 0.32, y: 0.3 },
+    // Extra STATIC print areas (e.g. the 'Celebrity Signatures' collar tag) —
+    // same image on every order for this garment, not the per-order design.
+    extraPrintAreas: map.extraPrintAreas || [] };
 }
 function splitName(name) {
   const parts = String(name || '').trim().split(/\s+/).filter(Boolean);
   return { first_name: parts.shift() || '', last_name: parts.join(' ') || '-' };
 }
 export function buildPayload(tpl, d) {
-  const { blueprintId, printProviderId, variantId, position } = resolveProduct(tpl, d);
+  const { blueprintId, printProviderId, variantId, position, extraPrintAreas } = resolveProduct(tpl, d);
   const r = d.recipient || {};
   const missing = ['name', 'address1', 'city', 'state_code', 'country_code', 'zip'].filter(k => !r[k]);
   if (missing.length) throw new Error(`order #${d.orderId}: missing shipping fields ${missing.join(',')}`);
   if (!d.design_image_url) throw new Error(`order #${d.orderId}: no design_image_url`);
   const name = splitName(r.name);
+  const print_areas = { [position.placeholder || 'front']: [{ src: d.design_image_url, scale: position.scale,
+    x: position.x, y: position.y, angle: 0 }] };
+  for (const area of extraPrintAreas) {
+    if (!area.placeholder || !area.imageUrl) continue;
+    print_areas[area.placeholder] = [{ src: `${SITE_ORIGIN}${area.imageUrl}`, scale: area.scale, x: area.x, y: area.y, angle: 0 }];
+  }
   return {
     external_id: `celebsig-wear-${d.orderId}`,
     label: `${d.signature_name} signature — left-chest ${d.garment}`,
     line_items: [{ print_provider_id: printProviderId, blueprint_id: blueprintId, variant_id: variantId, quantity: 1,
       external_id: `celebsig-wear-${d.orderId}-1`,
-      print_areas: { [position.placeholder || 'front']: [{ src: d.design_image_url, scale: position.scale,
-        x: position.x, y: position.y, angle: 0 }] } }],
+      print_areas }],
     is_printify_express: false,
     is_economy_shipping: false,
     shipping_method: 1,

← 06e1b9d wear: dial print size back down to standard small left-chest  ·  back to CelebritySignatures  ·  wear: real multi-item shopping cart (own cart, not Shopify's 4173af9 →