Skip to content

Dashboard tabset: second tab has inverted row/column layout orientation #14222

@thisisnic

Description

@thisisnic

I have:

  • searched the issue tracker for similar issues
  • installed the latest version of Quarto CLI
  • formatted my issue following the Bug Reports guide

Bug description

When a dashboard tabset contains tabs with nested #### Column and ##### Row layout headings, all even-positioned tabs (2nd, 4th, 6th...) render with inverted grid orientation (rows become columns, columns become rows). Odd-positioned tabs render correctly.

Steps to reproduce

---
title: "Second tab has inverted layout orientation"
format: dashboard
---

## Row {.tabset}

### Tab A

#### Column

##### Row

::: {.card title="Card 1"}
Should be side-by-side with Card 2
:::

::: {.card title="Card 2"}
Should be side-by-side with Card 1
:::

### Tab B

#### Column

##### Row

::: {.card title="Card 1"}
Should be side-by-side with Card 2 **BUT IS STACKED**
:::

::: {.card title="Card 2"}
Should be side-by-side with Card 1 **BUT IS STACKED**
:::

### Tab C

#### Column

##### Row

::: {.card title="Card 1"}
Should be side-by-side with Card 2
:::

::: {.card title="Card 2"}
Should be side-by-side with Card 1
:::

Actual behavior

  • Tab A: Cards are side-by-side (correct)
  • Tab B: Cards are stacked vertically (wrong - layout is inverted)
  • Tab C: Cards are side-by-side (correct)

Inspecting the HTML shows Tab B has grid-template-rows where Tab A and C have grid-template-columns, and vice versa for nested elements.

Expected behavior

All three tabs should render identically with cards side-by-side.

Possible root cause analysis

(I updated this as Claude had a few hypotheses before this conclusion we got to after we found a temp workaround of adding new dummy tabs which are hidden)

The bug is in the dashboard Lua filters. Two pieces of global mutable state persist across tab processing:

  1. lastLevel in main.lua (~line 7683) - tracks the last heading level seen
  2. currentOrientation in layout.lua (line 123) - tracks current layout orientation

The orientation logic in main.lua (~lines 8087-8097) rotates orientation when a new heading level is encountered:

local toOrientation = dashboard.layout.currentOrientation()
if level ~= lastLevel then
  lastLevel = level
  toOrientation = dashboard.layout.rotatedOrientation()
end

And orientContents mutates currentOrientation as a side effect.

When processing tabs with nested #### Column (level 4) and ##### Row (level 5):

  1. Tab A: #### Column is level 4 (new), rotates. ##### Row is level 5 (new), rotates again. After processing, lastLevel=5.

  2. Tab B: #### Column is level 4, but lastLevel is still 5 from Tab A. Since 4 ≠ 5, it rotates when it shouldn't. Then ##### Row is level 5, same as the new lastLevel, so it doesn't rotate when it should. Result: inverted layout.

  3. Tab C: Same pattern as Tab B, but the double-wrong rotations happen to cancel out and produce the correct layout.

This affects all even-positioned tabs (2, 4, 6...), not just the second one.

The fix should reset lastLevel before processing each tab's contents, so each tab starts fresh rather than inheriting state from the previous sibling.

Your environment

IDE: Positron 2025.02.0-149
OS: Ubuntu 24.04.4 LTS

Quarto check output

Quarto 1.9.35
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.8.3: OK
      Dart Sass version 1.87.0: OK
      Deno version 2.4.5: OK
      Typst version 0.14.2: OK
[✓] Checking Quarto installation......OK
      Version: 1.9.35
      Path: /opt/quarto/bin
[✓] Checking R installation...........OK
      Version: 4.5.2
      knitr: 1.51
      rmarkdown: 2.30

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions