Quarto output styling

Overview

This extension (1) provides a Lua filter to add a CSS class to computational output that contains warnings, to distinguish between warnings and messages, and (2) provides CSS definitions for styling computational output, errors, warnings, and messages.

It lets you go from this:

R output, messages, warnings, and errors that are indistinguishable

…to this (using the default setting):

R output, messages, warnings, and errors that use different fill colors

…or this (using the minimal setting):

R output, messages, warnings, and errors that use different border colors

You can also supply your own CSS rules and do whatever you want with the different types of output, using the custom setting.

Installation

To install this extension in your current directory (or into the Quarto project that you’re currently working in), use the following terminal command:

quarto add andrewheiss/quarto-output-styling

This will install the extension in the _extensions subdirectory. If you’re using version control, you will want to check in this directory.

Language support

The extension works with R, Python, Julia, and Observable JS computational output for HTML only. See these pages for more specific examples:

Usage

⭐✨⭐
See this page for full examples!
GitHub doesn’t allow for custom CSS, so nothing will be visible there.
⭐✨⭐

Enable the filter by including it in the YAML front matter of an HTML document:

---
title: Your title
filters:
  - output-styling
---

By default, regular cell output will have a gray border and errors, warnings, and messages will be filled with different Bootstrap colors. You can change this by setting the appearance option in the YAML front matter. There are three accepted values: default, minimal, and custom.

default

The default option will include CSS styles that fill the output divs with different Bootstrap colors (see _extensions/output-styling/output-styling-default.css for the complete CSS rules).

---
title: Your title
filters:
  - output-styling
output-styling:
  appearance: default  # Filled text boxes
---

R output, messages, warnings, and errors that use different fill colors

minimal

The minimal option will include CSS styles that only add colored borders to the output (see _extensions/output-styling/output-styling-minimal.css for the complete CSS rules).

---
title: Your title
filters:
  - output-styling
output-styling:
  appearance: minimal  # Unfilled text boxes with borders
---

R output, messages, warnings, and errors that use different border colors

custom

The custom option will not include any CSS. You’re responsible for doing that yourself. I’d recommend adapting from _extensions/output-styling/output-styling-default.css or _extensions/output-styling/output-styling-minimal.css.

Include the CSS however you want: in include-in-header, in a separate CSS file, or as part of the theme with an SCSS file.

---
title: Your title
filters:
  - output-styling
output-styling:
  appearance: custom  # Don't include any CSS
format:
  html:
    include-in-header: 
      - text: |
          <style>
            .cell-output.cell-output-stdout,   /* Regular output for R and warnings for Julia */
            .cell-output.cell-output-display,  /* Regular output for Python and Julia and Observable JS */
            .cell-output.cell-output-error,    /* Errors for R and Python and Julia */
            .cell-output.cell-output-stderr {  /* Warnings and messages for R and Python */
                margin: 0 0 1em;
                padding: .4em;
                border: 1px solid var(--bs-border-color);
                border-radius: .25rem;
            }
          </style>
---

Mixing styles

You can also use default or minimal and add your own custom CSS rules. Like here we can use minimal and make warnings show up in Comic Sans (lol don’t actually do this):

---
title: Your title
filters:
  - output-styling
output-styling:
  appearance: minimal
format:
  html:
    include-in-header: 
      - text: |
          <style>
          .cell-output.cell-output-error {
              font-family: "Comic Sans MS";
          }
          </style>
---

Why this extension?

Not enough contrast between text and output in HTML

I don’t generally like how the HTML output for computational cells in R is styled by default—especially creating tutorials and lessons and other teaching materials. In the absence of stronger visual cues, I find that the output fades into the text too easily.

For example, here are the first few rows of the new-to-R-4.5 penguins data:

head(penguins)
  species    island bill_len bill_dep flipper_len body_mass    sex year
1  Adelie Torgersen     39.1     18.7         181      3750   male 2007
2  Adelie Torgersen     39.5     17.4         186      3800 female 2007
3  Adelie Torgersen     40.3     18.0         195      3250 female 2007
4  Adelie Torgersen       NA       NA          NA        NA   <NA> 2007
5  Adelie Torgersen     36.7     19.3         193      3450 female 2007
6  Adelie Torgersen     39.3     20.6         190      3650   male 2007

The only visual signal that this is computational output is the monospaced font.

This gets messier when using lots of chunks, like this:

1 + 1
[1] 2

and this:

mean(penguins$body_mass, na.rm = TRUE)
[1] 4201.754

Adding contrast with CSS

Fortunately it’s really straightforward to add custom styles to this output. Computational cells in Quarto follow a specific HTML template and get assigned specific CSS classes. Here’s the raw HTML output of 1 + 1:

<div class="cell-output cell-output-stdout">
  <pre>
    <code>[1] 2</code>
  </pre>
</div>

We can target that cell-output-stdout class with CSS like this:

.cell-output.cell-output-stdout {
    margin: 0 0 1em;
    padding: .4em;
    border: 1px solid var(--bs-border-color);
    border-radius: .25rem;
}

.cell-output.cell-output-stdout pre {
    margin-bottom: 0;
}

With that ↑ CSS, R code output cells have a small gray border now:

head(penguins)
  species    island bill_len bill_dep flipper_len body_mass    sex year
1  Adelie Torgersen     39.1     18.7         181      3750   male 2007
2  Adelie Torgersen     39.5     17.4         186      3800 female 2007
3  Adelie Torgersen     40.3     18.0         195      3250 female 2007
4  Adelie Torgersen       NA       NA          NA        NA   <NA> 2007
5  Adelie Torgersen     36.7     19.3         193      3450 female 2007
6  Adelie Torgersen     39.3     20.6         190      3650   male 2007

…and this:

1 + 1
[1] 2

…and this:

mean(penguins$body_mass, na.rm = TRUE)
[1] 4201.754

Distinguishing between errors, warnings, and messages

Quarto also emits a couple other specific CSS classes for computational output.

  • cell-output-stdout: regular output

    <div class="cell-output cell-output-stdout">
      <pre>
        <code>Regular output</code>
      </pre>
    </div>
  • cell-output-stderr: messages and warnings

    <div class="cell-output cell-output-stderr">
      <pre>
        <code>Message or warning</code>
      </pre>
    </div>
  • cell-output-error: errors

    <div class="cell-output cell-output-error">
      <pre>
        <code>An error</code>
      </pre>
    </div>

This means that messages/warnings and errors can also be targeted in specific ways:

.cell-output.cell-output-stdour {
    /* Stuff for regular output */
}

.cell-output.cell-output-stderr {
    /* Stuff for messages and warnings */
}

.cell-output.cell-output-error {
    /* Stuff for errors */
}

Identifying warnings

However, there’s one downside! With R computational output, both warnings and messages use the cell-output-stderr class, so there’s no way to style them differently.

To fix this, this extension uses a Lua filter to add a cell-output-warning class to all computational cells that start with “Warning:”. They’re both still cell-output-stderr, but warnings get an extra class.

  • Message:

    <div class="cell-output cell-output-stderr">
      <pre>
        <code>Message</code>
      </pre>
    </div>
  • Warning:

    <div class="cell-output cell-output-stderr cell-output-warning">
      <pre>
        <code>Warning</code>
      </pre>
    </div>

This allows you to adjust the styling for messages and warnings independently:

.cell-output.cell-output-stderr {
    /* Stuff for messages */
}

.cell-output.cell-output-stderr.cell-output-warning {
    /* Stuff for warnings only */
}

Do I even need to use this extension?

No! You can style most of the Quarto code output cells with regular old CSS.

The main reason this exists is for the Lua script to detect warnings and add the cell-output-warning class to those divs. If you just want to have a border around cell output (or do any sort of styling), you can just add some CSS rules yourself.