Typography
Add line-height to body
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
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
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
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
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
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
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
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
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)
<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).
Force the color of links to be text’s
a.text-color {
color: inherit;
-webkit-text-fill-color: inherit;
}
Details
Sometimes there are so many links on a page that styling them using colors may disrupt the reading experience. As long as users can tell it’s a link, you should be OK.
If you want links to be the same color as text, you can inherit color
. The -webkit-text-fill-color
property forces iBooks to use the text’s color in night mode.
While this snippet should work OK in the Kindle Format 8, it won’t in Kindle Format X, and a blue color will be forced for links – there’s currently no known trick to get around this.
Layout
Make HTML5 tags behave as expected in legacy RMSDK
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
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
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
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
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
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
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
@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
@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
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
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.
Make an image responsive to its caption’s font-size
@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
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
@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
@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
@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
@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
@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.