diff --git a/Dockerfile b/Dockerfile
index 30d3ef6a0914321ee5f17512ac337a58cb025adb..5f535e56fcfd52f84bcd94165396c80b249f6194 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,8 +5,8 @@ RUN mkdir /usr/src/app/private
 WORKDIR /usr/src/app
 
 COPY package.json package-lock.json /usr/src/app/
-# Without the gh-badges package.json and CLI script in place, `npm ci` will fail.
-COPY gh-badges /usr/src/app/gh-badges/
+# Without the badge-maker package.json and CLI script in place, `npm ci` will fail.
+COPY badge-maker /usr/src/app/badge-maker/
 
 # We need dev deps to build the front end. We don't need Cypress, though.
 RUN NODE_ENV=development CYPRESS_INSTALL_BINARY=0 npm ci
diff --git a/README.md b/README.md
index 7388bf49df8676f014b9d8a99edd95745faada02..41ff58ce5233fab2461b6e1e26d055e4c1b18070 100644
--- a/README.md
+++ b/README.md
@@ -43,16 +43,16 @@ Every month it serves over 470 million images.
 This repo hosts:
 
 - The [Shields.io][shields.io] frontend and server code
-- An [NPM library for generating badges][gh-badges]
-  - [documentation][gh-badges-docs]
-  - [changelog][gh-badges-changelog]
+- An [NPM library for generating badges][badge-maker]
+  - [documentation][badge-maker-docs]
+  - [changelog][badge-maker-changelog]
 - The [badge design specification][badge-spec]
 
 [shields.io]: https://shields.io/
-[gh-badges]: https://www.npmjs.com/package/gh-badges
+[badge-maker]: https://www.npmjs.com/package/badge-maker
 [badge-spec]: https://github.com/badges/shields/tree/master/spec
-[gh-badges-docs]: https://github.com/badges/shields/tree/master/gh-badges/README.md
-[gh-badges-changelog]: https://github.com/badges/shields/tree/master/gh-badges/CHANGELOG.md
+[badge-maker-docs]: https://github.com/badges/shields/tree/master/badge-maker/README.md
+[badge-maker-changelog]: https://github.com/badges/shields/tree/master/badge-maker/CHANGELOG.md
 
 ## Examples
 
diff --git a/__snapshots__/make-badge.spec.js b/__snapshots__/make-badge.spec.js
index cfce1a6c5675d5561f2ca1a1669af5d5c6e0deed..43ce0ff6e526ca0ea979b03700572c367218bdf7 100644
--- a/__snapshots__/make-badge.spec.js
+++ b/__snapshots__/make-badge.spec.js
@@ -1,19 +1,127 @@
-exports['The badge generator SVG should always produce the same SVG (unless we have changed something!) 1'] = `
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h45v20H0z"/><path fill="#4c1" d="M45 0h45v20H45z"/><path fill="url(#b)" d="M0 0h90v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="235" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="235" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="665" y="140" transform="scale(.1)" textLength="350">grown</text></g> </svg>
+exports['The badge generator SVG should match snapshot 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="45" height="20" fill="#555"/><rect x="45" width="45" height="20" fill="#4c1"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="235" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="665" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
 `
 
-exports['The badge generator badges with logos should always produce the same badge shields GitHub logo custom color (whitesmoke) 1'] = `
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="113" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="113" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h59v20H54z"/><path fill="url(#b)" d="M0 0h113v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="github"/> <text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">label</text><text x="365" y="140" transform="scale(.1)" textLength="270">label</text><text x="825" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">message</text><text x="825" y="140" transform="scale(.1)" textLength="490">message</text></g> </svg>
+exports['The badge generator "flat" template badge generation should match snapshots: message/label, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="45" height="20" fill="#0f0"/><rect x="45" width="45" height="20" fill="#b3e"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="235" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="665" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
 `
 
-exports['The badge generator badges with logos should always produce the same badge shields GitHub logo default color (#333333) 1'] = `
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="113" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="113" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h59v20H54z"/><path fill="url(#b)" d="M0 0h113v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="github"/> <text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">label</text><text x="365" y="140" transform="scale(.1)" textLength="270">label</text><text x="825" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">message</text><text x="825" y="140" transform="scale(.1)" textLength="490">message</text></g> </svg>
+exports['The badge generator "flat" template badge generation should match snapshots: message/label, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="107" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="107" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="62" height="20" fill="#0f0"/><rect x="62" width="45" height="20" fill="#b3e"/><rect width="107" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="405" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="405" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="835" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
 `
 
-exports['The badge generator badges with logos should always produce the same badge simple-icons javascript logo custom color (rgba(46,204,113,0.8)) 1'] = `
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="113" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="113" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h59v20H54z"/><path fill="url(#b)" d="M0 0h113v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="javascript"/> <text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">label</text><text x="365" y="140" transform="scale(.1)" textLength="270">label</text><text x="825" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">message</text><text x="825" y="140" transform="scale(.1)" textLength="490">message</text></g> </svg>
+exports['The badge generator "flat" template badge generation should match snapshots: message only, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="45" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="45" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="0" height="20" fill="#b3e"/><rect x="0" width="45" height="20" fill="#b3e"/><rect width="45" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="225" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="225" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
 `
 
-exports['The badge generator badges with logos should always produce the same badge simple-icons javascript logo default color (#F7DF1E) 1'] = `
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="113" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="113" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h59v20H54z"/><path fill="url(#b)" d="M0 0h113v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="javascript"/> <text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">label</text><text x="365" y="140" transform="scale(.1)" textLength="270">label</text><text x="825" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">message</text><text x="825" y="140" transform="scale(.1)" textLength="490">message</text></g> </svg>
+exports['The badge generator "flat" template badge generation should match snapshots: message only, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="63" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="63" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="0" height="20" fill="#555"/><rect x="0" width="63" height="20" fill="#b3e"/><rect width="63" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="405" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="405" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat" template badge generation should match snapshots: message only, with logo and labelColor 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="69" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="69" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="24" height="20" fill="#0f0"/><rect x="24" width="45" height="20" fill="#b3e"/><rect width="69" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="455" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="455" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat" template badge generation should match snapshots: message/label, with links 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="45" height="20" fill="#0f0"/><rect x="45" width="45" height="20" fill="#b3e"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="235" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="665" y="140" transform="scale(.1)" textLength="350">grown</text></g><a target="_blank" xlink:href="https://www.google.co.uk/"><rect width="NaN" height="20" fill="rgba(0,0,0,0)"/></a><a target="_blank" xlink:href="https://shields.io/"><rect width="undefined" height="20" fill="rgba(0,0,0,0)"/></a></svg>
+`
+
+exports['The badge generator "flat-square" template badge generation should match snapshots: message/label, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20"><g shape-rendering="crispEdges"><rect width="45" height="20" fill="#0f0"/><rect x="45" width="45" height="20" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat-square" template badge generation should match snapshots: message/label, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="107" height="20"><g shape-rendering="crispEdges"><rect width="62" height="20" fill="#0f0"/><rect x="62" width="45" height="20" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="405" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="835" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat-square" template badge generation should match snapshots: message only, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="45" height="20"><g shape-rendering="crispEdges"><rect width="0" height="20" fill="#b3e"/><rect x="0" width="45" height="20" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="225" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat-square" template badge generation should match snapshots: message only, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="63" height="20"><g shape-rendering="crispEdges"><rect width="0" height="20" fill="#555"/><rect x="0" width="63" height="20" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="405" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat-square" template badge generation should match snapshots: message only, with logo and labelColor 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="69" height="20"><g shape-rendering="crispEdges"><rect width="24" height="20" fill="#0f0"/><rect x="24" width="45" height="20" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="455" y="140" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "flat-square" template badge generation should match snapshots: message/label, with links 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20"><g shape-rendering="crispEdges"><rect width="45" height="20" fill="#0f0"/><rect x="45" width="45" height="20" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="140" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="140" transform="scale(.1)" textLength="350">grown</text></g><a target="_blank" xlink:href="https://www.google.co.uk/"><rect width="NaN" height="20" fill="rgba(0,0,0,0)"/></a><a target="_blank" xlink:href="https://shields.io/"><rect width="undefined" height="20" fill="rgba(0,0,0,0)"/></a></svg>
+`
+
+exports['The badge generator "plastic" template badge generation should match snapshots: message/label, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="18"><linearGradient id="s" x2="0" y2="100%"><stop offset="0"  stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1"  stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="90" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="45" height="18" fill="#0f0"/><rect x="45" width="45" height="18" fill="#b3e"/><rect width="90" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="235" y="130" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="665" y="130" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "plastic" template badge generation should match snapshots: message/label, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="107" height="18"><linearGradient id="s" x2="0" y2="100%"><stop offset="0"  stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1"  stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="107" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="62" height="18" fill="#0f0"/><rect x="62" width="45" height="18" fill="#b3e"/><rect width="107" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="2" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="405" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="405" y="130" transform="scale(.1)" textLength="350">cactus</text><text x="835" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="835" y="130" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "plastic" template badge generation should match snapshots: message only, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="45" height="18"><linearGradient id="s" x2="0" y2="100%"><stop offset="0"  stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1"  stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="45" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="0" height="18" fill="#b3e"/><rect x="0" width="45" height="18" fill="#b3e"/><rect width="45" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="225" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="225" y="130" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "plastic" template badge generation should match snapshots: message only, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="63" height="18"><linearGradient id="s" x2="0" y2="100%"><stop offset="0"  stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1"  stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="63" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="0" height="18" fill="#555"/><rect x="0" width="63" height="18" fill="#b3e"/><rect width="63" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="2" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="405" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="405" y="130" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "plastic" template badge generation should match snapshots: message only, with logo and labelColor 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="69" height="18"><linearGradient id="s" x2="0" y2="100%"><stop offset="0"  stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1"  stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="69" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="24" height="18" fill="#0f0"/><rect x="24" width="45" height="18" fill="#b3e"/><rect width="69" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="2" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="455" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="455" y="130" transform="scale(.1)" textLength="350">grown</text></g></svg>
+`
+
+exports['The badge generator "plastic" template badge generation should match snapshots: message/label, with links 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="18"><linearGradient id="s" x2="0" y2="100%"><stop offset="0"  stop-color="#fff" stop-opacity=".7"/><stop offset=".1" stop-color="#aaa" stop-opacity=".1"/><stop offset=".9" stop-color="#000" stop-opacity=".3"/><stop offset="1"  stop-color="#000" stop-opacity=".5"/></linearGradient><clipPath id="r"><rect width="90" height="18" rx="4" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="45" height="18" fill="#0f0"/><rect x="45" width="45" height="18" fill="#b3e"/><rect width="90" height="18" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="235" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">cactus</text><text x="235" y="130" transform="scale(.1)" textLength="350">cactus</text><text x="665" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">grown</text><text x="665" y="130" transform="scale(.1)" textLength="350">grown</text></g><a target="_blank" xlink:href="https://www.google.co.uk/"><rect width="NaN" height="18" fill="rgba(0,0,0,0)"/></a><a target="_blank" xlink:href="https://shields.io/"><rect width="undefined" height="18" fill="rgba(0,0,0,0)"/></a></svg>
+`
+
+exports['The badge generator "for-the-badge" template badge generation should match snapshots: message/label, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="147" height="28"><g shape-rendering="crispEdges"><rect width="74" height="28" fill="#0f0"/><rect x="74" width="73" height="28" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><text x="370" y="175" transform="scale(.1)" textLength="500">CACTUS</text><text x="1105" y="175" font-weight="bold" transform="scale(.1)" textLength="490">GROWN</text></g></svg>
+`
+
+exports['The badge generator "for-the-badge" template badge generation should match snapshots: message/label, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="164" height="28"><g shape-rendering="crispEdges"><rect width="91" height="28" fill="#0f0"/><rect x="91" width="73" height="28" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="540" y="175" transform="scale(.1)" textLength="500">CACTUS</text><text x="1275" y="175" font-weight="bold" transform="scale(.1)" textLength="490">GROWN</text></g></svg>
+`
+
+exports['The badge generator "for-the-badge" template badge generation should match snapshots: message only, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="73" height="28"><g shape-rendering="crispEdges"><rect width="0" height="28" fill="#b3e"/><rect x="0" width="73" height="28" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><text x="365" y="175" font-weight="bold" transform="scale(.1)" textLength="490">GROWN</text></g></svg>
+`
+
+exports['The badge generator "for-the-badge" template badge generation should match snapshots: message only, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="91" height="28"><g shape-rendering="crispEdges"><rect width="0" height="28" fill="#555"/><rect x="0" width="91" height="28" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="545" y="175" font-weight="bold" transform="scale(.1)" textLength="490">GROWN</text></g></svg>
+`
+
+exports['The badge generator "for-the-badge" template badge generation should match snapshots: message only, with logo and labelColor 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="105" height="28"><g shape-rendering="crispEdges"><rect width="32" height="28" fill="#0f0"/><rect x="32" width="73" height="28" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="230" y="175" transform="scale(.1)" textLength="-60"></text><text x="685" y="175" font-weight="bold" transform="scale(.1)" textLength="490">GROWN</text></g></svg>
+`
+
+exports['The badge generator "for-the-badge" template badge generation should match snapshots: message/label, with links 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="147" height="28"><g shape-rendering="crispEdges"><rect width="74" height="28" fill="#0f0"/><rect x="74" width="73" height="28" fill="#b3e"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><text x="370" y="175" transform="scale(.1)" textLength="500">CACTUS</text><text x="1105" y="175" font-weight="bold" transform="scale(.1)" textLength="490">GROWN</text></g><a target="_blank" xlink:href="https://www.google.co.uk/"><rect width="NaN" height="28" fill="rgba(0,0,0,0)"/></a><a target="_blank" xlink:href="https://shields.io/"><rect width="undefined" height="28" fill="rgba(0,0,0,0)"/></a></svg>
+`
+
+exports['The badge generator "social" template badge generation should match snapshots: message/label, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="95" height="20"><style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="47" height="19" rx="2"/><rect x="53.5" y="0.5" width="41" height="19" rx="2" fill="#fafafa"/><rect x="53" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M53.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px"><text x="235" y="150" fill="#fff" transform="scale(.1)" textLength="370">Cactus</text><text x="235" y="140" transform="scale(.1)" textLength="370">Cactus</text><text x="735" y="150" fill="#fff" transform="scale(.1)" textLength="330">grown</text><text id="rlink" x="735" y="140" transform="scale(.1)" textLength="330">grown</text></g><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="47" height="19" rx="2" /></svg>
+`
+
+exports['The badge generator "social" template badge generation should match snapshots: message/label, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="112" height="20"><style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="64" height="19" rx="2"/><rect x="70.5" y="0.5" width="41" height="19" rx="2" fill="#fafafa"/><rect x="70" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M70.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px"><text x="405" y="150" fill="#fff" transform="scale(.1)" textLength="370">Cactus</text><text x="405" y="140" transform="scale(.1)" textLength="370">Cactus</text><text x="905" y="150" fill="#fff" transform="scale(.1)" textLength="330">grown</text><text id="rlink" x="905" y="140" transform="scale(.1)" textLength="330">grown</text></g><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="64" height="19" rx="2" /></svg>
+`
+
+exports['The badge generator "social" template badge generation should match snapshots: message only, no logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="59" height="20"><style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="11" height="19" rx="2"/><rect x="17.5" y="0.5" width="41" height="19" rx="2" fill="#fafafa"/><rect x="17" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M17.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px"><text x="55" y="150" fill="#fff" transform="scale(.1)" textLength="10"></text><text x="55" y="140" transform="scale(.1)" textLength="10"></text><text x="375" y="150" fill="#fff" transform="scale(.1)" textLength="330">grown</text><text id="rlink" x="375" y="140" transform="scale(.1)" textLength="330">grown</text></g><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="11" height="19" rx="2" /></svg>
+`
+
+exports['The badge generator "social" template badge generation should match snapshots: message only, with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="73" height="20"><style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="25" height="19" rx="2"/><rect x="31.5" y="0.5" width="41" height="19" rx="2" fill="#fafafa"/><rect x="31" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M31.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px"><text x="195" y="150" fill="#fff" transform="scale(.1)" textLength="10"></text><text x="195" y="140" transform="scale(.1)" textLength="10"></text><text x="515" y="150" fill="#fff" transform="scale(.1)" textLength="330">grown</text><text id="rlink" x="515" y="140" transform="scale(.1)" textLength="330">grown</text></g><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="25" height="19" rx="2" /></svg>
+`
+
+exports['The badge generator "social" template badge generation should match snapshots: message only, with logo and labelColor 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="73" height="20"><style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="25" height="19" rx="2"/><rect x="31.5" y="0.5" width="41" height="19" rx="2" fill="#fafafa"/><rect x="31" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M31.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px"><text x="195" y="150" fill="#fff" transform="scale(.1)" textLength="10"></text><text x="195" y="140" transform="scale(.1)" textLength="10"></text><text x="515" y="150" fill="#fff" transform="scale(.1)" textLength="330">grown</text><text id="rlink" x="515" y="140" transform="scale(.1)" textLength="330">grown</text></g><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="25" height="19" rx="2" /></svg>
+`
+
+exports['The badge generator "social" template badge generation should match snapshots: message/label, with links 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="95" height="20"><style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="47" height="19" rx="2"/><rect x="53.5" y="0.5" width="41" height="19" rx="2" fill="#fafafa"/><rect x="53" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M53.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px"><text x="235" y="150" fill="#fff" transform="scale(.1)" textLength="370">Cactus</text><text x="235" y="140" transform="scale(.1)" textLength="370">Cactus</text><text x="735" y="150" fill="#fff" transform="scale(.1)" textLength="330">grown</text><a target="_blank" xlink:href="https://www.google.co.uk/"><text id="rlink" x="735" y="140" transform="scale(.1)" textLength="330">grown</text></a></g><a target="_blank" xlink:href="https://shields.io/"><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="47" height="19" rx="2" /></a></svg>
+`
+
+exports['The badge generator badges with logos should always produce the same badge badge with logo 1'] = `
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="113" height="20"><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="113" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="54" height="20" fill="#555"/><rect x="54" width="59" height="20" fill="#4c1"/><rect width="113" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxu"/><text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">label</text><text x="365" y="140" transform="scale(.1)" textLength="270">label</text><text x="825" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">message</text><text x="825" y="140" transform="scale(.1)" textLength="490">message</text></g></svg>
 `
diff --git a/gh-badges/.npmignore b/badge-maker/.npmignore
similarity index 100%
rename from gh-badges/.npmignore
rename to badge-maker/.npmignore
diff --git a/gh-badges/CHANGELOG.md b/badge-maker/CHANGELOG.md
similarity index 67%
rename from gh-badges/CHANGELOG.md
rename to badge-maker/CHANGELOG.md
index eb3dc7be203593815a359a3a9885ab978842985d..7701d5e4fa47f7bf8a173dce62c5a195ab9f02e4 100644
--- a/gh-badges/CHANGELOG.md
+++ b/badge-maker/CHANGELOG.md
@@ -1,6 +1,67 @@
 # Changelog
 
-## 2.2.1
+## 3.0.0
+
+### Breaking Changes
+
+- Package name has changed to `badge-maker` and moved to https://www.npmjs.com/package/badge-maker
+- `BadgeFactory` class is removed and replaced by `makeBadge()` function.
+- Deprecated parameters have been removed. In version 2.2.0 the `colorA`, `colorB` and `colorscheme` params were deprecated. In version 3.0.0 these have been removed.
+- Only SVG output format is now provided. JSON format has been dropped and the `format` key has been removed.
+- The `text` array has been replaced by `label` and `message` keys.
+- The `template` key has been renamed `style`.
+  To upgrade from v2.1.1, change your code from:
+  ```js
+  const { BadgeFactory } = require('gh-badges')
+  const bf = new BadgeFactory()
+  const svg = bf.create({
+    text: ['build', 'passed'],
+    format: 'svg',
+    template: 'flat-square',
+  })
+  ```
+  to:
+  ```js
+  const { makeBadge } = require('badge-maker')
+  const svg = makeBadge({
+    label: 'build',
+    message: 'passed',
+    style: 'flat-square',
+  })
+  ```
+- `ValidationError` had been added and inputs are now validated. In previous releases, invalid inputs would be discarded and replaced with defaults. For example, in 2.2.1
+  ```js
+  const { BadgeFactory } = require('gh-badges')
+  const bf = new BadgeFactory()
+  const svg = bf.create({
+    text: ['build', 'passed'],
+    template: 'some invalid value',
+  })
+  ```
+  would generate an SVG badge. In version >=3
+  ```js
+  const { makeBadge } = require('badge-maker')
+  const svg = makeBadge({
+    label: 'build',
+    message: 'passed',
+    style: 'some invalid value',
+  })
+  ```
+  will throw a `ValidationError`.
+- Raster support has been removed from the CLI. It will now only output SVG. On the console, the output of `badge` can be piped to a utility like [imagemagick](https://imagemagick.org/script/command-line-processing.php). If you were previously using
+  ```sh
+  badge build passed :green .gif
+  ```
+  this could be replaced by
+  ```sh
+  badge build passed :green | magick svg:- gif:-
+  ```
+
+### Security
+
+- Removed dependency on doT library which has known vulnerabilities.
+
+## 2.2.1 - 2019-05-30
 
 ### Fixes
 
diff --git a/gh-badges/LICENSE b/badge-maker/LICENSE
similarity index 100%
rename from gh-badges/LICENSE
rename to badge-maker/LICENSE
diff --git a/gh-badges/README.md b/badge-maker/README.md
similarity index 65%
rename from gh-badges/README.md
rename to badge-maker/README.md
index 048b6e5c2531b6d844be3e12e15dc10b84e451e3..bb523ae554c296d8e9ee84ea77e0bf30cf4754b3 100644
--- a/gh-badges/README.md
+++ b/badge-maker/README.md
@@ -1,12 +1,12 @@
-# gh-badges
+# badge-maker
 
-[![npm version](https://img.shields.io/npm/v/gh-badges.svg)](https://npmjs.org/package/gh-badges)
-[![npm license](https://img.shields.io/npm/l/gh-badges.svg)](https://npmjs.org/package/gh-badges)
+[![npm version](https://img.shields.io/npm/v/badge-maker.svg)](https://npmjs.org/package/badge-maker)
+[![npm license](https://img.shields.io/npm/l/badge-maker.svg)](https://npmjs.org/package/badge-maker)
 
 ## Installation
 
 ```sh
-npm install gh-badges
+npm install badge-maker
 ```
 
 ## Usage
@@ -14,29 +14,34 @@ npm install gh-badges
 ### On the console
 
 ```sh
-npm install -g gh-badges
-badge build passed :green .png > mybadge.png
+npm install -g badge-maker
+badge build passed :green > mybadge.svg
 ```
 
 ### As a library
 
 ```js
-const { BadgeFactory } = require('gh-badges')
-
-const bf = new BadgeFactory()
+const { makeBadge, ValidationError } = require('badge-maker')
 
 const format = {
-  text: ['build', 'passed'],
+  label: 'build',
+  message: 'passed',
   color: 'green',
-  template: 'flat',
 }
 
-const svg = bf.create(format)
+const svg = makeBadge(format)
+console.log(svg) // <svg...
+
+try {
+  makeBadge({})
+} catch (e) {
+  console.log(e) // ValidationError: Field `message` is required
+}
 ```
 
 ### Node version support
 
-The latest version of gh-badges supports all currently maintained Node
+The latest version of badge-maker supports all currently maintained Node
 versions. See the [Node Release Schedule][].
 
 [node release schedule]: https://github.com/nodejs/Release#release-schedule
@@ -47,28 +52,17 @@ The format is the following:
 
 ```js
 {
-  text: [ 'build', 'passed' ],  // Textual information shown, in order
-
-  format: 'svg',  // Also supports json
-
-  color: '#4c1',
-  labelColor: '#555',
+  label: 'build',  // (Optional) Badge label
+  message: 'passed',  // (Required) Badge message
+  labelColor: '#555',  // (Optional) Label color
+  color: '#4c1',  // (Optional) Message color
 
-  // See templates/ for a list of available templates.
+  // (Optional) One of: 'plastic', 'flat', 'flat-square', 'for-the-badge' or 'social'
   // Each offers a different visual design.
-  template: 'flat',
-
-  // Deprecated attributes:
-  colorscheme: 'green', // Now an alias for `color`.
-  colorB: '#4c1', // Now an alias for `color`.
-  colorA: '#555', // Now an alias for `labelColor`.
+  style: 'flat',
 }
 ```
 
-### See also
-
-- [templates/](./templates) for the `template` option
-
 ## Colors
 
 There are three ways to specify `color` and `labelColor`:
@@ -126,3 +120,12 @@ There are three ways to specify `color` and `labelColor`:
 [lightslategray]: https://img.shields.io/badge/lightslategray-lightslategray.svg
 [css color]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
 [css/svg color]: http://www.w3.org/TR/SVG/types.html#DataTypeColor
+
+## Raster Formats
+
+Conversion to raster formats is no longer directly supported. In javascript
+code, SVG badges can be converted to raster formats using a library like
+[gm](https://www.npmjs.com/package/gm). On the console, the output of `badge`
+can be piped to a utility like
+[imagemagick](https://imagemagick.org/script/command-line-processing.php)
+e.g: `badge build passed :green | magick svg:- gif:-`.
diff --git a/gh-badges/lib/badge-cli.js b/badge-maker/lib/badge-cli.js
similarity index 50%
rename from gh-badges/lib/badge-cli.js
rename to badge-maker/lib/badge-cli.js
index 58d3a61e2129b826a9c72cda84548073fb566bf7..a930a01e4c867d7b4aba04874817a3abb3f7a79c 100755
--- a/gh-badges/lib/badge-cli.js
+++ b/badge-maker/lib/badge-cli.js
@@ -2,23 +2,18 @@
 
 'use strict'
 
-const makeBadge = require('./make-badge')
-const svg2img = require('./svg-to-img')
 const { namedColors } = require('./color')
+const { makeBadge } = require('./index')
 
 if (process.argv.length < 4) {
-  console.log('Usage: badge subject status [:color] [.output] [@style]')
-  console.log(
-    'Or:    badge subject status color [labelColor] [.output] [@style]'
-  )
+  console.log('Usage: badge label message [:color] [@style]')
+  console.log('Or:    badge label message color [labelColor] [@style]')
   console.log()
   console.log('  color, labelColor:')
   console.log(`    one of ${Object.keys(namedColors).join(', ')}.`)
   console.log('    #xxx (three hex digits)')
   console.log('    #xxxxxx (six hex digits)')
   console.log('    color (CSS color)')
-  console.log('  output:')
-  console.log('    svg, png, jpg, or gif')
   console.log()
   console.log('Eg: badge cactus grown :green @flat')
   console.log()
@@ -26,14 +21,8 @@ if (process.argv.length < 4) {
 }
 
 // Find a format specifier.
-let format = 'svg'
 let style = ''
 for (let i = 4; i < process.argv.length; i++) {
-  if (process.argv[i][0] === '.') {
-    format = process.argv[i].slice(1)
-    process.argv.splice(i, 1)
-    continue
-  }
   if (process.argv[i][0] === '@') {
     style = process.argv[i].slice(1)
     process.argv.splice(i, 1)
@@ -41,14 +30,14 @@ for (let i = 4; i < process.argv.length; i++) {
   }
 }
 
-const subject = process.argv[2]
-const status = process.argv[3]
+const label = process.argv[2]
+const message = process.argv[3]
 let color = process.argv[4] || ':green'
-const colorA = process.argv[5]
+const labelColor = process.argv[5]
 
-const badgeData = { text: [subject, status], format }
+const badgeData = { label, message }
 if (style) {
-  badgeData.template = style
+  badgeData.style = style
 }
 
 if (color[0] === ':') {
@@ -58,28 +47,17 @@ if (color[0] === ':') {
     console.error('Invalid color scheme.')
     process.exit(1)
   }
-  badgeData.colorscheme = color
+  badgeData.color = color
 } else {
-  badgeData.colorB = color
-  if (colorA) {
-    badgeData.colorA = colorA
-  }
-}
-
-async function main() {
-  const svg = makeBadge(badgeData)
-
-  if (/png|jpg|gif/.test(format)) {
-    const data = await svg2img(svg, format)
-    process.stdout.write(data)
-  } else {
-    console.log(svg)
+  badgeData.color = color
+  if (labelColor) {
+    badgeData.labelColor = labelColor
   }
 }
 
-;(async () => {
+;(() => {
   try {
-    await main()
+    console.log(makeBadge(badgeData))
   } catch (e) {
     console.error(e)
     process.exit(1)
diff --git a/gh-badges/lib/badge-cli.spec.js b/badge-maker/lib/badge-cli.spec.js
similarity index 74%
rename from gh-badges/lib/badge-cli.spec.js
rename to badge-maker/lib/badge-cli.spec.js
index 4c0478264d8968366c9e988427a34c574e57d195..b7c7eac69bc69d16c53f1e2ecc9f216cbd7f4ca0 100644
--- a/gh-badges/lib/badge-cli.spec.js
+++ b/badge-maker/lib/badge-cli.spec.js
@@ -1,7 +1,6 @@
 'use strict'
 
 const path = require('path')
-const isPng = require('is-png')
 const isSvg = require('is-svg')
 const { spawn } = require('child-process-promise')
 const { expect, use } = require('chai')
@@ -39,19 +38,4 @@ describe('The CLI', function() {
       .to.satisfy(isSvg)
       .and.to.include('#abcdef')
   })
-
-  it('should produce PNG badges', async function() {
-    const child = runCli(['cactus', 'grown', '.png'])
-
-    // The buffering done by `child-process-promise` doesn't seem correctly to
-    // handle binary data.
-    let chunk
-    child.childProcess.stdout.once('data', data => {
-      chunk = data
-    })
-
-    await child
-
-    expect(chunk).to.satisfy(isPng)
-  })
 })
diff --git a/badge-maker/lib/badge-renderers.js b/badge-maker/lib/badge-renderers.js
new file mode 100644
index 0000000000000000000000000000000000000000..ffa2483c55a9decd91ca6b895c483fcaa6cd0cbf
--- /dev/null
+++ b/badge-maker/lib/badge-renderers.js
@@ -0,0 +1,611 @@
+'use strict'
+
+const anafanafo = require('anafanafo')
+
+const fontFamily = 'font-family="DejaVu Sans,Verdana,Geneva,sans-serif"'
+const socialFontFamily =
+  'font-family="Helvetica Neue,Helvetica,Arial,sans-serif"'
+
+function capitalize(s) {
+  return `${s.charAt(0).toUpperCase()}${s.slice(1)}`
+}
+
+function escapeXml(s) {
+  if (s === undefined || typeof s !== 'string') {
+    return undefined
+  } else {
+    return s
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&apos;')
+  }
+}
+
+function roundUpToOdd(val) {
+  // Increase chances of pixel grid alignment.
+  return val % 2 === 0 ? val + 1 : val
+}
+
+function preferredWidthOf(str) {
+  return roundUpToOdd((anafanafo(str) / 10) | 0)
+}
+
+function computeWidths({ label, message }) {
+  return {
+    labelWidth: preferredWidthOf(label),
+    messageWidth: preferredWidthOf(message),
+  }
+}
+
+function renderLogo({
+  logo,
+  badgeHeight,
+  horizPadding,
+  logoWidth = 14,
+  logoPadding = 0,
+}) {
+  if (!logo) {
+    return {
+      hasLogo: false,
+      totalLogoWidth: 0,
+      renderedLogo: '',
+    }
+  }
+  const y = (badgeHeight - logoWidth) / 2
+  const x = horizPadding
+  return {
+    hasLogo: true,
+    totalLogoWidth: logoWidth + logoPadding,
+    renderedLogo: `<image x="${x}" y="${y}" width="${logoWidth}" height="14" xlink:href="${escapeXml(
+      logo
+    )}"/>`,
+  }
+}
+
+function renderText({
+  leftMargin,
+  horizPadding = 0,
+  content,
+  verticalMargin = 0,
+  shadow = false,
+}) {
+  if (!content.length) {
+    return { renderedText: '', width: 0 }
+  }
+
+  const textLength = preferredWidthOf(content)
+  const escapedContent = escapeXml(content)
+
+  const shadowMargin = 150 + verticalMargin
+  const textMargin = 140 + verticalMargin
+
+  const outTextLength = 10 * textLength
+  const x = 10 * (leftMargin + 0.5 * textLength + horizPadding)
+
+  let renderedText = ''
+  if (shadow) {
+    renderedText = `<text x="${x}" y="${shadowMargin}" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${outTextLength}">${escapedContent}</text>`
+  }
+  renderedText += `<text x="${x}" y="${textMargin}" transform="scale(.1)" textLength="${outTextLength}">${escapedContent}</text>`
+
+  return {
+    renderedText,
+    width: textLength,
+  }
+}
+
+function renderLinks({
+  links: [leftLink, rightLink] = [],
+  labelWidth,
+  messageWidth,
+  height,
+}) {
+  leftLink = escapeXml(leftLink)
+  rightLink = escapeXml(rightLink)
+  const hasLeftLink = leftLink && leftLink.length
+  const hasRightLink = rightLink && rightLink.length
+  const leftLinkWidth = hasRightLink ? labelWidth : labelWidth + messageWidth
+
+  function render({ link, width }) {
+    return `<a target="_blank" xlink:href="${link}"><rect width="${width}" height="${height}" fill="rgba(0,0,0,0)"/></a>`
+  }
+
+  return (
+    (hasRightLink
+      ? render({ link: rightLink, width: labelWidth + messageWidth })
+      : '') +
+    (hasLeftLink ? render({ link: leftLink, width: leftLinkWidth }) : '')
+  )
+}
+
+function renderBadge({ links, leftWidth, rightWidth, height }, main) {
+  const width = leftWidth + rightWidth
+  return `
+    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}">
+    ${main}
+    ${renderLinks({ links, leftWidth, rightWidth, height })}
+    </svg>`
+}
+
+function stripXmlWhitespace(xml) {
+  return xml
+    .replace(/>\s+/g, '>')
+    .replace(/<\s+/g, '<')
+    .trim()
+}
+
+class Badge {
+  static get fontFamily() {
+    throw new Error('Not implemented')
+  }
+
+  static get height() {
+    throw new Error('Not implemented')
+  }
+
+  static get verticalMargin() {
+    throw new Error('Not implemented')
+  }
+
+  static get shadow() {
+    throw new Error('Not implemented')
+  }
+
+  constructor({
+    label,
+    message,
+    links,
+    logo,
+    logoWidth,
+    logoPadding,
+    color = '#4c1',
+    labelColor,
+  }) {
+    const horizPadding = 5
+    const { hasLogo, totalLogoWidth, renderedLogo } = renderLogo({
+      logo,
+      badgeHeight: this.constructor.height,
+      horizPadding,
+      logoWidth,
+      logoPadding,
+    })
+    const hasLabel = label.length || labelColor
+    if (labelColor == null) {
+      labelColor = '#555'
+    }
+
+    labelColor = hasLabel || hasLogo ? labelColor : color
+    labelColor = escapeXml(labelColor)
+    color = escapeXml(color)
+
+    const labelMargin = totalLogoWidth + 1
+
+    const { renderedText: renderedLabel, width: labelWidth } = renderText({
+      leftMargin: labelMargin,
+      horizPadding,
+      content: label,
+      verticalMargin: this.constructor.verticalMargin,
+      shadow: this.constructor.shadow,
+    })
+
+    const leftWidth = hasLabel
+      ? labelWidth + 2 * horizPadding + totalLogoWidth
+      : 0
+
+    let messageMargin = leftWidth - (message.length ? 1 : 0)
+    if (!hasLabel) {
+      if (hasLogo) {
+        messageMargin = messageMargin + totalLogoWidth + horizPadding
+      } else {
+        messageMargin = messageMargin + 1
+      }
+    }
+
+    const { renderedText: renderedMessage, width: messageWidth } = renderText({
+      leftMargin: messageMargin,
+      horizPadding,
+      content: message,
+      verticalMargin: this.constructor.verticalMargin,
+      shadow: this.constructor.shadow,
+    })
+
+    let rightWidth = messageWidth + 2 * horizPadding
+    if (hasLogo && !hasLabel) {
+      rightWidth += totalLogoWidth + horizPadding - 1
+    }
+
+    const width = leftWidth + rightWidth
+
+    this.links = links
+    this.leftWidth = leftWidth
+    this.rightWidth = rightWidth
+    this.width = width
+    this.labelColor = labelColor
+    this.color = color
+    this.renderedLogo = renderedLogo
+    this.renderedLabel = renderedLabel
+    this.renderedMessage = renderedMessage
+  }
+
+  render() {
+    throw new Error('Not implemented')
+  }
+}
+
+class Plastic extends Badge {
+  static get fontFamily() {
+    return fontFamily
+  }
+
+  static get height() {
+    return 18
+  }
+
+  static get verticalMargin() {
+    return -10
+  }
+
+  static get shadow() {
+    return true
+  }
+
+  render() {
+    return renderBadge(
+      {
+        links: this.links,
+        leftWidth: this.leftWidth,
+        rightWidth: this.rightWidth,
+        height: this.constructor.height,
+      },
+      `
+      <linearGradient id="s" x2="0" y2="100%">
+        <stop offset="0"  stop-color="#fff" stop-opacity=".7"/>
+        <stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
+        <stop offset=".9" stop-color="#000" stop-opacity=".3"/>
+        <stop offset="1"  stop-color="#000" stop-opacity=".5"/>
+      </linearGradient>
+
+      <clipPath id="r">
+        <rect width="${this.width}" height="${this.constructor.height}" rx="4" fill="#fff"/>
+      </clipPath>
+
+      <g clip-path="url(#r)">
+        <rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
+        <rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
+        <rect width="${this.width}" height="${this.constructor.height}" fill="url(#s)"/>
+      </g>
+
+      <g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} font-size="110">
+        ${this.renderedLogo}
+        ${this.renderedLabel}
+        ${this.renderedMessage}
+      </g>`
+    )
+  }
+}
+
+class Flat extends Badge {
+  static get fontFamily() {
+    return fontFamily
+  }
+
+  static get height() {
+    return 20
+  }
+
+  static get verticalMargin() {
+    return 0
+  }
+
+  static get shadow() {
+    return true
+  }
+
+  render() {
+    return renderBadge(
+      {
+        links: this.links,
+        leftWidth: this.leftWidth,
+        rightWidth: this.rightWidth,
+        height: this.constructor.height,
+      },
+      `
+      <linearGradient id="s" x2="0" y2="100%">
+        <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
+        <stop offset="1" stop-opacity=".1"/>
+      </linearGradient>
+
+      <clipPath id="r">
+        <rect width="${this.width}" height="${this.constructor.height}" rx="3" fill="#fff"/>
+      </clipPath>
+
+      <g clip-path="url(#r)">
+        <rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
+        <rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
+        <rect width="${this.width}" height="${this.constructor.height}" fill="url(#s)"/>
+      </g>
+
+      <g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} font-size="110">
+        ${this.renderedLogo}
+        ${this.renderedLabel}
+        ${this.renderedMessage}
+      </g>`
+    )
+  }
+}
+
+class FlatSquare extends Badge {
+  static get fontFamily() {
+    return fontFamily
+  }
+
+  static get height() {
+    return 20
+  }
+
+  static get verticalMargin() {
+    return 0
+  }
+
+  static get shadow() {
+    return false
+  }
+
+  render() {
+    return renderBadge(
+      {
+        links: this.links,
+        leftWidth: this.leftWidth,
+        rightWidth: this.rightWidth,
+        height: this.constructor.height,
+      },
+      `
+      <g shape-rendering="crispEdges">
+        <rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
+        <rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
+      </g>
+
+      <g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} font-size="110">
+        ${this.renderedLogo}
+        ${this.renderedLabel}
+        ${this.renderedMessage}
+      </g>`
+    )
+  }
+}
+
+function plastic(params) {
+  const badge = new Plastic(params)
+  if (params.minify) {
+    return stripXmlWhitespace(badge.render())
+  }
+  return badge.render()
+}
+
+function flat(params) {
+  const badge = new Flat(params)
+  if (params.minify) {
+    return stripXmlWhitespace(badge.render())
+  }
+  return badge.render()
+}
+
+function flatSquare(params) {
+  const badge = new FlatSquare(params)
+  if (params.minify) {
+    return stripXmlWhitespace(badge.render())
+  }
+  return badge.render()
+}
+
+function social({
+  label,
+  message,
+  links = [],
+  logo,
+  logoWidth,
+  logoPadding,
+  color = '#4c1',
+  labelColor = '#555',
+  minify,
+}) {
+  // Social label is styled with a leading capital. Convert to caps here so
+  // width can be measured using the correct characters.
+  label = capitalize(label)
+
+  const externalHeight = 20
+  const internalHeight = 19
+  const horizPadding = 5
+  const { totalLogoWidth, renderedLogo } = renderLogo({
+    logo,
+    badgeHeight: externalHeight,
+    horizPadding,
+    logoWidth,
+    logoPadding,
+  })
+  const hasMessage = message.length
+
+  let { labelWidth, messageWidth } = computeWidths({ label, message })
+  labelWidth += 10 + totalLogoWidth
+  messageWidth += 10
+  messageWidth -= 4
+
+  const labelTextX = ((labelWidth + totalLogoWidth) / 2) * 10
+  const labelTextLength = (labelWidth - (10 + totalLogoWidth)) * 10
+  const escapedLabel = escapeXml(label)
+
+  let [leftLink, rightLink] = links
+  leftLink = escapeXml(leftLink)
+  rightLink = escapeXml(rightLink)
+  const hasLeftLink = leftLink && leftLink.length
+  const hasRightLink = rightLink && rightLink.length
+
+  function renderMessageBubble() {
+    const messageBubbleMainX = labelWidth + 6.5
+    const messageBubbleNotchX = labelWidth + 6
+    return `
+      <rect x="${messageBubbleMainX}" y="0.5" width="${messageWidth}" height="${internalHeight}" rx="2" fill="#fafafa"/>
+      <rect x="${messageBubbleNotchX}" y="7.5" width="0.5" height="5" stroke="#fafafa"/>
+      <path d="M${messageBubbleMainX} 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/>
+    `
+  }
+
+  function renderMessageText() {
+    const messageTextX = (labelWidth + messageWidth / 2 + 6) * 10
+    const messageTextLength = (messageWidth - 8) * 10
+    const escapedMessage = escapeXml(message)
+    const shadow = `<text x="${messageTextX}" y="150" fill="#fff" transform="scale(.1)" textLength="${messageTextLength}">${escapedMessage}</text>`
+    const text = `<text id="rlink" x="${messageTextX}" y="140" transform="scale(.1)" textLength="${messageTextLength}">${escapedMessage}</text>`
+    if (hasRightLink) {
+      return `
+      ${shadow}
+      <a target="_blank" xlink:href="${rightLink}">${text}</a>
+      `
+    }
+    return `
+      ${shadow}
+      ${text}
+    `
+  }
+
+  function renderLeftLink() {
+    const rect = `<rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="${labelWidth}" height="${internalHeight}" rx="2" />`
+    if (hasLeftLink) {
+      return `<a target="_blank" xlink:href="${leftLink}">${rect}</a>`
+    }
+    return rect
+  }
+
+  const badge = renderBadge(
+    {
+      links: [],
+      leftWidth: labelWidth + 1,
+      rightWidth: hasMessage ? messageWidth + 6 : 0,
+      height: externalHeight,
+    },
+    `
+    <style>a #llink:hover{fill:url(#b);stroke:#ccc}a #rlink:hover{fill:#4183c4}</style>
+    <linearGradient id="a" x2="0" y2="100%">
+      <stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/>
+      <stop offset="1" stop-opacity=".1"/>
+    </linearGradient>
+    <linearGradient id="b" x2="0" y2="100%">
+      <stop offset="0" stop-color="#ccc" stop-opacity=".1"/>
+      <stop offset="1" stop-opacity=".1"/>
+    </linearGradient>
+    <g stroke="#d5d5d5">
+      <rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="${labelWidth}" height="${internalHeight}" rx="2"/>
+      ${hasMessage ? renderMessageBubble() : ''}
+    </g>
+    ${renderedLogo}
+    <g fill="#333" text-anchor="middle" ${socialFontFamily} font-weight="700" font-size="110px" line-height="14px">
+      <text x="${labelTextX}" y="150" fill="#fff" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>
+      <text x="${labelTextX}" y="140" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>
+      ${hasMessage ? renderMessageText() : ''}
+    </g>
+    ${renderLeftLink()}
+    `
+  )
+
+  if (minify) {
+    return stripXmlWhitespace(badge)
+  }
+  return badge
+}
+
+function forTheBadge({
+  label,
+  message,
+  links,
+  logo,
+  logoWidth,
+  logoPadding,
+  color = '#4c1',
+  labelColor,
+  minify,
+}) {
+  // For the Badge is styled in all caps. Convert to caps here so widths can
+  // be measured using the correct characters.
+  label = label.toUpperCase()
+  message = message.toUpperCase()
+
+  let { labelWidth, messageWidth } = computeWidths({ label, message })
+  const height = 28
+  const hasLabel = label.length || labelColor
+  if (labelColor == null) {
+    labelColor = '#555'
+  }
+  const horizPadding = 9
+  const { hasLogo, totalLogoWidth, renderedLogo } = renderLogo({
+    logo,
+    badgeHeight: height,
+    horizPadding,
+    logoWidth,
+    logoPadding,
+  })
+
+  labelWidth += 10 + totalLogoWidth
+  if (label.length) {
+    labelWidth += 10 + label.length * 1.5
+  } else if (hasLogo) {
+    if (hasLabel) {
+      labelWidth += 7
+    } else {
+      labelWidth -= 7
+    }
+  } else {
+    labelWidth -= 11
+  }
+
+  messageWidth += 10
+  messageWidth += 10 + message.length * 2
+  const leftWidth = hasLogo && !hasLabel ? 0 : labelWidth
+  const rightWidth =
+    hasLogo && !hasLabel ? messageWidth + labelWidth : messageWidth
+
+  labelColor = hasLabel || hasLogo ? labelColor : color
+
+  color = escapeXml(color)
+  labelColor = escapeXml(labelColor)
+
+  function renderLabelText() {
+    const labelTextX = ((labelWidth + totalLogoWidth) / 2) * 10
+    const labelTextLength = (labelWidth - (24 + totalLogoWidth)) * 10
+    const escapedLabel = escapeXml(label)
+    return `
+      <text x="${labelTextX}" y="175" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>
+    `
+  }
+
+  const badge = renderBadge(
+    {
+      links,
+      leftWidth,
+      rightWidth,
+      height,
+    },
+    `
+    <g shape-rendering="crispEdges">
+      <rect width="${leftWidth}" height="${height}" fill="${labelColor}"/>
+      <rect x="${leftWidth}" width="${rightWidth}" height="${height}" fill="${color}"/>
+    </g>
+    <g fill="#fff" text-anchor="middle" ${fontFamily} font-size="100">
+      ${renderedLogo}
+      ${hasLabel ? renderLabelText() : ''}
+      <text x="${(labelWidth + messageWidth / 2) *
+        10}" y="175" font-weight="bold" transform="scale(.1)" textLength="${(messageWidth -
+      24) *
+      10}">
+        ${escapeXml(message)}</text>
+    </g>`
+  )
+
+  if (minify) {
+    return stripXmlWhitespace(badge)
+  }
+  return badge
+}
+
+module.exports = { plastic, flat, flatSquare, social, forTheBadge }
diff --git a/gh-badges/lib/color.js b/badge-maker/lib/color.js
similarity index 94%
rename from gh-badges/lib/color.js
rename to badge-maker/lib/color.js
index fbfa8a13391c9551b4ae5b11fe765b9fa61bf5c9..6273747f369e8902bc8a14ffe3d0e16bf020cce3 100644
--- a/gh-badges/lib/color.js
+++ b/badge-maker/lib/color.js
@@ -2,7 +2,7 @@
 
 const isCSSColor = require('is-css-color')
 
-// When updating these, be sure also to update the list in `gh-badges/README.md`.
+// When updating these, be sure also to update the list in `badge-maker/README.md`.
 const namedColors = {
   brightgreen: '#4c1',
   green: '#97ca00',
diff --git a/gh-badges/lib/color.spec.js b/badge-maker/lib/color.spec.js
similarity index 100%
rename from gh-badges/lib/color.spec.js
rename to badge-maker/lib/color.spec.js
diff --git a/badge-maker/lib/index.js b/badge-maker/lib/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..49bef6c58aee8cf0acd52518d2c894fcbee79861
--- /dev/null
+++ b/badge-maker/lib/index.js
@@ -0,0 +1,87 @@
+'use strict'
+/**
+ * @module badge-maker
+ */
+
+const _makeBadge = require('./make-badge')
+
+class ValidationError extends Error {}
+
+function _validate(format) {
+  if (format !== Object(format)) {
+    throw new ValidationError('makeBadge takes an argument of type object')
+  }
+
+  if (!('message' in format)) {
+    throw new ValidationError('Field `message` is required')
+  }
+
+  const stringFields = ['labelColor', 'color', 'message', 'label']
+  stringFields.forEach(function(field) {
+    if (field in format && typeof format[field] !== 'string') {
+      throw new ValidationError(`Field \`${field}\` must be of type string`)
+    }
+  })
+
+  const styleValues = [
+    'plastic',
+    'flat',
+    'flat-square',
+    'for-the-badge',
+    'social',
+  ]
+  if ('style' in format && !styleValues.includes(format.style)) {
+    throw new ValidationError(
+      `Field \`style\` must be one of (${styleValues.toString()})`
+    )
+  }
+}
+
+function _clean(format) {
+  const expectedKeys = ['label', 'message', 'labelColor', 'color', 'style']
+
+  const cleaned = {}
+  Object.keys(format).forEach(key => {
+    if (format[key] != null && expectedKeys.includes(key)) {
+      cleaned[key] = format[key]
+    } else {
+      throw new ValidationError(
+        `Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})`
+      )
+    }
+  })
+
+  // convert "public" format to "internal" format
+  cleaned.text = [cleaned.label || '', cleaned.message]
+  delete cleaned.label
+  delete cleaned.message
+  if ('style' in cleaned) {
+    cleaned.template = cleaned.style
+    delete cleaned.style
+  }
+
+  return cleaned
+}
+
+/**
+ * Create a badge
+ *
+ * @param {object} format Object specifying badge data
+ * @param {string} format.label (Optional) Badge label (e.g: 'build')
+ * @param {string} format.message (Required) Badge message (e.g: 'passing')
+ * @param {string} format.labelColor (Optional) Label color
+ * @param {string} format.color (Optional) Message color
+ * @param {string} format.style (Optional) Visual style e.g: 'flat'
+ * @returns {string} Badge in SVG or JSON format
+ * @see https://github.com/badges/shields/tree/master/badge-maker/README.md
+ */
+function makeBadge(format) {
+  _validate(format)
+  const cleanedFormat = _clean(format)
+  return _makeBadge(cleanedFormat)
+}
+
+module.exports = {
+  makeBadge,
+  ValidationError,
+}
diff --git a/badge-maker/lib/index.spec.js b/badge-maker/lib/index.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..27ed81591d7b0f86c40f7183d34d54ff46791622
--- /dev/null
+++ b/badge-maker/lib/index.spec.js
@@ -0,0 +1,75 @@
+'use strict'
+
+const { expect } = require('chai')
+const isSvg = require('is-svg')
+const { makeBadge, ValidationError } = require('.')
+
+describe('makeBadge function', function() {
+  it('should produce badge with valid input', function() {
+    expect(
+      makeBadge({
+        label: 'build',
+        message: 'passed',
+      })
+    ).to.satisfy(isSvg)
+    expect(
+      makeBadge({
+        message: 'passed',
+      })
+    ).to.satisfy(isSvg)
+    expect(
+      makeBadge({
+        label: 'build',
+        message: 'passed',
+        color: 'green',
+        style: 'flat',
+      })
+    ).to.satisfy(isSvg)
+  })
+
+  it('should throw a ValidationError with invalid inputs', function() {
+    ;[null, undefined, 7, 'foo', 4.25].forEach(x => {
+      console.log(x)
+      expect(() => makeBadge(x)).to.throw(
+        ValidationError,
+        'makeBadge takes an argument of type object'
+      )
+    })
+    expect(() => makeBadge({})).to.throw(
+      ValidationError,
+      'Field `message` is required'
+    )
+    expect(() => makeBadge({ label: 'build' })).to.throw(
+      ValidationError,
+      'Field `message` is required'
+    )
+    expect(() =>
+      makeBadge({ label: 'build', message: 'passed', labelColor: 7 })
+    ).to.throw(ValidationError, 'Field `labelColor` must be of type string')
+    expect(() =>
+      makeBadge({ label: 'build', message: 'passed', format: 'png' })
+    ).to.throw(ValidationError, "Unexpected field 'format'")
+    expect(() =>
+      makeBadge({ label: 'build', message: 'passed', template: 'flat' })
+    ).to.throw(ValidationError, "Unexpected field 'template'")
+    expect(() =>
+      makeBadge({ label: 'build', message: 'passed', foo: 'bar' })
+    ).to.throw(ValidationError, "Unexpected field 'foo'")
+    expect(() =>
+      makeBadge({
+        label: 'build',
+        message: 'passed',
+        style: 'something else',
+      })
+    ).to.throw(
+      ValidationError,
+      'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)'
+    )
+    expect(() =>
+      makeBadge({ label: 'build', message: 'passed', style: 'popout' })
+    ).to.throw(
+      ValidationError,
+      'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)'
+    )
+  })
+})
diff --git a/badge-maker/lib/make-badge.js b/badge-maker/lib/make-badge.js
new file mode 100644
index 0000000000000000000000000000000000000000..e9fc223855b2775cd3b65db6d78e60e1ceb0d2c4
--- /dev/null
+++ b/badge-maker/lib/make-badge.js
@@ -0,0 +1,64 @@
+'use strict'
+
+const camelcase = require('camelcase')
+const { normalizeColor, toSvgColor } = require('./color')
+const badgeRenderers = require('./badge-renderers')
+
+/*
+note: makeBadge() is fairly thinly wrapped so if we are making changes here
+it is likely this will impact on the package's public interface in index.js
+*/
+module.exports = function makeBadge({
+  format,
+  template = 'flat',
+  text,
+  color,
+  labelColor,
+  logo,
+  logoPosition,
+  logoWidth,
+  links = ['', ''],
+}) {
+  // String coercion and whitespace removal.
+  text = text.map(value => `${value}`.trim())
+
+  const [label, message] = text
+
+  color = normalizeColor(color)
+  labelColor = normalizeColor(labelColor)
+
+  // This ought to be the responsibility of the server, not `makeBadge`.
+  if (format === 'json') {
+    return JSON.stringify({
+      label,
+      message,
+      logoWidth,
+      color,
+      labelColor,
+      link: links,
+      name: label,
+      value: message,
+    })
+  }
+
+  const methodName = camelcase(template)
+  if (!(methodName in badgeRenderers)) {
+    throw new Error(`Unknown template: '${template}'`)
+  }
+  const render = badgeRenderers[methodName]
+
+  logoWidth = +logoWidth || (logo ? 14 : 0)
+
+  return render({
+    label,
+    message,
+    links,
+    logo,
+    logoPosition,
+    logoWidth,
+    logoPadding: logo && label.length ? 3 : 0,
+    color: toSvgColor(color),
+    labelColor: toSvgColor(labelColor),
+    minify: true,
+  })
+}
diff --git a/badge-maker/lib/make-badge.spec.js b/badge-maker/lib/make-badge.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..152f07b006cff01598fbdb7ca40071e73817d8fd
--- /dev/null
+++ b/badge-maker/lib/make-badge.spec.js
@@ -0,0 +1,571 @@
+'use strict'
+
+const { test, given, forCases } = require('sazerac')
+const { expect } = require('chai')
+const snapshot = require('snap-shot-it')
+const isSvg = require('is-svg')
+const makeBadge = require('./make-badge')
+
+function testColor(color = '', colorAttr = 'color') {
+  return JSON.parse(
+    makeBadge({
+      text: ['name', 'Bob'],
+      [colorAttr]: color,
+      format: 'json',
+    })
+  ).color
+}
+
+describe('The badge generator', function() {
+  describe('color test', function() {
+    test(testColor, () => {
+      // valid hex
+      forCases([
+        given('#4c1'),
+        given('#4C1'),
+        given('4C1'),
+        given('4c1'),
+      ]).expect('#4c1')
+      forCases([
+        given('#abc123'),
+        given('#ABC123'),
+        given('abc123'),
+        given('ABC123'),
+      ]).expect('#abc123')
+      // valid rgb(a)
+      given('rgb(0,128,255)').expect('rgb(0,128,255)')
+      given('rgba(0,128,255,0)').expect('rgba(0,128,255,0)')
+      // valid hsl(a)
+      given('hsl(100, 56%, 10%)').expect('hsl(100, 56%, 10%)')
+      given('hsla(25,20%,0%,0.1)').expect('hsla(25,20%,0%,0.1)')
+      // CSS named color.
+      given('papayawhip').expect('papayawhip')
+      // Shields named color.
+      given('red').expect('red')
+      given('green').expect('green')
+      given('blue').expect('blue')
+      given('yellow').expect('yellow')
+      // Semantic color alias
+      given('success').expect('brightgreen')
+      given('informational').expect('blue')
+
+      forCases(
+        // invalid hex
+        given('#123red'), // contains letter above F
+        given('#red'), // contains letter above F
+        // invalid rgb(a)
+        given('rgb(220,128,255,0.5)'), // has alpha
+        given('rgba(0,0,255)'), // no alpha
+        // invalid hsl(a)
+        given('hsl(360,50%,50%,0.5)'), // has alpha
+        given('hsla(0,50%,101%)'), // no alpha
+        // neither a css named color nor colorscheme
+        given('notacolor'),
+        given('bluish'),
+        given('almostred'),
+        given('brightmaroon'),
+        given('cactus')
+      ).expect(undefined)
+    })
+  })
+
+  describe('color aliases', function() {
+    test(testColor, () => {
+      forCases([given('#4c1', 'color')]).expect('#4c1')
+    })
+  })
+
+  describe('SVG', function() {
+    it('should produce SVG', function() {
+      const svg = makeBadge({ text: ['cactus', 'grown'], format: 'svg' })
+      expect(svg)
+        .to.satisfy(isSvg)
+        .and.to.include('cactus')
+        .and.to.include('grown')
+    })
+
+    it('should match snapshot', function() {
+      const svg = makeBadge({ text: ['cactus', 'grown'], format: 'svg' })
+      snapshot(svg)
+    })
+  })
+
+  describe('JSON', function() {
+    it('should produce the expected JSON', function() {
+      const json = makeBadge({
+        text: ['cactus', 'grown'],
+        format: 'json',
+        links: ['https://example.com/', 'https://other.example.com/'],
+      })
+      expect(JSON.parse(json)).to.deep.equal({
+        name: 'cactus',
+        label: 'cactus',
+        value: 'grown',
+        message: 'grown',
+        link: ['https://example.com/', 'https://other.example.com/'],
+      })
+    })
+
+    it('should replace undefined svg template with "flat"', function() {
+      const jsonBadgeWithUnknownStyle = makeBadge({
+        text: ['name', 'Bob'],
+        format: 'svg',
+      })
+      const jsonBadgeWithDefaultStyle = makeBadge({
+        text: ['name', 'Bob'],
+        format: 'svg',
+        template: 'flat',
+      })
+      expect(jsonBadgeWithUnknownStyle)
+        .to.equal(jsonBadgeWithDefaultStyle)
+        .and.to.satisfy(isSvg)
+    })
+
+    it('should fail with unknown svg template', function() {
+      expect(() =>
+        makeBadge({
+          text: ['name', 'Bob'],
+          format: 'svg',
+          template: 'unknown_style',
+        })
+      ).to.throw(Error, "Unknown template: 'unknown_style'")
+    })
+  })
+
+  describe('"flat" template badge generation', function() {
+    it('should match snapshots: message/label, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'flat',
+          color: '#b3e',
+          labelColor: '#0f0',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'flat',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'flat',
+          color: '#b3e',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'flat',
+          color: '#b3e',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo and labelColor', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'flat',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with links', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'flat',
+          color: '#b3e',
+          labelColor: '#0f0',
+          links: ['https://shields.io/', 'https://www.google.co.uk/'],
+        })
+      )
+    })
+  })
+
+  describe('"flat-square" template badge generation', function() {
+    it('should match snapshots: message/label, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'flat-square',
+          color: '#b3e',
+          labelColor: '#0f0',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'flat-square',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'flat-square',
+          color: '#b3e',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'flat-square',
+          color: '#b3e',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo and labelColor', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'flat-square',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with links', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'flat-square',
+          color: '#b3e',
+          labelColor: '#0f0',
+          links: ['https://shields.io/', 'https://www.google.co.uk/'],
+        })
+      )
+    })
+  })
+
+  describe('"plastic" template badge generation', function() {
+    it('should match snapshots: message/label, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'plastic',
+          color: '#b3e',
+          labelColor: '#0f0',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'plastic',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'plastic',
+          color: '#b3e',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'plastic',
+          color: '#b3e',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo and labelColor', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'plastic',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with links', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'plastic',
+          color: '#b3e',
+          labelColor: '#0f0',
+          links: ['https://shields.io/', 'https://www.google.co.uk/'],
+        })
+      )
+    })
+  })
+
+  describe('"for-the-badge" template badge generation', function() {
+    // https://github.com/badges/shields/issues/1280
+    it('numbers should produce a string', function() {
+      const svg = makeBadge({
+        text: [1998, 1999],
+        format: 'svg',
+        template: 'for-the-badge',
+      })
+      expect(svg)
+        .to.include('1998')
+        .and.to.include('1999')
+    })
+
+    it('lowercase/mixedcase string should produce uppercase string', function() {
+      const svg = makeBadge({
+        text: ['Label', '1 string'],
+        format: 'svg',
+        template: 'for-the-badge',
+      })
+      expect(svg)
+        .to.include('LABEL')
+        .and.to.include('1 STRING')
+    })
+
+    it('should match snapshots: message/label, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'for-the-badge',
+          color: '#b3e',
+          labelColor: '#0f0',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'for-the-badge',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'for-the-badge',
+          color: '#b3e',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'for-the-badge',
+          color: '#b3e',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo and labelColor', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'for-the-badge',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with links', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'for-the-badge',
+          color: '#b3e',
+          labelColor: '#0f0',
+          links: ['https://shields.io/', 'https://www.google.co.uk/'],
+        })
+      )
+    })
+  })
+
+  describe('"social" template badge generation', function() {
+    it('should produce capitalized string for badge key', function() {
+      const svg = makeBadge({
+        text: ['some-key', 'some-value'],
+        format: 'svg',
+        template: 'social',
+      })
+      expect(svg)
+        .to.include('Some-key')
+        .and.to.include('some-value')
+    })
+
+    // https://github.com/badges/shields/issues/1606
+    it('should handle empty strings used as badge keys', function() {
+      const svg = makeBadge({
+        text: ['', 'some-value'],
+        format: 'json',
+        template: 'social',
+      })
+      expect(svg)
+        .to.include('""')
+        .and.to.include('some-value')
+    })
+
+    it('should match snapshots: message/label, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'social',
+          color: '#b3e',
+          labelColor: '#0f0',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'social',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, no logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'social',
+          color: '#b3e',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'social',
+          color: '#b3e',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message only, with logo and labelColor', function() {
+      snapshot(
+        makeBadge({
+          text: ['', 'grown'],
+          format: 'svg',
+          template: 'social',
+          color: '#b3e',
+          labelColor: '#0f0',
+          logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+        })
+      )
+    })
+
+    it('should match snapshots: message/label, with links', function() {
+      snapshot(
+        makeBadge({
+          text: ['cactus', 'grown'],
+          format: 'svg',
+          template: 'social',
+          color: '#b3e',
+          labelColor: '#0f0',
+          links: ['https://shields.io/', 'https://www.google.co.uk/'],
+        })
+      )
+    })
+  })
+
+  describe('badges with logos should always produce the same badge', function() {
+    it('badge with logo', function() {
+      const svg = makeBadge({
+        text: ['label', 'message'],
+        format: 'svg',
+        logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxu',
+      })
+      snapshot(svg)
+    })
+  })
+})
diff --git a/gh-badges/package.json b/badge-maker/package.json
similarity index 83%
rename from gh-badges/package.json
rename to badge-maker/package.json
index 7ee056666176006d95e4969c8225076d5fd529c3..947bf5bc615d7fc7ebd6f6192a09df65a96a57e0 100644
--- a/gh-badges/package.json
+++ b/badge-maker/package.json
@@ -1,6 +1,6 @@
 {
-  "name": "gh-badges",
-  "version": "2.2.1",
+  "name": "badge-maker",
+  "version": "3.0.0-rc1",
   "description": "Shields.io badge library",
   "keywords": [
     "GitHub",
@@ -13,7 +13,7 @@
   "repository": {
     "type": "git",
     "url": "git+https://github.com/badges/shields.git",
-    "directory": "gh-badges"
+    "directory": "badge-maker"
   },
   "author": "Thaddée Tyl <thaddee.tyl@gmail.com>",
   "license": "CC0-1.0",
@@ -35,10 +35,7 @@
   },
   "dependencies": {
     "anafanafo": "^1.0.0",
-    "dot": "^1.1.2",
-    "gm": "^1.23.0",
-    "is-css-color": "^1.0.0",
-    "svgo": "^1.1.1"
+    "is-css-color": "^1.0.0"
   },
   "scripts": {
     "test": "echo 'Run tests from parent dir'; false"
diff --git a/core/badge-urls/make-badge-url.d.ts b/core/badge-urls/make-badge-url.d.ts
index 71f109ee0572038798470c9b036af6a75b911ee4..f2bdb8be5f969fb681e4542e0103091f2492e0ce 100644
--- a/core/badge-urls/make-badge-url.d.ts
+++ b/core/badge-urls/make-badge-url.d.ts
@@ -38,6 +38,7 @@ export function staticBadgeUrl({
   baseUrl,
   label,
   message,
+  labelColor,
   color,
   style,
   namedLogo,
@@ -46,6 +47,7 @@ export function staticBadgeUrl({
   baseUrl?: string
   label: string
   message: string
+  labelColor?: string
   color?: string
   style?: string
   namedLogo?: string
diff --git a/core/badge-urls/make-badge-url.js b/core/badge-urls/make-badge-url.js
index 25c1159b21fb1b608173d5b856676ef1394a63e8..415676589d3a8567412556d0940dd38ea43ad61d 100644
--- a/core/badge-urls/make-badge-url.js
+++ b/core/badge-urls/make-badge-url.js
@@ -59,6 +59,7 @@ function staticBadgeUrl({
   baseUrl = '',
   label,
   message,
+  labelColor,
   color = 'lightgray',
   style,
   namedLogo,
@@ -66,6 +67,7 @@ function staticBadgeUrl({
 }) {
   const path = [label, message, color].map(encodeField).join('-')
   const outQueryString = queryString.stringify({
+    labelColor,
     style,
     logo: namedLogo,
   })
diff --git a/core/base-service/base-non-memory-caching.js b/core/base-service/base-non-memory-caching.js
index d2671f3f56e77ffd4525624f3d67d6975e47f692..f441caf7167a194c78d005589c2072ac96ba01a9 100644
--- a/core/base-service/base-non-memory-caching.js
+++ b/core/base-service/base-non-memory-caching.js
@@ -1,6 +1,6 @@
 'use strict'
 
-const makeBadge = require('../../gh-badges/lib/make-badge')
+const makeBadge = require('../../badge-maker/lib/make-badge')
 const BaseService = require('./base')
 const { MetricHelper } = require('./metric-helper')
 const { setCacheHeaders } = require('./cache-headers')
diff --git a/core/base-service/base-static.js b/core/base-service/base-static.js
index c932b317bc9a256e22d19248cf037a9c109fc494..3781d450e3578b3081487ae13868592502887c17 100644
--- a/core/base-service/base-static.js
+++ b/core/base-service/base-static.js
@@ -1,6 +1,6 @@
 'use strict'
 
-const makeBadge = require('../../gh-badges/lib/make-badge')
+const makeBadge = require('../../badge-maker/lib/make-badge')
 const BaseService = require('./base')
 const {
   serverHasBeenUpSinceResourceCached,
diff --git a/core/base-service/base-svg-scraping.spec.js b/core/base-service/base-svg-scraping.spec.js
index 22154147a32bf86edc5aef0c9f2cfb08b023ee1a..520b7b9a2ff93c5f45f7c575f1452bb4054641a5 100644
--- a/core/base-service/base-svg-scraping.spec.js
+++ b/core/base-service/base-svg-scraping.spec.js
@@ -3,7 +3,7 @@
 const { expect } = require('chai')
 const sinon = require('sinon')
 const Joi = require('@hapi/joi')
-const makeBadge = require('../../gh-badges/lib/make-badge')
+const makeBadge = require('../../badge-maker/lib/make-badge')
 const BaseSvgScrapingService = require('./base-svg-scraping')
 
 function makeExampleSvg({ label, message }) {
diff --git a/core/base-service/base.spec.js b/core/base-service/base.spec.js
index ae4879630819c355f2abb8591aeaecc6905032ca..01f28d0f5fbc7cb864cd43dbe95fcba625766bdf 100644
--- a/core/base-service/base.spec.js
+++ b/core/base-service/base.spec.js
@@ -392,7 +392,7 @@ describe('BaseService', function() {
       expect(mockSendBadge).to.have.been.calledWith(expectedFormat, {
         text: ['cat', 'Hello namedParamA: bar with queryParamA: ?'],
         color: 'lightgrey',
-        template: undefined,
+        template: 'flat',
         namedLogo: undefined,
         logo: undefined,
         logoWidth: undefined,
diff --git a/core/base-service/coalesce-badge.js b/core/base-service/coalesce-badge.js
index a77b8dcc2198faf57532063db3f945c9d34162eb..ba664dfdc981e3eb3a533e752424bcd5908b321e 100644
--- a/core/base-service/coalesce-badge.js
+++ b/core/base-service/coalesce-badge.js
@@ -104,7 +104,23 @@ module.exports = function coalesceBadge(
     labelColor: defaultLabelColor,
   } = defaultBadgeData
 
-  const style = coalesce(overrideStyle, serviceStyle)
+  let style = coalesce(overrideStyle, serviceStyle)
+  if (typeof style !== 'string') {
+    style = 'flat'
+  }
+  if (style.startsWith('popout')) {
+    style = style.replace('popout', 'flat')
+  }
+  const styleValues = [
+    'plastic',
+    'flat',
+    'flat-square',
+    'for-the-badge',
+    'social',
+  ]
+  if (!styleValues.includes(style)) {
+    style = 'flat'
+  }
 
   let namedLogo, namedLogoColor, logoWidth, logoPosition, logoSvgBase64
   if (overrideLogo) {
diff --git a/core/base-service/coalesce-badge.spec.js b/core/base-service/coalesce-badge.spec.js
index 224aec40507c66229f8fbef75755b1e84eaee11e..443fec3db28a195dc1e6b063b7944ad642fea5ea 100644
--- a/core/base-service/coalesce-badge.spec.js
+++ b/core/base-service/coalesce-badge.spec.js
@@ -278,8 +278,21 @@ describe('coalesceBadge', function() {
   })
 
   describe('Style', function() {
-    it('overrides the template', function() {
-      expect(coalesceBadge({ style: 'pill' }, {}, {}).template).to.equal('pill')
+    it('falls back to flat with invalid style', function() {
+      expect(coalesceBadge({ style: 'pill' }, {}, {}).template).to.equal('flat')
+      expect(coalesceBadge({ style: 7 }, {}, {}).template).to.equal('flat')
+      expect(coalesceBadge({ style: undefined }, {}, {}).template).to.equal(
+        'flat'
+      )
+    })
+
+    it('replaces legacy popout styles', function() {
+      expect(coalesceBadge({ style: 'popout' }, {}, {}).template).to.equal(
+        'flat'
+      )
+      expect(
+        coalesceBadge({ style: 'popout-square' }, {}, {}).template
+      ).to.equal('flat-square')
     })
   })
 
diff --git a/core/base-service/legacy-request-handler.js b/core/base-service/legacy-request-handler.js
index f14ff236a72e2a6ef6be45d3634706d45237e26a..3d3096a5ecda4826c4f51b3be7a550e1fdbe098e 100644
--- a/core/base-service/legacy-request-handler.js
+++ b/core/base-service/legacy-request-handler.js
@@ -2,7 +2,7 @@
 
 const request = require('request')
 const queryString = require('query-string')
-const makeBadge = require('../../gh-badges/lib/make-badge')
+const makeBadge = require('../../badge-maker/lib/make-badge')
 const { setCacheHeaders } = require('./cache-headers')
 const {
   Inaccessible,
diff --git a/core/server/server.js b/core/server/server.js
index d2f2ef8d7c3bfe73efeea4854cca4d595bbe0f12..33fa141ca710d0ff75460b9195676962dda13550 100644
--- a/core/server/server.js
+++ b/core/server/server.js
@@ -9,7 +9,7 @@ const { URL } = url
 const bytes = require('bytes')
 const Camp = require('camp')
 const originalJoi = require('@hapi/joi')
-const makeBadge = require('../../gh-badges/lib/make-badge')
+const makeBadge = require('../../badge-maker/lib/make-badge')
 const GithubConstellation = require('../../services/github/github-constellation')
 const suggest = require('../../services/suggest')
 const { loadServiceClasses } = require('../base-service/loader')
diff --git a/doc/code-walkthrough.md b/doc/code-walkthrough.md
index f9ea519d7997dfd423160057016935766b4bf2f7..6907ae0348255adb321e6ac18d0f8b7d551d08d3 100644
--- a/doc/code-walkthrough.md
+++ b/doc/code-walkthrough.md
@@ -7,7 +7,7 @@ The Shields codebase is divided into several parts:
 1.  The frontend (about 7% of the code)
     1. [`frontend`][frontend]
 2.  The badge renderer (which is available as an npm package)
-    1.  [`gh-badges`][gh-badges]
+    1.  [`badge-maker`][badge-maker]
 3.  The base service classes (about 8% of the code, and probably the most important
     code in the codebase)
     1.  [`core/base-service`][base-service]
@@ -24,7 +24,7 @@ The Shields codebase is divided into several parts:
     1.  [`lib/suggest.js`][suggest]
 
 [frontend]: https://github.com/badges/shields/tree/master/frontend
-[gh-badges]: https://github.com/badges/shields/tree/master/gh-badges
+[badge-maker]: https://github.com/badges/shields/tree/master/badge-maker
 [base-service]: https://github.com/badges/shields/tree/master/core/base-service
 [server]: https://github.com/badges/shields/tree/master/core/server
 [token-pooling]: https://github.com/badges/shields/tree/master/core/token-pooling
@@ -36,7 +36,7 @@ The tests are also divided into several parts:
 1.  Unit and functional tests of the frontend
     1.  `frontend/**/*.spec.js`
 2.  Unit and functional tests of the badge renderer
-    1.  `gh-badges/**/*.spec.js`
+    1.  `badge-maker/**/*.spec.js`
 3.  Unit and functional tests of the core code
     1.  `core/**/*.spec.js`
 4.  Unit and functional tests of the service helper functions
diff --git a/doc/self-hosting.md b/doc/self-hosting.md
index f17a57a88997d08af82e8adfbee13c34765f2157..20e120775b521b32e2ef8ac8f62d5eea80c23d50 100644
--- a/doc/self-hosting.md
+++ b/doc/self-hosting.md
@@ -78,7 +78,7 @@ $ docker run --rm -p 8080:80 --name shields shields
 # or if you have shields.env file, run the following instead
 $ docker run --rm -p 8080:80 --env-file shields.env --name shields shields
 
-> gh-badges@1.1.2 start /usr/src/app
+> badge-maker@3.0.0 start /usr/src/app
 > node server.js
 
 http://[::1]/
diff --git a/frontend/components/development/style-page.tsx b/frontend/components/development/style-page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..85f0f1f7b583c1ca5ce9b5a1f80c0c15879b5b98
--- /dev/null
+++ b/frontend/components/development/style-page.tsx
@@ -0,0 +1,154 @@
+import React, { Fragment } from 'react'
+import styled from 'styled-components'
+// @ts-ingnore
+import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url'
+import { baseUrl } from '../../constants'
+import Meta from '../meta'
+// @ts-ignore
+import Header from '../header'
+import { H3, Badge } from '../common'
+
+const StyledTable = styled.table`
+  border: 1px solid #ccc;
+  border-collapse: collapse;
+
+  td {
+    border: 1px solid #ccc;
+    padding: 3px;
+    text-align: left;
+  }
+`
+
+interface BadgeData {
+  label: string
+  message: string
+  labelColor?: string
+  color: string
+  namedLogo?: string
+}
+
+function Badges({
+  baseUrl,
+  style,
+  badges,
+}: {
+  baseUrl: string
+  style: string
+  badges: BadgeData[]
+}): JSX.Element {
+  return (
+    <>
+      {badges.map(({ label, message, labelColor, color, namedLogo }) => (
+        <Fragment key={`${label}-${message}-${color}-${namedLogo}`}>
+          <Badge
+            alt="build"
+            src={staticBadgeUrl({
+              baseUrl,
+              label,
+              message,
+              labelColor,
+              color,
+              namedLogo,
+              style,
+            })}
+          />
+          <br />
+        </Fragment>
+      ))}
+    </>
+  )
+}
+
+interface StyleExamples {
+  title: string
+  badges: BadgeData[]
+}
+
+const examples = [
+  {
+    title: 'Basic examples',
+    badges: [
+      { label: 'build', message: 'passing', color: 'brightgreen' },
+      { label: 'tests', message: '5 passing, 1 failed', color: 'red' },
+      { label: 'python', message: '3.5 | 3.6 | 3.7', color: 'blue' },
+    ],
+  },
+  {
+    title: 'Logo',
+    badges: [
+      {
+        label: 'build',
+        message: 'passing',
+        color: 'brightgreen',
+        namedLogo: 'appveyor',
+      },
+    ],
+  },
+  {
+    title: 'No left text',
+    badges: [
+      { label: '', message: 'blueviolet', color: 'blueviolet' },
+      {
+        label: '',
+        message: 'passing',
+        color: 'brightgreen',
+        namedLogo: 'appveyor',
+      },
+      {
+        label: '',
+        message: 'passing',
+        color: 'brightgreen',
+        labelColor: 'grey',
+        namedLogo: 'appveyor',
+      },
+    ],
+  },
+]
+
+function StyleTable({ style }: { style: string }): JSX.Element {
+  return (
+    <StyledTable>
+      <thead>
+        <tr>
+          <td>Description</td>
+          <td>Badges (new)</td>
+          <td>Badges (old)</td>
+        </tr>
+      </thead>
+      <tbody>
+        {examples.map(({ title, badges }) => (
+          <tr key={title}>
+            <td>{title}</td>
+            <td>
+              <Badges badges={badges} baseUrl={baseUrl} style={style} />
+            </td>
+            <td>
+              <Badges
+                badges={badges}
+                baseUrl="http://img.shields.io"
+                style={style}
+              />
+            </td>
+          </tr>
+        ))}
+      </tbody>
+    </StyledTable>
+  )
+}
+
+const styles = ['flat', 'flat-square', 'for-the-badge', 'social', 'plastic']
+
+export default function StylePage(): JSX.Element {
+  return (
+    <div>
+      <Meta />
+      <Header />
+      {styles.map(style => (
+        <Fragment key={style}>
+          <H3>{style}</H3>
+          <StyleTable style={style} />
+        </Fragment>
+      ))}
+    </div>
+  )
+}
diff --git a/gatsby-node.js b/gatsby-node.js
index 64dddb967d58e5eaad11849c56801c32e71d9f21..01cedf480d952758da974027571920e20f395f3c 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -21,6 +21,12 @@ const { categories } = yaml.safeLoad(
 // https://www.gatsbyjs.org/docs/using-gatsby-without-graphql/#the-approach-fetch-data-and-use-gatsbys-createpages-api
 async function createPages({ actions: { createPage } }) {
   if (includeDevPages) {
+    createPage({
+      path: '/dev/styles',
+      component: require.resolve(
+        './frontend/components/development/style-page.tsx'
+      ),
+    })
     createPage({
       path: '/dev/logos',
       component: require.resolve(
diff --git a/gh-badges/lib/index.js b/gh-badges/lib/index.js
deleted file mode 100644
index 2ec589d6fde1759b424b920f136eb285e36222f4..0000000000000000000000000000000000000000
--- a/gh-badges/lib/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-'use strict'
-/**
- * @module gh-badges
- */
-
-const makeBadge = require('./make-badge')
-
-/**
- * BadgeFactory
- */
-class BadgeFactory {
-  constructor(options) {
-    if (options !== undefined) {
-      console.error(
-        'BadgeFactory: Constructor options are deprecated and will be ignored'
-      )
-    }
-  }
-
-  /**
-   * Create a badge
-   *
-   * @param {object} format Object specifying badge data
-   * @param {string[]} format.text Badge text in an array e.g: ['build', 'passing']
-   * @param {string} format.labelColor (Optional) Label color
-   * @param {string} format.color (Optional) Message color
-   * @param {string} format.colorA (Deprecated, Optional) alias for `labelColor`
-   * @param {string} format.colorscheme (Deprecated, Optional) alias for `color`
-   * @param {string} format.colorB (Deprecated, Optional) alias for `color`
-   * @param {string} format.format (Optional) Output format: 'svg' or 'json'
-   * @param {string} format.template (Optional) Visual template e.g: 'flat'
-   *    see {@link https://github.com/badges/shields/tree/master/gh-badges/templates}
-   * @returns {string} Badge in SVG or JSON format
-   * @see https://github.com/badges/shields/tree/master/gh-badges/README.md
-   */
-  create(format) {
-    return makeBadge(format)
-  }
-}
-
-module.exports = {
-  BadgeFactory,
-}
diff --git a/gh-badges/lib/index.spec.js b/gh-badges/lib/index.spec.js
deleted file mode 100644
index 68a6e7d3b17ae989675d795c8628a715fb938e68..0000000000000000000000000000000000000000
--- a/gh-badges/lib/index.spec.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const isSvg = require('is-svg')
-const { BadgeFactory } = require('.')
-
-const bf = new BadgeFactory()
-
-describe('BadgeFactory class', function() {
-  it('should produce badge with valid input', function() {
-    expect(
-      bf.create({
-        text: ['build', 'passed'],
-        format: 'svg',
-        colorscheme: 'green',
-        template: 'flat',
-      })
-    ).to.satisfy(isSvg)
-  })
-})
diff --git a/gh-badges/lib/make-badge.js b/gh-badges/lib/make-badge.js
deleted file mode 100644
index 7ae810bbee2e576e718b63d95279280e5bc0f4ca..0000000000000000000000000000000000000000
--- a/gh-badges/lib/make-badge.js
+++ /dev/null
@@ -1,185 +0,0 @@
-'use strict'
-
-const fs = require('fs')
-const path = require('path')
-const SVGO = require('svgo')
-const dot = require('dot')
-const anafanafo = require('anafanafo')
-const { normalizeColor, toSvgColor } = require('./color')
-
-// cache templates.
-const templates = {}
-const templateFiles = fs.readdirSync(path.join(__dirname, '..', 'templates'))
-dot.templateSettings.strip = false // Do not strip whitespace.
-templateFiles.forEach(async filename => {
-  if (filename[0] === '.') {
-    return
-  }
-  const templateData = fs
-    .readFileSync(path.join(__dirname, '..', 'templates', filename))
-    .toString()
-  const extension = path.extname(filename).slice(1)
-  const style = filename.slice(0, -`-template.${extension}`.length)
-  // Compile the template. Necessary to always have a working template.
-  templates[style] = dot.template(templateData)
-  // Substitute dot code.
-  const mapping = new Map()
-  let mappingIndex = 1
-  const untemplatedSvg = templateData.replace(/{{.*?}}/g, match => {
-    // Weird substitution that currently works for all templates.
-    const mapKey = `99999990${mappingIndex}.1`
-    mappingIndex++
-    mapping.set(mapKey, match)
-    return mapKey
-  })
-
-  const svgo = new SVGO()
-  const { data, error } = await svgo.optimize(untemplatedSvg)
-
-  if (error !== undefined) {
-    console.error(
-      `Template ${filename}: ${error}\n` +
-        '  Generated untemplated SVG:\n' +
-        `---\n${untemplatedSvg}---\n`
-    )
-    return
-  }
-
-  // Substitute dot code back.
-  let svg = data
-  const unmappedKeys = []
-  mapping.forEach((value, key) => {
-    let keySubstituted = false
-    svg = svg.replace(RegExp(key, 'g'), () => {
-      keySubstituted = true
-      return value
-    })
-    if (!keySubstituted) {
-      unmappedKeys.push(key)
-    }
-  })
-  if (unmappedKeys.length > 0) {
-    console.error(
-      `Template ${filename} has unmapped keys ` +
-        `${unmappedKeys.join(', ')}.\n` +
-        '  Generated untemplated SVG:\n' +
-        `---\n${untemplatedSvg}\n---\n` +
-        '  Generated template:\n' +
-        `---\n${svg}\n---\n`
-    )
-    return
-  }
-
-  templates[style] = dot.template(svg)
-})
-
-function escapeXml(s) {
-  if (s === undefined || typeof s !== 'string') {
-    return undefined
-  } else {
-    return s
-      .replace(/&/g, '&amp;')
-      .replace(/</g, '&lt;')
-      .replace(/>/g, '&gt;')
-      .replace(/"/g, '&quot;')
-      .replace(/'/g, '&apos;')
-  }
-}
-
-function capitalize(s) {
-  return s.charAt(0).toUpperCase() + s.slice(1)
-}
-
-/*
-note: makeBadge() is fairly thinly wrapped so if we are making changes here
-it is likely this will impact on the package's public interface in index.js
-*/
-module.exports = function makeBadge({
-  format,
-  template,
-  text,
-  colorscheme,
-  color,
-  colorA,
-  colorB,
-  labelColor,
-  logo,
-  logoPosition,
-  logoWidth,
-  links = ['', ''],
-}) {
-  // String coercion and whitespace removal.
-  text = text.map(value => `${value}`.trim())
-
-  let [left, right] = text
-
-  color = normalizeColor(color || colorB || colorscheme)
-  labelColor = normalizeColor(labelColor || colorA)
-
-  // This ought to be the responsibility of the server, not `makeBadge`.
-  if (format === 'json') {
-    return JSON.stringify({
-      label: left,
-      message: right,
-      logoWidth,
-      color,
-      labelColor,
-      link: links,
-      name: left,
-      value: right,
-    })
-  }
-
-  if (!(template in templates)) {
-    if (template === 'popout-square') {
-      template = 'flat-square'
-    } else {
-      template = 'flat'
-    }
-  }
-  if (template === 'social') {
-    left = capitalize(left)
-  } else if (template === 'for-the-badge') {
-    left = left.toUpperCase()
-    right = right.toUpperCase()
-  }
-
-  let leftWidth = (anafanafo(left) / 10) | 0
-  // Increase chances of pixel grid alignment.
-  if (leftWidth % 2 === 0) {
-    leftWidth++
-  }
-  let rightWidth = (anafanafo(right) / 10) | 0
-  // Increase chances of pixel grid alignment.
-  if (rightWidth % 2 === 0) {
-    rightWidth++
-  }
-
-  logoWidth = +logoWidth || (logo ? 14 : 0)
-
-  let logoPadding
-  if (left.length === 0) {
-    logoPadding = 0
-  } else {
-    logoPadding = logo ? 3 : 0
-  }
-
-  const context = {
-    text: [left, right],
-    escapedText: [left, right].map(escapeXml),
-    widths: [leftWidth + 10 + logoWidth + logoPadding, rightWidth + 10],
-    links: links.map(escapeXml),
-    logo: escapeXml(logo),
-    logoPosition,
-    logoWidth,
-    logoPadding,
-    colorA: toSvgColor(labelColor),
-    colorB: toSvgColor(color),
-    escapeXml,
-  }
-
-  const templateFn = templates[template]
-
-  // The call to template() can raise an exception.
-  return templateFn(context)
-}
diff --git a/gh-badges/lib/make-badge.spec.js b/gh-badges/lib/make-badge.spec.js
deleted file mode 100644
index 56d4f89ddcee2ce1ef27177e9eaf30ed78eb259c..0000000000000000000000000000000000000000
--- a/gh-badges/lib/make-badge.spec.js
+++ /dev/null
@@ -1,231 +0,0 @@
-'use strict'
-
-const { test, given, forCases } = require('sazerac')
-const { expect } = require('chai')
-const snapshot = require('snap-shot-it')
-const isSvg = require('is-svg')
-const makeBadge = require('./make-badge')
-
-function testColor(color = '', colorAttr = 'colorB') {
-  return JSON.parse(
-    makeBadge({
-      text: ['name', 'Bob'],
-      [colorAttr]: color,
-      format: 'json',
-    })
-  ).color
-}
-
-describe('The badge generator', function() {
-  describe('color test', function() {
-    test(testColor, () => {
-      // valid hex
-      forCases([
-        given('#4c1'),
-        given('#4C1'),
-        given('4C1'),
-        given('4c1'),
-      ]).expect('#4c1')
-      forCases([
-        given('#abc123'),
-        given('#ABC123'),
-        given('abc123'),
-        given('ABC123'),
-      ]).expect('#abc123')
-      // valid rgb(a)
-      given('rgb(0,128,255)').expect('rgb(0,128,255)')
-      given('rgba(0,128,255,0)').expect('rgba(0,128,255,0)')
-      // valid hsl(a)
-      given('hsl(100, 56%, 10%)').expect('hsl(100, 56%, 10%)')
-      given('hsla(25,20%,0%,0.1)').expect('hsla(25,20%,0%,0.1)')
-      // CSS named color.
-      given('papayawhip').expect('papayawhip')
-      // Shields named color.
-      given('red').expect('red')
-      given('green').expect('green')
-      given('blue').expect('blue')
-      given('yellow').expect('yellow')
-
-      forCases(
-        // invalid hex
-        given('#123red'), // contains letter above F
-        given('#red'), // contains letter above F
-        // invalid rgb(a)
-        given('rgb(220,128,255,0.5)'), // has alpha
-        given('rgba(0,0,255)'), // no alpha
-        // invalid hsl(a)
-        given('hsl(360,50%,50%,0.5)'), // has alpha
-        given('hsla(0,50%,101%)'), // no alpha
-        // neither a css named color nor colorscheme
-        given('notacolor'),
-        given('bluish'),
-        given('almostred'),
-        given('brightmaroon'),
-        given('cactus')
-      ).expect(undefined)
-    })
-  })
-
-  describe('color aliases', function() {
-    test(testColor, () => {
-      forCases([
-        given('#4c1', 'color'),
-        given('#4c1', 'colorB'),
-        given('#4c1', 'colorscheme'),
-      ]).expect('#4c1')
-    })
-  })
-
-  describe('SVG', function() {
-    it('should produce SVG', function() {
-      const svg = makeBadge({ text: ['cactus', 'grown'], format: 'svg' })
-      expect(svg)
-        .to.satisfy(isSvg)
-        .and.to.include('cactus')
-        .and.to.include('grown')
-    })
-
-    it('should always produce the same SVG (unless we have changed something!)', function() {
-      const svg = makeBadge({ text: ['cactus', 'grown'], format: 'svg' })
-      snapshot(svg)
-    })
-  })
-
-  describe('JSON', function() {
-    it('should produce the expected JSON', function() {
-      const json = makeBadge({
-        text: ['cactus', 'grown'],
-        format: 'json',
-        links: ['https://example.com/', 'https://other.example.com/'],
-      })
-      expect(JSON.parse(json)).to.deep.equal({
-        name: 'cactus',
-        label: 'cactus',
-        value: 'grown',
-        message: 'grown',
-        link: ['https://example.com/', 'https://other.example.com/'],
-      })
-    })
-
-    it('should replace unknown svg template with "flat"', function() {
-      const jsonBadgeWithUnknownStyle = makeBadge({
-        text: ['name', 'Bob'],
-        format: 'svg',
-        template: 'unknown_style',
-      })
-      const jsonBadgeWithDefaultStyle = makeBadge({
-        text: ['name', 'Bob'],
-        format: 'svg',
-        template: 'flat',
-      })
-      expect(jsonBadgeWithUnknownStyle)
-        .to.equal(jsonBadgeWithDefaultStyle)
-        .and.to.satisfy(isSvg)
-    })
-
-    it('should replace "popout-square" svg template with "flat-square"', function() {
-      const jsonBadgeWithUnknownStyle = makeBadge({
-        text: ['name', 'Bob'],
-        format: 'svg',
-        template: 'popout-square',
-      })
-      const jsonBadgeWithDefaultStyle = makeBadge({
-        text: ['name', 'Bob'],
-        format: 'svg',
-        template: 'flat-square',
-      })
-      expect(jsonBadgeWithUnknownStyle)
-        .to.equal(jsonBadgeWithDefaultStyle)
-        .and.to.satisfy(isSvg)
-    })
-  })
-
-  describe('"for-the-badge" template badge generation', function() {
-    // https://github.com/badges/shields/issues/1280
-    it('numbers should produce a string', function() {
-      const svg = makeBadge({
-        text: [1998, 1999],
-        format: 'svg',
-        template: 'for-the-badge',
-      })
-      expect(svg)
-        .to.include('1998')
-        .and.to.include('1999')
-    })
-
-    it('lowercase/mixedcase string should produce uppercase string', function() {
-      const svg = makeBadge({
-        text: ['Label', '1 string'],
-        format: 'svg',
-        template: 'for-the-badge',
-      })
-      expect(svg)
-        .to.include('LABEL')
-        .and.to.include('1 STRING')
-    })
-  })
-
-  describe('"social" template badge generation', function() {
-    it('should produce capitalized string for badge key', function() {
-      const svg = makeBadge({
-        text: ['some-key', 'some-value'],
-        format: 'svg',
-        template: 'social',
-      })
-      expect(svg)
-        .to.include('Some-key')
-        .and.to.include('some-value')
-    })
-
-    // https://github.com/badges/shields/issues/1606
-    it('should handle empty strings used as badge keys', function() {
-      const svg = makeBadge({
-        text: ['', 'some-value'],
-        format: 'json',
-        template: 'social',
-      })
-      expect(svg)
-        .to.include('""')
-        .and.to.include('some-value')
-    })
-  })
-  describe('badges with logos should always produce the same badge', function() {
-    it('shields GitHub logo default color (#333333)', function() {
-      const svg = makeBadge({
-        text: ['label', 'message'],
-        format: 'svg',
-        logo: 'github',
-      })
-      snapshot(svg)
-    })
-
-    it('shields GitHub logo custom color (whitesmoke)', function() {
-      const svg = makeBadge({
-        text: ['label', 'message'],
-        format: 'svg',
-        logo: 'github',
-        logoColor: 'whitesmoke',
-      })
-      snapshot(svg)
-    })
-
-    it('simple-icons javascript logo default color (#F7DF1E)', function() {
-      const svg = makeBadge({
-        text: ['label', 'message'],
-        format: 'svg',
-        logo: 'javascript',
-      })
-      snapshot(svg)
-    })
-
-    it('simple-icons javascript logo custom color (rgba(46,204,113,0.8))', function() {
-      const svg = makeBadge({
-        text: ['label', 'message'],
-        format: 'svg',
-        logo: 'javascript',
-        logoColor: 'rgba(46,204,113,0.8)',
-      })
-      snapshot(svg)
-    })
-  })
-})
diff --git a/gh-badges/lib/svg-to-img.js b/gh-badges/lib/svg-to-img.js
deleted file mode 100644
index a8d11b0ca0a724f2b575b42fd560a14f6c6873f3..0000000000000000000000000000000000000000
--- a/gh-badges/lib/svg-to-img.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict'
-
-const { promisify } = require('util')
-const gm = require('gm')
-
-const imageMagick = gm.subClass({ imageMagick: true })
-
-async function svgToImg(svg, format) {
-  const svgBuffer = Buffer.from(
-    `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>${svg}`
-  )
-
-  const chain = imageMagick(svgBuffer, `image.${format}`)
-    .density(90)
-    .background(format === 'jpg' ? '#FFFFFF' : 'none')
-    .flatten()
-  const toBuffer = chain.toBuffer.bind(chain)
-
-  return promisify(toBuffer)(format)
-}
-
-module.exports = svgToImg
-
-// To simplify testing.
-module.exports._imageMagick = imageMagick
diff --git a/gh-badges/lib/svg-to-img.spec.js b/gh-badges/lib/svg-to-img.spec.js
deleted file mode 100644
index cd46df659620c2d036a19d51cfb33f3119b64a31..0000000000000000000000000000000000000000
--- a/gh-badges/lib/svg-to-img.spec.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const isPng = require('is-png')
-const svg2img = require('./svg-to-img')
-const makeBadge = require('./make-badge')
-
-describe('The rasterizer', function() {
-  it('should produce PNG', async function() {
-    const svg = makeBadge({ text: ['cactus', 'grown'], format: 'svg' })
-    const data = await svg2img(svg, 'png')
-    expect(data).to.satisfy(isPng)
-  })
-})
diff --git a/gh-badges/templates/flat-square-template.svg b/gh-badges/templates/flat-square-template.svg
deleted file mode 100644
index 0bf8927e7aa0f8e8a0f32e68bcd99e996a8392de..0000000000000000000000000000000000000000
--- a/gh-badges/templates/flat-square-template.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=(it.widths[0] -= it.text[0].length ? 0 : (it.logo ? (it.colorA ? 0 : 7) : 11))+it.widths[1]}}" height="20">
-  <g shape-rendering="crispEdges">
-    <rect width="{{=it.widths[0]}}" height="20" fill="{{=it.escapeXml(it.text[0].length || it.logo && it.colorA ? (it.colorA||"#555") : (it.colorB||"#4c1"))}}"/>
-    <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="20" fill="{{=it.escapeXml(it.colorB||"#4c1")}}"/>
-  </g>
-  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
-    {{?it.logo}}
-      <image x="5" y="3" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
-    {{?}}
-    {{?it.text[0].length}}
-        <text x="{{=(((it.widths[0]+it.logoWidth+it.logoPadding)/2)+1)*10}}" y="140" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-    {{?}}
-    <text x="{{=(it.widths[0]+it.widths[1]/2-(it.text[0].length ? 1 : 0 ))*10}}" y="140" transform="scale(0.1)" textLength="{{=(it.widths[1]-10)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-  </g>
-  {{?(it.links[0] && it.links[0].length)}}
-    <a target="_blank" xlink:href="{{=it.links[0]}}">
-      <rect width="{{=it.widths[0]}}" height="20" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-  {{?(it.links[0] && it.links[0].length || it.links[1] && it.links[1].length)}}
-    <a target="_blank" xlink:href="{{=it.links[1] || it.links[0]}}">
-      <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="20" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-</svg>
diff --git a/gh-badges/templates/flat-template.svg b/gh-badges/templates/flat-template.svg
deleted file mode 100644
index a98f1b4772acbc618e03613258bf8ec9185a2b93..0000000000000000000000000000000000000000
--- a/gh-badges/templates/flat-template.svg
+++ /dev/null
@@ -1,39 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=(it.widths[0] -= it.text[0].length ? 0 : (it.logo ? (it.colorA ? 0 : 7) : 11))+it.widths[1]}}" height="20">
-  <linearGradient id="smooth" x2="0" y2="100%">
-    <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
-    <stop offset="1" stop-opacity=".1"/>
-  </linearGradient>
-
-  <clipPath id="round">
-    <rect width="{{=it.widths[0]+it.widths[1]}}" height="20" rx="3" fill="#fff"/>
-  </clipPath>
-
-  <g clip-path="url(#round)">
-    <rect width="{{=it.widths[0]}}" height="20" fill="{{=it.escapeXml(it.text[0].length || it.logo && it.colorA ? (it.colorA||"#555") : (it.colorB||"#4c1"))}}"/>
-    <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="20" fill="{{=it.escapeXml(it.colorB||"#4c1")}}"/>
-    <rect width="{{=it.widths[0]+it.widths[1]}}" height="20" fill="url(#smooth)"/>
-  </g>
-
-  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
-    {{?it.logo}}
-      <image x="5" y="3" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
-    {{?}}
-    {{?it.text[0].length}}
-        <text x="{{=(((it.widths[0]+it.logoWidth+it.logoPadding)/2)+1)*10}}" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-        <text x="{{=(((it.widths[0]+it.logoWidth+it.logoPadding)/2)+1)*10}}" y="140" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-    {{?}}
-    <text x="{{=(it.widths[0]+it.widths[1]/2-(it.text[0].length ? 1 : 0 ))*10}}" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="{{=(it.widths[1]-10)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-    <text x="{{=(it.widths[0]+it.widths[1]/2-(it.text[0].length ? 1 : 0 ))*10}}" y="140" transform="scale(0.1)" textLength="{{=(it.widths[1]-10)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-  </g>
-
-  {{?(it.links[0] && it.links[0].length)}}
-    <a target="_blank" xlink:href="{{=it.links[0]}}">
-      <rect width="{{=it.widths[0]}}" height="20" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-  {{?(it.links[0] && it.links[0].length || it.links[1] && it.links[1].length)}}
-    <a target="_blank" xlink:href="{{=it.links[1] || it.links[0]}}">
-      <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="20" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-</svg>
diff --git a/gh-badges/templates/for-the-badge-template.svg b/gh-badges/templates/for-the-badge-template.svg
deleted file mode 100644
index be72b83a0d54904c2624d644fbc0a9c52bd0f8bc..0000000000000000000000000000000000000000
--- a/gh-badges/templates/for-the-badge-template.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=(it.widths[0] -= it.text[0].length ? -(10+(it.text[0].length*1.5)) : (it.logo ? (it.colorA ? -7 : 7) : 11))+(it.widths[1]+=(10+(it.text[1].length*2)))}}" height="28">
-  <g shape-rendering="crispEdges">
-    <rect width="{{=it.widths[0]}}" height="28" fill="{{=it.escapeXml(it.text[0].length || it.logo && it.colorA ? (it.colorA||"#555") : (it.colorB||"#4c1"))}}"/>
-    <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="28" fill="{{=it.escapeXml(it.colorB||"#4c1")}}"/>
-  </g>
-  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100">
-    {{?it.logo}}
-      <image x="9" y="7" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
-    {{?}}
-    {{?it.text[0].length}}
-      <text x="{{=((it.widths[0]+it.logoWidth+it.logoPadding)/2)*10}}" y="175" transform="scale(0.1)" textLength="{{=(it.widths[0]-(24+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-    {{?}}
-    <text x="{{=(it.widths[0]+it.widths[1]/2)*10}}" y="175" font-weight="bold" transform="scale(0.1)" textLength="{{=(it.widths[1]-24)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-  </g>
-  {{?(it.text[0].length && it.links[0] && it.links[0].length)}}
-    <a target="_blank" xlink:href="{{=it.links[0]}}">
-      <rect width="{{=it.widths[0]}}" height="28" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-  {{?(it.links[0] && it.links[0].length || it.links[1] && it.links[1].length)}}
-    <a target="_blank" xlink:href="{{=it.links[1] || it.links[0]}}">
-      <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="28" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-</svg>
diff --git a/gh-badges/templates/plastic-template.svg b/gh-badges/templates/plastic-template.svg
deleted file mode 100644
index b99642f881f2f9a40e74d64d757bde21a18edfb1..0000000000000000000000000000000000000000
--- a/gh-badges/templates/plastic-template.svg
+++ /dev/null
@@ -1,41 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=(it.widths[0] -= it.text[0].length ? 0 : (it.logo ? (it.colorA ? 0 : 7) : 11))+it.widths[1]}}" height="18">
-  <linearGradient id="smooth" x2="0" y2="100%">
-    <stop offset="0"  stop-color="#fff" stop-opacity=".7"/>
-    <stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
-    <stop offset=".9" stop-color="#000" stop-opacity=".3"/>
-    <stop offset="1"  stop-color="#000" stop-opacity=".5"/>
-  </linearGradient>
-
-  <clipPath id="round">
-    <rect width="{{=it.widths[0]+it.widths[1]}}" height="18" rx="4" fill="#fff"/>
-  </clipPath>
-
-  <g clip-path="url(#round)">
-    <rect width="{{=it.widths[0]}}" height="18" fill="{{=it.escapeXml(it.text[0].length || it.logo && it.colorA ? (it.colorA||"#555") : (it.colorB||"#4c1"))}}"/>
-    <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="18" fill="{{=it.escapeXml(it.colorB||"#4c1")}}"/>
-    <rect width="{{=it.widths[0]+it.widths[1]}}" height="18" fill="url(#smooth)"/>
-  </g>
-
-  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
-    {{?it.logo}}
-      <image x="5" y="3" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
-    {{?}}
-    {{?it.text[0].length}}
-        <text x="{{=(((it.widths[0]+it.logoWidth+it.logoPadding)/2)+1)*10}}" y="140" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-        <text x="{{=(((it.widths[0]+it.logoWidth+it.logoPadding)/2)+1)*10}}" y="130" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-    {{?}}
-    <text x="{{=(it.widths[0]+it.widths[1]/2-(it.text[0].length ? 1 : 0))*10}}" y="140" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="{{=(it.widths[1]-10)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-    <text x="{{=(it.widths[0]+it.widths[1]/2-(it.text[0].length ? 1 : 0))*10}}" y="130" transform="scale(0.1)" textLength="{{=(it.widths[1]-10)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-  </g>
-
-  {{?(it.links[0] && it.links[0].length)}}
-    <a target="_blank" xlink:href="{{=it.links[0]}}">
-      <rect width="{{=it.widths[0]}}" height="18" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-  {{?(it.links[0] && it.links[0].length || it.links[1] && it.links[1].length)}}
-    <a target="_blank" xlink:href="{{=it.links[1] || it.links[0]}}">
-      <rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="18" fill="rgba(0,0,0,0)"/>
-    </a>
-  {{?}}
-</svg>
diff --git a/gh-badges/templates/social-template.svg b/gh-badges/templates/social-template.svg
deleted file mode 100644
index 37dd2ff219b4e6e3c74d234b9fdc4bfb69941ee1..0000000000000000000000000000000000000000
--- a/gh-badges/templates/social-template.svg
+++ /dev/null
@@ -1,39 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=it.widths[0]+1 + (it.text[1] && it.text[1].length > 0 ? it.widths[1]+2 : 0)}}" height="20">
-  {{it.widths[1]-=4;}}
-  <style type="text/css"><![CDATA[
-    a #llink:hover { fill:url(#b); stroke:#ccc; }
-    a #rlink:hover { fill:#4183C4; }
-  ]]></style>
-  <linearGradient id="a" x2="0" y2="100%">
-    <stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/>
-    <stop offset="1" stop-opacity=".1"/>
-  </linearGradient>
-  <linearGradient id="b" x2="0" y2="100%">
-    <stop offset="0" stop-color="#ccc" stop-opacity=".1"/>
-    <stop offset="1" stop-opacity=".1"/>
-  </linearGradient>
-  <g stroke="#d5d5d5">
-    <rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="{{=it.widths[0]}}" height="19" rx="2"/>
-    {{?(it.text[1] && it.text[1].length)}}
-    <rect y="0.5" x="{{=it.widths[0]+6.5}}" width="{{=it.widths[1]}}" height="19" rx="2" fill="#fafafa"/>
-    <rect x="{{=it.widths[0]+6}}" y="7.5" width="0.5" height="5" stroke="#fafafa"/>
-    <path d="M{{=it.widths[0]+6.5}} 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/>
-    {{?}}
-  </g>
-  {{?it.logo}}
-    <image x="5" y="3" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
-  {{?}}
-  <g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="110px" line-height="14px">
-    <text x="{{=((it.widths[0]+it.logoWidth+it.logoPadding)/2)*10}}" y="150" fill="#fff" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-    <text x="{{=((it.widths[0]+it.logoWidth+it.logoPadding)/2)*10}}" y="140" transform="scale(0.1)" textLength="{{=(it.widths[0]-(10+it.logoWidth+it.logoPadding))*10}}" lengthAdjust="spacing">{{=it.escapedText[0]}}</text>
-    {{?(it.text[1] && it.text[1].length)}}
-      <text x="{{=(it.widths[0]+it.widths[1]/2+6)*10}}" y="150" fill="#fff" transform="scale(0.1)" textLength="{{=(it.widths[1]-8)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-      {{?(it.links[0] && it.links[0].length || it.links[1] && it.links[1].length)}}<a target="_blank" xlink:href="{{=it.links[1] || it.links[0]}}">{{?}}
-        <text id="rlink" x="{{=(it.widths[0]+it.widths[1]/2+6)*10}}" y="140" transform="scale(0.1)" textLength="{{=(it.widths[1]-8)*10}}" lengthAdjust="spacing">{{=it.escapedText[1]}}</text>
-      {{?(it.links[0] && it.links[0].length || it.links[1] && it.links[1].length)}}</a>{{?}}
-    {{?}}
-  </g>
-  {{?(it.links[0] && it.links[0].length)}}<a target="_blank" xlink:href="{{=it.links[0]}}">{{?}}
-      <rect id="llink" stroke="#d5d5d5" fill="url(#a)" x="0.5" y="0.5" width="{{=it.widths[0]}}" height="19" rx="2"/>
-  {{?(it.links[0] && it.links[0].length)}}</a>{{?}}
-</svg>
diff --git a/lib/logos.js b/lib/logos.js
index adbda6c0b6e47651f77dee5b8ba7884f112a22be..d56cd9163c6d1c1afdaebdc42df47513ef0be91c 100644
--- a/lib/logos.js
+++ b/lib/logos.js
@@ -1,7 +1,7 @@
 'use strict'
 
 const Joi = require('@hapi/joi')
-const { toSvgColor } = require('../gh-badges/lib/color')
+const { toSvgColor } = require('../badge-maker/lib/color')
 const coalesce = require('../core/base-service/coalesce')
 const { svg2base64 } = require('./svg-helpers')
 const logos = require('./load-logos')()
diff --git a/package-lock.json b/package-lock.json
index 35757ce458b8ed055e265b6d2a2e3972ef72dc9a..e21c824c3ed68f4c87e362c053d7cbdb36c2f7da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3259,7 +3259,8 @@
     "@hapi/address": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
-      "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ=="
+      "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==",
+      "dev": true
     },
     "@hapi/bourne": {
       "version": "1.3.2",
@@ -4920,6 +4921,14 @@
       "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
       "dev": true
     },
+    "anafanafo": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-1.0.0.tgz",
+      "integrity": "sha512-pDPbI7SFRHu0byMXHAf+v74+LCcHSxnLYkcbfiV91XRlE+NSLpFCpEQdVUy9ZxZw/LuhTrOin4r8wlR3OFrKBA==",
+      "requires": {
+        "char-width-table-consumer": "^1.0.0"
+      }
+    },
     "ansi-align": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
@@ -5111,22 +5120,12 @@
       "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
       "dev": true
     },
-    "array-parallel": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz",
-      "integrity": "sha1-j3hTCJJu1apHjEfmTRszS2wMlH0="
-    },
     "array-reduce": {
       "version": "0.0.0",
       "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
       "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
       "dev": true
     },
-    "array-series": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz",
-      "integrity": "sha1-3103v8XC7wdV4qpPkv6ufUtaly8="
-    },
     "array-union": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -6697,6 +6696,13 @@
       "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
       "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
     },
+    "badge-maker": {
+      "version": "file:badge-maker",
+      "requires": {
+        "anafanafo": "^1.0.0",
+        "is-css-color": "^1.0.0"
+      }
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -6827,6 +6833,11 @@
       "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
       "dev": true
     },
+    "binary-search": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
+      "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+    },
     "bindings": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@@ -6910,7 +6921,8 @@
     "boolbase": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
-      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+      "dev": true
     },
     "boxen": {
       "version": "4.2.0",
@@ -7851,6 +7863,14 @@
         }
       }
     },
+    "char-width-table-consumer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz",
+      "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
+      "requires": {
+        "binary-search": "^1.3.5"
+      }
+    },
     "chardet": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
@@ -8514,6 +8534,7 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz",
       "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==",
+      "dev": true,
       "requires": {
         "q": "^1.1.2"
       }
@@ -8583,7 +8604,8 @@
     "colors": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
-      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM="
+      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+      "dev": true
     },
     "combined-stream": {
       "version": "1.0.6",
@@ -9195,6 +9217,7 @@
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
       "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
+      "dev": true,
       "requires": {
         "lru-cache": "^4.0.1",
         "which": "^1.2.9"
@@ -9339,7 +9362,8 @@
     "css-select-base-adapter": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz",
-      "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA="
+      "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA=",
+      "dev": true
     },
     "css-selector-tokenizer": {
       "version": "0.7.2",
@@ -9367,6 +9391,7 @@
       "version": "1.0.0-alpha.28",
       "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz",
       "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==",
+      "dev": true,
       "requires": {
         "mdn-data": "~1.1.0",
         "source-map": "^0.5.3"
@@ -9375,12 +9400,14 @@
     "css-url-regex": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz",
-      "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w="
+      "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=",
+      "dev": true
     },
     "css-what": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
-      "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0="
+      "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=",
+      "dev": true
     },
     "cssesc": {
       "version": "3.0.0",
@@ -9469,6 +9496,7 @@
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz",
       "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==",
+      "dev": true,
       "requires": {
         "css-tree": "1.0.0-alpha.29"
       },
@@ -9477,6 +9505,7 @@
           "version": "1.0.0-alpha.29",
           "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz",
           "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==",
+          "dev": true,
           "requires": {
             "mdn-data": "~1.1.0",
             "source-map": "^0.5.3"
@@ -10325,6 +10354,7 @@
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
       "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+      "dev": true,
       "requires": {
         "domelementtype": "~1.1.1",
         "entities": "~1.1.1"
@@ -10333,7 +10363,8 @@
         "domelementtype": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
-          "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
+          "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
+          "dev": true
         }
       }
     },
@@ -10352,7 +10383,8 @@
     "domelementtype": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
-      "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
+      "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
+      "dev": true
     },
     "domhandler": {
       "version": "2.3.0",
@@ -10373,11 +10405,6 @@
         "domelementtype": "1"
       }
     },
-    "dot": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.2.tgz",
-      "integrity": "sha1-xzdwGfxOVQeYkosrmv62ar+h8vk="
-    },
     "dot-prop": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -10662,7 +10689,8 @@
     "entities": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
-      "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
+      "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
+      "dev": true
     },
     "envinfo": {
       "version": "7.5.0",
@@ -16118,48 +16146,6 @@
         "assert-plus": "^1.0.0"
       }
     },
-    "gh-badges": {
-      "version": "file:gh-badges",
-      "requires": {
-        "anafanafo": "^1.0.0",
-        "dot": "^1.1.2",
-        "gm": "^1.23.0",
-        "is-css-color": "^1.0.0",
-        "svgo": "^1.1.1"
-      },
-      "dependencies": {
-        "@hapi/joi": {
-          "version": "15.0.3",
-          "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.0.3.tgz",
-          "integrity": "sha512-z6CesJ2YBwgVCi+ci8SI8zixoj8bGFn/vZb9MBPbSyoxsS2PnWYjHcyTM17VLK6tx64YVK38SDIh10hJypB+ig==",
-          "requires": {
-            "@hapi/address": "2.x.x",
-            "@hapi/topo": "3.x.x"
-          }
-        },
-        "anafanafo": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-1.0.0.tgz",
-          "integrity": "sha512-pDPbI7SFRHu0byMXHAf+v74+LCcHSxnLYkcbfiV91XRlE+NSLpFCpEQdVUy9ZxZw/LuhTrOin4r8wlR3OFrKBA==",
-          "requires": {
-            "char-width-table-consumer": "^1.0.0"
-          }
-        },
-        "binary-search": {
-          "version": "1.3.5",
-          "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.5.tgz",
-          "integrity": "sha512-RHFP0AdU6KAB0CCZsRMU2CJTk2EpL8GLURT+4gilpjr1f/7M91FgUMnXuQLmf3OKLet34gjuNFwO7e4agdX5pw=="
-        },
-        "char-width-table-consumer": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz",
-          "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
-          "requires": {
-            "binary-search": "^1.3.5"
-          }
-        }
-      }
-    },
     "git-config-path": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz",
@@ -16422,32 +16408,6 @@
         }
       }
     },
-    "gm": {
-      "version": "1.23.1",
-      "resolved": "https://registry.npmjs.org/gm/-/gm-1.23.1.tgz",
-      "integrity": "sha1-Lt7rlYCE0PjqeYjl2ZWxx9/BR3c=",
-      "requires": {
-        "array-parallel": "~0.1.3",
-        "array-series": "~0.1.5",
-        "cross-spawn": "^4.0.0",
-        "debug": "^3.1.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-        }
-      }
-    },
     "got": {
       "version": "9.6.0",
       "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -20436,6 +20396,7 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
       "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+      "dev": true,
       "requires": {
         "pseudomap": "^1.0.2",
         "yallist": "^2.1.2"
@@ -20605,7 +20566,8 @@
     "mdn-data": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
-      "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA=="
+      "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==",
+      "dev": true
     },
     "mdurl": {
       "version": "1.0.1",
@@ -20621,7 +20583,7 @@
     },
     "media-typer": {
       "version": "0.3.0",
-      "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
       "dev": true
     },
@@ -22289,6 +22251,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
       "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
+      "dev": true,
       "requires": {
         "boolbase": "~1.0.0"
       }
@@ -22871,6 +22834,7 @@
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
       "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+      "dev": true,
       "requires": {
         "define-properties": "^1.1.2",
         "es-abstract": "^1.5.1"
@@ -22897,6 +22861,7 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz",
       "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=",
+      "dev": true,
       "requires": {
         "define-properties": "^1.1.2",
         "es-abstract": "^1.6.1",
@@ -24768,7 +24733,8 @@
     "pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
     },
     "psl": {
       "version": "1.1.29",
@@ -24854,7 +24820,8 @@
     "q": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
+      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+      "dev": true
     },
     "qs": {
       "version": "6.5.2",
@@ -26453,7 +26420,8 @@
     "sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+      "dev": true
     },
     "sazerac": {
       "version": "1.1.0",
@@ -27711,7 +27679,8 @@
     "stable": {
       "version": "0.1.8",
       "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
-      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w=="
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+      "dev": true
     },
     "stack-trace": {
       "version": "0.0.10",
@@ -28866,6 +28835,7 @@
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.1.1.tgz",
       "integrity": "sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==",
+      "dev": true,
       "requires": {
         "coa": "~2.0.1",
         "colors": "~1.1.2",
@@ -28887,6 +28857,7 @@
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.0.tgz",
           "integrity": "sha512-MGhoq1S9EyPgZIGnts8Yz5WwUOyHmPMdlqeifsYs/xFX7AAm3hY0RJe1dqVlXtYPI66Nsk39R/sa5/ree6L2qg==",
+          "dev": true,
           "requires": {
             "boolbase": "^1.0.0",
             "css-what": "2.1",
@@ -28898,6 +28869,7 @@
           "version": "1.7.0",
           "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
           "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+          "dev": true,
           "requires": {
             "dom-serializer": "0",
             "domelementtype": "1"
@@ -28906,12 +28878,14 @@
         "minimist": {
           "version": "0.0.8",
           "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+          "dev": true
         },
         "mkdirp": {
           "version": "0.5.1",
           "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
           "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+          "dev": true,
           "requires": {
             "minimist": "0.0.8"
           }
@@ -29691,7 +29665,8 @@
     "unquote": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
-      "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ="
+      "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
+      "dev": true
     },
     "unset-value": {
       "version": "1.0.0",
@@ -30009,6 +29984,7 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
       "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+      "dev": true,
       "requires": {
         "define-properties": "^1.1.2",
         "object.getownpropertydescriptors": "^2.0.3"
@@ -30928,7 +30904,8 @@
     "yallist": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "dev": true
     },
     "yaml": {
       "version": "1.7.2",
diff --git a/package.json b/package.json
index 1774a857a3b5acf496b74b2cbcb664dffebebaa9..e64f56ad1d35e377359d3089c47ce68406e32367 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
     "escape-string-regexp": "^2.0.0",
     "fast-xml-parser": "^3.16.0",
     "fsos": "^1.1.6",
-    "gh-badges": "file:gh-badges",
+    "badge-maker": "file:badge-maker",
     "glob": "^7.1.6",
     "graphql": "^14.6.0",
     "graphql-tag": "^2.10.3",
@@ -84,7 +84,7 @@
     "test:frontend": "cross-env NODE_ENV=test ts-mocha --config .mocharc-frontend.yml \"frontend/**/*.spec.@(js|ts|tsx)\"",
     "test:e2e": "cypress run",
     "test:core": "cross-env NODE_CONFIG_ENV=test mocha \"core/**/*.spec.js\" \"lib/**/*.spec.js\" \"services/**/*.spec.js\"",
-    "test:package": "mocha \"gh-badges/**/*.spec.js\"",
+    "test:package": "mocha \"badge-maker/**/*.spec.js\"",
     "test:entrypoint": "cross-env NODE_CONFIG_ENV=test mocha entrypoint.spec.js",
     "test:integration": "cross-env NODE_CONFIG_ENV=test mocha \"core/**/*.integration.js\" \"services/**/*.integration.js\"",
     "test:services": "cross-env NODE_CONFIG_ENV=test mocha --delay core/service-test-runner/cli.js",
diff --git a/services/snyk/snyk-vulnerability-github.service.js b/services/snyk/snyk-vulnerability-github.service.js
index 878584ae7f1ee13e89e14050c21ed2552d2a895f..ede8a0a503e17d5444fe251b31307cb4239d979f 100644
--- a/services/snyk/snyk-vulnerability-github.service.js
+++ b/services/snyk/snyk-vulnerability-github.service.js
@@ -27,7 +27,7 @@ module.exports = class SnykVulnerabilityGitHub extends SynkVulnerabilityBase {
         namedParams: {
           user: 'badges',
           repo: 'shields',
-          manifestFilePath: 'gh-badges/package.json',
+          manifestFilePath: 'badge-maker/package.json',
         },
         staticPreview: this.render({ vulnerabilities: '0' }),
         documentation: `
diff --git a/services/snyk/snyk-vulnerability-github.tester.js b/services/snyk/snyk-vulnerability-github.tester.js
index ab660eb8e2d0c5d5f63aa8f854cb984a0dd62011..23305db826bb12ca896c17fbfb24ffa4e1a4e7fa 100644
--- a/services/snyk/snyk-vulnerability-github.tester.js
+++ b/services/snyk/snyk-vulnerability-github.tester.js
@@ -32,7 +32,7 @@ t.create('valid target manifest path')
   })
 
 t.create('invalid target manifest path')
-  .get('/badges/shields/gh-badges/requirements.txt.json')
+  .get('/badges/shields/badge-maker/requirements.txt.json')
   .timeout(20000)
   .expectBadge({
     label: 'vulnerabilities',
@@ -66,12 +66,12 @@ t.create('repo has vulnerabilities')
   })
 
 t.create('target manifest file has no vulnerabilities')
-  .get('/badges/shields/gh-badges/package.json.json')
+  .get('/badges/shields/badge-maker/package.json.json')
   .intercept(nock =>
     nock('https://snyk.io/test/github/badges/shields')
       .get('/badge.svg')
       .query({
-        targetFile: 'gh-badges/package.json',
+        targetFile: 'badge-maker/package.json',
       })
       .reply(200, zeroVulnerabilitiesSvg)
   )
@@ -82,12 +82,12 @@ t.create('target manifest file has no vulnerabilities')
   })
 
 t.create('target manifest file has vulnerabilities')
-  .get('/badges/shields/gh-badges/package.json.json')
+  .get('/badges/shields/badge-maker/package.json.json')
   .intercept(nock =>
     nock('https://snyk.io/test/github/badges/shields')
       .get('/badge.svg')
       .query({
-        targetFile: 'gh-badges/package.json',
+        targetFile: 'badge-maker/package.json',
       })
       .reply(200, twoVulnerabilitiesSvg)
   )