Creating Beautiful PDFs via HTML/CSS/JS

A naive attempt to re-invent LaTeX in the browser

Yihui Xie

2025-04-09 @ Nebraska R User Group

The xkcd cartoon

The dependency cartoon

My attention span


Why you should learn CSS (Cascading Style Sheets) & JS (JavaScript)


Why not LaTeX or Word?

[…] {xdvir} is built on LaTeX, with all of the joy and pain that that brings.

—Paul Murrell, The {xdvir} Package


The R Markdown ecosystem

classDiagram
  direction BT
  class htmlwidgets {
    +JS apps
    R-dep (26)
    web-dep (*.js/.css)
  }
  class rmarkdown {
    +HTML/LaTeX
    +RTF/Word/PowerPoint/EPub/...
    R-dep (knitr, evaluate, ... 25)
    sys-dep (Pandoc)
    web-dep (Bootstrap/jQuery/...)
  }
  class blogdown {
    +Websites
    R-dep (33)
    sys-dep (Hugo)
  }
  blogdown --|> bookdown
  class pagedown {
    +Paged HTML
    R-dep (38)
    web-dep (paged.js)
  }
  pagedown --|> bookdown
  class distill {
    +Grid layout
    R-dep (48)
    web-dep (distill)
  }
  distill --|> bookdown
  class pkgdown {
    +Package sites
    R-dep (52)
    web-dep (Bootstrap...)
  }
  pkgdown --|> rmarkdown
  class xaringan {
    +HTML slides
    R-dep (32)
    web-dep (remark.js)
  }
  xaringan --|> rmarkdown
  class tufte {
    +Two-column layout
    R-dep (26)
    web-dep (tufte.css)
  }
  tufte --|> rmarkdown
  class bookdown {
    +Books
    R-dep (26)
    web-dep (GitBook...)
  }
  bookdown --|> rmarkdown
  classDef default fill:none
  style rmarkdown fill:lightskyblue

One package to implement them all (not quite)

classDiagram
  class litedown {
    +HTML/LaTeX
    +min [ * ]
    R-dep (xfun, commonmark)
    web-dep (lite.js)
  }
  class rmarkdown {
    +[ * ]
  }
  class pkgdown {
    +[ * ]
  }
  class xaringan {
    +[ * ]
  }
  class tufte {
    +[ * ]
  }
  class blogdown {
    +[ * ]
  }
  class pagedown {
    +[ * ]
  }
  class distill {
    +[ * ]
  }
  class htmlwidgets {
    +[ * ]
  }
  class bookdown {
    +[ * ]
  }
  litedown *-- rmarkdown
  litedown *-- bookdown
  litedown *-- pkgdown
  litedown *-- xaringan
  litedown *-- tufte
  litedown *-- htmlwidgets
  litedown *-- blogdown
  litedown *-- pagedown
  litedown *-- distill
  rmarkdown <|-- bookdown
  rmarkdown <|-- pkgdown
  rmarkdown <|-- xaringan
  rmarkdown <|-- tufte
  rmarkdown <|-- htmlwidgets
  rmarkdown <|-- blogdown
  rmarkdown <|-- pagedown
  rmarkdown <|-- distill
  classDef default fill:none
  style litedown fill:lightcyan
  style pagedown fill:yellow

Creating PDF from HTML

Tariffs on exporting to PDF

— via @shelina.bsky.social


The problems


The solutions


Get started

install.packages('litedown')
litedown::roam()

Create a new document via the + button on the toolbar and then the check box pages.

output: 
  html: 
    meta: 
      css: ["@pages"]
      js: ["@pages"]

Demo


Parameters

:root {
  --paper-width: 210mm;
  --paper-height: 297mm;
  --paper-margin-top: 40px;
  --paper-margin-right: 80px;
  --paper-margin-bottom: 40px;
  --paper-margin-left: 80px;
  --page-header-height: 40px;
  --page-header-bottom: 40px;
  --page-footer-height: 40px;
  --page-footer-top: 40px;
}

Style odd/even pages differently

/* increase right margin and decrease left margin for even pages */
.page-even {
  --paper-margin-right: 120px;
  --paper-margin-left: 40px;
}
/* swap left/right margin values for odd pages */
.page-odd {
  --paper-margin-left: 120px;
  --paper-margin-right: 40px;
}
/* show page numbers on the left for even pages */
.page-even .pagesjs-footer {
  &::before {
    content: attr(data-page-number);
  }
  &::after {
    content: " ";
  }
}

Some small features


Rebecca purple

In CSS, “rebeccapurple” refers to the color with the hex code #663399, a shade of purple, which was added to the CSS color list in 2014 as a memorial to Rebecca Alison Meyer, the daughter of CSS developer Eric Meyer, who passed away on her sixth birthday.


The litedown documentation


Thank you!

https://github.com/yihui/litedown

  1. Yes, I’m in favor of imposing a tariff on exporting to PDF (from HTML).