Showing posts with label media queries. Show all posts
Showing posts with label media queries. Show all posts

Wednesday, February 8, 2012

Cross-platform multiple indents in ebooks

Sometimes I worry that I don't do enough real world work to know what the actual problems that ebook production people face. So when someone poses an interesting question on #eprdctn, I like to take a look.

Today, @ebookartisans was trying to get multiple indents to work on old Kindle, new Kindle, ADE, NOOK, and iBooks from a single EPUB file (obviously KindleGenned into a mobi for Kindles). She was using a combination of code from Joshua Tallent and myself. It turns out my code only works on first level indents on Kindle. Unfortunately, Rick Gordon (another #eprdctn regular) reports that the NOOK won't look at any code that comes after a media query, and so she was having trouble getting NOOK to do what she wanted.

Does this give you flashbacks of hacks for hiding CSS from IE? Well, it's the same show, so many years later. So, if you remember, you're getting old like me.

Thankfully, we have somewhat better tools this time around, in the form of media queries, which I started explaining a few weeks ago.

The problem with indents is that Kindle has some built-in funky behavior. So, if you were setting up the code just for old Kindle (e.g., not Fire) you might use the following:

<style type="text/css" media="amzn-mobi">
.level1 {text-align:left; text-indent: -30px; }
.level2 {text-align: left; text-indent: -60px; }
.level3 {text-align: left; text-indent: -90px; }
</style>


You'd also have to add extra spaces in front of each level2 and level3 (and successive levels) to give a left margin to the first line. This is Joshua Tallent's hack (actually he uses &#xa0;, but @ebookartisans says that doesn't work on NOOK):

<p class="level3"><span class="spaces">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The mean aunt had her own traumatic childhood, deserving of treatment in a Roald Dahl novel.</p>

Kindle- multiple indents

Though I salute Joshua's ingenuity, I admit I hate the spaces, and would only use such a hack in extreme need. Instead, I'd advise against using multiple indents. But in case the need arises, we'll forge on!

Since NOOK completely ignores the "not amzn-mobi" media query and any CSS that follows it, the trick is to put the NOOK (and other ereader code) in a stylesheet without a media query, and before the aforementioned old Kindle stylesheet. I'll also slip in some code that hides the extra spaces everywhere except in old Kindle.

<style type="text/css">
.level1 {margin:0 0 0 2em; text-indent: -2em;}
.level2 {margin:0 0 0 4em; text-indent: -2em;}
.level3 {margin:0 0 0 6em; text-indent: -2em;}
.spaces {display:none}

</style>


<style type="text/css" media="amzn-mobi">
.level1 {text-align:left; text-indent: -30px; }
.level2 {text-align: left; text-indent: -60px; }
.level3 {text-align: left; text-indent: -90px; }
.spaces {display:inline}
</style>


Here's where you have to keep track of CSS inheritance rules. Remember that later CSS overrides earlier CSS as long as it has equal importance. So, the text-indent in old Kindle will override the earlier CSS. That's what we want for text-indent but not for margin-left. So, just add in margin-left: 0; for each style in the Kindle stylesheet:

<style type="text/css">
.level1 {margin:0 0 0 2em; text-indent: -2em;}
.level2 {margin:0 0 0 4em; text-indent: -2em;}
.level3 {margin:0 0 0 6em; text-indent: -2em;}
.spaces {display:none}</style>


<style type="text/css" media="amzn-mobi">
.level1 {margin-left:0;text-align:left; text-indent: -30px; }
.level2 {margin-left:0;text-align: left; text-indent: -60px; }
.level3 {margin-left:0;text-align: left; text-indent: -90px; }
.spaces {display:inline}
</style>


That code works in old Kindle, Kindle Fire, NOOK, ADE, and iBooks:

Kindle-Multiple indentsKindleFire-Multiple Indents

NOOK-multiple indentsiPad-Multiple indents

ADE-multiple indents

It doesn't take a sharp eye to see that the words in the first line don't quite line up with the second and subsequent ones, AND differ from ereader to ereader. There is no tab in EPUB (or HTML), and each ereader seems to measure spaces its own way. If you want the lines perfectly aligned, you'll have to use a regular old list. It looks beautiful as long as you don't need to control what the bullet character looks like, as old Kindle will only give you a bullet:

Kindle-lists

Did someone say "EPUB in the Wild"?

Here are both the EPUB file as well as the Mobi file that I generated from it with Kindle Previewer 3.

Sunday, January 15, 2012

Media Queries for formatting Poetry on Kindle and EPUB

What a tangled web we weave! Amazon wants to maintain support for its legacy ereaders, we all want to support different size ereaders, but nobody supports the code the same way. What to do? One solution is to use media queries, something I've been meaning to explain for months.

A media query lets you create multiple sets of CSS (just what you want to do, right?) and then apply the most appropriate set automatically according to the ereader the ebook is opened in. So, in a single ebook file, there would be various choices of CSS, perhaps one best suited for old legacy Kindles, another for KF8, another for really small screens like iPhones, and yet another for the full-color 9.7” iPad. The ebook would adapt to its environment, giving each user the best possible experience.

That's the theory anyway.

Of course, there are a couple of caveats. Media queries only affect CSS, not HTML, which limits to some degree what can be changed for those ereaders that don't understand a lot of CSS (yes, I'm talking about clunky old mobi). And not all devices of the same screen size support CSS the same way. Ebook designers, like web designers before them, will continue to make multiple versions of the same ebook until the code is standardized. (And don't expect ebook reader manufacturers to adhere to standards without a lot of prodding.)

But media queries do help. Let's see how they work.

Suppose you want to format poetry in your ebook. Poetry is tricky because each line's length must somehow be respected, even in a screen where the line does not fit. The typical solution is to divide each line of poetry into multiple lines, with the 2nd and subsequent lines indented below the first. No matter how narrow the ereader screen, the reader will still be able to identify each line of poetry as a unit.

Let's look at Whitman's O Captain! My Captain! on the Kindle App in a landscape-oriented iPad. (All of these screenshots are from Kindle Previewer, just so I don't have to copy them to a bunch of different devices.) Notice that no matter how short, each line of poetry is displayed on its own line. Poetry doesn't reflow. It's hard to tell from this screenshot, but note that Kindle automatically indents lines of text 40px for Kindle and Kindle for iPad and iPhone, but not for Kindle Fire!

Poetry, little formatting

If we turn the iPad sideways, or make the text bigger, or both, the lines suddenly don't fit. Not only that, the automatic first line indent that Amazon adds makes poetry look particularly bad. It starts being hard to tell what is the second half of a long line, and what is an individual shorter line.

Poetry, vertical

Here's what it looks like on a legacy Kindle (what I'm calling "old mobi"):


Not pretty.

And here's what it looks like on Kindle Fire. Remember that Kindle Fire doesn't have an automatic first-line indent:

Poetry, Kindle Fire

The combination of automatic full justification with no first-line indent and no adjustment for the poetry lines looks particularly bad on the Kindle Fire. It's just a sea of text.

What's the solution? The convention for formatting poetry is to indent the part of the line that doesn't fit. What layout folks call a hanging indent.

You can do this pretty easily with CSS. Add a left margin of say, 2em, which will push the whole line over and then add a negative text-indent of -2em so that the first line starts at the flush left as usual. In this way, each line of poetry will start at the left margin, but any part of the line that overflows will be displayed indented on the second and subsequent lines. Here's the CSS code:

p {line-height: 1;padding:0;margin:0}
p.firstline {margin-top:2em; margin-left:2em; text-indent: -2em;}
p.line {margin-left:2em; text-indent: -2em;}

And here's what it looks like on the Kindle Fire:



I'd certainly want to adjust the general formatting a bit, but now the lines of poetry are at least inteligible.

But if you open it up with a legacy Kindle, it looks pretty bad:

Poetry - bad Kindle formatting

And it looks equally bad on Kindle for iPad:



What's going on?? It turns out that old mobi handles values for text-indent in a very strange way. If you don't set a text-indent value, old mobi automatically indents 40px. If you set a positive text-indent, it will use that instead. (You can use px or em, but if you use em, only whole numbers will work.) But if you set a negative text-indent, it actually creates a hanging indent, with the first line flush left and the second and subsequent indented as much as the absolute value of text-indent. Go figure. That's weird and unexpected, but if you set the left margin at the same time, it all gets mucked up and the details are way too boring to explain. Trust me, you don't want to go there.

To create a hanging indent on old mobi, you use just a negative text-indent, but no left margin. It shouldn't work, but it does.

h1 {text-align: left}
p {line-height: 1} 
p.firstline {margin-top:20px;text-indent:-40px}
p.line {text-indent:-40px}

And here's what it looks like on a legacy Kindle:



That's fine, but look at the same code on a Kindle Fire:



Because of those negative-indents and the lack of an automatic left margin, or whatever hack old mobi used, now our text is cut off. Unacceptable.

So, Kindle Fire and decent EPUB ereaders that support CSS like iBooks want a left margin and a negative text-indent, but old mobi can't handle that combination, being able to use only the negative text-indent.

The answer is to serve different CSS to different ereaders, by way of a media query. I'm going to show you how to do it in an internal stylesheet but the same principles would hold in an external stylesheet.

First, create a regular stylesheet with no media attribute that will contain the styles that should be applied to all versions of the ebook.


<style type="text/css">
    h1 {text-align: left}
    p {line-height: 1;padding:0;margin:0}
</style>


Next, create a second stylesheet with media="not amzn-mobi" in the opening style tag. The stylesheet should include all of the styles that should apply to all ereaders except legacy Kindle. Amazon says you should use media="kf8" but that's just because they imagine a world where all ereaders are either Kindle Fire or legacy Kindle. Let's just say that most people's worlds are bigger than that.

<style type="text/css" media="not amzn-mobi">
    p.firstline {margin-top:2em; margin-left:2em; text-indent: -2em;}
    p.line {margin-left:2em; text-indent: -2em;}
</style>


Finally, create a stylesheet just for legacy Kindle, adding media="amzn-mobi" to the opening style tag.

<style type="text/css" media="amzn-mobi">
    p.firstline {margin-top:20px;text-indent:-40px}
    p.line {text-indent:-40px}
</style>


Here's what it looks like on Kindle Fire:

Kindle Fire Poetry - good

And here's what the very same file looks like on a legacy Kindle:

Old Kindle Poetry -good

Success!

And just in case you were worrying, here's what the EPUB file looks like in iBooks on the iPad:



Indeed, it looks fine, because iBooks' support of CSS is pretty good. 

A technical note. There was some concern that you couldn't serve different indents to Kindle Fire and to legacy Kindle because the legacy Kindle code uses the width attribute (in a bizarre way) in the HTML, and not the CSS. But the technique described above works because KindleGen converts the CSS in a good EPUB into the weird, hackish old mobi code—complete with width tag—that legacy Kindles love, but creates "KF8" code for the new Kindle Fire, which, as I mentioned Thursday, is virtually the same as the original EPUB. And EPUB readers will get the original, good, EPUB file. So everyone's happy.

Except designers who have to do twice the work. Think standards don't matter? Think again.

One final note. I've been saying that the KF8 files that KindleGen generates from the original EPUB are practically identical to that original EPUB. That's true only for the CSS that KF8 supports. I haven't tested it extensively, but I suspect KindleGen will just ignore the CSS it doesn't support. And I have yet to determine (nor have seen elsewhere) just how well Kindle Fire supports CSS in the first place. I need a couple more days!


More of my books