diff --git a/config/software/designs.json b/config/software/designs.json
index beacd7f4d60..1981048f103 100644
--- a/config/software/designs.json
+++ b/config/software/designs.json
@@ -1,6 +1,7 @@
{
"accessories": {
"benjamin": "A FreeSewing pattern for a bow tie",
+ "bob": "FIXME: A FreeSewing pattern that needs a description",
"florence": "A FreeSewing pattern for a face mask",
"florent": "A FreeSewing pattern for a flat cap",
"hi": "A FreeSewing pattern for a shark plush toy",
@@ -57,4 +58,4 @@
"rendertest": "A FreeSewing pattern to test (y)our render engine our CSS",
"tutorial": "A FreeSewing pattern for a baby bib that's used in our tutorial"
}
-}
+}
\ No newline at end of file
diff --git a/designs/bob/CHANGELOG.md b/designs/bob/CHANGELOG.md
new file mode 100644
index 00000000000..1f7679bb78a
--- /dev/null
+++ b/designs/bob/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Change log for: @freesewing/bob
+
+
+
+This is the **initial release**, and the start of this change log.
+
+> Prior to version 2, FreeSewing was not a JavaScript project.
+> As such, that history is out of scope for this change log.
+
diff --git a/designs/bob/README.md b/designs/bob/README.md
new file mode 100644
index 00000000000..c39d755c685
--- /dev/null
+++ b/designs/bob/README.md
@@ -0,0 +1,268 @@
+
+
+
+
+
+
+
+
+
+
+
+
+# @freesewing/bob
+
+FIXME: A FreeSewing pattern that needs a description
+
+
+
+## What am I looking at? π€
+
+This repository is our *monorepo*
+holding [all our NPM packages](https://freesewing.dev/reference/packages/).
+
+This folder holds: @freesewing/bob
+
+If you're not entirely sure what to do or how to start, type this command:
+
+```
+npm run tips
+```
+
+> If you don't want to set up a dev environment, you can run it in your browser:
+>
+> [](https://gitpod.io/#https://github.com/freesewing/freesewing)
+>
+> We recommend that you fork our repository and then
+> put `gitpod.io/# to start up a browser-based dev environment of your own.
+
+## About FreeSewing π
+
+Where the world of makers and developers collide, that's where you'll find FreeSewing.
+
+If you're a maker, checkout [freesewing.org](https://freesewing.org/) where you can generate
+our sewing patterns adapted to your measurements.
+
+If you're a developer, our documentation is on [freesewing.dev](https://freesewing.dev/).
+Our [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox
+for parametric design of sewing patterns. But we also provide a range
+of [plugins](https://freesewing.dev/reference/plugins/) that further extend the
+functionality of the platform.
+
+If you have NodeJS installed, you can try it right now by running:
+
+```bash
+npx create-freesewing-pattern
+```
+
+Or, consult our getting started guides
+for [Linux](https://freesewing.dev/tutorials/getting-started-linux/),
+[MacOS](https://freesewing.dev/tutorials/getting-started-mac/),
+or [Windows](https://freesewing.dev/tutorials/getting-started-windows/).
+
+We also have a [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) that
+walks you through your first parametric design,
+and [a friendly community](https://freesewing.org/community/where/) with
+people who can help you when you get stuck.
+
+## Support FreeSewing: Become a patron π₯°
+
+FreeSewing is an open source project run by a community,
+and financially supported by our patrons.
+
+If you feel what we do is worthwhile, and you can spend a few coind without
+hardship, then you should [join us and become a patron](https://freesewing.org/community/join).
+
+## Links π©βπ»
+
+ - π» Makers website: [freesewing.org](https://freesewing.org)
+ - π» Developers website: [freesewing.dev](https://freesewing.dev)
+ - π¬ Chat: On Discord via [discord.freesewing.org](https://discord.freesewing.org/)
+ - β
Todo list/Kanban board: On Github via [todo.freesewing.org](https://todo.freesewing.org/)
+ - π¦ Twitter: [@freesewing_org](https://twitter.com/freesewing_org)
+ - π· Instagram: [@freesewing_org](https://instagram.com/freesewing_org)
+
+## License: MIT π€
+
+Β© [Joost De Cock](https://github.com/joostdecock).
+See [the license file](https://github.com/freesewing/freesewing/blob/develop/LICENSE) for details.
+
+## Where to get help π€―
+
+Our [chatrooms on Discord](https://chat.freesewing.org/) are the best place to ask questions,
+share your feedback, or just hang out.
+
+If you want to report a problem, please [create an issue](https://github.com/freesewing/freesewing/issues/new).
+
+
+
+## Contributors β¨
+
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+
+
+
+
+
+
+
+
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+
diff --git a/designs/bob/build.js b/designs/bob/build.js
new file mode 100644
index 00000000000..7248b245c69
--- /dev/null
+++ b/designs/bob/build.js
@@ -0,0 +1,57 @@
+/* This script will build the package with esbuild */
+const esbuild = require('esbuild')
+const pkg = require('./package.json')
+
+// Create banner based on package info
+const banner = `/**
+ * ${pkg.name} | v${pkg.version}
+ * ${pkg.description}
+ * (c) ${new Date().getFullYear()} ${pkg.author}
+ * @license ${pkg.license}
+ */`
+
+// Shared esbuild options
+const options = {
+ banner: { js: banner },
+ bundle: true,
+ entryPoints: ['src/index.js'],
+ external: ["@freesewing"],
+ metafile: process.env.VERBOSE ? true : false,
+ minify: process.env.NO_MINIFY ? false : true,
+ sourcemap: true,
+}
+
+// Different formats
+const formats = {
+ cjs: "dist/index.js",
+ esm: "dist/index.mjs",
+}
+
+// Let esbuild generate different formats
+let result
+(async () => {
+ for (const [format, outfile] of Object.entries(formats)) {
+ result = await esbuild
+ .build({ ...options, outfile, format })
+ .catch(() => process.exit(1))
+ }
+
+ if (process.env.VERBOSE) {
+ const info = await esbuild.analyzeMetafile(result.metafile)
+ console.log(info)
+ }
+
+ // Also build a version that has all dependencies bundled
+ // This makes it easy to run tests
+ await esbuild
+ .build({
+ ...options,
+ minify: false,
+ sourcemap: false,
+ outfile: 'tests/dist/index.mjs',
+ format: 'esm',
+ external: [],
+ })
+ .catch(() => process.exit(1))
+
+})()
diff --git a/designs/bob/config.js b/designs/bob/config.js
new file mode 100644
index 00000000000..ad641ed7143
--- /dev/null
+++ b/designs/bob/config.js
@@ -0,0 +1,23 @@
+import { version } from './package.json'
+
+export default {
+ version,
+ name: 'bob',
+ design: 'Joost De Cock',
+ code: 'Joost De Cock',
+ department: 'accessories',
+ type: 'pattern',
+ difficulty: 2,
+ optionGroups: {
+ fit: ['neckRatio', 'widthRatio', 'lengthRatio'],
+ size: ['headSize'],
+ },
+ optionalMeasurements: ['head'],
+ parts: ['bib'],
+ options: {
+ neckRatio: { pct: 80, min: 70, max: 90 },
+ widthRatio: { pct: 45, min: 35, max: 55 },
+ lengthRatio: { pct: 75, min: 55, max: 85 },
+ headSize: { pct: 100, min: 10, max: 200, snap: 5 },
+ },
+}
diff --git a/designs/bob/package.json b/designs/bob/package.json
new file mode 100644
index 00000000000..8673a6e0319
--- /dev/null
+++ b/designs/bob/package.json
@@ -0,0 +1,65 @@
+{
+ "name": "@freesewing/bob",
+ "version": "2.21.0-rc.0",
+ "description": "FIXME: A FreeSewing pattern that needs a description",
+ "author": "Joost De Cock (https://github.com/joostdecock)",
+ "homepage": "https://freesewing.org/",
+ "repository": "github:freesewing/freesewing",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/freesewing/freesewing/issues"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://freesewing.org/patrons/join"
+ },
+ "keywords": [
+ "freesewing",
+ "design",
+ "diy",
+ "fashion",
+ "made to measure",
+ "parametric design",
+ "pattern",
+ "sewing",
+ "sewing pattern"
+ ],
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "scripts": {
+ "build": "node build.js",
+ "clean": "rimraf dist",
+ "mbuild": "NO_MINIFY=1 node build.js",
+ "symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -",
+ "test": "BABEL_ENV=production npx mocha tests/*.test.mjs --require @babel/register",
+ "vbuild": "VERBOSE=1 node build.js",
+ "lab": "cd ../../sites/lab && yarn start",
+ "tips": "node ../../scripts/help.mjs",
+ "prettier": "npx prettier --write 'src/*.js' 'config/*.js'",
+ "testci": "BABEL_ENV=production npx mocha tests/*.test.mjs --require @babel/register --reporter ../../tests/reporters/terse.js",
+ "cibuild_step5": "node build.js"
+ },
+ "peerDependencies": {
+ "@freesewing/core": "^2.21.0-rc.0",
+ "@freesewing/plugin-bundle": "^2.21.0-rc.0",
+ "@freesewing/config-helpers": "^2.21.0-rc.0"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "mocha": "^10.0.0",
+ "chai": "^4.2.0"
+ },
+ "files": [
+ "dist/*",
+ "README.md",
+ "package.json"
+ ],
+ "publishConfig": {
+ "access": "public",
+ "tag": "next"
+ },
+ "engines": {
+ "node": ">=14.0.0",
+ "npm": ">=6"
+ }
+}
diff --git a/designs/bob/src/bib.js b/designs/bob/src/bib.js
new file mode 100644
index 00000000000..50bd28f67fe
--- /dev/null
+++ b/designs/bob/src/bib.js
@@ -0,0 +1,236 @@
+export default function (part) {
+ const {
+ Point,
+ points,
+ Path,
+ paths,
+ optionalMeasurements,
+ options,
+ macro,
+ raise,
+ complete,
+ snippets,
+ Snippet,
+ paperless,
+ } = part.shorthand()
+
+ // Head size
+ const head = (optionalMeasurements?.head || 360) * options.headSize
+
+ // Construct the neck opening
+ let tweak = 1
+ let target = (head * options.neckRatio) / 4
+ let delta
+ do {
+ points.right = new Point((tweak * head) / 10, 0)
+ points.bottom = new Point(0, (tweak * head) / 12)
+
+ points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
+ points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)
+
+ paths.neck = new Path()
+ .move(points.right)
+ .curve(points.rightCp1, points.bottomCp2, points.bottom)
+
+ delta = paths.neck.length() - target
+ if (delta > 0) tweak = tweak * 0.99
+ else tweak = tweak * 1.02
+ } while (Math.abs(delta) > 1)
+
+ points.rightCp2 = points.rightCp1.flipY()
+ points.bottomCp1 = points.bottomCp2.flipX()
+
+ points.left = points.right.flipX()
+ points.leftCp1 = points.rightCp2.flipX()
+ points.leftCp2 = points.rightCp1.flipX()
+
+ points.top = points.bottom.flipY()
+ points.topCp1 = points.bottomCp2.flipY()
+ points.topCp2 = points.bottomCp1.flipY()
+
+ // Construct the outline
+ let width = head * options.widthRatio
+ let length = head * options.lengthRatio
+
+ points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
+ points.topRight = points.topLeft.shift(0, width)
+ points.bottomLeft = points.topLeft.shift(-90, length)
+ points.bottomRight = points.topRight.shift(-90, length)
+
+ points.edgeLeft = new Point(points.topLeft.x, points.left.y)
+ points.edgeRight = new Point(points.topRight.x, points.right.y)
+ points.edgeTop = new Point(0, points.topLeft.y)
+
+ points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
+ points.edgeRightCp = points.edgeLeftCp.flipX()
+ points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
+ points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
+
+ // Round the end of the straps
+ let strap = points.edgeTop.dy(points.top)
+
+ points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
+ points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
+ points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
+
+ macro('round', {
+ from: points.edgeTop,
+ to: points.tipRight,
+ via: points.tipRightTop,
+ prefix: 'tipRightTop',
+ })
+ macro('round', {
+ from: points.tipRight,
+ to: points.top,
+ via: points.tipRightBottom,
+ prefix: 'tipRightBottom',
+ })
+
+ // Rotate straps so they don't overlap
+ let rotateThese = [
+ 'edgeTopLeftCp',
+ 'edgeTop',
+ 'tipRight',
+ 'tipRightTop',
+ 'tipRightTopStart',
+ 'tipRightTopCp1',
+ 'tipRightTopCp2',
+ 'tipRightTopEnd',
+ 'tipRightBottomStart',
+ 'tipRightBottomCp1',
+ 'tipRightBottomCp2',
+ 'tipRightBottomEnd',
+ 'tipRightBottom',
+ 'top',
+ 'topCp2',
+ ]
+
+ while (points.tipRightBottomStart.x > -1) {
+ for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
+ }
+
+ // Add points to anchor snaps on
+ points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
+
+ // Mirror points to the other side
+ points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
+ points.topCp1 = points.topCp2.flipX()
+ points.tipLeftTopStart = points.tipRightTopStart.flipX()
+ points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
+ points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
+ points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
+ points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
+ points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
+ points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
+ points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
+ points.snapRight = points.snapLeft.flipX()
+
+ // Round the bottom of the bib
+ // Radius is fixed, but you could use an option for it)
+ macro('round', {
+ from: points.topLeft,
+ to: points.bottomRight,
+ via: points.bottomLeft,
+ radius: points.bottomRight.x / 4,
+ prefix: 'bottomLeft',
+ })
+ macro('round', {
+ from: points.bottomLeft,
+ to: points.topRight,
+ via: points.bottomRight,
+ radius: points.bottomRight.x / 4,
+ prefix: 'bottomRight',
+ })
+
+ // Construct the path
+ paths.seam = new Path()
+ .move(points.edgeLeft)
+ .line(points.bottomLeftStart)
+ .curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
+ .line(points.bottomRightStart)
+ .curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
+ .line(points.edgeRight)
+ .curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
+ .curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
+ .curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
+ .curve(points.topCp1, points.rightCp2, points.right)
+ .curve(points.rightCp1, points.bottomCp2, points.bottom)
+ .curve(points.bottomCp1, points.leftCp2, points.left)
+ .curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
+ .curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
+ .curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
+ .curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
+ .close()
+ .attr('class', 'fabric')
+
+ raise.info(['biasTapeLength', { mm: paths.seam.length() }])
+ raise.info(['fabricHeight', { mm: points.tipRightTopStart.dy(points.bottomLeftEnd) }])
+ raise.info(['fabricWidth', { mm: points.bottomLeftStart.dx(points.bottomRightEnd) }])
+
+ // Complete?
+ if (complete) {
+ // Add the snaps
+ snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
+ snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
+
+ // Add the bias tape
+ paths.bias = paths.seam
+ .offset(-5)
+ .attr('class', 'various dashed')
+ .attr('data-text', 'finishWithBiasTape')
+ .attr('data-text-class', 'center fill-various')
+
+ // Add the title
+ points.title = points.bottom.shift(-90, 45)
+ macro('title', {
+ at: points.title,
+ nr: 1,
+ title: 'bib',
+ })
+
+ // Add the scalebox
+ points.scalebox = points.title.shift(-90, 55)
+ macro('scalebox', { at: points.scalebox })
+
+ // Add the logo
+ points.logo = new Point(0, 0)
+ snippets.logo = new Snippet('logo', points.logo)
+
+ // Paperless?
+ if (paperless) {
+ // Add dimensions
+ macro('hd', {
+ from: points.bottomLeftStart,
+ to: points.bottomRightEnd,
+ y: points.bottomLeft.y + 15,
+ })
+ macro('vd', {
+ from: points.bottomRightStart,
+ to: points.bottom,
+ x: points.bottomRight.x + 15,
+ })
+ macro('vd', {
+ from: points.bottomRightStart,
+ to: points.right,
+ x: points.bottomRight.x + 30,
+ })
+ macro('vd', {
+ from: points.bottomRightStart,
+ to: points.tipLeftTopStart,
+ x: points.bottomRight.x + 45,
+ })
+ macro('hd', {
+ from: points.left,
+ to: points.right,
+ y: points.left.y + 25,
+ })
+ macro('ld', {
+ from: points.tipLeftBottomEnd,
+ to: points.tipLeftTopStart,
+ d: -15,
+ })
+ }
+ }
+
+ return part
+}
diff --git a/designs/bob/src/box.js b/designs/bob/src/box.js
new file mode 100644
index 00000000000..18b3ab1e23b
--- /dev/null
+++ b/designs/bob/src/box.js
@@ -0,0 +1,49 @@
+export default function (part) {
+ const { options, Point, Path, points, paths, Snippet, snippets, complete, sa, paperless, macro } =
+ part.shorthand()
+
+ const w = 500 * options.size
+ points.topLeft = new Point(0, 0)
+ points.topRight = new Point(w, 0)
+ points.bottomLeft = new Point(0, w / 2)
+ points.bottomRight = new Point(w, w / 2)
+
+ paths.seam = new Path()
+ .move(points.topLeft)
+ .line(points.bottomLeft)
+ .line(points.bottomRight)
+ .line(points.topRight)
+ .line(points.topLeft)
+ .close()
+ .attr('class', 'fabric')
+
+ // Complete?
+ if (complete) {
+ points.logo = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5)
+ snippets.logo = new Snippet('logo', points.logo)
+ points.text = points.logo
+ .shift(-90, w / 8)
+ .attr('data-text', 'hello')
+ .attr('data-text-class', 'center')
+
+ if (sa) {
+ paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
+ }
+ }
+
+ // Paperless?
+ if (paperless) {
+ macro('hd', {
+ from: points.bottomLeft,
+ to: points.bottomRight,
+ y: points.bottomLeft.y + sa + 15,
+ })
+ macro('vd', {
+ from: points.bottomRight,
+ to: points.topRight,
+ x: points.topRight.x + sa + 15,
+ })
+ }
+
+ return part
+}
diff --git a/designs/bob/src/index.js b/designs/bob/src/index.js
new file mode 100644
index 00000000000..79d5f8ec73e
--- /dev/null
+++ b/designs/bob/src/index.js
@@ -0,0 +1,16 @@
+import freesewing from '@freesewing/core'
+import plugins from '@freesewing/plugin-bundle'
+import config from '../config'
+import draftBib from './bib'
+
+// Create new design
+const Bob = new freesewing.Design(config, plugins)
+
+// Attach the draft methods to the prototype
+Bob.prototype.draftBib = draftBib
+
+// Named exports
+export { config, Bob }
+
+// Default export
+export default Bob
diff --git a/designs/bob/tests/shared.test.mjs b/designs/bob/tests/shared.test.mjs
new file mode 100644
index 00000000000..264cb3fb140
--- /dev/null
+++ b/designs/bob/tests/shared.test.mjs
@@ -0,0 +1,42 @@
+// This file is auto-generated.
+// Changes you make will be overwritten.
+import chai from 'chai'
+import models from '@freesewing/models'
+import patterns from '@freesewing/pattern-info'
+import Pattern from './dist/index.mjs'
+
+// Shared tests
+import { testPatternConfig } from '../../../tests/patterns/config.mjs'
+import { testPatternDrafting } from '../../../tests/patterns/drafting.mjs'
+import { testPatternSampling } from '../../../tests/patterns/sampling.mjs'
+
+const expect = chai.expect
+
+
+// Test config
+testPatternConfig(
+ "bob",
+ new Pattern(),
+ expect,
+ models,
+ patterns,
+)
+
+// Test drafting
+testPatternDrafting(
+ "bob",
+ Pattern,
+ expect,
+ models,
+ patterns,
+ true
+)
+
+// Test sampling
+testPatternSampling(
+ "bob",
+ Pattern,
+ expect,
+ models,
+ patterns
+)
diff --git a/markdown/org/docs/patterns/bob/cutting/en.md b/markdown/org/docs/patterns/bob/cutting/en.md
new file mode 100644
index 00000000000..f7ac437efee
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/cutting/en.md
@@ -0,0 +1,13 @@
+---
+title: "Bob the Bib: Cutting Instructions"
+---
+
+- Cut **1 bib**
+
+
+
+#### Caveats
+
+- There is no seam allowance on the bib, it will be finished with bias tape
+
+
diff --git a/markdown/org/docs/patterns/bob/en.md b/markdown/org/docs/patterns/bob/en.md
new file mode 100644
index 00000000000..a60097b7bd7
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/en.md
@@ -0,0 +1,5 @@
+---
+title: "Bob the Bib"
+---
+
+
diff --git a/markdown/org/docs/patterns/bob/fabric/en.md b/markdown/org/docs/patterns/bob/fabric/en.md
new file mode 100644
index 00000000000..2678ffcf8da
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/fabric/en.md
@@ -0,0 +1,10 @@
+---
+title: "Bob the Bib: Fabric Options"
+---
+
+An bib should be soft, absorbent, and be able to handle plenty of washing cycles.
+In addition, natural fibres are much preferred, especially for babies.
+
+So use cotton, flannel, or jersey knit for the bib front of the bib, and back it with
+something like terrycloth (aka towel fabric) or a similar bamboo-based fabric.
+
diff --git a/markdown/org/docs/patterns/bob/instructions/en.md b/markdown/org/docs/patterns/bob/instructions/en.md
new file mode 100644
index 00000000000..77fd95d41d8
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/instructions/en.md
@@ -0,0 +1,56 @@
+---
+title: "Bob the Bib: Sewing Instructions"
+---
+
+
+
+#### Before we start: One layer or two?
+
+First thing you need to figure out is whether you're going to make a 1-layer (simple) bib,
+or do you want to make one out of two layers.
+
+A two-layer bib gives you more options, as you can use an absorbent backing layer like
+terrycloth, yet still have the entire cotton selection of your local fabric store available
+as your choice for the front layer, aka the visible one.
+
+
+
+## Step 1: Optional: Join layers
+
+If you decide to go for two layers, simply join them together at the edges before applying
+the bias tape.
+
+You probably want to pin or bast this prior to sewing. The two different types of fabric
+will behave differently, so secure them in place prior to sewing the together.
+
+Make sure to sew close to the edge so that the stitches will be under the bias tape
+finish later.
+
+
+## Step 2: Apply bias tape
+
+Wer're going to apply bias tape as a finish around the entire edge of the bib.
+
+You probably want to start at the bottom strap, as this way the joint of the bias
+seam will be covered when the bib is worn.
+
+Just work your way around, and take your time.
+
+
+
+Provide generic instructions for sewing bias tape
+
+
+
+
+## Step 3: Install snaps or button
+
+Next, install the snaps on the straps, as indicated on the pattern.
+
+If you don't have or want snaps, you can also use a button + buttonhole.
+
+
+## Step 4: You're done!
+
+That was easy π
+
diff --git a/markdown/org/docs/patterns/bob/measurements/en.md b/markdown/org/docs/patterns/bob/measurements/en.md
new file mode 100644
index 00000000000..f3170ac3b03
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/measurements/en.md
@@ -0,0 +1,5 @@
+---
+title: "Bob the Bib: Required Measurements"
+---
+
+
diff --git a/markdown/org/docs/patterns/bob/needs/en.md b/markdown/org/docs/patterns/bob/needs/en.md
new file mode 100644
index 00000000000..950e1cd3848
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/needs/en.md
@@ -0,0 +1,11 @@
+---
+title: "Bob the Bib What You Need"
+---
+
+To make Bob, you will need the following:
+
+- [Basic sewing supplies](/docs/sewing/basic-sewing-supplies)
+- A little bit of suitable fabric ([see Fabric options](/docs/patterns/aaron/fabric))
+- Enough bias tape to go around the entire bib
+- A snap or other type of fastener
+
diff --git a/markdown/org/docs/patterns/bob/options/en.md b/markdown/org/docs/patterns/bob/options/en.md
new file mode 100644
index 00000000000..892eb904f85
--- /dev/null
+++ b/markdown/org/docs/patterns/bob/options/en.md
@@ -0,0 +1,5 @@
+---
+title: "Bob the Bib: Design Options"
+---
+
+
diff --git a/packages/create-freesewing-pattern/package.json b/packages/create-freesewing-pattern/package.json
index 7a456e1d70a..1d0710c1eeb 100644
--- a/packages/create-freesewing-pattern/package.json
+++ b/packages/create-freesewing-pattern/package.json
@@ -42,7 +42,7 @@
"ora": "^5.4.1",
"p-each-series": "^2.2.0",
"parse-git-config": "^3.0.0",
- "validate-npm-package-name": "^4.0.0",
+ "validate-npm-package-name": "^3.0.0",
"which": "^2.0.2",
"@freesewing/i18n": "^2.21.0-rc.0",
"@freesewing/pattern-info": "^2.21.0-rc.0"
diff --git a/packages/pattern-info/src/index.js b/packages/pattern-info/src/index.js
index 807fdef5062..3b76b53d351 100644
--- a/packages/pattern-info/src/index.js
+++ b/packages/pattern-info/src/index.js
@@ -13,6 +13,7 @@ export const list = [
'bella',
'benjamin',
'bent',
+ 'bob',
'breanna',
'brian',
'bruce',
@@ -58,6 +59,7 @@ export const withoutBreasts = [
'albert',
'benjamin',
'bent',
+ 'bob',
'brian',
'bruce',
'carlton',
diff --git a/packages/pattern-info/src/prebuild.js b/packages/pattern-info/src/prebuild.js
index 8cb13308a2d..17e23b55984 100644
--- a/packages/pattern-info/src/prebuild.js
+++ b/packages/pattern-info/src/prebuild.js
@@ -6,6 +6,7 @@ const bee = require('@freesewing/bee').config
const bella = require('@freesewing/bella').config
const benjamin = require('@freesewing/benjamin').config
const bent = require('@freesewing/bent').config
+const bob = require('@freesewing/bob').config
const brian = require('@freesewing/brian').config
const breanna = require('@freesewing/breanna').config
const diana = require('@freesewing/diana').config
@@ -53,6 +54,7 @@ const patterns = {
bella,
benjamin,
bent,
+ bob,
brian,
breanna,
diana,