Blitz ebook Tricks

A collection of CSS snippets to do progressive enhancement and achieve better typography, layout and UX in eBooks.

Those snippets are primarily intended for EPUB3 though some will work in ePub2 and/or Kindle.

Typography

Add line-height to body

ePub2 EPUB3
body {
  line-height: 1.5;
}

body * {
  line-height: inherit;
}

Details

If you add an explicit line-height to elements, your CSS may break Kobo’s – and possibly others’ – user setting. By adding it to body and letting elements inherit from it, this problem is solved.

If you are targeting Kindle, please note the Publishing Guidelines recommends against setting a line-height on body.

Don’t define a line-height value which is less than 1.2

Kindle Format 8
element {
  line-height: 1.2;
}

Details

As stated in versions of the Kindle Publishing Guidelines prior to 2018.2, “to ensure pagination, the Kindle software does not honor line-height value less than 1.2em or 120%.”

In other words, elements for which line-height is less than 1.2 will be applied the default’s line-height, which is 1.75 on most Kindle devices.

Disable hyphens

ePub2 EPUB3 Kindle Format X
element {
  adobe-hyphenate: none;
  -webkit-hyphens: none;
  -moz-hyphens: none;
  -ms-hyphens: none;
  -epub-hyphens: none;
  hyphens: none;
}

Details

Usually, some elements like headings, centered text, etc. are not hyphenated.

Those declarations should cover all self-respecting Reading Systems.

Improve hyphenation

EPUB3
element {
  -webkit-hyphenate-limit-before: 3;
  -webkit-hyphenate-limit-after: 2;
  -ms-hyphenate-limit-chars: 6 3 2;
  hyphenate-limit-chars: 6 3 2;
  -webkit-hyphenate-limit-lines: 2;
  hyphenate-limit-lines: 2;
}

Details

When you set hyphens to auto, extra declarations can be set to improve hyphenation for your language.

You can indeed set the minimum number of letters a word must contain to be hyphenated, and the minimum number of letters which should be before and after the hyphen.

Finally, you can control the maximum number of consecutive lines for which hyphenation must happen. As Bringhurst advised, “avoid more than three consecutive hyphenated lines.”

Declare text-align for elements which should be left-aligned

ePub2 EPUB3 Kindle
element {
  text-align: left;
}

Details

If some elements like headings should be left-aligned, make sure to declare text-align.

Indeed, those elements will be justified if you don’t and the user sets full justification. Word-spacing will then be adjusted so that the text falls flush with both margins, which can result in terrible typography.

Prevent sub- and superscript from affecting line-height

ePub2 EPUB3 Kindle
sub {
  font-size: 0.675em;
  line-height: 1.2;
  vertical-align: sub;
  vertical-align: -20%;
}

sup {
  font-size: 0.675em;
  line-height: 1.2;
  vertical-align: super;
  vertical-align: 35%;
}

Details

Sub- and superscript will affect line-height if you just use their dedicated keyword for vertical-align.

By decreasing line-height to the minimum value Kindle supports (i.e. 1.2) and using % for vertical-align, we solve this problem and can vertically-align sub- and superscript more accurately.

Improve legibility

EPUB3
body {
  font-kerning: normal;
  font-variant: common-ligatures oldstyle-nums proportional-nums;
  font-feature-settings: "kern", "liga", "clig", "onum", "pnum";
}

h1, h2, h3 {
  font-variant: common-ligatures lining-nums proportional-nums;
  font-feature-settings: "kern", "liga", "clig", "lnum", "pnum";
}

table {
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "kern", "lnum", "tnum";
}

code {
  font-variant: no-common-ligatures lining-nums;
  font-feature-settings: "kern" 0, "liga" 0, "clig" 0, "lnum";
}

.fraction {
  font-variant-numeric: diagonal-fractions;
  font-feature-settings: "frac";
}

Details

OpenType features can dramatically improve the legibility of an eBook. Although not all default fonts provided by Reading Systems support all those features, you can still benefit from them.

The font-feature-settings property is a low-level feature designed to handle special cases where no other way to enable or access an OpenType font feature exists, which is why you should prefer font-variant and its associated longhand properties.

Please note font-feature-settings doesn’t inherit values from the parent element but resets them.

For a complete list of features, visit this Typekit help page.

Use real small capitals

EPUB3
element {
  font-variant: small-caps;
}

@supports not (font-variant-caps: small-caps) {
  element {
    font-variant: normal;
    font-feature-settings: "smcp", "onum", "pnum";
  }
}

@supports (font-variant-caps: small-caps) {
  element {
    font-variant: normal;
    font-variant-caps: small-caps;
  }
}

Details

The font-variant:small-caps property creates fake small caps. This can turn an enjoyable book into a mediocre experience since you can tell they are fake in the blink of an eye.

By using OpenType Features, we can use real small caps the typeface designer took special care getting right.

Please note that if the font doesn’t support this feature, it will fall back to fake small caps.

Use semantic asterisms and make them reflowable

ePub2 EPUB3 Kindle
hr.asterism {
  height: 1.5em;
  background: transparent url("../Images/asterism.svg") no-repeat center;
  background-size: 2.5em 1.25em;
  overflow: hidden;
  page-break-inside: avoid;
  break-inside: avoid;
}

@supports not ((page-break-inside: avoid) and (break-inside: avoid)) {
  hr.asterism {
    -webkit-column-break-inside: avoid;
  }
}

Details

Asterisms are context changes, thus you should use hr.

Problem is hr can’t contain anything so the only solution is to use a background image.

Your best option is SVG since it manages transparency, reflow without a loss in quality, can be designed to be compatible with night modes, etc.

Reading Systems which don’t support background-size will fall back to the width, height and viewbox attributes in your SVG so don’t get rid of them.

Fake asterisms (context change)

EPUB3
<div class="asterism" role="separator" aria-label="Interlude">
  * * *
</div>

Details

Sometimes you can’t use semantic asterisms (background-image for hr) and adding it with hr:before may prove to be problematic for accessibility.

In that case, the ARIA role and aria-label attributes may come in handy.

The separator role tells Reading Systems the div is intended to be a context change (hr) and the aria-label will override the text inside it for Text to Speech.

If you intend to mimick how hr will typically be handled for selection and copy, don’t forget to disable user’s selection though (see “Create an automated numbering system” trick).

Layout

Make HTML5 tags behave as expected in legacy RMSDK

ePub2 EPUB3 Kindle
article, aside, figure, figcaption, 
footer, header, main, nav, section {
  display: block;
}

Details

Did you know most of HTML elements have a default display value of inline? It’s up to browsers to set some elements to block.

Since the legacy RMSDK is not supposed to support HTML5, you must set grouping elements to block in your style sheet.

Prevent horizontal margins to reflow with font-size

ePub2 EPUB3 Kindle
element-1 {
  margin-left: {number}%;
}

element-2 {
  padding-left: {number}%;
  padding-right: {number}%;
}

Details

If you use em for horizontal margins, they will increase/decrease with the font-size user setting. This implies that the bigger the text is set, the smaller its container will be – it should be the opposite.

By using % for horizontal margins and paddings, Reading Systems will use the width of the page or parent container to compute them, not the current font-size.

Center a block element in legacy RMSDK

ePub2 EPUB3 Kindle
element {
  width: {number}%;
  margin-left: ((100 - {number}) / 2 )%;
  margin-right: ((100 - {number}) / 2 )%;
}

Details

Legacy RMSDK doesn’t really support the auto value for margin. As a matter of fact, it maps auto to 0, which is allowed in a footnote of the ePub2 specification.

In other words, if you want to center an element, you should declare a width then substract it from 100 and divide it by 2 to get your horizontal margins.

If your element is 80% then each margin will be (100-80)/2 or 10%.

Do not rely on page-breaks

ePub2 EPUB3
element {
  page-break-inside: avoid;
  break-inside: avoid;
}
    
@supports not ((page-break-inside: avoid) and (break-inside: avoid)) {
  element {
    -webkit-column-break-inside: avoid;
  }
}

Details

CSS break is a strange beast. But what’s important is that we shouldn’t only rely on paged media.

Modern Reading Systems will typically use CSS multi-columns to fake pagination since browsers don’t implement paged media, which means page-break-* is not necessarily aliased to column-break-*. And page-break-* itself is destined to become an alias for break-* at some point. For maximum compatibility, we must therefore use all three.

The feature query (@supports) allows us to get around some bug in iBooks. At some point, declaring -webkit-column-break-inside in the same rule as page-break-inside would indeed result in both styles being ignored.

Prefer page-break-after to page-break-before

ePub2 EPUB3 Kindle
element {
  page-break-after: always;
  break-after: always;
}

@supports not ((page-break-after: always) and (break-after: always)) {
  element {
    -webkit-column-break-after: always;
  }
}

Details

For some reason, it looks like page-break-after:always has got slightly better support than page-break-before:always in some Reading Systems.

As stated in iBooks Asset Guide, “if you include page breaks to mark a chapter break, use page-break-after to create a break at the end of a chapter, not page-break-before to insert the break at the beginning of the chapter. This modification improves performance with the table of contents.”

In any case, don’t use both since it will create a blank page in some Reading Systems.

Fix the layout of tables

ePub2 EPUB3 Kindle
table {
  table-layout: fixed;
}

th.third {
  width: 33%;
}

Details

By default, you can’t really tell how Reading Systems will compute the width of tables’ columns. All you know is that they will compute those widths depending on their cells’ contents.

You can force a width for each column by using table-layout:fixed then declaring a width for each element in the first line of the table.

Reading Systems will now use the width specified for those elements to compute the width of each column.

Create an automated numbering system

EPUB3 Kindle
parent {
  counter-increment: elements;
}

element:before {
  content: counter(elements);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

Details

Although it won't work in legacy RMSDK, this CSS snippet can be useful for numbered lines of code, headings, poetry, etc.

Make sure to disable selection in some cases; nobody wants to copy-paste code with numbers breaking it.

Check this MDN tutorial for further details.

Build a super simple responsive grid

EPUB3
@supports (display: -webkit-flex) or (display: flex) {
  ul.grid,
  ol.grid {
    display: -webkit-flex;
    display: flex;
    -webkit-flex-wrap: wrap;
    flex-wrap: wrap;
    -webkit-flex: 1 1 15em;
    flex: 1 1 15em;
  }

  ul.grid li,
  ol.grid li {
    page-break-inside: avoid;
    break-inside: avoid;
  }
}

Details

What if you could make the most of the page’s width for, say, an inline table of contents or a list of ingredients?

By using flexbox, you can create a responsive grid which should behave according to a preferred width (the third value in flex e.g. 15em).

Of course you may want to provide a fallback for Reading Systems which don't support flexbox.

Vertically-align elements on a page

EPUB3
@supports (display: -webkit-flex) or (display: flex) {
  parent {
    min-height: 95vh;
    display: -webkit-flex;
    display: flex;
    -webkit-flex-direction: column;
    flex-direction: column;
    -webkit-justify-content: {value};
    justify-content: {value};
  }
}

Details

Pages of an eBook don’t have a middle or a bottom, right? With flexbox, they now do.

Make sure to use the min-height property so that the container’s height can grow in case the user sets a huge font-size… or else contents will collapse.

If you’re not familiar with flex properties and values, check CSS-Tricks’ complete guide to flexbox.

Finally, you should provide a fallback for Reading Systems which don't support flexbox.

Give text wraps a modern twist

EPUB3
element {
  float: left;
}

@supports (-webkit-shape-outside: {value}) or (shape-outside: {value}) {
  element {
    -webkit-shape-outside: {value};
    shape-outside: {value};
    -webkit-clip-path: {value};
    clip-path: {value};
  }
}

Details

For decades, text has wrapped around the floated element’s bouding box, which means it couldn’t wrap the object like it does in DTP software.

By using shape-outside and clip-path, you can define a shape around which the text should wrap.

And there’s even a Chrome extension to help you do that.

Images

Force images to keep their aspect ratio

EPUB3
img {
  object-fit: contain;
}

Details

When sizing images based on width or height, you’ll probably end with distorted images in some contexts. It turns out there is a CSS property to manage that.

Get used to add object-fit:contain to img as this property prevent this distortion.

Keep an image with its caption

ePub2 EPUB3
figure {
  page-break-inside: avoid;
  break-inside: avoid;
}

@supports not ((page-break-inside: avoid) and (break-inside: avoid)) {
  figure {
    -webkit-column-break-inside: avoid;
  }
}

Details

Readability is as important as legibility and this is why you should take care of the relationship between elements.

For best comprehension, the image and its caption should be displayed on the same page so let’s avoid a page-break inside figures.

Make an image responsive to its caption’s font-size

EPUB3
@supports (height: calc(98vh - 5em)) {
  img {
    width: auto;
    max-width: 100%;
    min-height: 300px;
    height: calc(98vh - 5em);
    max-height: 95%;
    object-fit: contain;
  }
}

Details

For images with a portrait aspect ratio, you must go the extra mile so that the image and (part of) its caption are displayed on the same page.

This is where the CSS calc() function really shines. It allows you to dynamically compute the height of an image depending on the current font-size.

In this example, the image’s height should ideally be 98% of the page minus 3 lines of text (with a line-height of 1.5). Finally, min- and max-height provide a range for the image sizing.

Misc.

Use text color as a variable

ePub2 EPUB3 Kindle
element {
  border: 1px solid currentColor;
}

svg {
  fill: currentColor;
}

Details

If you want borders, backgrounds or SVG’s fill to be the same color as text, including in night modes, currentColor is your best bet.

Indeed, currentColor sort of behaves like a variable: it inherits the current color of text.

In other words, when Reading Systems set a light color for text in night mode, currentColor will become this color.

Style epub:type

EPUB3
@namespace epub "http://www.idpf.org/2007/ops";

[epub|type~="toc"] {
  /* styles */
}

Details

Problem with epub:type is that you have to escape the colon in CSS

Unless you declare a namespace at the top of your style sheet. You can now use epub|style instead of epub\:style, which is a lot more readable and maintainable.

Prevent unsupported styles from impacting legacy RMSDK

EPUB3
@supports ({property}: {value}) {
  element {
    {property}: {value};
  }
}

Details

If you want to do progressive enhancement and don't want to risk your stylesheet being entierely ignored in legacy RMSDK, feature queries are the way to go.

Besides asking Reading Systems if they support a CSS declaration, it will basically protect the nested styles from being parsed by the legacy RMSDK. It can be pretty useful with the CSS calc() function for instance.

And @supports can protect media queries too.

The only downside is that Internet Explorer 11 doesn't support feature queries and Adobe Digital Editions 4.5 is using its rendering engine (Trident) on Windows. As a consequence, those styles won't be applied even though Internet Explorer supports them.

Declare specific styles for Kindle

Kindle
@media amzn-kf8 {
  /* Specific KF8 styles (new format) */
}

@media amzn-mobi {
  /* Specific Mobi7 styles (old format) */
}

Details

If for some reason you must add or override styles for Kindle, those two media queries can help. KindleGen will indeed take them into account when converting your EPUB file.

Do not ever let them empty in your EPUB file since that would crash the legacy RMSDK and all the Reading Systems using it.

Style for monochrome

EPUB3
@media (monochrome) {
  /* Styles */
}

Details

Although this media query won't work with eInk devices, it can help solve accessibility issues.

MacOS and iOS indeed ship with a monochrome switch in system preferences (accessibility panel) which, when enabled, will trigger the monochrome media.

In other words, you could adapt colors to improve contrast for this mode.

Make sure to protect this media query from legacy RMSDK’s parsing because your entire stylesheet could be ignored or, even worse, crash some apps and devices.

Style for touch or click input

EPUB3
@media (pointer: coarse) {
  /* Styles for touch input */
}

@media (pointer: fine) {
  /* Styles for mouse input */
}

Details

Footnote references styled as in print (superscript) might provide users with a mediocre experience on touch devices. Fortunately, there is a media for such a case.

The pointer media returns the accuracy of the primary input mechanism of the device, which means touch will return coarse while mouse will return fine for instance.

In other words, you could increase the size of clickable elements for coarse.

Make sure to protect this media query from legacy RMSDK’s parsing because your entire stylesheet could be ignored or, even worse, crash some apps and devices.