Setup
Use Bindery.makeBook({ options })
to create a book and display it
immediately on page load. It takes an object of options as described below.
// With required options
Bindery.makeBook({
content: '#content',
});
content
If the content is on the same page, use a CSS selector or a reference to the node. If the content must be fetched from a remote page, pass an object in the form of { url: String, selector: String }
.
// CSS selector
Bindery.makeBook({
content: '#content',
});
// Element
const el = document.getElementByID('content');
Bindery.makeBook({
content: el,
});
// Fetch from a URL
Bindery.makeBook({
content: {
selector: '#content',
url: '/posts.html',
}
});
pageSetup
size:
Book size, in the form of{ width: String, height: String }
. Values must include absolute CSS units.margin:
Book margin, in the form of{ top: String, outer: String, bottom: String, inner: String }
. Values must include absolute CSS units.
Bindery.makeBook({
content: '#content',
pageSetup: {
size: { width: '4in', height: '6in' },
margin: { top: '12pt', inner: '12pt', outer: '16pt', bottom: '20pt' },
},
});
printSetup
Note that setting the paper size through bindery only works in Chrome and Opera as of 2017. Users with other browsers must set the size in the system print dialog.
layout:
PAGES
One page per sheet, in numerical orderdefault
SPREADS
Two pages per sheet, in numerical orderBOOKLET
Two pages per sheet, in booklet order. For printing double sided and folding into a saddle stitched booklet.
paper:
AUTO
Sets paper to the size of the page or, if the layout isspreads
orbooklet
, twice as wide as the page. Note that marks will not be visible.default
AUTO_BLEED
The size of the page plus the size of the bleed. Note that marks will not be visible.AUTO_MARKS
The size of the page plus room for crop and bleed marks.LETTER_PORTRAIT
LETTER_LANDSCAPE
A4_PORTRAIT
A4_LANDSCAPE
marks:
NONE
CROP
Note that crop marks are always outset by the bleed amount.default
BLEED
BOTH
bleed:
Amount of bleed. Values must include absolute CSS units. This affects the size of full-bleed pages and spreads, and sets the position of bleed and crop marks.
Bindery.makeBook({
content: '#content',
printSetup: {
layout: Bindery.Layout.BOOKLET,
paper: Bindery.Paper.AUTO_BLEED,
marks: Bindery.Marks.CROP,
bleed: '12pt',
},
});
Preview
view:
PREVIEW
shows the spreads of the book as they will appear when the book is trimmed and bound. If you choose this mode, Bindery will switch toPRINT
before printing.default
PRINT
shows the complete printed sheet, which may include multiple pages, marks, and bleed if those options are enabled. Note that when printing a booklet, pages will appear out of order.FLIPBOOK
shows a three-dimensional preview, making it easy to visualize which pages will end up on the backs of others. If you choose this mode, Bindery will switch toPRINT
before printing.
Bindery.makeBook({
content: '#content',
view: Bindery.View.FLIPBOOK,
})
Flowing Content
Book content runs within the margins on the front and back of every
page. You can set a series of rules that change the book flow.
Rules are triggered by selectors, like CSS. For example, you might want to
start all h2
elements on a new page, and make all .big-figure
elements
into a full-bleed spread across two pages:
Bindery.makeBook({
content: '#content',
rules: [
Bindery.PageBreak({ selector: 'h2', position: 'before' }),
Bindery.FullBleedSpread({ selector: '.big-figure' }),
],
});
You may prefer to create rules separately:
let breakRule = Bindery.PageBreak({
selector: 'h2',
position: 'before',
});
let spreadRule = Bindery.FullBleedSpread({
selector: '.big-figure',
});
Bindery.makeBook({
content: '#content',
rules: [ breakRule, spreadRule ],
});
PageBreak
Adds or avoids page breaks for the selected element.
selector:
Which elements the rule should be applied to.position:
'before'
insert a break before the element, so it starts on a new page'after'
insert a break after the element'both'
insert breaks before and after the element'avoid'
prevents the element from breaking in the middle, by pushing it to the next page.
continue:
will insert an extra break when appropriate so that the flow will resume on a specific page.Optional
'next'
default
'left'
'right'
// Make sure chapter titles always start on a righthand page.
Bindery.PageBreak({
selector: 'h2',
position: 'before',
continue: 'right'
})
Split
Add a class when an element splits across two pages, to customize the styling.
Bindery makes as few assumptions as possible about your intended design— by default,
text-indent will be removed from <p>
s that started on
the previous page, and the bullet will be hidden for
<li>
s that started on the previous page. Everything else you should specify
yourself—for example, you may want to remove margin, padding, or borders
when your element splits.
See example.
selector:
Which elements the rule should be applied to.toNext:
Class applied to elements that will continue onto the next page.Optional
fromPrevious:
Class applied to elements that started on a previous page.Optional
Bindery.Split({
selector: 'p',
toNext: 'to-next',
fromPrevious: 'from-previous',
}),
<!-- Before -->
<p>
Some books are
saddle stitched...
</p>
<!-- Page 1 -->
<p class='to-next'>
Some books are
</p>
<!-- Page 2 -->
<p class='from-previous'>
saddle stitched...
</p>
Counter
Increment a counter as the book flows. This is useful for numbering figures or sections. Bindery’s Counters can be used in place of CSS counters, which will not work as expected when the DOM is reordered to create a book.
incrementEl:
CSS Selector. Matching elements will increment the counter by 1.resetEl:
CSS Selector. Matching elements will set the counter to 0.Optional
replaceEl:
CSS Selector. Matching elements will display the value of the counter.replace:
A function that takes the selected element and the counter value, and returns an new element. By default, Bindery will simply replace the contents with the value of the counter.Optional
Here’s how you could number all <figure>
s, by replacing the
existing <span class='fig-num'>
s.
Bindery.Counter({
incrementEl: 'figure',
replaceEl: '.fig-num',
})
<!-- Before -->
<figure>
<img />
<figcaption>
<span class='fig-num'></span>
Here's the caption
</figcaption>
</figure>
<!-- After -->
<figure>
<img />
<figcaption>
<span class='fig-num'>1</span>
Here's the caption
</figcaption>
</figure>
Here’s how you could number all <p>
s, resetting each section,
by inserting new markup. See example.
Bindery.Counter({
incrementEl: 'p',
replaceEl: 'p',
resetEl: 'h2',
replace: (el, counterValue) => {
el.insertAdjacentHTML('afterbegin', `<i>P ${counterValue} </i>`);
return el;
}
}),
<!-- Before -->
<h2>Section 1</h2>
<p>Some books are
saddle stitched.</p>
<p>Other books are
perfect bound.</p>
<h2>Section 2</h2>
<p>eBooks are different
altogether.</p>
<!-- After -->
<h2>Section 1</h2>
<p><i>P 1</i>
Some books are
saddle stitched.</p>
<p><i>P 2</i>
Other books are
perfect bound.</p>
<h2>Section 2</h2>
<p><i>P 1</i>
eBooks are different
altogether.</p>
Page Elements
RunningHeader
An element added to each page. By default it will add a page number
at the top right of each page, but you can use render
to generate running
headers using your section titles.
Note that you can’t currently use multiple RunningHeader
rules at the same
time. However, you can create a custom rule that works similarly.
Bindery.RunningHeader({
render: (pageInfo) => pageInfo.isLeft
? `${pageInfo.number} · ${pageInfo.heading.h1}`
: `${pageInfo.heading.h2} · ${pageInfo.number}`
})
render:
A function that takes aPageInfo
and returns a string of HTML. You’ll probably want to use thenumber
,isLeft
,isEmpty
, andheading
properties — seePageInfo
for details.Optional
Footnote
Add a footnote to the bottom of the flow area. Footnotes cut into the area for text, so note that very large footnotes may bump the entire element to the next page.
selector:
Which elements the rule should be applied to.render:
A function that takes an element and number, and returns the footnote for that element. This footnote will be inserted at the bottom of the flow area.replace:
A function that takes the selected element and number, and returns an new element with a footnote indicator. By default, Bindery will simply insert the number as a superscript after the original element.Optional
Bindery.Footnote({
selector: 'p > a',
render: (element, number) => {
return '<i>' + number + '</i>: Link to ' + element.href;
}
}),
FullBleedPage
Removes the selected element from the ordinary flow of the book and places it on its own
page. Good for displaying figures and imagery. You can use CSS to do your own layout on this page— width: 100%; height: 100%
will fill the whole bleed area.
selector:
Which elements the rule should be applied to.continue:
Where to resume the book flow after adding the full bleed page.Optional
'same'
Continues on the previous page where the element would have been. This will fill the remainder of that page, avoiding a gap, though note that it results in a different order than your original markup.default
'next'
Continues on a new page'left'
Continues on the next left page, inserting another page when appropriate'right'
Continues on the next right page, inserting another page when appropriate
rotate:
Add a rotation the full-bleed content.Optional
'none'
default
'clockwise'
The top will become the left edge'counterclockwise'
The top will become the right edge'inward'
The top will become the outside edge'outward'
The top will become the inside edge
Bindery.FullBleedPage({
selector: '.big-figure',
continue: 'same'
}),
FullBleedSpread
The same as FullBleedPage
, but places the element across two pages.
selector:
Which elements the rule should be applied to.continue:
Where to resume the book flow after adding the full bleed element.Optional
'same'
default
Continue where the element was, so there’s not a blank gap before the spread.'next'
Continues on a new page after the spread.'left'
Continues on the next left page after the spread'right'
Continues on the next right page after the spread
rotate:
Add a rotation the full-bleed content.Optional
'none'
default
'clockwise'
The top will become the left edge'counterclockwise'
The top will become the right edge
Bindery.FullBleedSpread({
selector: '.wide-figure',
continue: 'next',
rotate: 'clockwise',
}),
Referencing Pages
If your web content has internal links or navigation, you can use a PageReference
to insert the page number the content will eventually end up on. You can use
them to create traditional book navigation elements, like a table of contents, index, endnotes,
without having to update them every time you change the page size or style.
PageReference
selector:
Which elements the rule should be applied to.replace:
A function that takes an element and a page range, and must return a new element. By default, Bindery will insert the page range after the original element.Optional
createTest:
A function that takes your reference element and returns a test function. The test function receives a page element, and should return true if the reference can be found. By default, the test function will look for the anchor tag of the reference element’shref
property, which is useful for a table of contents. Use a custom function to create an index.Optional
Creating a Table of Contents
A table of contents is a PageReference that points to a specific page. By default, PageReference will look for anchor links. To create a table of contents, do this:
Bindery.PageReference({
selector'.toc a',
replace: (element, number) => {
let row = document.createElement('div');
row.classList.add('toc-row');
row.innerHTML = element.textContent;
row.innerHTML += `<span class='num'>${number}</span>`;
return row;
}
})
You can use any library that creates HTML elements, for example nanohtml:
const html = import 'nanohtml';
Bindery.PageReference({
selector'.toc a',
replace: (element, number) => html`
<div class="toc-row">
<span>${element.textContent}</span>
<span class="num">${number}</span>
</div>
`;
})
This will transform the HTML of your anchor links like this:
<!-- Before -->
<nav class='toc'>
<a href='#chapter1'>
Chapter 1
</a>
</nav>
<!-- After -->
<nav class='toc'>
<div class='toc-row'>
<span>Chapter 1</span>
<span class='num'>5</span>
</div>
</nav>
Creating an Index
An index is a PageReference that points to content on a range of pages. There are many way you might create an index. In the following example, rather than
checking the href
, Bindery will search the entire text of each page to see if contains the text of your reference element.
Bindery.PageReference({
selector: '.index-content li',
createTest: (el) => {
const searchTerm = el.textContent.toLowerCase().trim();
return (page) => {
const textOfPage = page.textContent.toLowerCase();
return textOfPage.includes(searchTerm);
}
},
})
This will transform the list items as below:
<!-- Before -->
<p>
Some books are
saddle stitched...
</p>
<ul class='index-content'>
<li>Saddle Stitch</li>
</ul>
<!-- After -->
<p>
Some books are
saddle stitched...
</p>
<ul class='index-content'>
<li>Saddle Stitch, 5</li>
</ul>
If you didn’t want to match on the exact string, you could use other selectors or attribute, or use a fuzzier method of searching. Just create your own testing function from the index entry.
Bindery.PageReference({
selector: '[data-ref]',
createTest: (el) => {
let selector = el.getAttribute('data-ref');
return (page) => page.querySelector(selector);
},
})
<!-- Before -->
<p data-id='perfectBind'>
Most books are perfect bound.
</p>
<ul>
<li data-ref='perfectBind'>
Binding, Perfect
</li>
</ul>
<!-- After -->
<p data-id='perfectBind'>
Most books are perfect bound.
</p>
<ul>
<li data-ref='perfectBind'>
Binding, Perfect: 5
</li>
</ul>
Note that we can’t know what page something will end up on until the book layout
is complete, so make sure that your replace
function doesn’t
change the layout drastically.
Advanced
PageInfo
You may receive instances of this class when using custom rules, but will not create them yourself.
number
the page number, with the first page being 1heading
The current hierarchy of headings from previous pages, in the form of{ h1: String, h2: String, ... h6: String }
isEmpty
Bool
Whether the page includes flow contentisRight
Bool
The page is on the right (the front)isLeft
Bool
The page is on the left (the back)
BookInfo
You may receive instances of this class when using custom rules, but will not create them yourself.
pages
Array of PageInfo