Skip to content

Instantly share code, notes, and snippets.

@JayPanoz
Last active September 7, 2018 15:45
Show Gist options
  • Save JayPanoz/812c25dc01e0745c6f5d56ecb964f721 to your computer and use it in GitHub Desktop.
Save JayPanoz/812c25dc01e0745c6f5d56ecb964f721 to your computer and use it in GitHub Desktop.
Experimental CSS to get portrait-aspect-ratio image + caption on same page, on reflow
/* Current nugget */
/* This in your fallback CSS (for ePub 2) */
.portrait-caption {
height: 99%; /* must check if necessary */
margin: 1.5em 0;
page-break-before: auto;
}
.portrait-caption > img {
width: auto;
max-width: 100%;
height: 80%;
}
/* The following in another CSS (EPUB 3) */
/* EPUB 3 FALL-FALL-FALLBACK */
.portrait-caption {
background-color: yellow; /* I'm using bg-color to check which styles are applied in RS */
}
.portrait-caption > img {
height: 80vh;
max-height: 800px; /* Must check CSS specs — how max-height is supposed to behave when vh/calc on height */
object-fit: contain; /* to keep aspect-ratio of image. Thanks iBooks’ default CSS for that */
}
/* Feature query using @supports = progressive enhancement */
@supports (page-break-before: always) and (height: calc(99vh - 5em)) {
.portrait-caption {
min-height: 100vh; /* fallback for the following line if anything goes wrong */
min-height: calc(100vh - 0px); /* = 100vh but we make sure it is recomputed when doc is updated */
width: 100%; /* is probably useless */
background-color: LightGreen;
page-break-before: always; /* So… ADE doesn't support that though it says it does */
-webkit-column-break-inside: avoid; /* making ADE behaves */
display: block;
margin: 0; /* image in flow, it is not in its own xhtml file. That's the magic. */
}
.portrait-caption > img {
box-sizing: border-box; /* because padding */
max-width: 100%;
min-height: 60vh; /* So that it doesn't become ridiculously tiny when huge font-size is set */
height: -webkit-calc(98vh - 5em);
height: calc(98vh - 5em); /* 98vh - caption’s height || Fallback is 80vh */
object-fit: contain; /* to keep aspect-ratio of image. Thanks iBooks’ default CSS for that */
padding: 1vh 0; /* if for some reason it breaks inside, we make sure we got a padding at top of image, bottom for caption */
margin: 0 auto; /* centering */
}
}
/* For Readium scroll as the viewport is unreliable.
1. min-height depends on your documents’ sizes. Using `em`
makes sure it is recomputed when font-size is changed.
Maybe we could find something else?
2. FYI, if you just got an image in a xhtml file,
Readium’s default viewport in scroll is 300 x 150px
so your 100vh-height image will be 150px. */
@media screen and (min-height: 150em) {
.portrait-caption {
display: block;
margin: 1.5em 0;
min-height: 0; /* reset */
height: auto; /* reset */
background-color: red;
}
.portrait-caption > img {
min-height: 300px; /* reset */
height: auto; /* reset as we rely on max-width */
max-height: 100%;
max-width: 100%; /* Pic will scale based on its width */
padding: 0;
}
}
@JayPanoz
Copy link
Author

JayPanoz commented May 18, 2016

Original nugget:

/* So yeah will work in epub 3 only but hey, go to hell legacy RMSDK
   To sum up, img + caption stay on the same "page" until a huge font-size is set (in iBooks, text on following page) */


/* css support query (implemented in webkit and legacy RMSDK doesn't take it into account so afaic, it's safe) */
@supports (display: table) and (height: 100vh) {
    .portrait-caption {
        width: 100%;
        height: 99vh;
        margin: 1.5em 0;
        page-break-before: always;
        display: table;                       /* this is the trick */
    }
    .portrait-caption > img {
        box-sizing: border-box;               /* because padding. Shouldn't work with min-height but somehow does */
        min-height: -webkit-calc(99vh - 5em);
        min-height: calc(99vh - 5em);         /* min-height as it will override height is necessary. It's 99% viewport height less caption min-height */
        padding-bottom: 0.5em;                /* or else image + caption may "collapse" */
        display: table-row;
        margin: 0 auto;                       /* Or else it won’t be centered */
    }
    .portrait-caption > .caption {          /* you probably doesn't even need this (see next comment) */
        display: table-row;                   /* disclaimer: I forgot "." before "caption" because I'm stupid and it worked */
    }
}

So, let’s explain this a little bit.

The idea is to keep portrait-aspect-ratio image + it's caption on the same page if possible, to reflow the image’s height when the user increases or decreases font-size. It somehow works quite well at the moment.

Why not using extra divs in markup?

Because sometimes the people using your CSS (clients, authors, etc.) don't get it and won't know which extra divs to use and where to add them. So if it's automatic using the proper class on the wrapper, it's cool.

Known issues

  • It's a giant mess in Readium’s scroll mode as the min-height (calc() value) somehow triggers a bug → the computed height will be something like 13 000 or 24 000 px. ¯_(ツ)_/¯
  • it doesn't work in Kindle because Kindle doesn't support min-height.
  • it obviously doesn't work in legacy RMSDK because legacy RMSDK doesn't support display:table (they'll say they do but it's so fucked up they absolutely don't).
  • Kobo iOS doesn't necessarily update the computed value for calc() when you change font-size—that's why we need APIs, guys #justSayin
  • It seems the Google Play Books app ignore that because everybody knows portrait-aspect-ratio images are the proper aspect-ratio every time on every device and they can't haz figcaptions so let's go yolo and max-width all the things.

@JayPanoz
Copy link
Author

JayPanoz commented May 18, 2016

FYI, tried display: flex as it would bring support for Kindle but to no avail as it creates issues display: table doesn't have in iBooks (text overflow).

Here's the snippet if you want to experiment with it.

@supports (display: flex) and (height: 100vh) {
    .portrait-caption {
        box-sizing: border-box;
        width: 100%;
        height: 95vh;
        margin: 0;
        page-break-before: always;
        display: -webkit-flex;
        display: flex;
        -webkit-flex-direction: column;
        flex-direction: column;
        -webkit-align-items: flex-start;
        align-items: flex-start;
        -webkit-justify-content: flex-start;
        justify-content: flex-start;
    }
    .portrait-caption > img {
        -webkit-align-self: center;
                align-self: center;     
        box-sizing: border-box;
        height: 100%;
        padding-bottom: 0.5em;
    }
    .portrait-caption > .caption {
        -webkit-align-self: center;
                align-self: center; 
    }
}

@JayPanoz
Copy link
Author

OK so, flexbox updated. Works great in iBooks, not so much anywhere else—why Readium 😭 why so much flexboxhate?

@supports (display: flex) and (height: 100vh) { 
    .portrait-caption {
        box-sizing: border-box;
        width: 100%;
        height: 99vh;
        margin: 0;
        page-break-inside: avoid;
        display: -webkit-flex;
        display: flex;
        -webkit-flex-direction: column;
        flex-direction: column;
        -webkit-align-items: flex-start;
        align-items: flex-start;
        -webkit-justify-content: flex-start;
        justify-content: flex-start;
        overflow: hidden;                       
    }
    .portrait-caption > img {
        -webkit-align-self: center;
                align-self: center; 
        -webkit-flex-shrink: 2;
        flex-shrink: 2;    /* Must be grow-value +1 */
        padding-bottom: 0.5em;
    }
    .portrait-caption > .caption {
        -webkit-align-self: center;
                align-self: center; 
        -webkit-flex-grow: 1;
        flex-grow: 1;   /* That’s the trick */
        text-overflow: ellipsis;   /* In case huge font-size */
    }
    .portrait-caption > .caption:after {    /* Need that or else last line cut */
        content: ' ';
        display: inline-block;
        clear: both;
        width: 99%;
    }
}

… and I'm done. Might re-explore that someday.

@JayPanoz
Copy link
Author

JayPanoz commented May 19, 2016

So here we go, something that somehow works well quite anywhere, except the stupid RMSDK (ePub 3).

Demo → https://twitter.com/JiminyPan/status/733350036986302464

For the nasty readium.extension bug → https://twitter.com/JiminyPan/status/733234476789161984 (this freaking thing lives, it's a monster who feeds upon your despair)

Fallback ePub 2 (CSS 1)

.portrait-caption {
    height: 99%;                /* must check if necessary */
    margin: 1.5em 0; 
    page-break-before: auto;
}

.portrait-caption > img {
    height: 80%;
}

And now to the big show… (CSS 2 or else legacy RMSDK will ignore the entire stylesheet)

/* Fallback */
@supports not (min-height: calc(99vh - 5em)) {
  .portrait-caption {
    height: auto;
        text-align: center;
  }
  .portrait.caption > img {
    height: 80vh;
    max-height: 100%;
  } 
}

@supports (display: table) and (min-height: calc(99vh - 5em)) {
/* you may add a query for page-break-before to bulletproof that */
  .portrait-caption {
    width: 100%;
    height: 99vh;       /* Readium needs it */
    margin: 1.5em 0;    /* Margin in case you know, page-break-before is not supported */
    page-break-before: always;
    display: table;      /* that's the trick */
  }
  .portrait-caption > img {
    box-sizing: border-box;
    min-height: -webkit-calc(99vh - 5em);
    min-height: calc(99vh - 5em);            /* forces height because it overrides it and height doesn’t work else // 5em = height of your caption. So image reflows when font-size is updated */ 
    padding-bottom: 0.5em;
    display: table-row;
    margin: 0 auto;
  }
  .portrait-caption > .caption {
    display: table-row;
  }
}

/* Sooooooooooo vh is buggy in Readium's scroll mode so we must get around that. The viewport is the document’s height… so it’s huge but it depends on your xhtml size obviously */

    @media screen and (min-height: 150em) {
        .portrait-caption {
            display: block;
            page-break-before: auto;
            height: auto;
        }
        .portrait-caption > img {
            display: inline-block;  /* Reset */
            min-height: 300px;  /* Cos yeah, you'd better reset it because calc() */
            height: auto;
            max-width: 100%;
            margin: 0 auto;
            padding: 0;
        }   
        .portrait-caption > .caption {
            display: block;
        }
    }

My understanding is RMSDK doesn’t support @supports but somehow parses the styles inside those support queries, making a mess of this whole trick. It wouldn’t come as a surprise since those guys treats rem as em in legacy RMSDK, thus proving they’ve got the “skills” to parse CSS like no other.

Checked:

  • iBooks OS X + iOS (all modes)
  • Readium (Chrome extension — all modes)
  • Kobo iOS
  • Google Play Books
  • Legacy RMSDK

So… if anyone got an idea, we can haz adaptative portrait-aspect-ratio images + caption and then we’ll have to find a solution for Kindle. If not, gimme tables.

┻━┻︵ (°□°)/ ︵ ┻━┻

But hey at least there’s the fallback. ¯_(ツ)_/¯

@JayPanoz
Copy link
Author

JayPanoz commented May 20, 2016

Update number 4 (please someone stand up and save my life right now).

The nugget has been trimmed and well, got ourselves more issues to deal with.

Here’s the nugget at the moment.

@supports (height: 80vh) {
.portrait-caption {
    height: auto;
    page-break-before: always;
}
  .portrait-caption > img {
    height: 80vh;
  }
}

@supports (height: calc(99vh - 5em)) {
.portrait-caption {
    min-height: 99vh;
    page-break-before: always;
}
  .portrait-caption > img {
    box-sizing: border-box;
    min-height: 50vh;    /* So that it doesn't become ridiculously tiny, you know, when people set font-size to 256 pt in iBooks */
    height: -webkit-calc(99vh - 5em);
    height: calc(99vh - 5em);
    padding-bottom: 0.5em;
    margin: 0 auto;
  }
}

@media screen and (min-height: 150em) {
        .portrait-caption {
            min-height: none;
            height: auto;
        }
        .portrait-caption > img {
            min-height: 300px;
            height: auto;
            max-height: 100%;
            max-width: 100%;
            padding: 0;
        }   
    }

As you can see, table has been flipped dropped since it was the issue in ADE 4.5—thanks Adobe, how about you release software that just works?

But… dropping table unfortunately flipped some others.

(°□°)/ ︵ ┻━┻ YEAH! YEAH! YEAH! So many tables to flip. Oh look, my desk is now flipped.

  • CSS will now fail at some point (font-size) which means image cut and spanned on two pages;
  • ADE supports @supports but supports @supports not NOT—that’s very meta;
  • Kindle will ignore the amzn media queries if it finds @supports in the CSS—judging by the fact they don't know how to parse <header>, it's not surprising;
  • Kobo iOS… I don't want to talk about → https://twitter.com/JiminyPan/status/733625831650627588
  • Got myself a confirmation the viewport is messed up in Readium.ext scroll → https://twitter.com/JiminyPan/status/733599954028072961 + https://twitter.com/JiminyPan/status/733600733828927488 (which is the reason why the media kwery). What's interesting is that it is also messed up in ADE for iOS but vh is cool/safe.
  • ADE for iOS has a freaking cache. Not kidding. It has a cache and it's still soooooooooooo slooooooooooow at rendering each page.

And that's it.

Please grab the opportunity to become a hero.

@JayPanoz
Copy link
Author

JayPanoz commented May 20, 2016

Brace yourself, here comes “THE EPIC UPDATE”

tl;dr: I did it. Who’s your daddy now?

The ePub 2 Fallback

Should be in one CSS.

.portrait-caption {
  height: 99%;                /* must check if necessary */
  margin: 1.5em 0; 
  page-break-before: auto;
}
.portrait-caption > img {
  height: 80%;
}

The Golden Nugget

Should be in another CSS.

Here you go, use and abuse it, test it everywhere and report issues. Test, iterate, improve, enhance, media-querite and have fun.

/* EPUB 3 FALL-FALL-FALLBACK */

.portrait-caption {
  background-color: yellow;  /* I'm using bg-color to check which styles are applied in RS */
}
.portrait-caption > img {
  height: 80vh;
  max-height: 800px;  /* Must check CSS specs because it is useless at the moment*/
  object-fit: contain;    /* to keep aspect-ratio of image. Thanks iBooks’ default CSS for that */
}

/* Support query = progressive enhancement */

@supports (page-break-before: always) and (height: calc(99vh - 5em)) {
  .portrait-caption {
    min-height: calc(100vh - 0px);  /* = 100vh but we make sure it is recomputed when doc is updated */
    width: 100%;  /* is probably useless */
    background-color: LightGreen;
    page-break-before: always;  /* So… ADE doesn't support that though it says it does */
    -webkit-column-break-inside: avoid;  /* making ADE behaves */
    display: block;
    margin: 0;  /* image in flow, it is not in its own xhtml file. That's the magic. */
  }
  .portrait-caption > img {
    box-sizing: border-box;  /* because padding */
    max-width: 100%;
    min-height: 60vh;  /* So that it doesn't become ridiculously tiny when huge font-size is set */
    height: -webkit-calc(98vh - 5em); 
    height: calc(98vh - 5em);  /* 98vh - caption’s height  */
    object-fit: contain;  /* to keep aspect-ratio of image. Thanks iBooks’ default CSS for that */
    padding: 1vh 0;  /* if for some reason it breaks inside, we make sure we got a padding at top of image, bottom for caption */
    margin: 0 auto;  /* centering */
  }
}

/* For Readium scroll as the viewport is unreliable */

@media screen and (min-height: 150em) {
  .portrait-caption {
    display: block;
    margin: 1.5em 0;
    min-height: 0;   /* reset */
    height: auto;  /* reset */
    background-color: red;
  }
  .portrait-caption > img {
    min-height: 300px;   /* reset */
    height: auto;  /* reset as we rely on max-width */
    max-height: 100%;
    max-width: 100%;  /* Pic will scale based on its width*/
    padding: 0;
  } 
}

Checked on:

  • iBooks (iOS + OS X, all modes)
  • Readium (Chrome extension — all modes)
  • Google Play Books (will default to fallback)
  • Modern RMSDK (ADE 4.5 OS X + iOS)
  • Legacy RMSDK
  • Kobo iOS (it seems they override img styles so maybe try a class or an id?)

The process

It's epic, much more epic that “Emojyles” actually → https://twitter.com/JiminyPan/status/730756695106916352

  1. I somehow started with display: table as there is a nasty trick for legacy RMSDK: using display: row and display: cell instead of display: table-row and display: table-cell—yes, it is that bad. Problem is you must then use min-height for img, which limits what you can do.
  2. I tried flexbox but well, it doesn’t even work very well in iBooks (last line of caption doesn’t behave well at reflow) so it’s a dead end at the moment.
  3. I discovered display: table was actually the problem in “modern RMSDK” so I dropped it—and yeah I wondered why I’ve been using it in the first place.
  4. I was somewhat reminded of object-fit: contain; so I wanted to test it.
  5. Being absolutely pissed by ADE 4.5 (see here) I tried adding page-break-before in the support query.
  6. Dat arsehole (ADE 4.5) told me it supports page-break-before while it clearly doesn’t (see there).
  7. Then I remembered the -webkit-column-break-whatever properties, which aren’t standard at all but oh yeah, it looks like ADE 4.5 is using that. See https://developer.mozilla.org/en-US/docs/Web/CSS/break-inside for more info (note at the end, especially) so I don't know, maybe ADE 4.5 is a fucking liar and claims it supports page-break-before while it supports -webkit-column-break-before. But anyway… I flipped so much tables that I don't have another one left to flip.
  8. And then using -webkit-column-break-inside: avoid, it fucking worked in ADE 4.5. See there.
  9. I concluded that my stupidity at persevering helped me achieve this. It is sad but awesome at the same time.

It’s up to you now. TEST DAT BITCHY NUGGET and make reflowable books great again!

@JayPanoz
Copy link
Author

JayPanoz commented May 23, 2016

Further details + notes

height, max-height and min-height

As a reminder:

min-height > max-height > height

So you can set a max-height if there is no min-height (fallbacks) but you’ll have to !important it for some reason (I guess because vh?).

I can’t get the “reflow image please” part to behave with max-height, even when setting a min-height smaller than the max-height (in px). So maybe height: calc() is the culprit. I really don't know, found absolutely nothing about that.

Edit: Or maybe it is once again those fucking RS overrides since it it working as intended in web browsers.

So the workaround is to make sure your pictures are freaking huge enough so that you don’t need max-height.

iBooks

iBooks won’t take your max-height into account, at all. I guess there is a bug when vh are used. Though the inspector shows iBooks’ default max-height: 95% is overridden by your own max-height, applied styles show max-height: 95% is being used… which means:

min-height > iBooks’ max-height > max-height > height

Edit: Or maybe it is twice again those fucking RS overrides + an iBooks bug on top of it, dropping the max-height when height in vh or calc() since it it working as intended in web browsers.

So the workaround is to make sure your pictures are freaking huge enough so that you don’t need `max-height.

Constrain caption’s font-size

There’s one trick if you want to constrain the caption’s font-size on iOS: -webkit-text-size-adjust: [x * 100]% . Used it in the past and It works at least in iBooks but I didn't check for this nugget so please correct me if I'm wrong.

@tooolbox
Copy link

tooolbox commented Oct 9, 2016

I applaud you, sir, even if all of this hoop-jumping makes me want to cry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment