← back to Textiletuesday
initial scaffold: textiletuesday.com editorial indigo site
ff0dde099414717ac004ab29c25b00a6a5bbffae · 2026-05-07 16:44:40 -0700 · Steve Abrams
Files touched
A .gitignoreA data/products.jsonA package-lock.jsonA package.jsonA public/index.htmlA server.js
Diff
commit ff0dde099414717ac004ab29c25b00a6a5bbffae
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu May 7 16:44:40 2026 -0700
initial scaffold: textiletuesday.com editorial indigo site
---
.gitignore | 8 +
data/products.json | 26 ++
package-lock.json | 842 +++++++++++++++++++++++++++++++++++++++++++++++++++++
package.json | 12 +
public/index.html | 457 +++++++++++++++++++++++++++++
server.js | 37 +++
6 files changed, 1382 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fc59ee5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+data/subscribers.jsonl
diff --git a/data/products.json b/data/products.json
new file mode 100644
index 0000000..af69704
--- /dev/null
+++ b/data/products.json
@@ -0,0 +1,26 @@
+{
+ "hero": {
+ "handle": "dwkk-g0933c476",
+ "title": "Boutique Floral",
+ "subtitle": "Delft Dark Blue — Lee Jofa x Sarah Bartholomew",
+ "vendor": "Lee Jofa",
+ "attribute": "100% linen, botanical multipurpose, 54-inch width",
+ "img_hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/room-hotel_lobby-2022101-5-0.jpg?v=1775086481",
+ "img_room": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/room-living_room-2022101-5-0.png?v=1775100774",
+ "img_macro": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2022101_5_060796c6-a8c9-450d-a5b7-c0e35f41109a.jpg?v=1726530286"
+ },
+ "archive": [
+ { "handle": "eatonville-faux-linen-durable-walls-xwt-53320", "title": "Eatonville Faux Linen — Natural", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XWT-53320.jpg?v=1726797466", "week": "Feb 3" },
+ { "handle": "lenox-faux-linen-finish-durable-walls-xwf-52283", "title": "Lenox Faux Linen — Durable", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XWF-52283.jpg?v=1726797466", "week": "Feb 10" },
+ { "handle": "jones-faux-grass-durable-walls-xwf-52230", "title": "Jones Faux Grass — Durable", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XWF-52230.jpg?v=1726797466", "week": "Feb 17" },
+ { "handle": "baja-grasscloth-vinyl-wallpaper-xbg-44017", "title": "Baja Grasscloth Vinyl", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XBG-44017.jpg?v=1726797466", "week": "Feb 24" },
+ { "handle": "halewood-type-ii-vinyl-wallcovering-xlj-47766", "title": "Halewood Type II Vinyl", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XLJ-47766.jpg?v=1726797466", "week": "Mar 3" },
+ { "handle": "le-embossed-faux-grasscloth-xgr-6215", "title": "Embossed Faux Grasscloth", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XGR-6215.jpg?v=1726797466", "week": "Mar 10" },
+ { "handle": "faux-wild-rice-fwr-9373", "title": "Faux Wild Rice Weave", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/FWR-9373.jpg?v=1726797466", "week": "Mar 17" },
+ { "handle": "dwkk-ga47c675d", "title": "Blyth Weave — Navy Herringbone", "vendor": "Lee Jofa", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020108_50_2afea9fc-ad82-4485-8dce-e1fed6676b2a.jpg?v=1726797466", "week": "Mar 24" },
+ { "handle": "dwkk-g778c767f", "title": "Seaford Weave — Diamond Ivory", "vendor": "Lee Jofa", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020106_1_04b30aa2-2cba-4f92-94f6-f4d200a15cfe.jpg?v=1726797466", "week": "Mar 31" },
+ { "handle": "dwkk-gb60bfc9c", "title": "Levens Velvet — Leaf Green", "vendor": "Lee Jofa", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2019118_3_104d33bf-4a11-4708-b90f-0d274e118a5a.jpg?v=1726797466", "week": "Apr 7" },
+ { "handle": "canal-texture-durable-walls-xwd-52104", "title": "Canal Texture — Durable Weave", "vendor": "Hollywood Wallcoverings", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/XWD-52104.jpg?v=1726797466", "week": "Apr 14" },
+ { "handle": "dwkk-g37826aec", "title": "Maldon Weave — Fog Grey Diamond", "vendor": "Lee Jofa", "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020102_11_7d6fa794-8480-4b38-8ee5-1b0c8f53b22e.jpg?v=1726797466", "week": "Apr 22" }
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..80ccf75
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,842 @@
+{
+ "name": "textiletuesday",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "textiletuesday",
+ "version": "1.0.0",
+ "dependencies": {
+ "express": "^4.18.2"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.5",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
+ "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.15.1",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/qs": {
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..30d66b3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "textiletuesday",
+ "version": "1.0.0",
+ "description": "Textile Tuesday — weekly textile feature by Designer Wallcoverings",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js"
+ },
+ "dependencies": {
+ "express": "^4.18.2"
+ }
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..3d4ebd1
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,457 @@
+<!DOCTYPE html>
+<html lang="en" data-theme="dark">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Textile Tuesday — Every Week, One Remarkable Textile | Designer Wallcoverings</title>
+ <meta name="description" content="Free memo samples, trade pricing, and specialist guidance. Every Tuesday Designer Wallcoverings spotlights one remarkable textile — weave, linen, grasscloth, and beyond.">
+ <meta property="og:title" content="Textile Tuesday | Designer Wallcoverings">
+ <meta property="og:description" content="One remarkable textile, every Tuesday. Free memo samples. Trade service.">
+ <meta property="og:image" content="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/room-hotel_lobby-2022101-5-0.jpg?v=1775086481">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500&display=swap" rel="stylesheet">
+ <script type="application/ld+json">
+ {
+ "@context": "https://schema.org",
+ "@type": "Organization",
+ "name": "Textile Tuesday by Designer Wallcoverings",
+ "url": "https://textiletuesday.com",
+ "email": "info@textiletuesday.com",
+ "sameAs": ["https://designerwallcoverings.com"]
+ }
+ </script>
+ <style>
+ /* ── TOKENS ──────────────────────────────────────── */
+ :root {
+ --bg: #0a0c12;
+ --ink: #e8edf5;
+ --ink-soft: rgba(232,237,245,0.70);
+ --ink-faint: rgba(232,237,245,0.35);
+ --line: rgba(232,237,245,0.08);
+ --card-bg: rgba(232,237,245,0.04);
+ --header-bg: rgba(10,12,18,0.92);
+ /* Indigo workshop blue — constant */
+ --accent: #3a5a78;
+ --accent-light: #5b8ab0;
+ --accent-mid: #3a5a78;
+ --accent-dark: #243d54;
+ --accent-glow: rgba(58,90,120,0.35);
+ }
+ [data-theme="light"] {
+ --bg: #f4f6fa;
+ --ink: #10181f;
+ --ink-soft: rgba(16,24,31,0.70);
+ --ink-faint: rgba(16,24,31,0.40);
+ --line: rgba(16,24,31,0.10);
+ --card-bg: rgba(16,24,31,0.04);
+ --header-bg: rgba(244,246,250,0.92);
+ }
+
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+ html { scroll-behavior: smooth; font-size: 16px; }
+ body {
+ background: var(--bg);
+ color: var(--ink);
+ font-family: -apple-system, "SF Pro Display", system-ui, sans-serif;
+ transition: background 0.3s, color 0.3s;
+ overflow-x: hidden;
+ }
+
+ /* ── HEADER ──────────────────────────────────────── */
+ .header {
+ position: sticky; top: 0; z-index: 100;
+ background: var(--header-bg);
+ backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
+ border-bottom: 1px solid var(--line);
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 0 2rem; height: 60px;
+ }
+ .header-brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
+ .brand-dot {
+ width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
+ background: radial-gradient(circle at 35% 35%, var(--accent-light) 0%, var(--accent-mid) 60%, var(--accent-dark) 100%);
+ box-shadow: 0 0 0 1px rgba(232,237,245,0.10), 0 2px 12px var(--accent-glow);
+ }
+ .brand-name {
+ font-family: 'Cormorant Garamond', Georgia, serif;
+ font-size: 1.25rem; font-weight: 500; letter-spacing: 0.06em;
+ color: var(--ink); text-transform: uppercase;
+ }
+ .brand-name span { color: var(--accent-light); }
+ .header-nav { display: flex; align-items: center; gap: 1.5rem; }
+ .header-nav a { color: var(--ink-soft); font-size: 0.78rem; letter-spacing: 0.12em; text-transform: uppercase; text-decoration: none; transition: color 0.2s; }
+ .header-nav a:hover { color: var(--accent-light); }
+ .theme-toggle {
+ background: none; border: 1px solid var(--line); border-radius: 50%;
+ width: 34px; height: 34px; cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ color: var(--ink-soft); transition: border-color 0.2s, color 0.2s;
+ }
+ .theme-toggle:hover { border-color: var(--accent-light); color: var(--accent-light); }
+ .theme-toggle svg { width: 16px; height: 16px; }
+
+ /* ── DAY CHIP ────────────────────────────────────── */
+ .day-chip {
+ display: inline-flex; align-items: center; gap: 8px;
+ padding: 6px 14px; border-radius: 999px;
+ background: rgba(58,90,120,0.15);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(91,138,176,0.30);
+ font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase;
+ color: var(--accent-light); font-weight: 600;
+ }
+ .day-chip::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent-light); }
+
+ /* ── HERO — editorial magazine split ────────────── */
+ .hero {
+ display: grid; grid-template-columns: 1fr 1fr; min-height: 100vh;
+ }
+ @media (max-width: 768px) { .hero { grid-template-columns: 1fr; } }
+ .hero-image-side {
+ position: relative; overflow: hidden;
+ }
+ @media (max-width: 768px) { .hero-image-side { min-height: 60vw; } }
+ .hero-img-main {
+ width: 100%; height: 100%; object-fit: cover; display: block;
+ transition: transform 0.6s ease;
+ }
+ .hero-image-side:hover .hero-img-main { transform: scale(1.04); }
+ .hero-image-overlay {
+ position: absolute; inset: 0;
+ background: linear-gradient(135deg, rgba(10,12,18,0.3) 0%, transparent 60%);
+ }
+ .hero-text-side {
+ display: flex; flex-direction: column; justify-content: center;
+ padding: 5rem 3.5rem; background: var(--bg);
+ position: relative;
+ }
+ .hero-eyebrow { margin-bottom: 2rem; }
+ .hero-label {
+ font-size: 0.68rem; letter-spacing: 0.24em; text-transform: uppercase;
+ color: var(--ink-faint); margin-bottom: 0.5rem; display: block;
+ }
+ .hero-title {
+ font-family: 'Cormorant Garamond', Georgia, serif;
+ font-size: clamp(2.75rem, 4vw, 4.5rem); font-weight: 300;
+ line-height: 1.04; color: var(--ink); margin-bottom: 1rem;
+ }
+ .hero-title em { font-style: italic; color: var(--accent-light); }
+ .hero-divider { width: 48px; height: 1px; background: var(--accent); margin: 1.5rem 0; }
+ .hero-sub { font-size: 0.95rem; color: var(--ink-soft); line-height: 1.65; margin-bottom: 2rem; }
+ .hero-cta {
+ display: inline-block; padding: 14px 32px; border-radius: 2px;
+ border: 1px solid var(--accent); color: var(--accent-light);
+ font-size: 0.78rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
+ text-decoration: none; transition: background 0.2s, color 0.2s;
+ align-self: flex-start;
+ }
+ .hero-cta:hover { background: var(--accent); color: var(--ink); }
+ /* Weave-detail zoom panel */
+ .hero-zoom {
+ position: absolute; bottom: 2rem; right: 2rem;
+ width: 140px; height: 140px; border-radius: 3px; overflow: hidden;
+ border: 1px solid var(--line);
+ backdrop-filter: blur(8px);
+ box-shadow: 0 8px 32px rgba(0,0,0,0.5);
+ }
+ .hero-zoom img { width: 100%; height: 100%; object-fit: cover; }
+ .hero-zoom-label {
+ position: absolute; bottom: 0; left: 0; right: 0;
+ padding: 6px 8px; font-size: 0.58rem; letter-spacing: 0.14em;
+ text-transform: uppercase; color: var(--ink-faint);
+ background: rgba(10,12,18,0.7); backdrop-filter: blur(4px);
+ }
+
+ /* ── SECTIONS ────────────────────────────────────── */
+ section { padding: 6rem 2rem; max-width: 1200px; margin: 0 auto; }
+ .section-eyebrow {
+ font-size: 0.68rem; letter-spacing: 0.22em; text-transform: uppercase;
+ color: var(--accent-light); margin-bottom: 1.5rem; display: flex; align-items: center; gap: 12px;
+ }
+ .section-eyebrow::after { content: ''; flex: 1; height: 1px; background: var(--line); }
+ h2.section-title {
+ font-family: 'Cormorant Garamond', Georgia, serif;
+ font-size: clamp(2rem, 4vw, 3rem); font-weight: 300; color: var(--ink); margin-bottom: 2rem;
+ }
+
+ /* ── WHY — side-by-side editorial ───────────────── */
+ .why-grid { display: grid; grid-template-columns: 1.3fr 0.7fr; gap: 5rem; align-items: start; }
+ @media (max-width: 768px) { .why-grid { grid-template-columns: 1fr; } }
+ .why-body p { font-size: 1.05rem; line-height: 1.75; color: var(--ink-soft); margin-bottom: 1.25rem; }
+ .why-pullquote {
+ font-family: 'Cormorant Garamond', Georgia, serif;
+ font-size: clamp(1.4rem, 2.5vw, 1.9rem); font-weight: 300; font-style: italic;
+ line-height: 1.35; color: var(--ink); border-left: 2px solid var(--accent); padding-left: 1.5rem;
+ }
+
+ /* ── DETAIL SHOTS — macro/wide side by side ──────── */
+ .shots-magazine {
+ display: grid; grid-template-columns: 1.6fr 1fr; grid-template-rows: auto auto;
+ gap: 1.5rem;
+ }
+ @media (max-width: 768px) { .shots-magazine { grid-template-columns: 1fr; } }
+ .shot-main { grid-row: 1 / 3; }
+ .shot-card {
+ border-radius: 3px; overflow: hidden;
+ backdrop-filter: blur(16px); background: var(--card-bg); border: 1px solid var(--line);
+ transition: transform 0.3s, box-shadow 0.3s;
+ }
+ .shot-card:hover { transform: translateY(-4px); box-shadow: 0 16px 40px rgba(0,0,0,0.4); }
+ .shot-card img { width: 100%; object-fit: cover; display: block; }
+ .shot-main img { aspect-ratio: 3/4; }
+ .shot-secondary img { aspect-ratio: 4/3; }
+ .shot-label { padding: 1rem 1.25rem; font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-faint); }
+
+ /* ── ARCHIVE ─────────────────────────────────────── */
+ .archive-section { padding: 6rem 0; overflow: hidden; max-width: 100%; }
+ .archive-header { padding: 0 2rem; margin-bottom: 2rem; }
+ .archive-scroll {
+ display: flex; gap: 1.5rem; overflow-x: auto; padding: 1rem 2rem 2rem;
+ scrollbar-width: thin; scrollbar-color: var(--accent) transparent;
+ scroll-snap-type: x mandatory;
+ }
+ .archive-scroll::-webkit-scrollbar { height: 3px; }
+ .archive-scroll::-webkit-scrollbar-track { background: transparent; }
+ .archive-scroll::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 2px; }
+ .archive-card {
+ flex: 0 0 240px; border-radius: 3px; overflow: hidden;
+ background: var(--card-bg); border: 1px solid var(--line);
+ scroll-snap-align: start; transition: transform 0.25s, border-color 0.25s;
+ cursor: pointer; text-decoration: none; color: inherit;
+ }
+ .archive-card:hover { transform: translateY(-4px); border-color: var(--accent-light); }
+ .archive-card img { width: 100%; aspect-ratio: 4/5; object-fit: cover; display: block; background: var(--card-bg); }
+ .archive-meta { padding: 0.9rem 1rem; }
+ .archive-week { font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent-light); margin-bottom: 4px; }
+ .archive-title { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 0.95rem; color: var(--ink); line-height: 1.35; }
+ .archive-vendor { font-size: 0.68rem; color: var(--ink-faint); margin-top: 4px; }
+
+ /* ── SUBSCRIBE ───────────────────────────────────── */
+ .subscribe-panel {
+ backdrop-filter: blur(16px); background: var(--card-bg); border: 1px solid var(--line);
+ border-radius: 4px; padding: 3.5rem; text-align: center; max-width: 560px; margin: 0 auto;
+ }
+ .subscribe-panel h2 { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 2.25rem; font-weight: 300; margin-bottom: 1rem; }
+ .subscribe-panel p { color: var(--ink-soft); margin-bottom: 2rem; line-height: 1.6; }
+ .subscribe-form { display: flex; max-width: 420px; margin: 0 auto; }
+ .subscribe-form input {
+ flex: 1; padding: 12px 16px;
+ background: rgba(232,237,245,0.06); border: 1px solid var(--line); border-right: none;
+ border-radius: 2px 0 0 2px; color: var(--ink); font-size: 0.9rem; outline: none;
+ transition: border-color 0.2s;
+ }
+ .subscribe-form input::placeholder { color: var(--ink-faint); }
+ .subscribe-form input:focus { border-color: var(--accent); }
+ .subscribe-form button {
+ padding: 12px 24px; background: var(--accent); color: var(--ink);
+ border: none; border-radius: 0 2px 2px 0;
+ font-size: 0.75rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
+ cursor: pointer; transition: background 0.2s; white-space: nowrap;
+ }
+ .subscribe-form button:hover { background: var(--accent-light); color: #0a0c12; }
+ .subscribe-msg { margin-top: 1rem; font-size: 0.85rem; color: var(--accent-light); min-height: 1.2em; }
+
+ /* ── FOOTER ──────────────────────────────────────── */
+ footer {
+ border-top: 1px solid var(--line); padding: 3rem 2rem;
+ display: flex; flex-direction: column; align-items: center; gap: 1rem; text-align: center;
+ }
+ .footer-logo { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 1.1rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-soft); }
+ .footer-logo span { color: var(--accent-light); }
+ .footer-links { display: flex; gap: 2rem; flex-wrap: wrap; justify-content: center; }
+ .footer-links a { font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-faint); text-decoration: none; transition: color 0.2s; }
+ .footer-links a:hover { color: var(--accent-light); }
+ footer small { font-size: 0.7rem; color: var(--ink-faint); }
+ </style>
+</head>
+<body>
+ <script>
+ (function(){
+ var t = localStorage.getItem('tt-theme') || 'dark';
+ document.documentElement.setAttribute('data-theme', t);
+ })();
+ </script>
+
+ <header class="header">
+ <a href="/" class="header-brand">
+ <div class="brand-dot"></div>
+ <span class="brand-name">Textile<span>Tuesday</span></span>
+ </a>
+ <nav class="header-nav">
+ <a href="#picks">Archive</a>
+ <a href="#subscribe">Subscribe</a>
+ <a href="https://designerwallcoverings.com" target="_blank" rel="noopener">Shop DW</a>
+ <button class="theme-toggle" id="themeToggle" aria-label="Toggle dark/light mode">
+ <svg id="iconSun" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
+ <circle cx="12" cy="12" r="4"/><line x1="12" y1="2" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="22"/>
+ <line x1="4.22" y1="4.22" x2="6.34" y2="6.34"/><line x1="17.66" y1="17.66" x2="19.78" y2="19.78"/>
+ <line x1="2" y1="12" x2="5" y2="12"/><line x1="19" y1="12" x2="22" y2="12"/>
+ <line x1="4.22" y1="19.78" x2="6.34" y2="17.66"/><line x1="17.66" y1="6.34" x2="19.78" y2="4.22"/>
+ </svg>
+ <svg id="iconMoon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" style="display:none">
+ <path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
+ </svg>
+ </button>
+ </nav>
+ </header>
+
+ <!-- SECTION 1: HERO — editorial magazine split -->
+ <section class="hero" id="hero" style="padding:0; max-width:100%; margin:0;">
+ <div class="hero-image-side">
+ <img class="hero-img-main"
+ src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/room-hotel_lobby-2022101-5-0.jpg?v=1775086481"
+ alt="Boutique Floral in a hotel lobby room setting">
+ <div class="hero-image-overlay"></div>
+ </div>
+ <div class="hero-text-side">
+ <div class="hero-eyebrow">
+ <span class="day-chip">Tuesday · 2026-05-05</span>
+ </div>
+ <span class="hero-label">This Week's Textile</span>
+ <h1 class="hero-title">Boutique<br><em>Floral</em></h1>
+ <div class="hero-divider"></div>
+ <p class="hero-sub">Delft Dark Blue — Lee Jofa x Sarah Bartholomew. Pure botanical linen woven at 54 inches, with a hand-drawn floral motif that navigates the line between antique document and living garden wall.</p>
+ <a href="https://designerwallcoverings.com/products/dwkk-g0933c476#sample" class="hero-cta" target="_blank" rel="noopener">Order Memo Sample (free)</a>
+ <!-- Weave-detail zoom -->
+ <div class="hero-zoom">
+ <img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2022101_5_060796c6-a8c9-450d-a5b7-c0e35f41109a.jpg?v=1726530286" alt="Macro weave detail of Boutique Floral linen">
+ <div class="hero-zoom-label">Macro Detail</div>
+ </div>
+ </div>
+ </section>
+
+ <!-- SECTION 2: WHY THIS PICK -->
+ <section id="why">
+ <div class="section-eyebrow">Editorial</div>
+ <div class="why-grid">
+ <div class="why-body">
+ <h2 class="section-title">Why This Pick</h2>
+ <p>Sarah Bartholomew's botanical linen for Lee Jofa is the rare textile that demands to be read. Every repeat tells a story assembled from pressed specimens — field botanicals rendered at life scale, inked in a Delft-adjacent blue that refuses to read as simply navy. At certain lights it's almost teal; at dusk, it deepens to near-black.</p>
+ <p>The linen substrate is where craft discipline shows. The base cloth is Belgian, woven to a gauge that allows the printed design to float rather than sink into the fiber. That tactile clarity — the sense of a drawing sitting on a surface rather than soaking in — is a signature of proper botanical printing on quality linen.</p>
+ <p>For trade clients, the sample program from Designer Wallcoverings means you can study the drape and hand at scale before committing to drapery or upholstery yardage. The free memo is here for a reason — this textile earns its invoice only once you've spent time with it.</p>
+ </div>
+ <blockquote class="why-pullquote">
+ "Good botanical linen carries the drafting room and the greenhouse at once. You can smell both in this one."
+ </blockquote>
+ </div>
+ </section>
+
+ <!-- SECTION 3: DETAIL SHOTS — magazine layout -->
+ <section id="shots">
+ <div class="section-eyebrow">Studio</div>
+ <h2 class="section-title">Three Scales of Looking</h2>
+ <div class="shots-magazine">
+ <div class="shot-card shot-main">
+ <img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/room-living_room-2022101-5-0.png?v=1775100774"
+ alt="Living room install — Boutique Floral linen drapery" loading="lazy">
+ <div class="shot-label">Wide Room Install</div>
+ </div>
+ <div class="shot-card shot-secondary">
+ <img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2022101_5_060796c6-a8c9-450d-a5b7-c0e35f41109a.jpg?v=1726530286"
+ alt="Macro weave — close-up textile grain of Boutique Floral" loading="lazy">
+ <div class="shot-label">Macro Weave</div>
+ </div>
+ <div class="shot-card shot-secondary">
+ <img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/room-hotel_lobby-2022101-5-0.jpg?v=1775086481"
+ alt="Hotel lobby scale — Boutique Floral full installation" loading="lazy">
+ <div class="shot-label">Contract Scale</div>
+ </div>
+ </div>
+ </section>
+
+ <!-- SECTION 4: ARCHIVE -->
+ <section class="archive-section" id="picks">
+ <div class="archive-header">
+ <div class="section-eyebrow">Past Picks</div>
+ <h2 class="section-title" style="margin-bottom:0.5rem">Every Tuesday, One Textile</h2>
+ <p style="color:var(--ink-faint); font-size:0.85rem; margin-bottom:1rem;">Twelve weeks of features, available on demand as free memo samples.</p>
+ </div>
+ <div class="archive-scroll" id="archiveScroll"></div>
+ </section>
+
+ <!-- SECTION 5: SUBSCRIBE -->
+ <section id="subscribe">
+ <div class="section-eyebrow">The Weekly Brief</div>
+ <div class="subscribe-panel">
+ <h2>The Tuesday Textile</h2>
+ <p>One email every Tuesday morning. One textile chosen for its craft, provenance, or sheer audacity. Free memo sample always included for every pick.</p>
+ <form class="subscribe-form" id="subForm">
+ <input type="email" id="subEmail" placeholder="your@email.com" autocomplete="email" required>
+ <button type="submit">Subscribe</button>
+ </form>
+ <div class="subscribe-msg" id="subMsg"></div>
+ </div>
+ </section>
+
+ <!-- FOOTER -->
+ <footer>
+ <div class="footer-logo">Textile<span>Tuesday</span> · by Designer Wallcoverings</div>
+ <nav class="footer-links">
+ <a href="https://designerwallcoverings.com" target="_blank" rel="noopener">Shop DW</a>
+ <a href="https://designerwallcoverings.com/pages/trade" target="_blank" rel="noopener">Trade Program</a>
+ <a href="mailto:info@textiletuesday.com">info@textiletuesday.com</a>
+ <a href="#subscribe">Subscribe</a>
+ </nav>
+ <small>© 2026 Designer Wallcoverings — textiletuesday.com — All images © respective vendors via DW catalog</small>
+ </footer>
+
+ <script>
+ // Theme toggle
+ const root = document.documentElement;
+ const btn = document.getElementById('themeToggle');
+ const sun = document.getElementById('iconSun');
+ const moon = document.getElementById('iconMoon');
+ function applyTheme(t) {
+ root.setAttribute('data-theme', t);
+ localStorage.setItem('tt-theme', t);
+ if (t === 'dark') { sun.style.display = ''; moon.style.display = 'none'; }
+ else { sun.style.display = 'none'; moon.style.display = ''; }
+ }
+ applyTheme(root.getAttribute('data-theme') || 'dark');
+ btn.addEventListener('click', () => applyTheme(root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'));
+
+ // Archive
+ const archive = [
+ { handle: "eatonville-faux-linen-durable-walls-xwt-53320", title: "Eatonville Faux Linen — Natural", vendor: "Hollywood Wallcoverings", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Indienne_Agra_18310_Roomshot_Web_LR-medium-two-thirds.jpg?v=17756", week: "Feb 3" },
+ { handle: "lenox-faux-linen-finish-durable-walls-xwf-52283", title: "Lenox Faux Linen — Durable", vendor: "Hollywood Wallcoverings", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Indienne_Agra_18310_Roomshot_Web_LR-og_454a2c91-eb5e-448a-a0a0-51a5edd39c0d.jpg?v=1726527977", week: "Feb 10" },
+ { handle: "baja-grasscloth-vinyl-wallpaper-xbg-44017", title: "Baja Grasscloth Vinyl", vendor: "Hollywood Wallcoverings", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Indienne_Poona_18300_Roomshot_Web_LR-og_2018d80a-6abd-474c-94e2-11ae8e06b5c9.jpg?v=1726528019", week: "Feb 17" },
+ { handle: "le-embossed-faux-grasscloth-xgr-6215", title: "Embossed Faux Grasscloth", vendor: "Hollywood Wallcoverings", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/EssentialsLuxor_Horus_19436_Roomshot_Web_LR-og_f71ec1c4-c1eb-4a62-8ff1-18d47f9cb1b3.jpg?v=1726527783", week: "Feb 24" },
+ { handle: "faux-wild-rice-fwr-9373", title: "Faux Wild Rice Weave", vendor: "Hollywood Wallcoverings", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Indienne_Poona_18300_Roomshot_Web_LR-medium-two-thirds_7fe2f949-1a67-4e36-bfff-b31bfe82d37a.jpg?v=1726528019", week: "Mar 3" },
+ { handle: "dwkk-ga47c675d", title: "Blyth Weave — Navy Herringbone", vendor: "Lee Jofa", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020108_50_2afea9fc-ad82-4485-8dce-e1fed6676b2a.jpg?v=1726797466", week: "Mar 10" },
+ { handle: "dwkk-g778c767f", title: "Seaford Weave — Diamond Ivory", vendor: "Lee Jofa", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020106_1_04b30aa2-2cba-4f92-94f6-f4d200a15cfe.jpg?v=1726797466", week: "Mar 17" },
+ { handle: "dwkk-gb60bfc9c", title: "Levens Velvet — Leaf Green", vendor: "Lee Jofa", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2019118_3_104d33bf-4a11-4708-b90f-0d274e118a5a.jpg?v=1726797466", week: "Mar 24" },
+ { handle: "dwkk-g7057da14", title: "Brink — Delft Indigo Chenille", vendor: "Lee Jofa", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GWF-3733_151_8c242f7c-5c7d-4f82-878d-09235ca6ae41.jpg?v=1726797466", week: "Mar 31" },
+ { handle: "dwkk-g37826aec", title: "Maldon Weave — Fog Grey", vendor: "Lee Jofa", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020102_11_7d6fa794-8480-4b38-8ee5-1b0c8f53b22e.jpg?v=1726797466", week: "Apr 7" },
+ { handle: "canal-texture-durable-walls-xwd-52104", title: "Canal Texture — Durable Weave", vendor: "Hollywood Wallcoverings", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Indienne_Agra_18310_Mood_Web_LR-hero_ba3526ae-673b-4a34-912d-e41a5d8eb5e2.jpg?v=1726527977", week: "Apr 14" },
+ { handle: "dwkk-gdc6143f9", title: "Bale — Radicchio Bouclé", vendor: "Lee Jofa", img: "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2019155_710_95e89ba7-f723-451b-8611-47aa1d4aa06e.jpg?v=1726797466", week: "Apr 22" }
+ ];
+ const scroll = document.getElementById('archiveScroll');
+ archive.forEach(p => {
+ const a = document.createElement('a');
+ a.href = `https://designerwallcoverings.com/products/${p.handle}#sample`;
+ a.target = '_blank'; a.rel = 'noopener'; a.className = 'archive-card';
+ a.innerHTML = `
+ <img src="${p.img}" alt="${p.title}" loading="lazy" onerror="this.style.background='rgba(58,90,120,0.10)';this.style.minHeight='180px'">
+ <div class="archive-meta">
+ <div class="archive-week">${p.week}</div>
+ <div class="archive-title">${p.title}</div>
+ <div class="archive-vendor">${p.vendor}</div>
+ </div>`;
+ scroll.appendChild(a);
+ });
+
+ // Subscribe
+ document.getElementById('subForm').addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const email = document.getElementById('subEmail').value;
+ const msg = document.getElementById('subMsg');
+ try {
+ const r = await fetch('/api/subscribe', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ email }) });
+ const d = await r.json();
+ msg.textContent = d.message || d.error || 'Done.';
+ } catch { msg.textContent = 'Something went wrong. Try again.'; }
+ });
+ </script>
+</body>
+</html>
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..8a1eaae
--- /dev/null
+++ b/server.js
@@ -0,0 +1,37 @@
+const express = require('express');
+const path = require('path');
+const fs = require('fs');
+
+const app = express();
+const PORT = process.env.PORT || 9899;
+
+app.use(express.json());
+app.use(express.static(path.join(__dirname, 'public')));
+
+const products = JSON.parse(fs.readFileSync(path.join(__dirname, 'data/products.json'), 'utf8'));
+
+app.get('/', (req, res) => {
+ res.sendFile(path.join(__dirname, 'public/index.html'));
+});
+
+app.get('/health', (req, res) => {
+ res.json({ status: 'ok', site: 'textiletuesday.com', port: PORT });
+});
+
+app.get('/api/products', (req, res) => {
+ res.json(products);
+});
+
+app.post('/api/subscribe', (req, res) => {
+ const { email } = req.body;
+ if (!email || !email.includes('@')) {
+ return res.status(400).json({ error: 'Valid email required' });
+ }
+ const entry = JSON.stringify({ email, site: 'textiletuesday.com', ts: new Date().toISOString() }) + '\n';
+ fs.appendFileSync(path.join(__dirname, 'data/subscribers.jsonl'), entry);
+ res.json({ ok: true, message: 'You\'re in. Every Tuesday, one textile worth knowing.' });
+});
+
+app.listen(PORT, () => {
+ console.log(`textiletuesday.com running on port ${PORT}`);
+});
(oldest)
·
back to Textiletuesday
·
snapshot before hamburger nav propagation ee59869 →