Five years ago, when, for the first time ever, I was invited to speak at one of the best front-end conferences in Europe, I had quite a mixture of feelings. Obviously, I was incredibly proud and happy: I had never had a chance to do this before for a diverse audience of people with different skillsets. But the other feelings I had were quite destructive.
I sincerely could not understand how I could be interesting to anyone: Even though I had been working in front-end for many years by then, I was very silent in the community.
Recently, I was leading a training session for one of our clients on best practices for implementing designs using HTML and CSS. Part of our time included a discussion of processes such as style-guide-driven development, approaches such as OOCSS and SMACSS, and modular design. Near the end of the last day, someone asked, “But how will we know if we’ve done it right?”
At first, I was confused. I had just spent hours telling them everything they need to “do it right.
Word association: Content marketing. You’re thinking ‘blog post,’ ‘infographic,’ maybe ‘video,’ ‘white paper.’ That kind of thing. Right? Not FAQs, 404 pages, About Us… The thing is, those pages are content too. Visitors read them to learn, understand and make decisions. FAQs are one of the most underused content marketing opportunities out there. About Us pages? They’re an incredible opportunity to tell your brand’s story. Put like that, it seems obvious – yet they’re often a lifeless formality. It’s the same with 404s, out of stock pages and thank you pages: in each case, you have a visitor’s attention when…
I’m big on modular design. I’ve long been sold on dividing websites into components, not pages, and amalgamating those components dynamically into interfaces. Flexibility, efficiency and maintainability abound.
But I don’t want my design to look like it’s made out of unrelated things. I’m making an interface, not a surrealist photomontage. As luck would have it, there is already a technology, called CSS, which is designed specifically to solve this problem. Using CSS, I can propagate styles that cross the borders of my HTML components, ensuring a consistent design with minimal effort.
We all recognize emoji. They’ve become the global pop stars of digital communication. But what are they, technically speaking? And what might we learn by taking a closer look at these images, characters, pictographs… whatever they are ? (Thinking Face). We will dig deep to learn about how these thingamajigs work.
Please note: Depending on your browser, you may not be able to see all emoji featured in this article (especially the Tifinagh characters).
How often do you have to explain the purpose of a study, objectives, goals or measurements within your company? Maybe you need to prepare a presentation or a brief overview of what next steps should be taken, or maybe you simply need to build a shiny, new pattern library?
Whatever project you may be working on, today’s icon sets will come in handy. All of the vector icons were tirelessly crafted by the design team at Ecommerce Website Design, and come in various formats that can be used for personal as well as commercial purposes.
Dariel Fitzkee, the famous magician, once said, “Magic is both in the details and in the performance.” Interaction design is just like that. Designers love to get the big picture right, but if the details aren’t handled properly, the solution will fail. The magic is all in the details. That’s why well-designed microinteractions make experiences feel crafted.
To get a better understanding of how your design benefits from microinteractions, it will help to sketch out your app ideas.
Alright, folks, this week we’re getting technical.
This post is geared toward Web Developers who’re working in conversion optimization, specifically those who are testing on AngularJS (or who are trying to test on AngularJS).
Angular, while allowing for more dynamic web applications, presents a problem for optimization on the development side.
It basically throws a wrench in the whole “I’m trying to show you a variation instead of the original webpage without you knowing it’s a variation”-thing for reasons I’ll get into in a minute.
At WiderFunnel, our Dev team has to tackle technical obstacles daily: many different clients means many different frameworks and tools to master.
Recently, the topic of How the heck do you test on Angular came up and Tom Davis, WiderFunnel Front End Developer, was like, “I can help with that.”
So here we go. Here are the tips, tricks, and workarounds we use to test on AngularJS.
Let’s start with the basics:
What is AngularJS?
Why is AngularJS popular?
The real question here is why are JS front-end frameworks and libraries popular? Angular isn’t the only framework you can use, of course: there’s EmberJS, React.js, BackBone etc., and different Developers prefer different frameworks.
But frameworks, in general, are popular because they offer a means of providing a rich user experience that is both responsive and dynamic. Without Angular, a user clicks a button or submits a form on your site, the browser communicates with the server, and the server provides entirely new HTML content that then loads in the browser.
When you’re using Angular, however, a user clicks a button or submits a form and the browser is able to build that content itself, while simultaneously performing server tasks (like database submissions) in the background.
For example, let’s think about form validations.
A user submits a form to create an account on a site. The browser talks to the server and the server says, “There’s a problem. We can’t validate this form because this username already exists.” The server then has to serve up entirely new HTML content and the browser re-renders all of that new content.
This can lead to a laggy, cumbersome user experience, where changes only happen on full page reloads.
A user submits a form to create an account on a site. The browser talks to the server via JSON (a collection of data) and the server says, “There’s a problem. We can’t validate this form because this username already exists.” The browser has already loaded the necessary HTML (on the first load) and then simply fills in the blanks with the data it gets back from the server.
Disclaimer: If you don’t have a basic understanding of web development, the rest of this post may be tough to decipher. There is a Glossary at the end of this post, if you need a quick refresher on certain terms.
Why it can be tricky to test on Angular apps
As mentioned above, Angular acts as an HTML extension. This means that the normal behaviors of the DOM* are being manipulated.
Angular manipulates the DOM using two-way data binding. This means that the content in the DOM is bound to a model. Take a look at the example below:
The class “ng-binding” indicates that the H1 element is bound to a model, in this case $scope.helloWorld. In Angular, model data is referred to in an object called $scope. Any changes to the input field value will change helloWorld in the $scope object. This value is then propagated down to the H1 text.
This means that, if you make any changes to the H1 element through jQuery or native JS, they will essentially be overridden by $scope. This is not good in a test environment: you cannot guarantee that your changes will show up when you intend them to, without breaking the original code.
Laymen’s terms: $scope.helloWorld is bound to the H1 tag, meaning if anything in the variable helloWorld changes, the H1 element will change and vice versa. That’s the power of Angular.
A developer will wait until the page has loaded, hide the content, change elements in the background, and show everything to the user post-change. (Because the page is hidden while these changes are being made, the user is none-the-wiser.)
We’re trying to do this switcheroo without anyone seeing it.
– Thomas Davis, Front End Developer, WiderFunnel
Tom explained that, as a dev trying to do conversion optimization on an Angular application, you find yourself constantly trying to answer this question:
How can I make this change without directly affecting my (or my client’s) built-in functionality? In other words, how can I make sure I don’t break this app?
How to influence Angular through the DOM
Angular makes for a complicated testing environment, but there are ways to test on Angular. Here are a few that we use at WiderFunnel (straight from Tom’s mouth to your eyeballs).
Note: In the examples below, we are working in the Inspector. This is just to prove that the changes are happening outside the context of the app and, therefore, an external script would be able to render the same results.
1. Use CSS wherever possible
When you’re running a test on Angular, use CSS whenever possible to make styling changes.
CSS is simply a set of styling rules that the browser applies to matching elements. Styling will always be applied on repaints regardless of how the DOM is bound to Angular. Everytime something changes within the browser, the browser goes through its list of styling rules and reapplies them to the correct element.
Let’s say, in a variation, you want to hide a banner. You can find the element you want to hide and add a styling tag that has an attribute of display none. CSS will always apply this styling and that element will never be displayed.
Of course, you can’t rely on CSS all of the time. It isn’t a scripting language, so you can’t do logic. For instance, CSS can’t say “If [blank] is true, make the element color green. If [blank] is false, make the element color red.”
In other cases, you may want to try $apply.
2. Using $scope/$apply in the DOM
We’ve established that Angular’s two-way data binding makes it difficult to develop consistent page changes outside of the context of Angular. Difficult…but not impossible.
Say you want to change the value of $scope.helloWorld. You need a way to tell Angular, “Hey, a value has changed — you need to propagate this change throughout the app.”
Angular checks $scope variables for changes whenever an event happens. An event attribute like ng-click or ng-model will force Angular to run the Digest Loop*, where a process called dirty checking* is used to update the whole of the app with any new values.
If you want to change the value of $scope.helloWorld and have it propagated throughout the app, you need to trick Angular into thinking an event has occurred.
First step: You’ll need to access the model in the $scope object. You can do this simply by querying it in the DOM.
In this example, you’re looking at the $scope object containing all models available to the H1 element. You’re looking at the helloWorld variable exposed.
Once you have access to helloWorld, you can reassign it. But wait! You’ve probably noticed that the text hasn’t changed in the window… That’s because your code is running outside the context of Angular — Angular doesn’t know that a change has actually been made. You need to tell Angular to run the digest loop, which will apply the change within it’s context.
Fortunately, Angular comes equipped with an $apply function, that can force a $digest, as shown below.
3. Watch for changes
This workaround is a little manual, but very important. If the source code changes a variable or calls a function bound to $scope, you’ll need to be able to detect this change in order to keep your test functional.
That’s where Angular’s $watch function comes in. You can use $watch to listen to $scope and provide a callback when changes happen.
In the example below, $watch is listening to $scope.helloWorld. If helloWorld changes, Angular will run a callback that provides the new value and the old value of helloWorld as parameters.
Custom directives and dependency injection
It’s important that you don’t default to writing jQuery when testing on Angular apps. Remember, you have access to all the functionality of Angular, so use it. For complex experiments, you can use custom directives to manage code structure and make it easy to debug.
To do this, you can implement an injector to apply components in the context of the app that you’re testing on. Here’s a simple example that will alert you if your helloWorld variable changes:
For more details on how to use an injector, click here.
These are just a few of the tactics that the WiderFunnel Dev team uses to run successful conversion optimization on Angular apps. That said, we would love to hear from all of you about how you do CRO on Angular!
Do you use the same tactics described here? Do you know of other workarounds not mentioned here? How do you test successfully on Angular apps? Let us know in the comments!
DOM: The Document Object Model (DOM) is a cross-platform and language-independent convention for representing and interacting with objects in HTML, XHTML, and XML documents
$scope: Scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events.
$apply: Apply is used to execute an expression in Angular from outside of the Angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
Two-way data binding: Data-binding in Angular apps is the automatic synchronization of data between the model and view components. The way that Angular implements data-binding allows you to treat the model as the single source of truth in your application.
Digest Loop: There is an internal cycle called $digest that runs through the application and executes watch expressions and compares the value returned with the previous value and if the values do not match then a listener is fired. This $digest cycle keeps looping until no more listeners are fired.
Dirty Checking: Dirty checking is a simple process that boils down to a very basic concept: It checks whether a value has changed that hasn’t yet been synchronized across the app
Marketing in the B2B space can be challenging. Complicated products, higher prices and longer chains of approval all present barriers to prospects when they’re making their buying decisions.
And what if you’re stuck in a boring industry? Like, how do you give boxes of business software a personality that will attract people to your brand?
Even if your product or service isn’t “sexy,” there’s no excuse for putting your social media followers to sleep. Image by Sophie via Flickr.
Social media marketing can be a great way to insert yourself into conversations prospects are having about the problems they’re facing, but you’ve got to be original if you plan on standing out.
With so much online noise, we can all benefit from looking in unusual places for inspiration for our social media campaigns.
Using examples I’ve come across personally, let me show you how to (respectfully) swipe some successful marketing concepts from familiar industries and make them work for you.
Reach outside your circle of BFFs
My fiancé and I have different hobbies. He plays video games online and I enjoy… going outside. Still, we share some of the same friends and like each other.
Similarly, if you’re a SaaS company looking for new audiences to tap, you shouldn’t limit yourself to “people who love business software.” Your audience doesn’t live and breathe your product. They have other interests, too.
Think about brands that offer different products or services, but have an audience with similar needs and interests.
Not sure who that might be?
Use Facebook Search to find parallel audiences
The quickest way to find other companies with crossover audiences is to use Facebook’s search functionality by simply typing in exactly what you’re looking for into the address bar at the top of your personal Facebook feed. For example, “Pages liked by people who like FreshBooks.”
It’s not foolproof (IKEA Canada might not be the best match) but a range of pages to choose from will appear:
Next, have a look at these companies’ channels to see what kind of content resonates with their fans. That’ll be your breeding ground for fresh social media campaign ideas.
Not sure what that might look like? Let’s dig into some specific examples.
Take a page out of the winning playbooks
Nobody appreciates their fans more than pro sports teams. As a proud New England Patriots fan (sorry not sorry Stef), I soak up every bit of content the team puts out.
By posting a variety of heartfelt and funny photos and videos to engage their social followers on Facebook, they stay relevant and fresh.
Player profiles, new memorabilia products, milestone graphics, the Pats volunteering in the community, and even fans’ pets get thousands of interactions.
But the most important thing I’ve noticed is that they show a lot of gratitude to their fans.
This may seem easy for a popular franchise, but what could you do for your own business if your brand isn’t number one in the world? Take a look at what Shopify posted on Facebook at the 150,000 shop owner mark:
It’s a fairly simple post, but 441 interactions means it reached a lot of people.
There is a good chance a large base of your fans are also your customers — and they’re rooting for you to be successful. Don’t be afraid to share big wins with those who are willing to show their support. It doesn’t hurt to bank some extra social proof, either!
Be the host with the most
Talk shows have been around since the earliest days of radio. They’ve evolved from simple back-and-forth interviews to spawning some of the most viral videos of all time.
Jimmy Fallon is the master of helping the viewer get to know his guest on a deeper level by playing games like Box of Lies or performing epic lip sync battles. These clips have garnered millions of hits on YouTube and loyal, enthusiastic fans.
Drawing inspiration from the world of late night television, Influitive ran an episode of BAM!TV: it was injected with humour, and made fantastic use of their own brand advocates who were invited on the show as “special guests.”
These guests did a great job of educating viewers about the importance of advocacy, in turn validating the need for Influitive’s software. The brand also took advantage of the digital nature of the show by providing downloadable links to their marketing materials right on screen.
Using a combination of social, influencer, partner promotion and manual outreach, BAM!TV received over 1,500 views in the first 24 hours (their original goal was 2,000 total and they’re past the 4,000 mark now) and 461 pre-event leads collected on the page. During the launch week, Influitive tracked 574 tweets using the #BAMTV hashtag, with a combined reach of over 183,000 accounts.
Influitive’s VP of Marketing, Jim Williams, explains the rationale behind the campaign:
For us, BAM!TV was more than just a lead gen effort — it was a brand-building campaign for Influitive and a category-building initiative for advocate marketing. We did, however, come up with some clever ways to generate leads: calls to action to download relevant content, such as ebooks and case studies, were placed next to the video player on the landing page.
Another hilarious example of late-night material turned marketing gold was Hootsuite’s “Mean Tweets” video that was used to launch their new dashboard UI. Their clever use of pop culture scored them over over 86,000 views and spread the word about the campaign:
One word of warning: As our Marketing Manager, Corey, always reminds us: when you’re building out your own social campaigns, try not to let the element of “creativity” overpower the clarity of your message.
It can be tempting to add layers of complexity in an effort to be unique, but sometimes keeping it simple is best.
Get hungry for change
It may seem like “everything has been done before,” but try to step back and think about how you can use existing technologies in a new way.
Chef Dennis Littley was innovative when he started using Hangouts On Air for his Around the Kitchen Table cooking show. Rather than clinging to the standard Hangout interview format that was popular at the time, he was preparing dishes for his audience and friends live on the air.
The result is a rich viewing experience that makes you feel like a real insider. When it’s over, you’re so intrigued by his culinary skills that you can’t help but head over to his blog and sign up so you never miss a recipe.
Chef Dennis is now a well-known Google+ influencer with more than 709,000 followers.
When we were thinking of our own way to add Google HOA to Unbounce’s social media mix, we decided it had to be something different (and fun). And so Page Fights was born: our way of educating our audience about landing page best practices through a new medium.
We now have over 3,000 Page Fights subscribers and receive hundreds of landing page submissions per episode. Our viewers are some of our most passionate community members.
P.S. Want to have Oli and Peep of ConversionXL tear your page apart? Submit it to the next round of Page Fights!
Channel your inner schoolteacher
Truly great social media marketing aims not only to delight potential customers but to educate and empower them as well. If you find yourself in a messaging rut, try going back to basics and considering how you can deliver content as if you were a teacher simplifying a foreign concept for your students:
You may not be able to tie your product back to burgers, but dig into your repertoire of information to see what bite-sized chunks you can turn into lessons for your followers. Think about what they’re eager to learn and create content that both educates and leads your prospects closer to a conversion.
Evosite does an excellent job of instilling trust in its brand by releasing this series of “52 ways to improve conversion rates” on Google+:
These virtual “educational posters” make the information visually appealing and highly shareable. Image source.
Evosite gently piques interest via social by giving away small pieces of advice for free, establishing their credibility and hopefully enticing visitors to hire them for their services.
Salesforce takes a similar approach by sharing industry stats, but maintains a bit of mystery so prospects click through (to a blog post or ebook download landing page):
Where can you source content for this type of campaign?
Consider all the ways you can repurpose some of the great content you already have: focus on key takeaways from your most popular blog posts, stats that help validate your product or service, or even answers to FAQs in your existing support community.
Keep it classy
While it’s perfectly fine to borrow ideas from other industries, don’t copy too closely — nothing will jeopardize the trust your customer has in you faster than straight-up plagiarism.
Put your own spin on the content that inspires you and keep it true to your brand.
Where’s your favorite place to shop for new social content ideas? Have you come across any unique posts that could be turned into material for another industry? I’d love to see more examples shared in the comments.
If you mention printing with CSS to many people who work on the web, print style sheets1 are the use that comes to mind. We are all well used to creating a style sheet that is called upon when a web document is printed. These style sheets ensure that the print version is legible and that we don’t cause a user to print out huge images. However, CSS is also being used to format books, catalogs and brochures — content that may never have been designed to be a web page at all.
In this article, we’ll take a look at the CSS modules that have been created not for use in web browsers, but to deal with printed and paged media. I’ll explain how the selectors, properties and values that they introduce work. I’ll finish up with a working example that you can use as a starting point for your own experiments. For that example, we’ll need a user agent that supports this specialized CSS. I’ll be using Prince, which is a commercial product. However, Prince has a free version that can be used for non-commercial use, making it a good tool to try out these examples.
Why HTML And CSS Make Sense For Print
It may seem a bit strange that content not particularly destined for the web should be maintained as HTML and formatted with CSS. It seems less strange when you realize that popular eReader formats such as EPUB and MOBI are HTML and CSS under the hood. In addition, even if the entirety of a manuscript or catalog isn’t to be published on a website, some of it likely will be. HTML becomes a handy format to standardize on, far easier to deal with than having everything in a Word document or a traditional desktop publishing package.
The Differences Between CSS For The Web And CSS For Print
The biggest difference, and conceptual shift, is that printed documents refer to a page model that is of a fixed size. Whereas on the web we are constantly reminded that we have no idea of the size of the viewport, in print the fixed size of each page has a bearing on everything that we do. Due to this fixed page size, we have to consider our document as a collection of pages, paged media, rather than the continuous media that is a web page.
Paged media introduces concepts that make no sense on the web. For example, you need to be able to generate page numbers, put chapter titles in margins, break content appropriately in order that figures don’t become disassociated from their captions. You might need to create cross-references and footnotes, indexes and tables of content from your document. You could import the document into a desktop publishing package and create all of this by hand, however, the work would then need redoing the next time you update the copy. This is where CSS comes in, whose specifications are designed for use in creating paged media.
Because the specifications are designed for paged media, we won’t be considering browser support in this article — it wouldn’t make a lot of sense. Later on, we’ll look at a user agent designed to turn your HTML and CSS into a PDF using these specifications.
The @page rule lets you specify various aspects of a page box. For example, you will want to specify the dimensions of your pages. The rule below specifies a default page size of 5.5 by 8.5 inches. If you intend to print a book, perhaps by a print-on-demand service, then finding out the sizes you can use is important.
size: 5.5in 8.5in;
In addition to specifying sizes with length values, you may also use paper size keywords, such as “A4″ or “legal.”
You may also use a keyword to specify the page’s orientation — “portrait” or “landscape.”
size: A4 landscape;
Understanding the Page Model
Before going any further, we should understand how the page model for paged media works, because it behaves somewhat differently to how things work on screen.
The page model defines a page area and then 16 surrounding margin boxes4. You can control the size of the page area and the size of the margin between the edge of the page area and the end of the page itself. The table in the specification explains very well how these boxes are sized.
The page area is the space on the page into which your page’s content will flow. When it runs out of room, another page will be created. The margin boxes are used only for CSS-generated content.
Left and Right Page Spreads
Another aspect of the page model is that it defines pseudo-class selectors for the left and right pages of your document. If you look at any printed book you have on hand, you’ll probably see that the margin’s size and the margin’s content are different on the left and right pages.
We can use these selectors to define different margin sizes for our pages.
Two other pseudo-class selectors are defined. The :first selector targets the first page of a document.
The :blank pseudo-class selector targets any page that is “intentionally left blank.” To add this text, we can use generated content that targets the top-center margin box.
@top-center content: "This page is intentionally left blank."
Generated Content and Paged Media
In the last example, we used CSS-generated content to add the text to the top-center margin box. As you will discover, generated content is vitally important to creating our book. It’s the only way things can be added to our margin boxes at all. For example, if we want to add the title of the book to the bottom-left margin box of right-hand pages, we would do this using generated content.
Also part of the “Paged Media” specification is information about how to control page breaks. As already described, once the content fills a page area, it will move onto a new page. If a heading has just been written to the page, you might end up with a page that finishes with a heading, with the related content beginning on the next page. In a printed book, you would try to avoid this situation. Other places you might want to avoid a break are in the middle of a table and between a figure and its caption.
Starting a new chapter of a book with an h1 heading is common. To force this heading to always be the beginning of a page, set page-break-before to always.
To avoid breaks directly after a heading, use page-break-after.
h1, h2, h3, h4, h5
To avoid breaking figures and tables, use the page-break-inside property.
Books are all about numbering things — pages, chapters, even figures. We can actually add these numbers via CSS, saving us from having to renumber everything because we decided to, say, add a new figure partway through a chapter. We do this using CSS counters7.
The obvious place to start is with page numbers. CSS gives us a predefined page counter; it starts at 1 and increments with every new page. In your style sheet, you would use this counter as the value of generated content, to put the page counter in one of your margin boxes. In the example below, we are adding page numbers to the bottom-right of right-hand pages and the bottom-left of left-hand pages.
You can create your own named counters and increment and reset them as you require. To create a counter, use the counter-reset property, increment it with counter-increment. The CSS rules below will create a counter for chapters named chapternum and increment it with each h1 — being the start of a chapter in this book. We then use the value of that counter in generated content to add the chapter number and a period before the chapter’s actual title.
content: counter(chapternum) ". ";
We can do the same for figures in the book. A common way to number figures is to use chapternum.figurenum. So, “Figure 3-2″ would be the second figure in chapter 3. On the h1, we could reset figurenum in order that it starts from 1 for each chapter.
Take a look at a printed book again. As you leaf through a chapter, you’ll probably see that the chapter’s title is printed on the left or right page. As strange as it may sound, the “Generated Content for Paged Media” specification lets us achieve this using CSS.
We do this using a property named string-set in the selector that we want to take the content from. For the chapter title, this would be the h1. The value of string-set is the name you would like to give this content and then content(). You can then output this as generated content using string().
When your paged media is generated, each time an h1 occurs, the content is written to doctitle and then outputted in the top-right margin box of right-hand pages, changing only when another h1 occurs.
Footnotes are a part of the “CSS Generated Content for Paged Media Module8” specification. The way footnotes work is that you would add the text of your footnote inline, wrapped in HTML tags (probably a span), with a class to identify it as a footnote. When the page is generated, the content of that “footnote element” is removed and turned into a footnote.
In your CSS, use the footnote value of the float property to create a rule for your footnote class.
In your document, use that class to wrap any footnote text.
<p>Footnotes<span class="footnotes">Footnotes and notes placed in the footer of a document to reference the text. The footnote will be removed from the flow when the page is created.</span> are useful in books and printed documents.</p>
Footnotes have a predefined counter that behaves in the same way as the page counter. Typically, you will want to increment the counter by 1 each time a fn class occurs and reset it at the beginning of each chapter.
The various parts of a footnote can be targeted with CSS pseudo-elements. The footnote-call is the numeric anchor in the text that indicates there is a footnote. This uses the value of the footnote counter as generated content.
The footnote-marker is the numeric marker placed in front of the footnote text in the footer of your document. These behave in a similar way to the numbers generated for an ordered list in CSS.
The footnotes themselves are placed in the margin, within a special area of the page named @footnotes. You would target and style that area as follows.
border-top: 1pt solid black;
Before moving on to a working example of everything we’ve learned, let’s look at cross-references. On the web, we cross-reference things by adding links. In a book or other printed document, you would normally refer to the page number where that reference is to be found. Because page numbers might change according to the format that the book is printed in — and between editions — doing this with CSS saves us from having to go through and change all of the numbers.
We use another new property, target-counter, to add these numbers. Start by creating links in your document, giving them an href, which is the ID of the element in the document that you want to target. Also, add a class to identify them as a cross-reference, rather than an external link; I’m using xref.
We’ll be looking at this technique in practice when we create a table of contents for the working example.
Putting It All Together: An Example Book
We’ve looked at a lot of different properties here in isolation. They make more sense once you put them to use by building a book.
To actually create a book using this CSS, you’ll need a user agent that supports it. Currently, very few things implement this specification well; the one that is most accessible is Prince129. A standalone commercial license for Prince is expensive, however, you may use Prince free of charge for non-commercial projects. This means that if you just want to try out these techniques, you can. Additionally, if you do have non-commercial uses for this technology, you may use Prince to format those books.
I have extracted passages from one of my favorite books on Project Gutenberg, Our Cats by Harrison Weir10. I’ve chosen this book because I like cats and because it has images and footnotes that I can use to demonstrate formatting.
You can find the files I am using, plus a generated PDF, over on GitHub11. If you want to experiment with the CSS and build the book yourself, then you will need to download and install Prince129. Prince is a command-line tool on the Mac, and although there is a Windows GUI, I’ll use the command line because it really is very simple.
Using a Terminal window, switch to your book’s directory or the location where you downloaded my files from GitHub.
Now, run Prince:
prince -s pdf-styles.css book.html builds/book.pdf
This will create a PDF in the builds folder named book.pdf. Now, if you make any changes to the CSS or HTML, you can run Prince to see what is different.
The HTML Document
My entire “book” is compiled in a single HTML document. Compiling documents in Prince is possible, but I’ve found it simpler to just deal with one large document. Before the chapters, which start with an h1, I have a div that contains the cover image, and then the table of contents for the book.
The table of contents links to the IDs of the chapters’ h1 headings.
<html dir="ltr" lang="en-US">
<meta charset="utf-8" />
<title>Our Cats and All About Them</title>
<meta name="author" content="Harrison Weir"/>
<meta name="subject" content="Cats. Their Varieties, Habits and Management; and for show, the Standard of Excellence and Beauty"/>
<meta name="keywords" content="cats,feline"/>
<meta name="date" content="2014-12-05"/>
<h1>Extracts from Our Cats and All About Them by Harrison Weir</h1>
<li><a href="#ch1">The First Cat Show</a></li>
<li><a href="#ch2">Trained Cats</a></li>
<li><a href="#ch3">General Management</a></li>
<li><a href="#ch4">Superstition and Witchcraft</a></li>
<h1 id="ch1" class="chapter">The First Cat Show</h1>
<h1 id="ch2" class="chapter">Trained Cats</h1>
<h1 id="ch3" class="chapter">General Management</h1>
<h1 id="ch4" class="chapter">Superstition and Witchcraft</h1>
The CSS then uses all of the things we have described so far. To start, we need to set up a size for the book using the @page rule. We then use the :first pseudo-class selector to remove the margin on page 1, because this page will have the cover image.
Next, we deal with the specifics of the left- and right-hand pages, using the :right and :left spread pseudo-classes.
The right-hand spread will have the title of the book in the bottom-left margin box, a page counter in the bottom-right, and the chapter’s title in the top-right. The chapter’s title is set using string-set further down in the style sheet.
The next section of the style sheet deals with counters. In addition to the preset page counter, we are defining counters for chapters and figures.
/* Reset chapter and figure counters on the body */
counter-reset: chapternum figurenum;
font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif;
/* Get the title of the current chapter, which will be the content of the h1.
Reset figure counter because figures start from 1 in each chapter. */
string-set: doctitle content();
/* Increment chapter counter */
content: counter(chapternum) ". ";
/* Increment and display figure counter */
content: counter(chapternum) "-" counter(figurenum) ". ";
Chapters now have their number placed before the title. Figures also display their number.
We create footnotes as in the explanation earlier, superscripting the footnote’s call.
We then add some rules to control where pages break. You need to be fairly careful about being too heavy handed with this. If your book has a lot of tables and figures, then adding many specific rules here could cause a lot of long gaps in the book. Experimenting and testing will show how far you can take the control of breaks. I have found the rules below to be a good starting point.
Remember that this is a suggestion to the user agent. In some cases, keeping a table from breaking will be impossible if the table doesn’t fit on a page!
Finally, we style the table of contents, and we use an interesting trick here. When describing cross-references, I explained how we use target-counter to display the page number that the ID is on. This is what we’ll do for our table of contents. The rule below puts the page number after the link to each chapter in the table of contents.
Commonly in books, however, you would use leader dots to line up all of the page numbers against the right margin. Amazingly, CSS gives us a way to do this, by adding leader() before the number in the generated content.
We now have a complete style sheet with which to build our book. I’ve avoided spending a lot of time on typography here, concentrating instead on the specifics of creating a book. From this point, however, you can experiment and add your own styles to create a unique book design.
Not Just Books!
Remember that these techniques are not just for books. You could use them to generate print and PDF versions of a product catalog directly from the HTML of a website that you have developed for a client. Or you could create flyers and brochures from web content.
If you want to create PDF documents from a website using Prince, then DocRaptor27 is a great option. This service uses Prince via an API. You can send documents via the API and receive a PDF — perfect for allowing users to download content as a PDF on the fly. Everything we have looked at in this article is possible via an API integration with DocRaptor.
Even if you don’t have an immediate need for PDF generation, it’s a fascinating aspect of CSS — and it’s a useful skill to have tucked away, so that you know what is possible when a use case presents itself.