A transcript of WWDC12 session 515
HTML, CSS, and DOM for Book Authors
Tess: Good afternoon, everybody. Is everyone having a great WWDC thus far?
… Applause …
That's what I like to hear.
If you'd like to make a digital book, you've come to the right place. This is Session 515, HTML, CSS, and DOM for Book Authors. I'm Theresa O'Connor, a web technology engineer on the Safari and WebKit team here at Apple.
So, iBooks. iBooks supports three kinds of books:
- Flowing books, like novels, which consist primarily of text and images.
- Fixed-layout books, which allow the book designer precise control over the placement and position of everything on the page.
- Multi-Touch Books, exceptionally easy to author with iBooks Author. Rich, beautiful books your readers will love.
So, three formats, with one core set of technologies for all three: web technology.
- In Multi-Touch Books, you can build rich, interactive widgets with HTML.
- For flowing and fixed-layout books, EPUB provides the structure of your book, but the contents are HTML, CSS, and JavaScript, the core web technologies.
So if you're not a web developer or, if the last time you looked at HTML, the blink tag was the new hotness, you're in the right place.
We're going to take a look at the key parts of web technology that are essential to building a book. Technology for making great digital books.
Let's get started.
I thought we could take a look at a document that hopefully you've all heard of. So I took a copy of the U.S. Constitution, exported it to HTML, and dropped it into an EPUB.
This is what it looks like. We've got a little bit of work to do to make this be a really great-looking book. In a few minutes, you'll know everything you'll need to make it look like this instead, something far more compelling for your readers.
We'll start by looking at the basic structure of your document.
So the tool I used basically just gave me automatic line breaks. This is the <br>
tag in HTML. For iBooks to format your book nicely, we first need to tell it what each part of our content is. HTML provides a variety of elements. For basic page elements, so you have the <p>
element for your paragraphs, various heading elements for your headings and subheadings, and so on. And there are many great tutorials and references online that you can use to look up the basic structural HTML elements. And as you can see, we've immediately gotten from iBooks much better formatting by default.
So as I flip through this book, I'm seeing that each of my paragraphs has this extra space in between, and the first lines aren't indented. This is the default presentation of text on the web, but it's not what you expect in a book. In a book, you typically have your paragraphs right next to each other, and you indent those first lines.
To do this in web technology, we're going to use what's called a stylesheet. CSS stands for Cascading Stylesheets, is the language we use to describe what our content should look like.
So first, we'll link to our stylesheet from our HTML. But what goes in the stylesheet? Well, you need to identify the parts of the content you'd like to alter and then provide iBooks with commands that will describe how you'd like it to look.
In this case, we want to affect the indentation of every paragraph. So we use what's called an element name selector. In this case, I've named the <p>
element. This will match every paragraph in our book.
And as you can see, we've got this nice indentation on our first lines. But one of the key differences between digital books and print is that your reader can change the font size they use to read your book. So if we do that, we'll find our nice-looking indent from before doesn't look so nice now. Well, what happened? We've used a unit of length, the pixel, that's relative to the screen, not to the size of the font. You really want to express things like indentation in terms that are relative to the font size, so that when your user changes the font, it still looks great.
There are several such font-relative units available in CSS.
One of them is the em
. The name comes to us from print typography. It used to refer to the width of the uppercase M. And now it's a little more complicated, but that'll do. So once we've done this, we change the font size, our indentation looks great.
So now that we've taken care of that, we still have these spaces between our paragraphs. To fix this, we need to learn about how elements have space around them in CSS. There are two kinds of space that you need to worry about: margin and padding. And this is because all elements can have a border put around them. Margins are the space around the border, whereas padding is the space inside the border.
By default, paragraphs in HTML get a top-and-bottom margin. So to remove that, we simply set margin: 0
in our CSS. So this is already looking a lot more like a book and a lot less like a web page.
It's common in print for the first paragraph after a heading or subheading to not be indented, that only subsequent paragraphs should be indented. We can achieve this in CSS, by using what's called a sibling selector. So remember, our element name selector picked out every paragraph in the document. The sibling selector says, "Give me all the elements that follow this other element." So here we've said, "I'd like only the paragraphs that follow another paragraph directly in the document." So now our indentation is only applied where we'd like it.
Something else you may have noticed is that iBooks is automatically hyphenating your content. This is really great. And it's dynamic. When the user changes the font size or orientation, The hyphenation will update automatically, and your text will continue looking great.
Automatic hyphenation is wonderful, but sometimes you need to override it. Maybe you're writing a technical manual and you're using a technical word that's not in the default hyphenation dictionary. You can use what's called the soft hyphen character, ­
, to manually specify each hyphenation point.
Conversely, you might want to disable hyphenation. This is very common with headings. You can use the <a href=https://idpf.org/epub/30/spec/epub30-contentdocs.html#:~:text=epub-hyphens>-epub-hyphens
property to disable hyphenation.
Another common structural element of flowing books are quotes. Unfortunately, there aren't any quotes in the Constitution, but I'm sure you can imagine in the hot Philadelphia summer, People scribbled various notes in the margin, and via extensive historical research, I found one such note.
So as you can see, the element we use in HTML for quotes is called <blockquote>
. And it gets, by default, a little bit of margin on the left and right to distinguish it from the surrounding text.
One thing you might want to do here is restrict the width of a quote or many other elements. This is very easy to do in CSS. You simply use the width
property. Here I've chosen to use a percentage. This allows for the element to take up the same amount of space on the screen on an iPad, an iPhone, or an iPod Touch.
Once you've restricted the width of an element like this, you might want to center it on the page. And we can do this with a special kind of margin called an auto margin. So before we had one value that we were giving to a margin. When you give it two values, the first specifies the vertical margin and the second the horizontal. So here, we've given it auto
for our horizontal margin. iBooks knows that the element takes up 75% of the width, so it automatically allocates the remaining 25% on either side.
This quote still appears to be part of the main document, but really we want it to be off to the side as a pull quote or margin quote. We do this in CSS by floating an element. The float
property does several things. It removes the element from the main flow and moves it off to either the left or the right. So as you can see, the Article 1 heading is following immediately after the preamble as if there weren't anything in between. Floating an element also causes the rest of the content to flow around it automatically, so you don't have any ugly overlaps.
Now that we have our quote off to the side, we could use a variety of CSS properties to make it visually distinct from the main text. And there are many great tutorials and references online for the various CSS properties at your disposal.
One very common thing to do is to use a different font. So let's take a look a little bit at how to work with fonts in your books.
So the first thing to think about here is that your reader can use a different font than you specified. iBooks supplies a font menu with several great options. So if you're looking for a font to specify for your book, you might first look at iBooks’ list. These are all fonts that are designed for readability on the screen.
But you're not limited to these fonts. iOS comes with many other fonts. One of my favorites is Charter. Charter is very readable, but even better would be Marker Felt. I'm pretty sure this is exactly what the Founding Fathers had in mind.
So this brings up a good point. If you want to change a font, you should always have your readers in mind. Readability is the goal here. In this case, Marker Felt might not be a suitable font for your body text. Maybe it's better just for headings or other runs of large text.
You're not limited to the fonts available on the system. You can include a custom font that you have a license for in your book. You simply include the font file in your EPUB, and then you tell iBooks about this font by using what's called an @font-face
rule. This looks a little more complicated than the CSS we've seen already, but it's pretty straightforward. It contains two parts. We use the font-family
descriptor to name the font, and then the src
descriptor to specify the font file.
And then in our CSS, we just refer to it like any other system font. So in this case, I've loaded what's called IM Fell English, which is a revival of an 18th century manual typeface, because I thought it's a historical document, and an appropriate historical typeface might fit with it. But I'm using Charter for my body for better readability.
Custom fonts typically come in multiple files, so your italic will be in a separate file from the bold, et cetera. To load multiple files for the same font, you just use multiple @font-face
rules. And you use the font-style
and font-weight
descriptors to specify which is which.
Now remember, your reader can change the font they're using to read your book. If you specify a custom font, how do you make sure that your user can get back to that custom font if they've chosen a different one? In your Apple Display Options file inside your EPUB, you include the specified fonts option, and this enables the original item in the font menu.
When I think about custom fonts and the Constitution, I think of this sort of iconic first page with this ginormous "We the People," a very bold statement. So I'd like to bling up our preamble a bit.
Now, before, in our CSS, we've selected whole classes of elements, all of the paragraphs, et cetera. To select just the preamble, I can give it a class in my markup and then refer to that class from CSS using a class selector, which is just the class name with a dot prepended. So I've made it a lot bigger, made it italic. It's starting to look kind of nice.
It's very, very common in books to want to style a line, or the first line or the first letter of a paragraph. But if the user changes the font size, what's included on that first line will change. Fortunately, we have the ::first-line
and ::first-letter
pseudo-elements in CSS. And this will dynamically update and always match what's in that first line as orientation changes and font size changes.
I still want that "We the People" to really kind of pop out, though. There wasn't any element just surrounding "We the People," so I added one. And I'm using a similar class selector like before.
Now unlike print, you're not going to get charged extra for using different colored ink. So just don't make it really ugly. Think about it. So that's looking pretty nice.
Flowing books are more than just text. The judicious use of excellent images can really improve the look of your book. Working with images in iBooks is really, really easy.
So here's a portrait of the signing of the Constitution. Let's pop that right in there. We use the <img>
element in HTML to put an image in our markup. It takes two attributes. The src=""
attribute specifies the image file, and the alt=""
attribute specifies alternative text that will be used instead of the image. (Remember, many of your readers cannot see the images you've included in your book. And if an image is worth a thousand words, you can probably spare 20 or 30 to describe each of your images appropriately.)
One thing I haven't done here, I haven't told iBooks anything about how to size or place this image. That's because iBooks goes to great lengths to figure out an appropriate size for your image, to make sure it won't break across pages, and that sort of thing. It does this across devices. This looks great on the iPhone, too. And it didn't have to do anything.
You might want to control the width or placement of your image. The best way to do this in iBooks is to put the image in a container and then style that container. So here I've used the same width and auto margins from before to center and give a little space around my image.
At this point, we can start to have fun. I decided to put a little bit of a shadow behind my image. In CSS, there's a box-shadow
property, which takes four arguments. The first two are the offset from the image, then a bit of a blur, and finally the color of the shadow.
Another thing to keep in mind when working with images and books is that your reader can change the theme they use to read your book in iBooks. I do this all the time. My favorite is the night theme. Actually, my wife's favorite is the night theme because, when I can't put my book down, and I've been in bed for an hour with the light off, I'm keeping her up. So I turn on the night theme, and hopefully the book still looks great.
In this case, though, something's not right. This image has a white background. And in normal theme, it looked great. In the night theme, not so much. This is an easy problem to avoid. Simply use an image with a transparent background. This will continue to look great in normal mode and in the night theme as well.
So now we've looked at dealing with the basic structure of our documents and how to work with custom fonts and how to include images in them. But digital books are so much more than a static recreation of print onto the screen. So let's go beyond print and look at some of the things that you can only do in a digital book.
We'll start by looking at animating content in a fixed-layout book.
A fixed-layout book is almost like a sequence of web pages. Each page is its own HTML document. So for each page, we have to tell iBooks how big the page expects to be. We do this by defining a viewport in our HTML, and then we set our body dimensions to match that in CSS. Be sure to set zero margins on your body so that the dimensions will actually match.
So now we have this blank page, a blank canvas. We can tell any story we want. So I thought we could create a story about a sheep.
But not just any sheep. This sheep has an overriding goal in life, to help children sleep by jumping fences. Over and over and over again, as many times as it takes.
So we'll need a fence. In fixed-layout books, you typically have a background image on each page. To set a background image in CSS, you use the background
property. You point to the image, and that's it. Make sure the dimensions of your image match the viewport dimensions that you've set.
Okay, so we've got our field, our fence. Let's put our sheep on there. This is just like before. You use the img element in your markup, and there we have our sheep.
There's a bit of a problem, though. It looks like our sheet might be falling from a great height. And I'm a little worried. We need to put them on the ground. So to position elements like this on the page, we can use absolute positioning. In CSS, you set your position
property to absolute
, and then you use the top
, right
, bottom
, and left
properties to position the element wherever you'd like. So there he is, safe on the ground.
Let's give him some friends. So I have an image of a flock of sheep, positioned exactly like the first sheep. But where did the sheep go? The flock is sitting on top of them. When you use absolute positioning to position elements on the page, they can overlap. So to control the stacking of your elements, you can use the z-index
property.
It's very simple. The bigger z-index
value wins. So by setting the sheep z-index
higher than my flock's z-index
, I've put it back on top.
This is all you need to know to position and stack every element on your page, but we still haven't made them move. To do this, we'll use what's called keyframe animations.
A keyframe animation is a series of steps. Each step is a keyframe. You say, "I'm going to create a keyframe." "This far through, this is how things should look." "This much farther, this is how things should look." And so on.
So I thought we could start by defining an animation. Let's have our sheep just walk across the screen. (We'll get to what goes in our keyframes in a bit.) To tell our sheep to animate, we use the animation-name
property, and name — we give it the same name that we gave our keyframes.
We have to tell iBooks how long the animation should go. We do this with the animation-duration
property. In this case, I've said one second.
And you can control the timing of your animation with the animation-timing-function
property. "Well, what does that mean?" This is the scariest slide. So the easiest one is a linear
timing function. And this simply says— You know, at 50% of the way through our animation, we should be 50% through our keyframes. So in a one-second animation, a half a second will be at 50%.
Another common timing function is called ease
, and this provides a gentle acceleration at the beginning and a deceleration at the end, which will make your animation look really smooth. There are several other predefined timing functions in CSS, but you might want to describe your own custom timing function.
You can also do this. Okay. So we've wired up our animation. We've told it how long it will take. But what goes in those keyframes? Well, if you think about it, when you want to animate things, you typically want to move them around on the canvas. You can make them bigger or smaller, maybe rotate them.
We can do all of these things with one CSS property, the transform
property. To use the transform
property to move something around, you can use the translate()
function, which takes an X and a Y offset and scoots your element along that offset. To make something bigger or smaller, you use the scale()
transform. In this case, a scale of two doubles the size. And to spin or rotate things, you guessed it, the rotate()
transform, which takes an angle and does the rest.
So to cause our sheep to walk across the screen, we'll use the translate()
transform. We'll just need two keyframes at the start. We don't want to translate it all. And at the end, we will have fully translated to our destination. And there you have it.
To show you how to use keyframe animations and CSS transforms to cause our sheep to actually jump that fence, I'd like to invite up Adele Peterson.
Adele: Thanks, Tess. Hi, I'm Adele Peterson. We're going to start with an animation very similar to the one that Tess just showed you guys in the slides, and then I'll show you how to make it fancier and then eventually get the sheep to jump the fence.
So you guys see the beginning wiggle there? There he goes. And then he kind of scoots. OK.
So let's go back to our machine. We're going to drop in a few more keyframes. Okay, so now I added a few more.
Again, we're continuing to translate along the x-axis, and we're doing that wiggle back and forth. Look at him go. All right, all right. Okay.
And finally, we're going to jump the fence. Okay, so here we now have an extra parameter in our translate()
function. So by 80%, we're going to translate in the Y direction 200 pixels up. And we've straightened our rotation back out to zero so that the sheep is on firm footing as he makes his jump. Okay. Come on, Brucie, you can do it. And there he goes. All right. Back to you, Tess.
Tess: So we've seen how to animate elements on the page.
One of the other things we can do in digital books is make use of multimedia like audio and video. I don't have much to tell you about this because there isn't much to say. It's very easy. Just like we use the img element to include an image, we can use the <audio>
or <video>
element to include audio or video. They both take a src=""
attribute just like the <img>
element. The <video>
element also takes a poster=""
attribute which specifies an image to be used as a placeholder like so until the user has hit play. You specify the controls
attribute so that the user has controls to play and pause and scrub your media. And just like that, you've got great sound and video in your book.
Sometimes you'd like your book to do something that you can't express in an animation alone.
You'd like to change the content on the page dynamically. One way we could do this, or one reason we might want to do this, is to count the number of times Bruce the Sheep has jumped the fence already. This thing can get outrageously large and somewhat depressing. Why isn't Iris asleep yet?
Well, how would we achieve this with web technology?
JavaScript is the programming language of the web, and we can use it in our books as well. Everything in your HTML is exposed to JavaScript in what's called the Document Object Model, or DOM. So all of your elements have some kind of corresponding JavaScript object that you can manipulate from a program. You can add or remove elements, or modify their contents.
So if we want to count each time the sheep jumps the fence, we're going to need to get some kind of message about when this happens. And in the DOM, we have what are called DOM events. And these are just messages that happen as something changes.
Keyframe animations cause several different kinds of events to happen.
- The
animationstart
event happens when the animation initially kicks off. - The
animationiteration
event happens at the end of every iteration of that animation. - And finally, the
animationend
event happens when the animation stops.
Sadly for Bruce, this animation is never going to stop.
So what we want to do is we is used the DOM first to get a reference to the image that represents our sheep. The document.getElementById()
method does this very straightforwardly. You just pass it the ID of that element.
Next, we'll try to listen for the animationiteration
event. And each time we get it, we'll update that count by using some DOM manipulation. So the next iteration, our count goes up.
It was kind of ugly that it said zero. The Romans didn't even have a concept of zero! So maybe that's a bit too much for a children's book. We could hide the zero and wait until the first iteration happens to then display our count. To hide and show elements in CSS, you use the display
property. So in this case, I've put a class on my count. I've used that class to hide the elements.
And then from the DOM, I can remove that class to cause the count to appear. HTML5 provides a very convenient classList
API for the manipulation of classes on elements. You can add or remove classes. You can check to see if a class is there or toggle the presence of one. By using classes like this, we can describe different states of our animation and the dynamic behavior of our book with meaningful names which makes it much easier to work with for you as the book author. So in this case, we can simply call classList.remove()
and give it our class to cause our count to be shown.
So I'm going to invite Adele back up, and she's going to have a little bit of fun with making our sheep do a bit more. Here's Adele.
Adele: Thanks, Tess.
So I'm going to quickly walk you through adding the counter that Tess just showed you, and then we're going to add a super realistic sound effect. OK. So— Let's take a look at our JavaScript here. So here we've added an event listener to our window
to listen for the DOMContentLoaded
event.
So when the web page, or in this case, the page of the book, first loads, we'll end up calling this init()
function. We'll do our initialization. So here is where we have our sheep element.
So actually, before I go farther, Let's add in our HTML for the counter. Okay, so pretty simple. We have our paragraph and our counter, and here's that class that Tess mentioned, and that's how we're going to control the appearance of this class.
Okay, so back to the JavaScript. All right, so we have this initialization function, and the first thing that we're going to do is to add that event listener for the animationiteration
event. So this fires at the end of each iteration of our animation.
When that happens, we're going to call this updateCount()
function. So updateCount()
is fairly simple as well. We increment our jumped sheep, we grab our counter element, and we set the textContent
property of the counter to the number of jumped sheep.
Okay, now we're going to go and hide the counter. Let's see, where's my… Okay, so… We're just going to drop in some really basic CSS to set that zero class to display: none
to hide it. And then in our JavaScript, in updateCount()
, when we have reached the first iteration, We're going to use the classList
API to remove that class.
Okay, so let's switch over and see how that looks. We're continuing to count, so that's good.
Okay, so now let's add our super realistic sound effect. So this may defeat the purpose of trying to get your kid to go to sleep, but it's kind of cool anyway. So you may have to learn how to build a mute button into this later. Okay, so again, let's go back to our XHTML, and we'll add our <audio>
element.
Now, this is a little different from the example that Tess showed you in her slides because it doesn't have a controls
attribute. And the reason for that is we don't want the user to control this media. We want to control it ourselves in JavaScript. So I'm going to show you how to do that.
So we are going to add a little more code to our initialization function. We're getting the <audio>
element by ID, using document.getElementById()
, and we're calling load
on that <audio>
element. This is just so the audio file actually gets loaded and is ready to play when it's time to play. We're going to add our function to actually play the audio.
And here, when we actually play the sound effect, we're going to play it on a slight delay. So this is so if it starts playing at the beginning of an iteration, we can actually play it a little bit later, maybe around the time that the sheep is jumping the fence. And then every time we iterate, I'm sorry, let's see. Every time we iterate, we'll call play sound effect.
Okay, so there's actually a problem with this code. Every time you iterate, you're going to play the sound effect, which happens on a delay. This actually means that the first time the sheep jumps the fence, you're going to miss getting the sound effect. That's because this event, this animationiteration
event, happens at the end of each iteration. So we're going to also add an event listener for animationstart
and also start playing the sound effect then.
Okay, so I think we are set up to make this work. Let's switch over.
Sheep: Bah-ha-ha-ha. Bah-ha-ha-ha.
Adele: All right, back to you, Tess.
Tess: Well, what have we learned today? First off, it's exceptionally easy to make a great-looking, flowable book using just a couple of CSS properties and some very basic markup.
Secondly, you have very fine-grained control over layout and the appearance of things in a fixed-layout book using, again, just a little bit of CSS, And it's also very easy to add some fairly complex enhancements, such as animation and multimedia.
Remember, This is all just web technology. And there's an amazing amount of material out there. It's all very applicable to making great books.
There have been many related sessions here at WWDC this year, several of which have already occurred. If you missed them, the videos will be posted very soon.
- Tomorrow morning, there's a session on accessibility in books, which is going to be awesome. I'm really looking forward to it.
- And then on Friday, there's actually a repeat of the Building Books with iBooks Author session.
- It should be a great session if you missed it the first time. I think it was Monday. No, sorry, Tuesday.
And of course, for more information, you can contact Vicki Murley, our Safari Technologies Evangelist. And you can participate in our developer forums on apple.com
.
Thank you so much.