{"_id":"@aarongustafson/eleventy-plugin-share-card","_rev":"6-59bb28baac473332e9ecb1c057a720ee","name":"@aarongustafson/eleventy-plugin-share-card","dist-tags":{"latest":"1.0.6"},"versions":{"0.0.1":{"name":"@aarongustafson/eleventy-plugin-share-card","version":"0.0.1","keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"author":{"url":"https://www.aaron-gustafson.com/","name":"Aaron Gustafson","email":"aaron@easy-designs.net"},"license":"MIT","_id":"@aarongustafson/eleventy-plugin-share-card@0.0.1","maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"dist":{"shasum":"0eddd1a65ab86045e9804bf767ffadb3a3c04597","tarball":"https://registry.npmjs.org/@aarongustafson/eleventy-plugin-share-card/-/eleventy-plugin-share-card-0.0.1.tgz","fileCount":6,"integrity":"sha512-LAcaz2924N0o9zEse76st/Jrl4Xme5gfpyYp08AQ1LzuHo+ZjxSmYN2vV/rtPwETDn9B7JEAqdtwJq0jl4oGiQ==","signatures":[{"sig":"MEUCIQD6zps1uOeW0RW4ZqwQeRW5MIxwuRREH/bwgkjzP4xqxwIgASwpsE/12KPwR1paM0mAq+ZCUnlTrKg0SpTq8IO7z1k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32659},"main":"index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./index.js"},"gitHead":"048a70e89007b4f26c77b1ea437aa52b687c1781","scripts":{"lint":"npm run lint:eslint && npm run lint:prettier","format":"npm run format:eslint && npm run format:prettier","lint:eslint":"eslint .","postversion":"git push --follow-tags","format:eslint":"eslint . --fix","lint:prettier":"prettier \"**/*.js\" --check --ignore-path .gitignore","format:prettier":"prettier \"**/*.js\" --write --ignore-path .gitignore"},"_npmUser":{"name":"aarongustafson","email":"aaron@easy-designs.net"},"repository":{"url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git","type":"git"},"_npmVersion":"11.6.2","description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","directories":{},"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","prettier":"^3.6.2","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"sharp":">=0.33.0","@11ty/eleventy":">=3.0.0"},"peerDependenciesMeta":{"@11ty/eleventy":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eleventy-plugin-share-card_0.0.1_1777843003502_0.7879949050686657","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@aarongustafson/eleventy-plugin-share-card","version":"1.0.0","keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"author":{"url":"https://www.aaron-gustafson.com/","name":"Aaron Gustafson","email":"aaron@easy-designs.net"},"license":"MIT","_id":"@aarongustafson/eleventy-plugin-share-card@1.0.0","maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"dist":{"shasum":"1b2535633044dc016599bccfc7b7b0ac0d3ffe7a","tarball":"https://registry.npmjs.org/@aarongustafson/eleventy-plugin-share-card/-/eleventy-plugin-share-card-1.0.0.tgz","fileCount":6,"integrity":"sha512-uyxi2EwX6JoSZJokYFA2tOCyxfFVZ675TR9eVcp9+lhnbqLmkYlR+foKcoL7C8QRV0edCSjARXLVZ6bI3aMMbA==","signatures":[{"sig":"MEUCIQCfWwXlDsUU953CSv5q2+29RHpZ90e4QPd/z6k4rM6YVQIgcEiWfJlj7pemVYiS7ofQ6QIxXkWvi7eZTU5k4OHzITc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32659},"main":"index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./index.js"},"gitHead":"313506e626dba6192f97588d97742f6569e908a8","scripts":{"lint":"npm run lint:eslint && npm run lint:prettier","format":"npm run format:eslint && npm run format:prettier","lint:eslint":"eslint .","postversion":"git push --follow-tags","format:eslint":"eslint . --fix","lint:prettier":"prettier \"**/*.js\" --check --ignore-path .gitignore","format:prettier":"prettier \"**/*.js\" --write --ignore-path .gitignore"},"_npmUser":{"name":"aarongustafson","email":"aaron@easy-designs.net"},"repository":{"url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git","type":"git"},"_npmVersion":"11.6.2","description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","directories":{},"_nodeVersion":"24.11.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","prettier":"^3.6.2","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"sharp":">=0.33.0","@11ty/eleventy":">=3.0.0"},"peerDependenciesMeta":{"@11ty/eleventy":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eleventy-plugin-share-card_1.0.0_1777843226394_0.10578791503067975","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@aarongustafson/eleventy-plugin-share-card","version":"1.0.2","keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"author":{"url":"https://www.aaron-gustafson.com/","name":"Aaron Gustafson","email":"aaron@easy-designs.net"},"license":"MIT","_id":"@aarongustafson/eleventy-plugin-share-card@1.0.2","maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"dist":{"shasum":"08d4bbc10deb3090caa2d916419e7602cada653d","tarball":"https://registry.npmjs.org/@aarongustafson/eleventy-plugin-share-card/-/eleventy-plugin-share-card-1.0.2.tgz","fileCount":6,"integrity":"sha512-AB+ar0ycmLgn3s1CNW4+4LCLncf0sHw1cQWfBwg70YUCLX268Q89vNBoe6cRinHp/I8bOVZzl+X39jVN67BhUQ==","signatures":[{"sig":"MEUCIGIyySyqgbjy2KsUMmX63etw1yEBZvGAqCeklNUsUlfgAiEAs2od8LCzgI9/QNTIHqLZTkqJd8pZUqk6EXPJ5SJFKbo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aarongustafson%2feleventy-plugin-share-card@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":34878},"main":"index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./index.js"},"gitHead":"c91d4f133d41231f428929ea742802073349205c","scripts":{"lint":"npm run lint:eslint && npm run lint:prettier","test":"vitest run","format":"npm run format:eslint && npm run format:prettier","preversion":"npm run lint && npm run test","test:watch":"vitest","lint:eslint":"eslint .","postversion":"git push --follow-tags","format:eslint":"eslint . --fix","lint:prettier":"prettier \"**/*.js\" --check --ignore-path .gitignore","prepublishOnly":"npm run lint && npm run test","format:prettier":"prettier \"**/*.js\" --write --ignore-path .gitignore"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:801a0f49-5d3d-444b-b339-d573cee1a988"}},"repository":{"url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git","type":"git"},"_npmVersion":"11.13.0","description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","vitest":"^4.1.5","prettier":"^3.6.2","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"sharp":">=0.33.0","@11ty/eleventy":">=3.0.0","proper-lockfile":"^4.1.2"},"peerDependenciesMeta":{"@11ty/eleventy":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eleventy-plugin-share-card_1.0.2_1777850350161_0.6798074589951977","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@aarongustafson/eleventy-plugin-share-card","version":"1.0.3","keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"author":{"url":"https://www.aaron-gustafson.com/","name":"Aaron Gustafson","email":"aaron@easy-designs.net"},"license":"MIT","_id":"@aarongustafson/eleventy-plugin-share-card@1.0.3","maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"dist":{"shasum":"a79247d78bb6c91f89298812611b0b120689d0c3","tarball":"https://registry.npmjs.org/@aarongustafson/eleventy-plugin-share-card/-/eleventy-plugin-share-card-1.0.3.tgz","fileCount":6,"integrity":"sha512-vYdBX6RsPO2GMGVNYpQPT6AlitXXz34eMk6ry8le4dmyp310YDAZR1kka0uoMQdrclvnOWl+PZO6VYFJuAZDyw==","signatures":[{"sig":"MEUCIQCvSVHpfAF6Sedp4Ib2IKldh/gCD0Q/9hsmATxJrLxxMAIgLHupDviLSVgqfA0ZGmGMeND8/SP88mcuWRI/jz7WDtM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aarongustafson%2feleventy-plugin-share-card@1.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":36596},"main":"index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./index.js"},"gitHead":"b6d3b2676ead5bec8ef7ef1ad507d47d3b1c5c53","scripts":{"lint":"npm run lint:eslint && npm run lint:prettier","test":"vitest run","format":"npm run format:eslint && npm run format:prettier","preversion":"npm run lint && npm run test","test:watch":"vitest","lint:eslint":"eslint .","postversion":"git push --follow-tags","format:eslint":"eslint . --fix","lint:prettier":"prettier \"**/*.js\" --check --ignore-path .gitignore","prepublishOnly":"npm run lint && npm run test","format:prettier":"prettier \"**/*.js\" --write --ignore-path .gitignore"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:801a0f49-5d3d-444b-b339-d573cee1a988"}},"repository":{"url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git","type":"git"},"_npmVersion":"11.13.0","description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","vitest":"^4.1.5","prettier":"^3.6.2","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"sharp":">=0.33.0","@11ty/eleventy":">=3.0.0","proper-lockfile":"^4.1.2"},"peerDependenciesMeta":{"@11ty/eleventy":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eleventy-plugin-share-card_1.0.3_1777851855198_0.48695922409481973","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@aarongustafson/eleventy-plugin-share-card","version":"1.0.5","keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"author":{"url":"https://www.aaron-gustafson.com/","name":"Aaron Gustafson","email":"aaron@easy-designs.net"},"license":"MIT","_id":"@aarongustafson/eleventy-plugin-share-card@1.0.5","maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"dist":{"shasum":"6b4b8fe95587c3032e40be29f515a3d40b5df12b","tarball":"https://registry.npmjs.org/@aarongustafson/eleventy-plugin-share-card/-/eleventy-plugin-share-card-1.0.5.tgz","fileCount":6,"integrity":"sha512-2sD+HNLQI1mCsAyyGu212o3xTB0N6mMnwZlDFjW7oBw7BwToWmuRG1stmKhmjuBPJJCiDx8nCL8FdMTWsOta+g==","signatures":[{"sig":"MEUCIQCo8OhQjqKT6IDkYOOOQLLfhdr2r/kH2YGUJTJiwt/wtQIgeEEAkYgjFeqbXRJgnn0oYyLa3332jmV6fYvsfynnt6I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aarongustafson%2feleventy-plugin-share-card@1.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":40977},"main":"index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./index.js"},"gitHead":"5e55f6666254c89885e8a055170920aca4cb4156","scripts":{"lint":"npm run lint:eslint && npm run lint:prettier","test":"vitest run","format":"npm run format:eslint && npm run format:prettier","preversion":"npm run lint && npm run test","test:watch":"vitest","lint:eslint":"eslint .","postversion":"git push --follow-tags","format:eslint":"eslint . --fix","lint:prettier":"prettier \"**/*.js\" --check --ignore-path .gitignore","prepublishOnly":"npm run lint && npm run test","format:prettier":"prettier \"**/*.js\" --write --ignore-path .gitignore"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:801a0f49-5d3d-444b-b339-d573cee1a988"}},"repository":{"url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git","type":"git"},"_npmVersion":"11.13.0","description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","vitest":"^4.1.5","prettier":"^3.6.2","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"sharp":">=0.33.0","@11ty/eleventy":">=3.0.0","proper-lockfile":"^4.1.2"},"peerDependenciesMeta":{"@11ty/eleventy":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eleventy-plugin-share-card_1.0.5_1777908448071_0.044470818717814","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@aarongustafson/eleventy-plugin-share-card","version":"1.0.6","description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","type":"module","main":"index.js","exports":{".":"./index.js"},"repository":{"type":"git","url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git"},"homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"author":{"name":"Aaron Gustafson","email":"aaron@easy-designs.net","url":"https://www.aaron-gustafson.com/"},"license":"MIT","peerDependencies":{"@11ty/eleventy":">=3.0.0","proper-lockfile":"^4.1.2","sharp":">=0.33.0"},"peerDependenciesMeta":{"@11ty/eleventy":{"optional":true}},"engines":{"node":">=20"},"scripts":{"format":"npm run format:eslint && npm run format:prettier","format:eslint":"eslint . --fix","format:prettier":"prettier \"**/*.js\" --write --ignore-path .gitignore","lint":"npm run lint:eslint && npm run lint:prettier","lint:eslint":"eslint .","lint:prettier":"prettier \"**/*.js\" --check --ignore-path .gitignore","prepublishOnly":"npm run lint && npm run test","preversion":"npm run lint && npm run test","postversion":"git push --follow-tags","test":"vitest run","test:watch":"vitest"},"devDependencies":{"eslint":"^9.39.1","eslint-config-prettier":"^10.1.8","prettier":"^3.6.2","vitest":"^4.1.5"},"publishConfig":{"access":"public"},"gitHead":"e7a3267c822243683daf55f9563b291b6408dd78","_id":"@aarongustafson/eleventy-plugin-share-card@1.0.6","_nodeVersion":"24.14.1","_npmVersion":"11.13.0","dist":{"integrity":"sha512-P1HqOAJS9ya7gDaLuzdDX0d3RAsXWS9U4TjaQw5qjOmuLEYTJ67+W0F0QD2R62h4b8M1KRMc7KCEwkGP0CmIqA==","shasum":"d1b61c16a0955080e612925fa25c95d141b3382d","tarball":"https://registry.npmjs.org/@aarongustafson/eleventy-plugin-share-card/-/eleventy-plugin-share-card-1.0.6.tgz","fileCount":6,"unpackedSize":42766,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aarongustafson%2feleventy-plugin-share-card@1.0.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDuzq7JSr5iqeaTxElBn7xkBzP57G8lPeiGRWL/SPztpAiB1i9L7OYImgXQU0Lecfo9UpDvhLQiMFbgHqHkLBiD1dQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:801a0f49-5d3d-444b-b339-d573cee1a988"}},"directories":{},"maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/eleventy-plugin-share-card_1.0.6_1777910899056_0.9652283617327952"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-03T21:16:43.368Z","modified":"2026-05-04T16:08:19.590Z","0.0.1":"2026-05-03T21:16:43.657Z","1.0.0":"2026-05-03T21:20:26.532Z","1.0.2":"2026-05-03T23:19:10.308Z","1.0.3":"2026-05-03T23:44:15.364Z","1.0.5":"2026-05-04T15:27:28.224Z","1.0.6":"2026-05-04T16:08:19.263Z"},"bugs":{"url":"https://github.com/aarongustafson/eleventy-plugin-share-card/issues"},"author":{"name":"Aaron Gustafson","email":"aaron@easy-designs.net","url":"https://www.aaron-gustafson.com/"},"license":"MIT","homepage":"https://github.com/aarongustafson/eleventy-plugin-share-card#readme","keywords":["eleventy","eleventy-plugin","11ty","og-image","share-card","open-graph","social-image","sharp"],"repository":{"type":"git","url":"git+https://github.com/aarongustafson/eleventy-plugin-share-card.git"},"description":"Build-time social share-card (OG image) generator for Eleventy. Composites text over a base image using sharp + SVG — no external services required.","maintainers":[{"name":"aarongustafson","email":"aaron@easy-designs.net"}],"readme":"# eleventy-plugin-share-card\n\nBuild-time social share-card (OG image) generator for [Eleventy](https://www.11ty.dev/). No external services required — images are composited locally at build time using [sharp](https://sharp.pixelplumbing.com/) and cached on disk so unchanged posts are never re-processed.\n\n## Features\n\n- 🖼 Composites any number of text layers onto a base template image\n- 🔤 Embeds custom fonts (WOFF2 / TTF / OTF) directly into the SVG — no system font installation needed\n- 💾 Disk cache keyed by content hash — only regenerates when text changes\n- 🧩 Works as an **Eleventy plugin** (registers a `shareCard` filter) **or** as a standalone **generator factory** for use inside `eleventyComputed` data files\n- 🚫 Zero calls to Cloudinary, imgix, or any other external image API\n\n## Installation\n\n```bash\nnpm install eleventy-plugin-share-card sharp proper-lockfile\n```\n\n> `sharp` and `proper-lockfile` are peer dependencies. If your project already has them, you're all set.\n\n### Optional: fonts via fontsource\n\nThe plugin accepts any local font file — you can source them however you like. [Fontsource](https://fontsource.org/) is an easy option:\n\n```bash\nnpm install @fontsource/source-serif-4 @fontsource/open-sans\n```\n\n---\n\n## Quick start\n\n### Option A — Eleventy plugin (adds a `shareCard` filter)\n\n```js\n// .eleventy.js\nimport shareCardPlugin from \"eleventy-plugin-share-card\";\n\nexport default (eleventyConfig) => {\n  eleventyConfig.addPlugin(shareCardPlugin, {\n    baseImagePath: \"./src/_images/share-card.jpg\",\n    outputDir:     \"./src/static/i/share-cards\",\n    outputUrlPath: \"/i/share-cards\",\n    cacheFile:     \"./_cache/share-cards.json\",\n    verbose:       true,\n    imageWidth:    1280,\n    imageHeight:   669,\n    layers: [\n      {\n        font:        \"Source Serif 4\",\n        fontPath:    \"node_modules/@fontsource/source-serif-4/files/source-serif-4-latin-700-normal.woff2\",\n        fontSize:    72,\n        fontWeight:  700,\n        color:       \"#2C2825\",\n        x:           480,\n        y:           { from: \"bottom\", value: 205 },\n        maxWidth:    760,\n        lineSpacing: -18,\n      },\n      {\n        font:        \"Open Sans\",\n        fontPath:    \"node_modules/@fontsource/open-sans/files/open-sans-latin-300-normal.woff2\",\n        fontSize:    36,\n        fontWeight:  300,\n        color:       \"#505050\",\n        x:           480,\n        y:           { from: \"top\", value: 505 },\n        maxWidth:    760,\n        lineSpacing: -5,\n      },\n    ],\n  });\n};\n```\n\n### Option B — Generator factory in an `eleventyComputed` data file\n\nThis is the recommended pattern for data files because it gives you full control over which text goes into each layer.\n\nThe generator uses **deferred generation**: every call is queued (most-recent data wins per slug) and images are generated once in a single `eleventy.after` pass at the end of the build. This prevents the redundant double-generation that occurs because Eleventy re-evaluates `eleventyComputed` properties on every collection access — sometimes with incomplete data on the first pass.\n\nCreate the generator inside your Eleventy config and register it as a JavaScript template function so data files can invoke it via `this`:\n\n```js\n// .eleventy.js\nimport { createGenerator } from \"eleventy-plugin-share-card\";\n\nexport default (eleventyConfig) => {\n  const generateShareCard = createGenerator(\n    {\n      baseImagePath: \"./src/_images/share-card.jpg\",\n      outputDir:     \"./src/static/i/share-cards\",\n      outputUrlPath: \"/i/share-cards\",\n      verbose:       true,\n      layers: [\n        { font: \"Source Serif 4\", fontPath: \"...\", fontSize: 72, fontWeight: 700,\n          color: \"#2C2825\", x: 480, y: { from: \"bottom\", value: 205 }, maxWidth: 760, lineSpacing: -18 },\n        { font: \"Open Sans\",      fontPath: \"...\", fontSize: 36, fontWeight: 300,\n          color: \"#505050\", x: 480, y: { from: \"top\",    value: 505 }, maxWidth: 760, lineSpacing: -5  },\n      ],\n    },\n    eleventyConfig,    // required — registers the eleventy.after flush hook\n  );\n\n  // Expose the generator as a callable JavaScript template function so that\n  // eleventyComputed data files can invoke it via `this.generateShareCard(…)`.\n  // See https://www.11ty.dev/docs/languages/javascript/#javascript-template-functions\n  eleventyConfig.addJavaScriptFunction(\"generateShareCard\", generateShareCard);\n};\n```\n\n```js\n// src/posts/posts.11tydata.js\nexport default {\n  eleventyComputed: {\n    image: async function (data) {\n      if (data.hero) return `${data.site.url}${data.hero.src}`;\n      // `this.generateShareCard` is the function registered above\n      return this.generateShareCard(\n        [data.title, myTagsToHashtags(data.tags)],\n        data.page.fileSlug,\n      );\n    },\n  },\n};\n```\n\n**Important:** Use a regular function expression or declaration, not an arrow function. Arrow functions do not have Eleventy's dynamic `this` binding:\n\n```js\n// ✅ Correct — this is bound to Eleventy's context\nimage: async function (data) { return this.generateShareCard(...); }\n\n// ❌ Incorrect — this does not reference Eleventy's context\nimage: async (data) => { return this.generateShareCard(...); }  // `this` is undefined\n```\n\n### Alternative: avoid `this` by importing the generator directly\n\nIf you prefer to avoid relying on `this` binding, you can store the generator in a shared module:\n\n```js\n// src/_includes/lib/share-card.js\nimport { createGenerator } from \"eleventy-plugin-share-card\";\n\nlet generateShareCard;\n\nexport function initShareCardGenerator(eleventyConfig, options) {\n  generateShareCard = createGenerator(options, eleventyConfig);\n}\n\nexport function getShareCardGenerator() {\n  return generateShareCard;\n}\n```\n\nThen initialize it in `.eleventy.js`:\n\n```js\nimport { initShareCardGenerator } from './src/_includes/lib/share-card.js';\n\nexport default (eleventyConfig) => {\n  initShareCardGenerator(eleventyConfig, {\n    baseImagePath: \"./src/_images/share-card.jpg\",\n    outputDir:     \"./src/static/i/share-cards\",\n    outputUrlPath: \"/i/share-cards\",\n    verbose:       true,\n    layers: [ /* ... */ ],\n  });\n};\n```\n\nAnd call it from your data file:\n\n```js\nimport { getShareCardGenerator } from './_includes/lib/share-card.js';\n\nexport default {\n  eleventyComputed: {\n    image: async (data) => {\n      if (data.hero) return `${data.site.url}${data.hero.src}`;\n      const generateShareCard = getShareCardGenerator();\n      return generateShareCard(\n        [data.title, myTagsToHashtags(data.tags)],\n        data.page.fileSlug,\n      );\n    },\n  },\n};\n```\n\n---\n\n## Options reference\n\n### `createGenerator(options, eleventyConfig)`\n\n`options` is the same object as the top-level plugin options above.\n\n`eleventyConfig` is the Eleventy `UserConfig` object (**required**).  It is\nused to register the `eleventy.after` listener that flushes the generation\nqueue at the end of the build.\n\n### Top-level options\n\n| Option | Type | Required | Default | Description |\n|---|---|---|---|---|\n| `baseImagePath` | `string` | ✅ | — | Path to the template image (JPEG or PNG). Relative paths are resolved from `process.cwd()`. |\n| `outputDir` | `string` | ✅ | — | Directory where generated share-card images are written. Created automatically if missing. |\n| `outputUrlPath` | `string` | ✅ | — | URL prefix returned in the generated image path (e.g. `\"/i/share-cards\"`). |\n| `cacheFile` | `string` | | `./_cache/share-cards.json` | Path to the JSON cache file. |\n| `verbose` | `boolean` | | `false` | Logs cache hits/misses and generation progress to the console. |\n| `imageWidth` | `number` | | `1280` | Width of `baseImagePath` in pixels (used for SVG canvas size). |\n| `imageHeight` | `number` | | `669` | Height of `baseImagePath` in pixels. |\n| `jpegQuality` | `number` | | `90` | Output JPEG quality (1–100). |\n| `layers` | `Layer[]` | ✅ | — | Text layer definitions — see below. |\n\n### Layer options\n\nEach entry in `layers` configures one text block composited over the base image.\n\n| Option | Type | Required | Default | Description |\n|---|---|---|---|---|\n| `font` | `string` | ✅ | — | CSS `font-family` value (e.g. `\"Source Serif 4\"`). |\n| `fontFallback` | `string` | | `\"serif\"` | Generic CSS font family used as a fallback when the custom font is unavailable (e.g. `\"sans-serif\"` for sans-serif faces, `\"monospace\"` for monospace). |\n| `fontPath` | `string` | | — | Path to a WOFF2, WOFF, TTF, or OTF file. When provided the font is embedded as a base64 data URI so the generator works without system fonts. Relative paths are resolved from `process.cwd()`. |\n| `fontSize` | `number` | ✅ | — | Font size in pixels. |\n| `fontWeight` | `number` | | `400` | CSS font-weight (e.g. `700` for bold, `300` for light). |\n| `color` | `string` | | `\"#000000\"` | Hex color with or without leading `#`. |\n| `x` | `number` | ✅ | — | Left edge of the text area in pixels from the left of the image. |\n| `y` | `number \\| {from, value}` | ✅ | — | Vertical position. A plain number is treated as pixels from the top. Pass `{ from: \"bottom\", value: N }` to anchor the *bottom* of the text block N pixels from the bottom of the image (like Cloudinary's `south_west` gravity). |\n| `maxWidth` | `number` | ✅ | — | Maximum text-area width in pixels. Text is word-wrapped to fit. |\n| `lineSpacing` | `number` | | `0` | Adjustment to the default `1.2×` line height in pixels. Negative values tighten lines (same semantic as Cloudinary's `line_spacing` parameter). |\n\n### Generator function signature\n\n```ts\ngenerateShareCard(texts: string[], slug: string): Promise<string>\n```\n\n| Argument | Description |\n|---|---|\n| `texts` | Array of strings, one per configured layer (in the same order). Empty strings skip that layer. |\n| `slug` | Unique identifier used as the output filename (e.g. `data.page.fileSlug`). The cache is also keyed by slug. |\n\n**Returns** the public URL path (e.g. `\"/i/share-cards/my-post.jpg\"`) or `\"\"` on error.\n\n---\n\n## Caching\n\nGenerated images are cached in two layers by slug:\n\n1. **Build queue** (per build): every `generateShareCard()` call is queued\n   instead of executed immediately.  When the same slug is queued multiple\n   times (because Eleventy re-evaluates `eleventyComputed` properties for each\n   collection access), only the **last** call's data is used — ensuring the\n   image is always generated with the most complete data.  The actual image\n   generation happens once per slug in the `eleventy.after` event at the end\n   of the build.\n2. **File cache** (`cacheFile`): persists across builds and skips generation\n   when the hash and output file still match.\n\nOn each cache miss the generator:\n\n1. Computes a SHA-256 hash of all text strings for that image.\n2. Looks up the slug in `cacheFile`.\n3. If the hash matches **and** the output file exists → returns the cached URL immediately (no image processing).\n4. Otherwise → generates the image, writes the file, and updates the cache.\n\nThis means only posts whose titles or tags have changed (or new posts) will trigger image generation on incremental builds.\n\nWith `verbose: true`, logs show cache source so you can verify behavior:\n\n- `queued share-card for \"slug\"` *(call accepted into queue)*\n- `flushing N queued share-card(s)...` *(eleventy.after flush start)*\n- `file cache hit for \"slug\"`\n- `cache miss for \"slug\" (generating...)`\n\n### Netlify / CI\n\nAdd `_cache/share-cards.json` and your `outputDir` to your Netlify cache plugin configuration to persist the cache and generated images between deploys:\n\n```toml\n# netlify.toml\n[[plugins]]\npackage = \"netlify-plugin-cache-folder\"\n```\n\n---\n\n## How text layout works\n\nThe plugin uses a **character-width estimation** algorithm rather than full font shaping. It categorises each character into one of six width buckets (narrow punctuation, wide letters like `m`/`w`, uppercase, digits, spaces, and average lowercase) and sums them to estimate line widths. This approach:\n\n- Requires no native addons or browser APIs\n- Works accurately enough for Latin titles and tags\n- Can be tuned per layer via (future) `charWidthRatio` override\n\nIf you find that a particular font renders significantly wider or narrower, open an issue or PR — the algorithm is small and easy to adjust.\n\n---\n\n## Migrating from `@jlengstorf/get-share-image`\n\n`@jlengstorf/get-share-image` generates a Cloudinary URL; this plugin generates the image itself. The layer options map directly to Cloudinary's text overlay parameters:\n\n| Cloudinary | This plugin |\n|---|---|\n| `cloudName` / `imagePublicID` | `baseImagePath` |\n| `titleFont` | `layers[0].font` |\n| `titleFontSize` | `layers[0].fontSize` |\n| `titleExtraConfig: \"_700_...\"` | `layers[0].fontWeight: 700` |\n| `titleLeftOffset` | `layers[0].x` |\n| `titleBottomOffset` | `layers[0].y: { from: \"bottom\", value: N }` |\n| `textAreaWidth` | `layers[0].maxWidth` |\n| `textColor` | `layers[0].color` |\n| `taglineGravity: \"north_west\"` | `layers[1].y: { from: \"top\", value: N }` |\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}